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/edit.c,v 1.108 2011/06/18 04:44:51 william Exp $
19  */
20 
21 #define _INCLUDE_FROM_EDIT_C_
22 
23 #include "tgifdefs.h"
24 #include "cmdids.h"
25 
26 #include "align.e"
27 #include "arc.e"
28 #include "attr.e"
29 #include "auxtext.e"
30 #include "button.e"
31 #include "choice.e"
32 #include "cmd.e"
33 #include "color.e"
34 #include "cutpaste.e"
35 #include "cursor.e"
36 #include "dialog.e"
37 #include "drawing.e"
38 #include "dup.e"
39 #include "edit.e"
40 #include "eps.e"
41 #include "exec.e"
42 #include "file.e"
43 #include "font.e"
44 #include "grid.e"
45 #include "group.e"
46 #include "mainloop.e"
47 #include "mainmenu.e"
48 #include "mark.e"
49 #include "menu.e"
50 #include "menuinfo.e"
51 #include "miniline.e"
52 #include "move.e"
53 #include "msg.e"
54 #include "names.e"
55 #include "navigate.e"
56 #include "obj.e"
57 #include "page.e"
58 #include "pattern.e"
59 #include "pin.e"
60 #include "poly.e"
61 #include "polygon.e"
62 #include "raster.e"
63 #include "rect.e"
64 #include "ruler.e"
65 #include "select.e"
66 #include "setup.e"
67 #include "shape.e"
68 #include "special.e"
69 #include "spline.e"
70 #include "stretch.e"
71 #include "strtbl.e"
72 #include "text.e"
73 #include "util.e"
74 #include "xbitmap.e"
75 #include "xpixmap.e"
76 
77 struct SelRec *outerSelForFind=NULL;
78 struct SelRec *innerSelForFind=NULL;
79 
80 int ignoreObjectShadowInfoInFile=TRUE;
81 int objShadowXOffset=2, objShadowYOffset=2;
82 char objShadowColorStr[MAXSTRING];
83 
84 int pngExportHasTransparentColor=FALSE;
85 
86 static struct ObjRec *tmpTopObj=NULL, *tmpBotObj=NULL;
87 static struct SelRec *tmpTopSel=NULL, *tmpBotSel=NULL;
88 
89 static char *gpszSearch=NULL;
90 static int gnSearchLen=0;
91 static int gnSearchCaseSensitive=TRUE;
92 
93 static int gnFoundStartCharIndex=0, gnFoundEndCharIndex=0;
94 static StrBlockInfo *gpFoundStartStrBlock=NULL, *gpFoundEndStrBlock=NULL;
95 
96 static int convertToBezierNumSegs=50;
97 
CleanOuterInnerSelForFind()98 void CleanOuterInnerSelForFind()
99 {
100    struct SelRec *sel_ptr=NULL, *next_sel=NULL;
101 
102    if (outerSelForFind != NULL) {
103       for (sel_ptr=outerSelForFind; sel_ptr != NULL; sel_ptr=next_sel) {
104          next_sel = sel_ptr->next;
105          free(sel_ptr);
106       }
107       outerSelForFind = innerSelForFind = NULL;
108    }
109 }
110 
111 static
CopyOuterSelToOuterSelForFind()112 void CopyOuterSelToOuterSelForFind()
113 {
114    struct SelRec *sel_ptr=NULL;
115 
116    CleanOuterInnerSelForFind();
117    for (sel_ptr=outerSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
118       AddObjIntoSel(sel_ptr->obj, NULL, outerSelForFind, &outerSelForFind,
119             &innerSelForFind);
120    }
121 }
122 
CleanUpEdit()123 void CleanUpEdit()
124 {
125    CleanOuterInnerSelForFind();
126    if (gpszSearch != NULL) free(gpszSearch);
127    gpszSearch = NULL;
128 }
129 
InitEdit()130 void InitEdit()
131 {
132    char *c_ptr=NULL, spec[MAXSTRING];
133 
134    if (PRTGIF && !cmdLineOpenDisplay) return;
135 
136    convertToBezierNumSegs = 50;
137    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,
138          "ConvertToBezierSegments")) != NULL) {
139       SetBezierConvertNumSegsValue(c_ptr);
140    }
141    objShadowXOffset = objShadowYOffset = 2;
142    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME, "ObjectShadowOffsets")) !=
143          NULL) {
144       UtilStrCpyN(spec, sizeof(spec), c_ptr);
145       UtilTrimBlanks(spec);
146       if (!ParseXYSpec(spec, &objShadowXOffset, &objShadowYOffset)) {
147          fprintf(stderr, TgLoadString(STID_INVALID_XDEF_USE_ALT_STR),
148                TOOL_NAME, "ObjectShadowOffsets", spec, "2,2");
149          fprintf(stderr, "\n");
150       }
151    }
152    strcpy(objShadowColorStr, "#c0c0c0");
153    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,"ObjectShadowColor")) != NULL) {
154       int new_alloc=0;
155 
156       UtilStrCpyN(spec, sizeof(spec), c_ptr);
157       UtilTrimBlanks(spec);
158 
159       if (QuickFindColorIndex(NULL, spec, &new_alloc, FALSE) == INVALID) {
160          fprintf(stderr, TgLoadString(STID_INVALID_XDEF_COLORXPM_GET),
161                TOOL_NAME, "ObjectShadowColor", spec);
162          fprintf(stderr, "\n");
163          return;
164       }
165       UtilStrCpyN(objShadowColorStr, sizeof(objShadowColorStr), spec);
166    }
167    ignoreObjectShadowInfoInFile = TRUE;
168    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,
169          "IgnoreObjectShadowInfoInFile")) != NULL &&
170          UtilStrICmp(c_ptr, "false") == 0) {
171       ignoreObjectShadowInfoInFile = FALSE;
172    }
173    pngExportHasTransparentColor = FALSE;
174    if ((c_ptr=XGetDefault(mainDisplay,TOOL_NAME,
175          "PNGExportHasTransparentColor")) != NULL &&
176          UtilStrICmp(c_ptr, "true") == 0) {
177       pngExportHasTransparentColor = TRUE;
178    }
179    tighterStructSplines = TRUE;
180 }
181 
CanPerformImageProc()182 int CanPerformImageProc()
183 {
184    struct SelRec *sel_ptr=NULL;
185 
186    if (topSel == NULL) return FALSE;
187 
188    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
189       if (sel_ptr->obj->type != OBJ_XPM) return FALSE;
190    }
191    return TRUE;
192    /*
193     * if (topSel != NULL && topSel == botSel && topSel->obj->type == OBJ_XPM) {
194     *    return !IsLinkedJpegObj(topSel->obj);
195     * }
196     * return FALSE;
197     */
198 }
199 
200 /* --------------------- ConvertIntSpline() --------------------- */
201 
202 static
ConvertObjIntSpline(ObjPtr)203 int ConvertObjIntSpline(ObjPtr)
204    struct ObjRec *ObjPtr;
205 {
206    register struct ObjRec *obj_ptr;
207    register int i, changed=FALSE;
208 
209    switch (ObjPtr->type) {
210    case OBJ_POLY:
211       if (ObjPtr->detail.p->curved == LT_INTSPLINE) {
212          struct PolyRec *poly_ptr=ObjPtr->detail.p;
213          int new_n, n, index=0;
214          char *smooth=poly_ptr->smooth;
215          IntPoint *vs;
216 
217          changed = TRUE;
218          if (smooth != NULL) free(smooth);
219          n = poly_ptr->n;
220          new_n = (n == 2 ? n : ((n-2)<<1)+n);
221 
222          vs = (IntPoint*)malloc((new_n+1)*sizeof(IntPoint));
223          if (vs == NULL) FailAllocMessage();
224          smooth = (char*)malloc((new_n+1)*sizeof(char));
225          if (smooth == NULL) FailAllocMessage();
226 
227          smooth[0] = smooth[new_n-1] = FALSE;
228          vs[0] = poly_ptr->vlist[0];
229          vs[new_n-1] = poly_ptr->vlist[n-1];
230          for (i=1; i < n-1; i++) {
231             index++;
232             smooth[index] = TRUE;
233             vs[index] = poly_ptr->intvlist[(i<<1)-1];
234             index++;
235             smooth[index] = FALSE;
236             vs[index] = poly_ptr->vlist[i];
237             index++;
238             smooth[index] = TRUE;
239             vs[index] = poly_ptr->intvlist[i<<1];
240          }
241          poly_ptr->curved = LT_SPLINE;
242          free(poly_ptr->vlist);
243          free(poly_ptr->intvlist);
244          poly_ptr->vlist = vs;
245          poly_ptr->n = new_n;
246          poly_ptr->smooth = smooth;
247          poly_ptr->intvlist = NULL;
248          poly_ptr->intn = 0;
249          AdjObjSplineVs(ObjPtr);
250          UpdPolyBBox(ObjPtr, poly_ptr->n, poly_ptr->vlist);
251       }
252       break;
253    case OBJ_POLYGON:
254       if (ObjPtr->detail.g->curved == LT_INTSPLINE) {
255          struct PolygonRec *polygon_ptr=ObjPtr->detail.g;
256          int new_n, n, index=0;
257          char *smooth=polygon_ptr->smooth;
258          IntPoint *vs;
259 
260          changed = TRUE;
261          if (smooth != NULL) free(smooth);
262          n = polygon_ptr->n;
263          new_n = ((n-1)<<1)+n;
264 
265          vs = (IntPoint*)malloc((new_n+1)*sizeof(IntPoint));
266          if (vs == NULL) FailAllocMessage();
267          smooth = (char*)malloc((new_n+1)*sizeof(char));
268          if (smooth == NULL) FailAllocMessage();
269 
270          smooth[0] = FALSE;
271          vs[0] = polygon_ptr->vlist[0];
272          for (i=0; i < n-1; i++) {
273             index++;
274             smooth[index] = TRUE;
275             vs[index] = polygon_ptr->intvlist[i<<1];
276             index++;
277             smooth[index] = TRUE;
278             vs[index] = polygon_ptr->intvlist[(i<<1)+1];
279             index++;
280             smooth[index] = FALSE;
281             vs[index] = polygon_ptr->vlist[i+1];
282          }
283          polygon_ptr->curved = LT_SPLINE;
284          free(polygon_ptr->vlist);
285          free(polygon_ptr->intvlist);
286          polygon_ptr->vlist = vs;
287          polygon_ptr->n = new_n;
288          polygon_ptr->smooth = smooth;
289          polygon_ptr->intvlist = NULL;
290          polygon_ptr->intn = 0;
291          AdjObjSplineVs(ObjPtr);
292          UpdPolyBBox(ObjPtr, polygon_ptr->n, polygon_ptr->vlist);
293       }
294       break;
295    case OBJ_GROUP:
296    case OBJ_SYM:
297       for (obj_ptr=ObjPtr->detail.r->last; obj_ptr!=NULL;
298             obj_ptr=obj_ptr->prev) {
299          if (ConvertObjIntSpline(obj_ptr)) {
300             changed = TRUE;
301          }
302       }
303       break;
304    }
305    if (changed) AdjObjBBox(ObjPtr);
306    return changed;
307 }
308 
ConvertIntSpline()309 void ConvertIntSpline()
310 {
311    struct SelRec *sel_ptr=NULL;
312    int changed=FALSE;
313 
314    if (topSel == NULL) {
315       MsgBox(TgLoadString(STID_NO_INTSPLINE_SELECTED), TOOL_NAME, INFO_MB);
316       return;
317    }
318    HighLightReverse();
319    StartCompositeCmd();
320    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
321       PrepareToReplaceAnObj(sel_ptr->obj);
322       if (ConvertObjIntSpline(sel_ptr->obj)) {
323          changed = TRUE;
324          RecordReplaceAnObj(sel_ptr->obj);
325       } else {
326          AbortPrepareCmd(CMD_REPLACE);
327       }
328    }
329    EndCompositeCmd();
330 
331    if (changed) {
332       SetFileModified(TRUE);
333       RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
334             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
335       UpdSelBBox();
336       justDupped = FALSE;
337       Msg(TgLoadString(STID_INTSPLINE_CONVERTED_TO_SPLINE));
338    } else {
339       HighLightForward();
340       MsgBox(TgLoadString(STID_NO_INTSPLINE_SELECTED), TOOL_NAME, INFO_MB);
341       return;
342    }
343    HighLightForward();
344 }
345 
346 /* --------------------- ConvertToBezier() --------------------- */
347 
348 static
SetBezierPoints(n,vs,new_n,bezier_vs)349 void SetBezierPoints(n, vs, new_n, bezier_vs)
350    int n, new_n;
351    IntPoint *vs, *bezier_vs;
352 {
353    int i=0, *n_choose_i=(int*)malloc((n+1)*sizeof(int)), index=0;
354    double t=(double)0, dt=(((double)1.0)/((double)(new_n-1)));
355    double final_t=((double)1)+(dt/((double)2));
356 
357    if (n_choose_i == NULL) FailAllocMessage();
358    memset(n_choose_i, 0, (n+1)*sizeof(int));
359 
360    n--;
361    n_choose_i[0] = 1;
362    for (i=1; i <= n; i++) {
363       n_choose_i[i] = n_choose_i[i-1] * (n-i+1) / i;
364    }
365    for (t=(double)0; t <= final_t; t+=dt, index++) {
366       double s=((double)1)-t, xt=(double)0, yt=(double)0;
367 
368       for (i=0; i <= n; i++) {
369          /* Bi = B_{i,n}(t) */
370          double Bi=((double)(n_choose_i[i]));
371          int j=0;
372 
373          for (j=1; j <= (n-i); j++) Bi *= s;
374          for (j=1; j <= i; j++) Bi *= t;
375          xt += Bi * ((double)(vs[i].x));
376          yt += Bi * ((double)(vs[i].y));
377       }
378       bezier_vs[index].x = round(xt);
379       bezier_vs[index].y = round(yt);
380    }
381    free(n_choose_i);
382 }
383 
384 static
ConvertObjToBezier(ObjPtr)385 int ConvertObjToBezier(ObjPtr)
386    struct ObjRec *ObjPtr;
387 {
388    struct ObjRec *obj_ptr=NULL;
389    int i=0, changed=FALSE;
390 
391    switch (ObjPtr->type) {
392    case OBJ_POLY:
393       if (ObjPtr->detail.p->curved == LT_STRAIGHT ||
394             ObjPtr->detail.p->curved == LT_SPLINE) {
395          struct PolyRec *poly_ptr=ObjPtr->detail.p;
396          int n=poly_ptr->n;
397          char *smooth=poly_ptr->smooth;
398 
399          if (n == 3) {
400             if (!smooth[1]) {
401                smooth[1] = TRUE;
402                changed = TRUE;
403                poly_ptr->curved = LT_SPLINE;
404                AdjObjSplineVs(ObjPtr);
405                UpdPolyBBox(ObjPtr, poly_ptr->n, poly_ptr->vlist);
406             }
407          } else if (n > 3) {
408             int new_n=convertToBezierNumSegs+1;
409             IntPoint *vs=NULL;
410 
411             changed = TRUE;
412             if (smooth != NULL) free(smooth);
413 
414             vs = (IntPoint*)malloc((new_n+1)*sizeof(IntPoint));
415             if (vs == NULL) FailAllocMessage();
416             memset(vs, 0, (new_n+1)*sizeof(IntPoint));
417             smooth = (char*)malloc((new_n+1)*sizeof(char));
418             if (smooth == NULL) FailAllocMessage();
419             memset(smooth, 0, (new_n+1)*sizeof(char));
420 
421             SetBezierPoints(n, poly_ptr->vlist, new_n, vs);
422 
423             smooth[0] = smooth[new_n-1] = FALSE;
424             for (i=1; i < new_n-1; i++) smooth[i] = TRUE;
425             poly_ptr->curved = LT_SPLINE;
426             free(poly_ptr->vlist);
427             poly_ptr->vlist = vs;
428             poly_ptr->n = new_n;
429             poly_ptr->smooth = smooth;
430             AdjObjSplineVs(ObjPtr);
431             UpdPolyBBox(ObjPtr, poly_ptr->n, poly_ptr->vlist);
432          }
433       }
434       break;
435    case OBJ_GROUP:
436    case OBJ_SYM:
437       for (obj_ptr=ObjPtr->detail.r->last; obj_ptr!=NULL;
438             obj_ptr=obj_ptr->prev) {
439          if (ConvertObjToBezier(obj_ptr)) {
440             changed = TRUE;
441          }
442       }
443       break;
444    }
445    if (changed) AdjObjBBox(ObjPtr);
446    return changed;
447 }
448 
ConvertToBezier()449 void ConvertToBezier()
450 {
451    struct SelRec *sel_ptr=NULL;
452    int changed=FALSE;
453 
454    if (topSel == NULL) {
455       MsgBox(TgLoadString(STID_NO_SPLINE_SELECTED), TOOL_NAME, INFO_MB);
456       return;
457    }
458    HighLightReverse();
459    StartCompositeCmd();
460    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
461       PrepareToReplaceAnObj(sel_ptr->obj);
462       if (ConvertObjToBezier(sel_ptr->obj)) {
463          changed = TRUE;
464          RecordReplaceAnObj(sel_ptr->obj);
465       } else {
466          AbortPrepareCmd(CMD_REPLACE);
467       }
468    }
469    EndCompositeCmd();
470 
471    if (changed) {
472       SetFileModified(TRUE);
473       RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
474             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
475       UpdSelBBox();
476       justDupped = FALSE;
477       Msg(TgLoadString(STID_SPLINE_CONVERTED_TO_BEZIER));
478    } else {
479       HighLightForward();
480       MsgBox(TgLoadString(STID_NO_SPLINE_SELECTED), TOOL_NAME, INFO_MB);
481       return;
482    }
483    HighLightForward();
484 }
485 
SetBezierConvertNumSegsValue(spec)486 int SetBezierConvertNumSegsValue(spec)
487    char *spec;
488 {
489    int ival=0;
490 
491    if (sscanf(spec, "%d", &ival) != 1) {
492       sprintf(gszMsgBox, TgLoadCachedString(CSTID_MALFORMED_INPUT_STR), spec);
493       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
494       return FALSE;
495    } else if (ival < 2) {
496       sprintf(gszMsgBox, TgLoadString(STID_ENT_VAL_RANGE_ENTER_GE_INT),
497             spec, 3);
498       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
499       return FALSE;
500    }
501    convertToBezierNumSegs = ival;
502 
503    return TRUE;
504 }
505 
SetBezierConvertNumSegs(psz_num_segs)506 void SetBezierConvertNumSegs(psz_num_segs)
507    char *psz_num_segs;
508 {
509    char spec[MAXSTRING+1];
510 
511    *spec = '\0';
512    if (psz_num_segs != NULL && strcmp(psz_num_segs, "-1") != 0) {
513       UtilStrCpyN(spec, sizeof(spec), psz_num_segs);
514    } else {
515       sprintf(gszMsgBox, TgLoadString(STID_ENTER_BEZIER_NUM_SEGS_CUR_IS),
516             convertToBezierNumSegs);
517       if (Dialog(gszMsgBox, NULL, spec) == INVALID) return;
518    }
519    UtilTrimBlanks(spec);
520    if (*spec == '\0') return;
521 
522    if (SetBezierConvertNumSegsValue(spec)) {
523       sprintf(gszMsgBox, TgLoadString(STID_BEZIER_NUM_SEGS_SET_TO_INT),
524             convertToBezierNumSegs);
525       Msg(gszMsgBox);
526    }
527 }
528 
529 static
SelectModeToggleSmoothHinge()530 void SelectModeToggleSmoothHinge()
531 {
532    register struct ObjRec *obj_ptr;
533    struct PolyRec *poly_ptr=NULL;
534    struct PolygonRec *polygon_ptr=NULL;
535    int index, n, pt_toggled=FALSE, toggling=TRUE;
536    int root_x, root_y, old_x, old_y, curved=(-1);
537    unsigned int status;
538    Window root_win, child_win;
539    XEvent input, ev;
540    char *smooth=NULL;
541 
542    if (!(topSel != NULL && topSel == botSel &&
543          (topSel->obj->type == OBJ_POLY || topSel->obj->type == OBJ_POLYGON))) {
544       MsgBox(TgLoadString(STID_SELECT_ONLY_ONE_POLY_POLYGON),
545             TOOL_NAME, INFO_MB);
546       return;
547    }
548    obj_ptr = topSel->obj;
549    switch (obj_ptr->type) {
550    case OBJ_POLY:
551       poly_ptr = obj_ptr->detail.p;
552       smooth = poly_ptr->smooth;
553       curved = poly_ptr->curved;
554       break;
555    case OBJ_POLYGON:
556       polygon_ptr = obj_ptr->detail.g;
557       smooth = polygon_ptr->smooth;
558       curved = polygon_ptr->curved;
559       break;
560    }
561    if (curved == LT_INTSPLINE) {
562       MsgBox(TgLoadString(STID_CANNOT_TOGGLE_FOR_INTSPLINE), TOOL_NAME,
563             INFO_MB);
564       return;
565    } else if (curved == LT_STRUCT_SPLINE) {
566       MsgBox(TgLoadString(STID_CANNOT_TOGGLE_FOR_STRUCT_SPLN), TOOL_NAME,
567             INFO_MB);
568       return;
569    }
570    if (smooth == NULL) {
571       FatalUnexpectedError(TgLoadString(STID_BAD_POLY_IN_TOGGLE_SMOOTH), NULL);
572       return;
573    }
574    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
575    SaveStatusStrings();
576    SetMouseStatus(TgLoadCachedString(CSTID_TOGGLE_SMOOTH_HINGE),
577          TgLoadCachedString(CSTID_FINISH), TgLoadCachedString(CSTID_FINISH));
578    TwoLineMsg(TgLoadString(STID_CLICK_LEFT_BUTTON_TO_TOGGLE),
579          TgLoadString(STID_CLICK_OTHER_BUTTON_TO_QUIT));
580 
581    if (!debugNoPointerGrab) {
582       XGrabPointer(mainDisplay, drawWindow, False,
583             PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
584             GrabModeAsync, GrabModeAsync, None, defaultCursor, CurrentTime);
585    }
586    XQueryPointer(mainDisplay, drawWindow, &root_win, &child_win,
587          &root_x, &root_y, &old_x, &old_y, &status);
588    XSetFont(mainDisplay, revDefaultGC, defaultFontPtr->fid);
589    /* do not translate -- program constants */
590    XDrawString(mainDisplay, drawWindow, revDefaultGC,
591          old_x+4, old_y+defaultFontAsc, "S/H", 3);
592    MarkRulers(old_x, old_y);
593 
594    while (toggling) {
595       XNextEvent(mainDisplay, &input);
596 
597       if (input.type == Expose || input.type == VisibilityNotify) {
598          ExposeEventHandler(&input, TRUE);
599       } else if (input.type == ButtonPress) {
600          if (input.xbutton.button == Button1) {
601             if ((obj_ptr->type == OBJ_POLY &&
602                   PtInPolyMark(obj_ptr, input.xbutton.x, input.xbutton.y,
603                   poly_ptr->n, poly_ptr->vlist, &index) &&
604                   index != 0 && index != poly_ptr->n-1) ||
605                   (obj_ptr->type == OBJ_POLYGON &&
606                   PtInPolyMark(obj_ptr, input.xbutton.x, input.xbutton.y,
607                   polygon_ptr->n-1, polygon_ptr->vlist, &index))) {
608                int sel_ltx=selLtX, sel_lty=selLtY;
609                int sel_rbx=selRbX, sel_rby=selRbY;
610 
611                pt_toggled = TRUE;
612                HighLightReverse();
613                switch (obj_ptr->type) {
614                case OBJ_POLY:
615                   n = poly_ptr->n;
616                   if (smooth[index]) {
617                      smooth[index] = FALSE;
618                   } else {
619                      smooth[index] = TRUE;
620                   }
621                   AdjObjSplineVs(obj_ptr);
622                   UpdPolyBBox(obj_ptr, poly_ptr->n, poly_ptr->vlist);
623                   break;
624                case OBJ_POLYGON:
625                   n = polygon_ptr->n;
626                   if (smooth[index]) {
627                      smooth[index] = FALSE;
628                   } else {
629                      smooth[index] = TRUE;
630                   }
631                   if (index == 0) smooth[n-1] = smooth[0];
632                   AdjObjSplineVs(obj_ptr);
633                   UpdPolyBBox(obj_ptr, polygon_ptr->n, polygon_ptr->vlist);
634                   break;
635                }
636                AdjObjBBox(obj_ptr);
637 
638                XDrawString(mainDisplay, drawWindow, revDefaultGC, old_x+4,
639                      old_y+defaultFontAsc, "S/H", 3);
640                old_x = input.xbutton.x;
641                old_y = input.xbutton.y;
642                UpdSelBBox();
643                RedrawAreas(botObj,
644                      sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
645                      sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
646                      selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
647                      selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
648                HighLightForward();
649                if (obj_ptr != NULL) {
650                   XDrawString(mainDisplay, drawWindow, revDefaultGC,
651                         old_x+4, old_y+defaultFontAsc, "S/H", 3);
652                }
653                SetFileModified(TRUE);
654                justDupped = FALSE;
655             }
656          } else {
657             XUngrabPointer(mainDisplay, CurrentTime);
658             Msg("");
659             toggling = FALSE;
660             XDrawString(mainDisplay, drawWindow, revDefaultGC,
661                   old_x+4, old_y+defaultFontAsc, "S/H", 3);
662          }
663       } else if (input.type == MotionNotify) {
664          XDrawString(mainDisplay, drawWindow, revDefaultGC,
665                old_x+4, old_y+defaultFontAsc, "S/H", 3);
666          old_x = input.xmotion.x;
667          old_y = input.xmotion.y;
668          XDrawString(mainDisplay, drawWindow, revDefaultGC,
669                old_x+4, old_y+defaultFontAsc, "S/H", 3);
670          MarkRulers(old_x, old_y);
671          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
672       }
673    }
674    RestoreStatusStrings();
675    if (pt_toggled) {
676       RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
677    } else {
678       AbortPrepareCmd(CMD_REPLACE);
679    }
680 }
681 
ToggleSmoothHinge()682 void ToggleSmoothHinge()
683 {
684    register int i;
685    int changed=FALSE, ltx=selLtX, lty=selLtY, rbx=selRbX, rby=selRbY;
686    struct VSelRec *vsel_ptr;
687    struct ObjRec *obj_ptr;
688 
689    if (curChoice == NOTHING) {
690       SelectModeToggleSmoothHinge();
691       return;
692    }
693    if (curChoice != VERTEXMODE) {
694       MsgBox(TgLoadString(STID_ONLY_TOGGLE_SMOOTH_IN_MODES), TOOL_NAME,
695             INFO_MB);
696       return;
697    }
698    if (topVSel == NULL) {
699       if (topSel == NULL) return;
700       HighLightReverse();
701       JustRemoveAllVSel();
702       HighLightForward();
703       SelectModeToggleSmoothHinge();
704       return;
705    }
706    for (vsel_ptr=topVSel; vsel_ptr != NULL; vsel_ptr=vsel_ptr->next) {
707       if ((vsel_ptr->obj->type == OBJ_POLY &&
708             vsel_ptr->obj->detail.p->curved == LT_INTSPLINE) ||
709             (vsel_ptr->obj->type == OBJ_POLYGON &&
710             vsel_ptr->obj->detail.g->curved == LT_INTSPLINE)) {
711          MsgBox(TgLoadString(STID_CANNOT_TOGGLE_FOR_INTSPLINE), TOOL_NAME,
712                INFO_MB);
713          return;
714       } else if ((vsel_ptr->obj->type == OBJ_POLY &&
715             vsel_ptr->obj->detail.p->curved == LT_STRUCT_SPLINE) ||
716             (vsel_ptr->obj->type == OBJ_POLYGON &&
717             vsel_ptr->obj->detail.g->curved == LT_STRUCT_SPLINE)) {
718          MsgBox(TgLoadString(STID_CANNOT_TOGGLE_FOR_STRUCT_SPLN), TOOL_NAME,
719                INFO_MB);
720          return;
721       }
722    }
723    HighLightReverse();
724    StartCompositeCmd();
725    for (vsel_ptr=botVSel; vsel_ptr != NULL; vsel_ptr=vsel_ptr->prev) {
726       int obj_changed=FALSE, n;
727       IntPoint *v;
728       char *smooth=NULL;
729 
730       obj_ptr = vsel_ptr->obj;
731       switch (obj_ptr->type) {
732       case OBJ_POLY:
733          v = obj_ptr->detail.p->vlist;
734          n = obj_ptr->detail.p->n;
735          smooth = obj_ptr->detail.p->smooth;
736          for (i=0; i < vsel_ptr->n; i++) {
737             if (vsel_ptr->v_index[i] != 0 ||
738                   vsel_ptr->v_index[i] != n-1) {
739                break;
740             }
741          }
742          if (i == vsel_ptr->n) continue;
743          break;
744       case OBJ_POLYGON:
745          v = obj_ptr->detail.g->vlist;
746          n = obj_ptr->detail.g->n;
747          smooth = obj_ptr->detail.g->smooth;
748          break;
749       default: continue;
750       }
751       PrepareToReplaceAnObj(obj_ptr);
752       for (i=0; i < vsel_ptr->n; i++) {
753          int index=vsel_ptr->v_index[i];
754 
755          if (!(obj_ptr->type == OBJ_POLY && (index == 0 || index == n-1))) {
756             smooth[index] = (smooth[index] ? FALSE : TRUE);
757             obj_changed = TRUE;
758          }
759       }
760       AdjObjSplineVs(obj_ptr);
761       UpdPolyBBox(obj_ptr, n, v);
762       if (obj_changed) {
763          RecordReplaceAnObj(obj_ptr);
764          changed = TRUE;
765       } else {
766          AbortPrepareCmd(CMD_REPLACE);
767       }
768    }
769    EndCompositeCmd();
770    if (changed) {
771       Msg(TgLoadString(STID_SMOOTHNESS_TOGGLED));
772       UpdSelBBox();
773       RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
774          rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
775          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
776          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
777       SetFileModified(TRUE);
778       justDupped = FALSE;
779    }
780    HighLightForward();
781 }
782 
783 static
BreakATextObj(ObjPtr,how,poli)784 void BreakATextObj(ObjPtr, how, poli)
785    struct ObjRec *ObjPtr;
786    int how;
787    ObjListInfo *poli;
788 {
789    struct ObjRec *prototype=NULL;
790    struct TextRec *text_ptr=NULL;
791    struct SelRec *sel_ptr=NULL;
792    MiniLinesInfo *minilines=NULL;
793    int tx_to_move=0, ty_to_move=0;
794    char proto_buf[3];
795 
796    prototype = DupObj(ObjPtr);
797    text_ptr = prototype->detail.t;
798    InvalidateTextCache(text_ptr);
799    minilines = (&text_ptr->minilines);
800    FreeMiniLines(minilines, FALSE);
801    /* do not translate -- program constants */
802    strcpy(proto_buf, "XX");
803    CreateMiniLineFromString(proto_buf, &minilines->first, &minilines->last);
804    minilines->first->owner_minilines = minilines;
805    text_ptr->lines = 1;
806 
807    text_ptr = ObjPtr->detail.t;
808    minilines = (&text_ptr->minilines);
809 
810    PushCurFont();
811    ObjFontInfoToCurFontInfo(text_ptr);
812    SetCanvasFont();
813 
814    if (ObjPtr->ctm != NULL) {
815       TransformPointThroughCTM(0, 0, prototype->ctm, &tx_to_move, &ty_to_move);
816    }
817    BreakMiniLines(minilines, how, ObjPtr->x, text_ptr->baseline_y,
818          prototype, tx_to_move, ty_to_move, poli);
819 
820    PopCurFont();
821    FreeTextObj(prototype);
822    for (sel_ptr=poli->top_sel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
823       struct ObjRec *obj_ptr=sel_ptr->obj;
824 
825       UpdTextBBox(obj_ptr);
826       AdjObjSplineVs(obj_ptr);
827       AdjObjBBox(obj_ptr);
828    }
829 }
830 
831 static
GetBreakSpec()832 int GetBreakSpec()
833    /* return TRUE if breaking into words */
834 {
835    int how=INVALID, can_do_lines=FALSE, can_do_words=FALSE;
836    char spec[MAXSTRING+1];
837    struct SelRec *sel_ptr=NULL;
838 
839    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
840       struct ObjRec *obj_ptr=sel_ptr->obj;
841 
842       if (obj_ptr->type == OBJ_TEXT && !obj_ptr->locked) {
843          struct TextRec *text_ptr=obj_ptr->detail.t;
844          MiniLinesInfo *minilines=(&text_ptr->minilines);
845 
846          if (text_ptr->read_only) continue;
847          if (CanBreakMiniLinesIntoWords(&text_ptr->minilines)) {
848             can_do_words = TRUE;
849          }
850          if (minilines->first->next != NULL) can_do_lines = TRUE;
851       }
852       if (can_do_lines && can_do_words) break;
853    }
854    if (can_do_lines) {
855       if (can_do_words) {
856          strcpy(gszMsgBox, TgLoadString(STID_BREAK_TEXT_CWL));
857       } else {
858          strcpy(gszMsgBox, TgLoadString(STID_BREAK_TEXT_CL));
859       }
860    } else if (can_do_words) {
861       strcpy(gszMsgBox, TgLoadString(STID_BREAK_TEXT_CW));
862    } else {
863       return BREAK_CHAR;
864    }
865    *spec = '\0';
866    if (Dialog(gszMsgBox, NULL, spec) == INVALID) {
867       return INVALID;
868    }
869    UtilTrimBlanks(spec);
870    if (*spec == '\0') strcpy(spec, "c");
871    strcpy(gszMsgBox, spec);
872    gszMsgBox[1] = '\0';
873    UtilStrLower(gszMsgBox);
874    if (can_do_lines) {
875       if (can_do_words) {
876          switch (*gszMsgBox) {
877          case 'c': how = BREAK_CHAR; break;
878          case 'w': how = BREAK_WORD; break;
879          case 'l': how = BREAK_LINE; break;
880          }
881       } else {
882          switch (*gszMsgBox) {
883          case 'c': how = BREAK_CHAR; break;
884          case 'l': how = BREAK_LINE; break;
885          }
886       }
887    } else if (can_do_words) {
888       switch (*gszMsgBox) {
889       case 'c': how = BREAK_CHAR; break;
890       case 'w': how = BREAK_WORD; break;
891       }
892    }
893    if (how == INVALID) {
894       sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC_ON_BREAK_TEXT), spec);
895       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
896    }
897    return how;
898 }
899 
900 static
GetBreakSpecFromCh(spec)901 int GetBreakSpecFromCh(spec)
902    char *spec;
903 {
904    char buf[2];
905    int how=INVALID;
906 
907    buf[0] = *spec;
908    buf[1] = '\0';
909    UtilStrLower(buf);
910 
911    switch (*buf) {
912    case 'c': how = BREAK_CHAR; break;
913    case 'w': how = BREAK_WORD; break;
914    case 'l': how = BREAK_LINE; break;
915    }
916    if (how == INVALID) {
917       sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC_ON_BREAK_TEXT), spec);
918       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
919    }
920    return how;
921 }
922 
DoBreakUpText(spec,forced_justify)923 void DoBreakUpText(spec, forced_justify)
924    char *spec;
925    int forced_justify;
926 {
927    struct ObjRec *obj_ptr=NULL;
928    struct SelRec *sel_ptr=NULL, *next_sel=NULL;
929    int sel_ltx=selLtX, sel_lty=selLtY, sel_rbx=selRbX, sel_rby=selRbY;
930    int how=INVALID, changed=FALSE, read_only_text_exists=FALSE;
931 
932    if (topSel == NULL) {
933       MsgBox(TgLoadString(STID_NO_TEXT_OBJ_TO_BREAK_UP), TOOL_NAME, INFO_MB);
934       return;
935    }
936    if (spec == NULL || *spec == '\0' || strcmp(spec, "-1") == 0) {
937       if ((how=GetBreakSpec()) == INVALID) return;
938    } else {
939       if ((how=GetBreakSpecFromCh(spec)) == INVALID) return;
940    }
941    HighLightReverse();
942 
943    StartCompositeCmd();
944    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=next_sel) {
945       next_sel = sel_ptr->next;
946       obj_ptr = sel_ptr->obj;
947       if (obj_ptr->type == OBJ_TEXT && !obj_ptr->locked) {
948          ObjListInfo oli;
949 
950          if (obj_ptr->detail.t->read_only) {
951             read_only_text_exists = TRUE;
952             continue;
953          }
954          memset(&oli, 0, sizeof(ObjListInfo));
955 
956          changed = TRUE;
957 
958          PrepareToReplaceAnObj(obj_ptr);
959          BreakATextObj(obj_ptr, how, &oli);
960 
961          oli.top_sel->obj->prev = obj_ptr->prev;
962          if (obj_ptr->prev == NULL) {
963             curPage->top = topObj = oli.top_sel->obj;
964          } else {
965             obj_ptr->prev->next = oli.top_sel->obj;
966          }
967          oli.bot_sel->obj->next = obj_ptr->next;
968          if (obj_ptr->next == NULL) {
969             curPage->bot = botObj = oli.bot_sel->obj;
970          } else {
971             obj_ptr->next->prev = oli.bot_sel->obj;
972          }
973          RecordCmd(CMD_ONE_TO_MANY, NULL, oli.top_sel, oli.bot_sel, oli.count);
974 
975          oli.top_sel->prev = sel_ptr->prev;
976          if (sel_ptr->prev == NULL) {
977             topSel = oli.top_sel;
978          } else {
979             sel_ptr->prev->next = oli.top_sel;
980          }
981          oli.bot_sel->next = sel_ptr->next;
982          if (sel_ptr->next == NULL) {
983             botSel = oli.bot_sel;
984          } else {
985             sel_ptr->next->prev = oli.bot_sel;
986          }
987          FreeObj(obj_ptr);
988          free(sel_ptr);
989       }
990    }
991    EndCompositeCmd();
992    if (read_only_text_exists) {
993       MsgBox(TgLoadString(STID_SOME_TEXT_NOT_BROKEN_UP_SIZE), TOOL_NAME,
994             INFO_MB);
995    }
996    if (changed) {
997       UpdSelBBox();
998       RedrawAreas(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
999             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1),
1000             sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
1001             sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1));
1002       SetFileModified(TRUE);
1003       justDupped = FALSE;
1004       Msg(TgLoadString(STID_TEXT_BROKEN_INTO_CHARS));
1005    } else if (!read_only_text_exists) {
1006       MsgBox(TgLoadString(STID_NO_TEXT_OBJ_TO_BREAK_UP), TOOL_NAME, INFO_MB);
1007    }
1008    HighLightForward();
1009 }
1010 
BreakUpText(spec)1011 void BreakUpText(spec)
1012    char *spec;
1013 {
1014    DoBreakUpText(spec, '\0');
1015 }
1016 
1017 static
DoSetTextFillPatternColor(ObjPtr)1018 int DoSetTextFillPatternColor(ObjPtr)
1019    struct ObjRec *ObjPtr;
1020 {
1021    struct ObjRec *obj_ptr=NULL;
1022    int changed=FALSE;
1023 
1024    switch (ObjPtr->type) {
1025    case OBJ_TEXT:
1026       if (ObjPtr->color != colorIndex) {
1027          ObjPtr->color = colorIndex;
1028          if (mainDisplay != NULL) {
1029             UtilStrCpyN(ObjPtr->color_str, sizeof(ObjPtr->color_str),
1030                   colorMenuItems[colorIndex]);
1031          }
1032          changed = TRUE;
1033       }
1034       break;
1035 
1036    case OBJ_SYM:
1037    case OBJ_GROUP:
1038    case OBJ_ICON:
1039       for (obj_ptr=ObjPtr->detail.r->last; obj_ptr != NULL;
1040             obj_ptr=obj_ptr->prev) {
1041          if (DoSetTextFillPatternColor(obj_ptr)) {
1042             changed = TRUE;
1043          }
1044       }
1045       break;
1046    case OBJ_PIN:
1047       obj_ptr = GetPinObj(ObjPtr);
1048       if (DoSetTextFillPatternColor(obj_ptr)) {
1049          changed = TRUE;
1050       }
1051       break;
1052    }
1053    return changed;
1054 }
1055 
SetTextFillPatternColor()1056 void SetTextFillPatternColor()
1057 {
1058    struct SelRec *sel_ptr=NULL;
1059    int changed=FALSE;
1060 
1061    if (curChoice == DRAWTEXT) {
1062       if (curTextObj->color != colorIndex) {
1063          curTextObj->color = colorIndex;
1064          if (mainDisplay != NULL) {
1065             UtilStrCpyN(curTextObj->color_str, sizeof(curTextObj->color_str),
1066                   colorMenuItems[colorIndex]);
1067          }
1068          SetFileModified(TRUE);
1069          sprintf(gszMsgBox, TgLoadString(STID_CUR_TEXT_BG_SET_TO_NAMED),
1070                colorMenuItems[colorIndex]);
1071          Msg(gszMsgBox);
1072       }
1073       return;
1074    }
1075    if (topSel == NULL) {
1076       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
1077       return;
1078    }
1079    HighLightReverse();
1080    StartCompositeCmd();
1081    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
1082       struct ObjRec *obj_ptr=sel_ptr->obj;
1083 
1084       PrepareToReplaceAnObj(obj_ptr);
1085       if (DoSetTextFillPatternColor(obj_ptr)) {
1086          changed = TRUE;
1087          RecordReplaceAnObj(obj_ptr);
1088       } else {
1089          AbortPrepareCmd(CMD_REPLACE);
1090       }
1091    }
1092    EndCompositeCmd();
1093    if (changed) {
1094       RedrawAnArea(botObj,
1095             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
1096             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
1097       HighLightForward();
1098       SetFileModified(TRUE);
1099       justDupped = FALSE;
1100       sprintf(gszMsgBox, TgLoadString(STID_SOME_TEXT_BG_SET_TO_NAMED),
1101             colorMenuItems[colorIndex]);
1102       Msg(gszMsgBox);
1103    }
1104 }
1105 
MakeRegularPolygon()1106 void MakeRegularPolygon()
1107 {
1108    register int i;
1109    int vertex_at_right, xc, yc;
1110    int sel_ltx, sel_lty, sel_rbx, sel_rby, sides, radius;
1111    int ltx, lty, rbx, rby;
1112    double inc, angle, r;
1113    struct ObjRec *obj_ptr, *new_obj_ptr;
1114    struct PolygonRec *polygon_ptr;
1115 
1116    if (topSel == NULL) {
1117       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
1118       return;
1119    } else if (topSel != botSel || topSel->obj->type != OBJ_POLYGON) {
1120       MsgBox(TgLoadString(STID_SEL_ONE_POLYGON_TO_MAKE_REG), TOOL_NAME,
1121             INFO_MB);
1122       return;
1123    } else if (topSel->obj->locked) {
1124       MsgBox(TgLoadString(STID_POLYGON_LOCKED), TOOL_NAME, INFO_MB);
1125       return;
1126    }
1127    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
1128 
1129    sel_ltx = selLtX; sel_lty = selLtY;
1130    sel_rbx = selRbX; sel_rby = selRbY;
1131 
1132    obj_ptr = topSel->obj;
1133 
1134    radius = (min(obj_ptr->obbox.rbx-obj_ptr->obbox.ltx,
1135          obj_ptr->obbox.rby-obj_ptr->obbox.lty))>>1;
1136    if (radius < 1) {
1137       MsgBox(TgLoadString(STID_POLYGON_TOO_SMALL_FOR_REGULAR), TOOL_NAME,
1138             INFO_MB);
1139       return;
1140    }
1141    sprintf(gszMsgBox, TgLoadString(STID_VERTEX_AT_3_OCLOCK_YNC));
1142    if ((vertex_at_right=MsgBox(gszMsgBox, TOOL_NAME, YNC_MB)) ==
1143          MB_ID_CANCEL) {
1144       return;
1145    }
1146    tmpTopObj = tmpBotObj = NULL;
1147    tmpTopSel = tmpBotSel = NULL;
1148 
1149    HighLightReverse();
1150 
1151    new_obj_ptr = DupObj(obj_ptr);
1152 
1153    UnlinkObj(obj_ptr);
1154 
1155    polygon_ptr = new_obj_ptr->detail.g;
1156    sides = polygon_ptr->n-1;
1157    inc = 2*M_PI/sides;
1158    angle = (vertex_at_right==MB_ID_YES) ? 0 : inc/2;
1159 
1160    xc = obj_ptr->obbox.ltx+radius;
1161    yc = obj_ptr->obbox.lty+radius;
1162 
1163    if ((sides%4)==0 && vertex_at_right==MB_ID_NO) {
1164       r = ((double)(min(obj_ptr->obbox.rbx-obj_ptr->obbox.ltx,
1165             obj_ptr->obbox.rby-obj_ptr->obbox.lty)) / cos(angle)) / 2;
1166    } else {
1167       r = radius;
1168    }
1169    ltx = obj_ptr->obbox.rbx;
1170    lty = obj_ptr->obbox.rby;
1171    rbx = obj_ptr->obbox.ltx;
1172    rby = obj_ptr->obbox.lty;
1173    for (i=0; i < sides; i++, angle+=inc) {
1174       polygon_ptr->vlist[i].x = xc + round(r*cos(angle));
1175       polygon_ptr->vlist[i].y = yc - round(r*sin(angle));
1176       if (polygon_ptr->vlist[i].x < ltx) ltx = polygon_ptr->vlist[i].x;
1177       if (polygon_ptr->vlist[i].y < lty) lty = polygon_ptr->vlist[i].y;
1178       if (polygon_ptr->vlist[i].x > rbx) rbx = polygon_ptr->vlist[i].x;
1179       if (polygon_ptr->vlist[i].y > rby) rby = polygon_ptr->vlist[i].y;
1180    }
1181    polygon_ptr->vlist[sides].x = polygon_ptr->vlist[0].x;
1182    polygon_ptr->vlist[sides].y = polygon_ptr->vlist[0].y;
1183    new_obj_ptr->obbox.ltx = ltx;
1184    new_obj_ptr->obbox.lty = lty;
1185    new_obj_ptr->obbox.rbx = rbx;
1186    new_obj_ptr->obbox.rby = rby;
1187    AdjObjSplineVs(new_obj_ptr);
1188    AdjObjBBox(new_obj_ptr);
1189 
1190    topSel->obj = botSel->obj = new_obj_ptr;
1191    AddObj(NULL, topObj, new_obj_ptr);
1192    RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
1193    FreeObj(obj_ptr);
1194 
1195    UpdSelBBox();
1196    RedrawAnArea(botObj, sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
1197          sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1));
1198    SetFileModified(TRUE);
1199    justDupped = FALSE;
1200    HighLightForward();
1201 }
1202 
DeleteStructuredSplinePoint(index,poly_ptr,polygon_ptr)1203 int DeleteStructuredSplinePoint(index, poly_ptr, polygon_ptr)
1204    int index; /* index into ssvlist */
1205    struct PolyRec *poly_ptr;
1206    struct PolygonRec *polygon_ptr;
1207    /* return FALSE if the object should be deleted */
1208 {
1209    int i=0, j=0, n=0, idx=0, num_hinge_points=0, ssn=0;
1210    IntPoint *vs=NULL;
1211    StretchStructuredSplineInfo sssi;
1212 
1213    memset(&sssi, 0, sizeof(StretchStructuredSplineInfo));
1214 
1215    if (poly_ptr != NULL) {
1216       ssn = poly_ptr->ssn;
1217       n = poly_ptr->n;
1218       vs = poly_ptr->vlist;
1219       SetIPTInfoForStretchPoly(index, n, vs, &sssi);
1220    } else if (polygon_ptr != NULL) {
1221       ssn = polygon_ptr->ssn;
1222       n = polygon_ptr->n;
1223       vs = polygon_ptr->vlist;
1224       SetIPTInfoForStretchPolygon(index, n, vs, &sssi);
1225    }
1226    if (sssi.hinge) {
1227       num_hinge_points = (n+2)/3;
1228       if ((poly_ptr != NULL && num_hinge_points-1 < 2) ||
1229             (polygon_ptr != NULL && num_hinge_points-1 < 3)) {
1230          return FALSE;
1231       }
1232       if (!sssi.prev_valid) {
1233          /* first point of poly */
1234          if (sssi.ipt.later_valid) {
1235             if (ssn-2 < 2) return FALSE;
1236          } else {
1237             if (ssn-1 < 2) return FALSE;
1238          }
1239          for (i=0; i < n-3; i++) {
1240             vs[i].x = vs[i+3].x;
1241             vs[i].y = vs[i+3].y;
1242          }
1243          poly_ptr->n = n-3;
1244       } else if (!sssi.next_valid) {
1245          /* last point of poly */
1246          if (sssi.ipt.earlier_valid) {
1247             if (ssn-2 < 2) return FALSE;
1248          } else {
1249             if (ssn-1 < 2) return FALSE;
1250          }
1251          poly_ptr->n = n-3;
1252       } else {
1253          if (poly_ptr != NULL) {
1254             if (sssi.ipt.earlier_valid) {
1255                if (ssn-3 < 2) return FALSE;
1256             } else {
1257                if (ssn-1 < 2) return FALSE;
1258             }
1259             for (i=0, j=0; i < num_hinge_points; i++, j+=3) {
1260                if (j != sssi.orig_hinge_index) {
1261                   if (i == 0) {
1262                      vs[idx].x = vs[j].x;
1263                      vs[idx].y = vs[j].y;
1264                      vs[idx+1].x = vs[j+1].x;
1265                      vs[idx+1].y = vs[j+1].y;
1266                      idx += 2;
1267                   } else if (i == num_hinge_points-1) {
1268                      vs[idx].x = vs[j-1].x;
1269                      vs[idx].y = vs[j-1].y;
1270                      vs[idx+1].x = vs[j].x;
1271                      vs[idx+1].y = vs[j].y;
1272                      idx += 2;
1273                   } else {
1274                      vs[idx].x = vs[j-1].x;
1275                      vs[idx].y = vs[j-1].y;
1276                      vs[idx+1].x = vs[j].x;
1277                      vs[idx+1].y = vs[j].y;
1278                      vs[idx+2].x = vs[j+1].x;
1279                      vs[idx+2].y = vs[j+1].y;
1280                      idx += 3;
1281                   }
1282                }
1283             }
1284             poly_ptr->n = n-3;
1285          } else if (polygon_ptr != NULL) {
1286             if (sssi.orig_hinge_index == 0 || sssi.orig_hinge_index == n-1) {
1287                IntPoint v2, v3;
1288 
1289                if ((sssi.orig_hinge_index == 0 && sssi.ipt.later_valid) ||
1290                      (sssi.orig_hinge_index == n-1 && sssi.ipt.earlier_valid)) {
1291                   if (ssn-3 < 4) return FALSE;
1292                } else {
1293                   if (ssn-1 < 4) return FALSE;
1294                }
1295                v2.x = vs[2].x;
1296                v2.y = vs[2].y;
1297                v3.x = vs[3].x;
1298                v3.y = vs[3].y;
1299                for (j=0; j < n-5; j++) {
1300                   vs[j].x = vs[j+3].x;
1301                   vs[j].y = vs[j+3].y;
1302                }
1303                vs[n-5].x = v2.x;
1304                vs[n-5].y = v2.y;
1305                vs[n-4].x = v3.x;
1306                vs[n-4].y = v3.y;
1307             } else {
1308                if (sssi.ipt.later_valid) {
1309                   if (ssn-3 < 4) return FALSE;
1310                } else {
1311                   if (ssn-1 < 4) return FALSE;
1312                }
1313                for (i=0, j=0; i < num_hinge_points; i++, j+=3) {
1314                   if (j != sssi.orig_hinge_index) {
1315                      if (i == 0) {
1316                         vs[idx].x = vs[j].x;
1317                         vs[idx].y = vs[j].y;
1318                         vs[idx+1].x = vs[j+1].x;
1319                         vs[idx+1].y = vs[j+1].y;
1320                         idx += 2;
1321                      } else if (i == num_hinge_points-1) {
1322                         vs[idx].x = vs[j-1].x;
1323                         vs[idx].y = vs[j-1].y;
1324                         vs[idx+1].x = vs[j].x;
1325                         vs[idx+1].y = vs[j].y;
1326                         idx += 2;
1327                      } else {
1328                         vs[idx].x = vs[j-1].x;
1329                         vs[idx].y = vs[j-1].y;
1330                         vs[idx+1].x = vs[j].x;
1331                         vs[idx+1].y = vs[j].y;
1332                         vs[idx+2].x = vs[j+1].x;
1333                         vs[idx+2].y = vs[j+1].y;
1334                         idx += 3;
1335                      }
1336                   }
1337                }
1338             }
1339             polygon_ptr->n = n-3;
1340          }
1341       }
1342    } else {
1343       if (!sssi.prev_valid) {
1344          /* first point of poly */
1345          ssn--;
1346          if ((poly_ptr != NULL && ssn < 2) ||
1347                (polygon_ptr != NULL && ssn < 4)) {
1348             return FALSE;
1349          }
1350          vs[1].x = vs[0].x;
1351          vs[1].y = vs[0].y;
1352       } else if (!sssi.next_valid) {
1353          /* last point of poly */
1354          ssn--;
1355          if ((poly_ptr != NULL && ssn < 2) ||
1356                (polygon_ptr != NULL && ssn < 4)) {
1357             return FALSE;
1358          }
1359          vs[n-2].x = vs[n-1].x;
1360          vs[n-2].y = vs[n-1].y;
1361       } else {
1362          ssn -= 2;
1363          if ((poly_ptr != NULL && ssn < 2) ||
1364                (polygon_ptr != NULL && ssn < 4)) {
1365             return FALSE;
1366          }
1367          if (poly_ptr != NULL) {
1368             vs[sssi.orig_hinge_index-1].x = vs[sssi.orig_hinge_index].x;
1369             vs[sssi.orig_hinge_index-1].y = vs[sssi.orig_hinge_index].y;
1370             vs[sssi.orig_hinge_index+1].x = vs[sssi.orig_hinge_index].x;
1371             vs[sssi.orig_hinge_index+1].y = vs[sssi.orig_hinge_index].y;
1372          } else if (polygon_ptr != NULL) {
1373             if (sssi.orig_hinge_index == 0 || sssi.orig_hinge_index == n-1) {
1374                vs[1].x = vs[0].x;
1375                vs[1].y = vs[0].y;
1376                vs[n-2].x = vs[n-1].x;
1377                vs[n-2].y = vs[n-1].y;
1378             } else {
1379                vs[sssi.orig_hinge_index-1].x = vs[sssi.orig_hinge_index].x;
1380                vs[sssi.orig_hinge_index-1].y = vs[sssi.orig_hinge_index].y;
1381                vs[sssi.orig_hinge_index+1].x = vs[sssi.orig_hinge_index].x;
1382                vs[sssi.orig_hinge_index+1].y = vs[sssi.orig_hinge_index].y;
1383             }
1384          }
1385       }
1386    }
1387    return TRUE;
1388 }
1389 
DeletePoint()1390 void DeletePoint()
1391 {
1392    int i=0, n=0, pt_deleted=FALSE, deleting=TRUE, curved=(-1);
1393    int root_x=0, root_y=0, old_x=0, old_y=0, delete_obj=FALSE;
1394    struct ObjRec *obj_ptr=NULL;
1395    struct PolyRec *poly_ptr=NULL;
1396    struct PolygonRec *polygon_ptr=NULL;
1397    unsigned int status;
1398    Window root_win, child_win;
1399    IntPoint *vs=NULL;
1400    char *smooth=NULL;
1401 
1402    if (!(topSel != NULL && topSel == botSel &&
1403          (topSel->obj->type == OBJ_POLY || topSel->obj->type == OBJ_POLYGON))) {
1404       MsgBox(TgLoadString(STID_SELECT_ONLY_ONE_POLY_POLYGON), TOOL_NAME,
1405             INFO_MB);
1406       return;
1407    } else if (topSel->obj->locked) {
1408       MsgBox(TgLoadString(STID_CANNOT_DEL_PT_FOR_LOCKED), TOOL_NAME, INFO_MB);
1409       return;
1410    }
1411    if (curChoice == VERTEXMODE) {
1412       HighLightReverse();
1413       JustRemoveAllVSel();
1414       HighLightForward();
1415    }
1416    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
1417    obj_ptr = topSel->obj;
1418    if (!GetPolyOrPolygonControlPoints(obj_ptr, &poly_ptr, &polygon_ptr, &curved,
1419          &n, &vs, &smooth)) {
1420       return;
1421    }
1422    SaveStatusStrings();
1423    SetMouseStatus(TgLoadCachedString(CSTID_DEL_A_VERTEX),
1424          TgLoadCachedString(CSTID_FINISH), TgLoadCachedString(CSTID_FINISH));
1425    TwoLineMsg(TgLoadString(STID_LEFT_BTN_TO_DEL_PTS),
1426          TgLoadString(STID_CLICK_OTHER_BUTTON_TO_QUIT));
1427 
1428    if (!debugNoPointerGrab) {
1429       XGrabPointer(mainDisplay, drawWindow, False,
1430             PointerMotionMask | ButtonPressMask,
1431             GrabModeAsync, GrabModeAsync, None, defaultCursor, CurrentTime);
1432    }
1433    XQueryPointer(mainDisplay, drawWindow, &root_win, &child_win,
1434          &root_x, &root_y, &old_x, &old_y, &status);
1435    XSetFont(mainDisplay, revDefaultGC, defaultFontPtr->fid);
1436    /* do not translate -- program constants */
1437    XDrawString(mainDisplay, drawWindow, revDefaultGC,
1438          old_x+4, old_y+defaultFontAsc, "DEL", 3);
1439    MarkRulers(old_x, old_y);
1440 
1441    while (deleting) {
1442       XEvent input;
1443 
1444       XNextEvent(mainDisplay, &input);
1445 
1446       if (input.type == Expose || input.type == VisibilityNotify) {
1447          ExposeEventHandler(&input, TRUE);
1448       } else if (input.type == ButtonPress) {
1449          if (input.xbutton.button == Button1) {
1450             int index=0;
1451 
1452             if (curved == LT_STRUCT_SPLINE) {
1453                if ((poly_ptr != NULL && PtInPolyMark(obj_ptr, input.xbutton.x,
1454                      input.xbutton.y, poly_ptr->ssn, poly_ptr->ssvlist,
1455                      &index)) || (polygon_ptr != NULL && PtInPolyMark(obj_ptr,
1456                      input.xbutton.x, input.xbutton.y, polygon_ptr->ssn-1,
1457                      polygon_ptr->ssvlist, &index))) {
1458                   int sel_ltx=selLtX, sel_lty=selLtY;
1459                   int sel_rbx=selRbX, sel_rby=selRbY;
1460 
1461                   /* index is the index into ssvlist */
1462                   pt_deleted = TRUE;
1463                   HighLightReverse();
1464                   if (DeleteStructuredSplinePoint(index, poly_ptr,
1465                         polygon_ptr)) {
1466                      AdjObjSplineVs(obj_ptr);
1467                      if (poly_ptr != NULL) {
1468                         UpdPolyBBox(obj_ptr, poly_ptr->n, poly_ptr->vlist);
1469                      } else if (polygon_ptr != NULL) {
1470                         UpdPolyBBox(obj_ptr, polygon_ptr->n,
1471                               polygon_ptr->vlist);
1472                      }
1473                      AdjObjBBox(obj_ptr);
1474 
1475                      XDrawString(mainDisplay, drawWindow, revDefaultGC, old_x+4,
1476                            old_y+defaultFontAsc, "DEL", 3);
1477                      old_x = input.xbutton.x;
1478                      old_y = input.xbutton.y;
1479                      UpdSelBBox();
1480                      RedrawAreas(botObj,
1481                            sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
1482                            sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
1483                            selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
1484                            selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
1485                      HighLightForward();
1486                      if (obj_ptr != NULL) {
1487                         XDrawString(mainDisplay, drawWindow, revDefaultGC,
1488                               old_x+4, old_y+defaultFontAsc, "DEL", 3);
1489                      }
1490                   } else {
1491                      XUngrabPointer(mainDisplay, CurrentTime);
1492                      Msg("");
1493                      deleting = FALSE;
1494                      delete_obj = TRUE;
1495                   }
1496                   SetFileModified(TRUE);
1497                   justDupped = FALSE;
1498                }
1499             } else if ((obj_ptr->type == OBJ_POLY &&
1500                   PtInPolyMark(obj_ptr, input.xbutton.x, input.xbutton.y,
1501                   poly_ptr->n, poly_ptr->vlist, &index)) ||
1502                   (obj_ptr->type == OBJ_POLYGON &&
1503                   PtInPolyMark(obj_ptr, input.xbutton.x, input.xbutton.y,
1504                   polygon_ptr->n-1, polygon_ptr->vlist, &index))) {
1505                pt_deleted = TRUE;
1506                HighLightReverse();
1507                if ((obj_ptr->type == OBJ_POLY && poly_ptr->n == 2) ||
1508                      (obj_ptr->type == OBJ_POLYGON && polygon_ptr->n == 4)) {
1509                   XUngrabPointer(mainDisplay, CurrentTime);
1510                   Msg("");
1511                   deleting = FALSE;
1512                   delete_obj = TRUE;
1513                } else {
1514                   int sel_ltx=selLtX, sel_lty=selLtY;
1515                   int sel_rbx=selRbX, sel_rby=selRbY;
1516 
1517                   switch (obj_ptr->type) {
1518                   case OBJ_POLY:
1519                      n = poly_ptr->n;
1520                      smooth = poly_ptr->smooth;
1521                      for (i = index+1; i < n; i++) {
1522                         poly_ptr->vlist[i-1] = poly_ptr->vlist[i];
1523                         if (smooth != NULL) smooth[i-1] = smooth[i];
1524                      }
1525                      if (smooth != NULL) {
1526                         if (index == 0) {
1527                            smooth[0] = FALSE;
1528                         } else if (index == n-1) {
1529                            smooth[n-2] = FALSE;
1530                         }
1531                      }
1532                      poly_ptr->n--;
1533                      AdjObjSplineVs(obj_ptr);
1534                      if (poly_ptr->curved != LT_INTSPLINE) {
1535                         UpdPolyBBox(obj_ptr, poly_ptr->n, poly_ptr->vlist);
1536                      } else {
1537                         UpdPolyBBox(obj_ptr, poly_ptr->intn,
1538                               poly_ptr->intvlist);
1539                      }
1540                      break;
1541                   case OBJ_POLYGON:
1542                      n = polygon_ptr->n;
1543                      smooth = polygon_ptr->smooth;
1544                      for (i = index+1; i < n; i++) {
1545                         polygon_ptr->vlist[i-1] = polygon_ptr->vlist[i];
1546                         if (smooth != NULL) smooth[i-1] = smooth[i];
1547                      }
1548                      polygon_ptr->n--;
1549                      n--;
1550                      if (index == 0) {
1551                         polygon_ptr->vlist[n-1] = polygon_ptr->vlist[0];
1552                         if (smooth != NULL) smooth[n-1] = smooth[0];
1553                      }
1554                      AdjObjSplineVs(obj_ptr);
1555                      if (polygon_ptr->curved != LT_INTSPLINE) {
1556                         UpdPolyBBox(obj_ptr, polygon_ptr->n,
1557                               polygon_ptr->vlist);
1558                      } else {
1559                         UpdPolyBBox(obj_ptr, polygon_ptr->intn,
1560                               polygon_ptr->intvlist);
1561                      }
1562                      break;
1563                   }
1564                   AdjObjBBox(obj_ptr);
1565 
1566                   XDrawString(mainDisplay, drawWindow, revDefaultGC, old_x+4,
1567                         old_y+defaultFontAsc, "DEL", 3);
1568                   old_x = input.xbutton.x;
1569                   old_y = input.xbutton.y;
1570                   UpdSelBBox();
1571                   RedrawAreas(botObj,
1572                         sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
1573                         sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
1574                         selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
1575                         selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
1576                   HighLightForward();
1577                   if (obj_ptr != NULL) {
1578                      XDrawString(mainDisplay, drawWindow, revDefaultGC,
1579                            old_x+4, old_y+defaultFontAsc, "DEL", 3);
1580                   }
1581                }
1582                SetFileModified(TRUE);
1583                justDupped = FALSE;
1584             }
1585          } else {
1586             XUngrabPointer(mainDisplay, CurrentTime);
1587             Msg("");
1588             deleting = FALSE;
1589             XDrawString(mainDisplay, drawWindow, revDefaultGC,
1590                   old_x+4, old_y+defaultFontAsc, "DEL", 3);
1591          }
1592       } else if (input.type == MotionNotify &&
1593             input.xany.window == drawWindow) {
1594          XEvent ev;
1595 
1596          XDrawString(mainDisplay, drawWindow, revDefaultGC,
1597                old_x+4, old_y+defaultFontAsc, "DEL", 3);
1598          old_x = input.xmotion.x;
1599          old_y = input.xmotion.y;
1600          XDrawString(mainDisplay, drawWindow, revDefaultGC,
1601                old_x+4, old_y+defaultFontAsc, "DEL", 3);
1602          MarkRulers(old_x, old_y);
1603          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
1604       }
1605    }
1606    if (delete_obj) {
1607       DelObj(obj_ptr);
1608       obj_ptr = NULL;
1609       free(topSel);
1610       topSel = botSel = NULL;
1611 
1612       XDrawString(mainDisplay, drawWindow, revDefaultGC, old_x+4,
1613             old_y+defaultFontAsc, "DEL", 3);
1614       RedrawAnArea(botObj,
1615             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
1616             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
1617       HighLightForward();
1618       UpdSelBBox();
1619    }
1620    RestoreStatusStrings();
1621    if (pt_deleted) {
1622       if (topSel == NULL) {
1623          ChangeReplaceOneCmdToDeleteCmd();
1624       } else {
1625          RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
1626       }
1627    } else {
1628       AbortPrepareCmd(CMD_REPLACE);
1629    }
1630 }
1631 
1632 #define ADDPOINT_DRAW  (FALSE)
1633 #define ADDPOINT_ERASE (TRUE)
1634 #define ADDPOINT_CLICK (FALSE)
1635 #define ADDPOINT_DRAG  (TRUE)
1636 
1637 #define ADDPOINT_STARTSHOW 0
1638 #define ADDPOINT_DOSHOW    1
1639 #define ADDPOINT_ENDSHOW   2
1640 
1641 static
AddPointMeasureCursor(start,abs_w,abs_h,abs_x,abs_y)1642 void AddPointMeasureCursor(start, abs_w, abs_h, abs_x, abs_y)
1643    int start, abs_w, abs_h, abs_x, abs_y;
1644 {
1645    char buf[MAXSTRING], w_buf[80], h_buf[80], x_buf[80], y_buf[80];
1646 
1647    PixelToMeasurementUnit(w_buf, abs_w);
1648    PixelToMeasurementUnit(h_buf, abs_h);
1649    PixelToMeasurementUnit(x_buf, abs_x);
1650    PixelToMeasurementUnit(y_buf, abs_y);
1651    /* do not translate -- program constants */
1652    sprintf(buf, "ADD: dx=%s\n     dy=%s\n     x=%s\n     y=%s",
1653          w_buf, h_buf, x_buf, y_buf);
1654    switch (start) {
1655    case ADDPOINT_STARTSHOW:
1656       StartShowMeasureCursor(OFFSET_X(abs_x), OFFSET_Y(abs_y), buf, FALSE);
1657       break;
1658    case ADDPOINT_DOSHOW:
1659       ShowMeasureCursor(OFFSET_X(abs_x), OFFSET_Y(abs_y), buf, FALSE);
1660       break;
1661    case ADDPOINT_ENDSHOW:
1662       EndShowMeasureCursor(OFFSET_X(abs_x), OFFSET_Y(abs_y), buf, FALSE);
1663       break;
1664    }
1665 }
1666 
1667 static
DetermineBefore(prev_dist,next_dist,new_prev_dist,new_next_dist)1668 int DetermineBefore(prev_dist, next_dist, new_prev_dist, new_next_dist)
1669    double prev_dist, next_dist, new_prev_dist, new_next_dist;
1670 {
1671    double prev_diff, next_diff;
1672    int before=FALSE;
1673 
1674    if (new_prev_dist < prev_dist) {
1675       if (new_next_dist < next_dist) {
1676          /* got closer to both previous and next vertices */
1677          prev_diff = prev_dist-new_prev_dist;
1678          next_diff = next_dist-new_next_dist;
1679          if (prev_diff < next_diff) {
1680             before = FALSE;
1681          } else if (prev_diff > next_diff) {
1682             before = TRUE;
1683          } else {
1684             before = INVALID;
1685          }
1686       } else {
1687          /* got closer to previous vertex */
1688          before = TRUE;
1689       }
1690    } else if (new_prev_dist > prev_dist) {
1691       if (new_next_dist > next_dist) {
1692          /* got further from both previous and next vertices */
1693          prev_diff = prev_dist-new_prev_dist;
1694          next_diff = next_dist-new_next_dist;
1695          if (prev_diff < next_diff) {
1696             before = FALSE;
1697          } else if (prev_diff > next_diff) {
1698             before = TRUE;
1699          } else {
1700             before = INVALID;
1701          }
1702       } else {
1703          /* got closer to next vertex */
1704          before = FALSE;
1705       }
1706    } else {
1707       /* same distance from previous vertex */
1708       if (new_next_dist < next_dist) {
1709          before = FALSE;
1710       } else if (new_next_dist > next_dist) {
1711          before = TRUE;
1712       } else {
1713          /* same distance from both previous and next vertices */
1714          before = INVALID;
1715       }
1716    }
1717    return before;
1718 }
1719 
1720 static
CanAddSmoothPointForStructuredSpline(button,psssi)1721 int CanAddSmoothPointForStructuredSpline(button, psssi)
1722    unsigned int button;
1723    StretchStructuredSplineInfo *psssi;
1724 {
1725    if (button == Button2) {
1726       if (psssi->hinge) {
1727          if (!psssi->prev_valid) {
1728             /* first point of poly */
1729             if (psssi->ipt.later_valid) {
1730                return FALSE;
1731             }
1732          } else if (!psssi->next_valid) {
1733             /* last point of poly */
1734             if (psssi->ipt.earlier_valid) {
1735                return FALSE;
1736             }
1737          } else {
1738             if (psssi->ipt.later_valid) {
1739                return FALSE;
1740             }
1741          }
1742       } else {
1743          return FALSE;
1744       }
1745    }
1746    return TRUE;
1747 }
1748 
1749 typedef struct tagAddStructuredPointInfo {
1750    struct ObjRec *obj_ptr;
1751    struct PolyRec *poly_ptr;
1752    struct PolygonRec *polygon_ptr;
1753    unsigned int button;
1754    int index; /* index into ssvlist */
1755    int start_mouse_x, start_mouse_y, n, ssn, already_moved, before;
1756    IntPoint *vlist, *ssvlist, vs[5], vs2[5];
1757    char smooth[5], smooth2[5];
1758    int num_vs, num_vs2, sn, sn2, saved_sn, saved_sn2;
1759    int dx_off, dy_off, extra_smooth, extra_smooth2;
1760    XPoint *sv, *sv2, dashed_vs[2];
1761    StretchStructuredSplineInfo sssi;
1762    int prev_x, prev_y, prev_tx, prev_ty, x, y, tx, ty;
1763    int next_x, next_y, next_tx, next_ty, ruler_abs_x, ruler_abs_y;
1764    int orig_grid_x, orig_grid_y, grid_x, grid_y, mouse_x, mouse_y;
1765    int sel_ltx, sel_lty, sel_rbx, sel_rby;
1766    double prev_angle, next_angle, prev_dist, next_dist, ddx, ddy;
1767 } AddStructuredPointInfo;
1768 
1769 static
ContinueAddStructuredPolyOrPolygonPointSetup(paspi)1770 int ContinueAddStructuredPolyOrPolygonPointSetup(paspi)
1771    AddStructuredPointInfo *paspi;
1772 {
1773    int i=0, tmp_x=0, tmp_y=0;
1774    IntPoint *vs=NULL, *vs2=NULL, *ssvlist=NULL;
1775    IntPoint *obj_ssvlist=NULL, *obj_vlist=NULL;
1776    char *smooth=NULL, *smooth2=NULL;
1777    struct ObjRec *obj_ptr=paspi->obj_ptr;
1778    struct PolyRec *poly_ptr=paspi->poly_ptr;
1779    struct PolygonRec *polygon_ptr=paspi->polygon_ptr;
1780 
1781    if (poly_ptr != NULL) {
1782       paspi->ssn = poly_ptr->ssn;
1783       paspi->n = poly_ptr->n;
1784       obj_ssvlist = poly_ptr->ssvlist;
1785       obj_vlist = poly_ptr->vlist;
1786    } else if (polygon_ptr != NULL) {
1787       paspi->ssn = polygon_ptr->ssn;
1788       paspi->n = polygon_ptr->n;
1789       obj_ssvlist = polygon_ptr->ssvlist;
1790       obj_vlist = polygon_ptr->vlist;
1791    }
1792    paspi->ssvlist = (IntPoint*)malloc((paspi->ssn+1)*sizeof(IntPoint));
1793    if (paspi->ssvlist == NULL) FailAllocMessage();
1794    for (i=0; i < paspi->ssn; i++) {
1795       paspi->ssvlist[i].x = obj_ssvlist[i].x;
1796       paspi->ssvlist[i].y = obj_ssvlist[i].y;
1797    }
1798    if (obj_ptr->ctm != NULL) {
1799       for (i=0; i < paspi->ssn; i++) {
1800          TransformPointThroughCTM(paspi->ssvlist[i].x-obj_ptr->x,
1801                paspi->ssvlist[i].y-obj_ptr->y, paspi->obj_ptr->ctm,
1802                &tmp_x, &tmp_y);
1803          paspi->ssvlist[i].x = obj_ptr->x+tmp_x;
1804          paspi->ssvlist[i].y = obj_ptr->y+tmp_y;
1805       }
1806    }
1807    paspi->vlist = (IntPoint*)malloc((paspi->n+1)*sizeof(IntPoint));
1808    if (paspi->vlist == NULL) FailAllocMessage();
1809    for (i=0; i < paspi->n; i++) {
1810       paspi->vlist[i].x = obj_vlist[i].x;
1811       paspi->vlist[i].y = obj_vlist[i].y;
1812    }
1813    if (obj_ptr->ctm != NULL) {
1814       for (i=0; i < paspi->n; i++) {
1815          TransformPointThroughCTM(paspi->vlist[i].x-obj_ptr->x,
1816                paspi->vlist[i].y-obj_ptr->y, paspi->obj_ptr->ctm,
1817                &tmp_x, &tmp_y);
1818          paspi->vlist[i].x = obj_ptr->x+tmp_x;
1819          paspi->vlist[i].y = obj_ptr->y+tmp_y;
1820       }
1821    }
1822    if (poly_ptr != NULL) {
1823       SetIPTInfoForStretchPoly(paspi->index, paspi->n, paspi->vlist,
1824             &paspi->sssi);
1825    } else if (polygon_ptr != NULL) {
1826       SetIPTInfoForStretchPolygon(paspi->index, paspi->n, paspi->vlist,
1827             &paspi->sssi);
1828    }
1829    if (!CanAddSmoothPointForStructuredSpline(paspi->button, &paspi->sssi)) {
1830       Msg(TgLoadString(STID_CANNOT_ADD_SMOOTH_VERTEX));
1831       return FALSE;
1832    }
1833    paspi->sel_ltx = selLtX; paspi->sel_lty = selLtY;
1834    paspi->sel_rbx = selRbX; paspi->sel_rby = selRbY;
1835 
1836    ssvlist = paspi->ssvlist;
1837    vs = paspi->vs;
1838    smooth = paspi->smooth;
1839    vs2 = paspi->vs2;
1840    smooth2 = paspi->smooth2;
1841    memset(smooth, 0, sizeof(5*sizeof(char)));
1842    memset(smooth2, 0, sizeof(5*sizeof(char)));
1843 
1844    paspi->x = paspi->tx = ssvlist[paspi->index].x;
1845    paspi->y = paspi->ty = ssvlist[paspi->index].y;
1846    if (paspi->sssi.hinge) {
1847       /* hinge point, need to wait */
1848    } else {
1849       /* must be adding a hinge point, i.e., Button1 is clicked */
1850       if (!paspi->sssi.prev_valid) {
1851          /* first point of poly */
1852          paspi->before = FALSE;
1853 
1854          paspi->num_vs = 3;
1855          vs[0].x = ssvlist[1].x; vs[0].y = ssvlist[1].y;
1856          vs[1].x = ssvlist[1].x; vs[1].y = ssvlist[1].y;
1857          vs[2].x = ssvlist[0].x; vs[2].y = ssvlist[0].y;
1858          smooth[1] = TRUE;
1859 
1860          vs2[0].x = ssvlist[1].x; vs2[0].y = ssvlist[1].y;
1861          if (paspi->sssi.ipt_next.earlier_valid) {
1862             paspi->num_vs2 = 3;
1863             vs2[1].x = paspi->sssi.ipt_next.earlier_smooth_pt.x;
1864             vs2[1].y = paspi->sssi.ipt_next.earlier_smooth_pt.y;
1865             smooth2[1] = TRUE;
1866          } else {
1867             paspi->num_vs2 = 2;
1868          }
1869          vs2[paspi->num_vs2-1].x = paspi->sssi.ipt_next.hinge_pt.x;
1870          vs2[paspi->num_vs2-1].y = paspi->sssi.ipt_next.hinge_pt.y;
1871       } else if (!paspi->sssi.next_valid) {
1872          /* last point of poly */
1873          paspi->before = TRUE;
1874 
1875          vs[0].x = ssvlist[paspi->ssn-2].x; vs[0].y = ssvlist[paspi->ssn-2].y;
1876          if (paspi->sssi.ipt_prev.later_valid) {
1877             paspi->num_vs = 3;
1878             vs[1].x = paspi->sssi.ipt_prev.later_smooth_pt.x;
1879             vs[1].y = paspi->sssi.ipt_prev.later_smooth_pt.y;
1880             smooth[1] = TRUE;
1881          } else {
1882             paspi->num_vs = 2;
1883          }
1884          vs[paspi->num_vs-1].x = paspi->sssi.ipt_prev.hinge_pt.x;
1885          vs[paspi->num_vs-1].y = paspi->sssi.ipt_prev.hinge_pt.y;
1886 
1887          paspi->num_vs2 = 3;
1888          vs2[0].x = ssvlist[paspi->ssn-2].x; vs2[0].y = ssvlist[paspi->ssn-2].y;
1889          vs2[1].x = ssvlist[paspi->ssn-2].x; vs2[1].y = ssvlist[paspi->ssn-2].y;
1890          vs2[2].x = ssvlist[paspi->ssn-1].x; vs2[2].y = ssvlist[paspi->ssn-1].y;
1891          smooth2[1] = TRUE;
1892       } else {
1893          if (paspi->sssi.earlier_smooth_selected) {
1894             paspi->before = TRUE;
1895 
1896             vs[0].x = ssvlist[paspi->index].x;
1897             vs[0].y = ssvlist[paspi->index].y;
1898             if (paspi->sssi.ipt_prev.later_valid) {
1899                paspi->num_vs = 3;
1900                vs[1].x = paspi->sssi.ipt_prev.later_smooth_pt.x;
1901                vs[1].y = paspi->sssi.ipt_prev.later_smooth_pt.y;
1902                smooth[1] = TRUE;
1903             } else {
1904                paspi->num_vs = 2;
1905             }
1906             vs[paspi->num_vs-1].x = paspi->sssi.ipt_prev.hinge_pt.x;
1907             vs[paspi->num_vs-1].y = paspi->sssi.ipt_prev.hinge_pt.y;
1908 
1909             vs2[0].x = ssvlist[paspi->index].x;
1910             vs2[0].y = ssvlist[paspi->index].y;
1911             vs2[1].x = paspi->sssi.ipt.earlier_smooth_pt.x;
1912             vs2[1].y = paspi->sssi.ipt.earlier_smooth_pt.y;
1913             vs2[2].x = paspi->sssi.ipt.hinge_pt.x;
1914             vs2[2].y = paspi->sssi.ipt.hinge_pt.y;
1915             smooth2[1] = TRUE;
1916             paspi->num_vs2 = 3;
1917          } else {
1918             paspi->before = FALSE;
1919 
1920             vs[0].x = ssvlist[paspi->index].x;
1921             vs[0].y = ssvlist[paspi->index].y;
1922             vs[1].x = paspi->sssi.ipt.later_smooth_pt.x;
1923             vs[1].y = paspi->sssi.ipt.later_smooth_pt.y;
1924             vs[2].x = paspi->sssi.ipt.hinge_pt.x;
1925             vs[2].y = paspi->sssi.ipt.hinge_pt.y;
1926             smooth[1] = TRUE;
1927             paspi->num_vs = 3;
1928 
1929             vs2[0].x = ssvlist[paspi->index].x;
1930             vs2[0].y = ssvlist[paspi->index].y;
1931             if (paspi->sssi.ipt_next.earlier_valid) {
1932                paspi->num_vs2 = 3;
1933                vs2[1].x = paspi->sssi.ipt_next.earlier_smooth_pt.x;
1934                vs2[1].y = paspi->sssi.ipt_next.earlier_smooth_pt.y;
1935                smooth2[1] = TRUE;
1936             } else {
1937                paspi->num_vs2 = 2;
1938             }
1939             vs2[paspi->num_vs2-1].x = paspi->sssi.ipt_next.hinge_pt.x;
1940             vs2[paspi->num_vs2-1].y = paspi->sssi.ipt_next.hinge_pt.y;
1941          }
1942       }
1943       paspi->already_moved = TRUE;
1944       paspi->sv = MakeMultiSplinePolyVertex(LT_STRUCT_SPLINE, &paspi->sn,
1945             paspi->smooth, drawOrigX, drawOrigY, paspi->num_vs, vs);
1946       paspi->sv2 = MakeMultiSplinePolyVertex(LT_STRUCT_SPLINE, &paspi->sn2,
1947             paspi->smooth2, drawOrigX, drawOrigY, paspi->num_vs2, vs2);
1948       XDrawLines(mainDisplay, drawWindow, revDefaultGC, paspi->sv, paspi->sn,
1949             CoordModeOrigin);
1950       XDrawLines(mainDisplay, drawWindow, revDefaultGC, paspi->sv2, paspi->sn2,
1951             CoordModeOrigin);
1952 
1953       GridXY(paspi->start_mouse_x, paspi->start_mouse_y,
1954             &paspi->orig_grid_x, &paspi->orig_grid_y);
1955       paspi->grid_x = paspi->orig_grid_x;
1956       paspi->grid_y = paspi->orig_grid_y;
1957       paspi->mouse_x = paspi->start_mouse_x;
1958       paspi->mouse_y = paspi->start_mouse_y;
1959       paspi->ruler_abs_x = paspi->tx;
1960       paspi->ruler_abs_y = paspi->ty;
1961 
1962       MARKHR(drawWindow, revDefaultGC, OFFSET_X(paspi->ruler_abs_x),
1963             OFFSET_Y(paspi->ruler_abs_y));
1964       AddPointMeasureCursor(ADDPOINT_STARTSHOW, 0, 0, paspi->tx, paspi->ty);
1965 
1966       return TRUE;
1967    }
1968    if (paspi->index == 0) {
1969       paspi->next_x = paspi->next_tx = paspi->ssvlist[1].x;
1970       paspi->next_y = paspi->next_ty = paspi->ssvlist[1].y;
1971       paspi->prev_x = paspi->prev_tx = (paspi->x<<1)-paspi->next_x;
1972       paspi->prev_y = paspi->prev_ty = (paspi->y<<1)-paspi->next_y;
1973    } else if (paspi->index == paspi->n-1) {
1974       paspi->prev_x = paspi->prev_tx = paspi->ssvlist[paspi->n-2].x;
1975       paspi->prev_y = paspi->prev_ty = paspi->ssvlist[paspi->n-2].y;
1976       paspi->next_x = paspi->next_tx = (paspi->x<<1)-paspi->prev_x;
1977       paspi->next_y = paspi->next_ty = (paspi->y<<1)-paspi->prev_y;
1978    } else {
1979       paspi->prev_x = paspi->prev_tx = ssvlist[paspi->index-1].x;
1980       paspi->prev_y = paspi->prev_ty = ssvlist[paspi->index-1].y;
1981       paspi->next_x = paspi->next_tx = ssvlist[paspi->index+1].x;
1982       paspi->next_y = paspi->next_ty = ssvlist[paspi->index+1].y;
1983    }
1984    paspi->ddx = (double)(paspi->prev_tx - paspi->tx);
1985    paspi->ddy = (double)(paspi->prev_ty - paspi->ty);
1986    paspi->prev_dist = paspi->ddx*paspi->ddx+paspi->ddy*paspi->ddy;
1987    paspi->ddx = (double)(paspi->next_tx - paspi->tx);
1988    paspi->ddy = (double)(paspi->next_ty - paspi->ty);
1989    paspi->next_dist = paspi->ddx*paspi->ddx+paspi->ddy*paspi->ddy;
1990 
1991    paspi->prev_angle = (paspi->prev_tx==paspi->tx) ?
1992          ((paspi->prev_ty>=paspi->ty) ? M_PI/2.0 : -M_PI/2.0) :
1993          atan2((double)(paspi->prev_ty-paspi->ty),
1994          (double)(paspi->prev_tx-paspi->tx));
1995    paspi->next_angle = (paspi->next_tx==paspi->tx) ?
1996          ((paspi->next_ty>=paspi->ty) ? M_PI/2.0 : -M_PI/2.0) :
1997          atan2((double)(paspi->next_ty-paspi->ty),
1998          (double)(paspi->next_tx-paspi->tx));
1999    GridXY(paspi->start_mouse_x, paspi->start_mouse_y,
2000          &paspi->orig_grid_x, &paspi->orig_grid_y);
2001    paspi->grid_x = paspi->orig_grid_x;
2002    paspi->grid_y = paspi->orig_grid_y;
2003    paspi->mouse_x = paspi->start_mouse_x;
2004    paspi->mouse_y = paspi->start_mouse_y;
2005 
2006    AddPointMeasureCursor(ADDPOINT_STARTSHOW, 0, 0, paspi->tx, paspi->ty);
2007 
2008    return TRUE;
2009 }
2010 
2011 static
ContinueAddStructuredPolyOrPolygonPointFirstMoved(paspi)2012 void ContinueAddStructuredPolyOrPolygonPointFirstMoved(paspi)
2013    AddStructuredPointInfo *paspi;
2014 {
2015    double new_prev_dist=(double)0, new_next_dist=(double)0;
2016    int new_x=paspi->ruler_abs_x, new_y=paspi->ruler_abs_y;
2017 
2018    paspi->already_moved = TRUE;
2019 
2020    paspi->ddx = (double)(paspi->prev_tx - paspi->mouse_x);
2021    paspi->ddy = (double)(paspi->prev_ty - paspi->mouse_y);
2022    new_prev_dist = paspi->ddx*paspi->ddx+paspi->ddy*paspi->ddy;
2023    paspi->ddx = (double)(paspi->next_tx - paspi->mouse_x);
2024    paspi->ddy = (double)(paspi->next_ty - paspi->mouse_y);
2025    new_next_dist = paspi->ddx*paspi->ddx+paspi->ddy*paspi->ddy;
2026 
2027    paspi->before = DetermineBefore(paspi->prev_dist, paspi->next_dist,
2028          new_prev_dist, new_next_dist);
2029    if (paspi->before == INVALID) {
2030       double new_angle=(double)0, theta_1=(double)0, theta_2=(double)0;
2031 
2032       new_angle = (new_x==paspi->tx) ? ((new_y>=paspi->ty) ?
2033             M_PI/2.0 : -M_PI/2.0) : atan2((double)(new_y-paspi->ty),
2034             (double)(new_x-paspi->tx));
2035       theta_1 = fabs(paspi->prev_angle - new_angle);
2036       theta_2 = fabs(paspi->next_angle - new_angle);
2037       if (theta_1 > M_PI) theta_1 = 2*M_PI-theta_1;
2038       if (theta_2 > M_PI) theta_2 = 2*M_PI-theta_2;
2039       paspi->before = (theta_1 <= theta_2);
2040    }
2041 #ifdef _TGIF_DBG /* debug, do not translate */
2042    TgAssert(paspi->sssi.hinge,
2043          "paspi->sssi.hinge is FALSE in ContinueAddStructuredPolyOrPolygonPointFirstMoved()", NULL);
2044 #endif /* _TGIF_DBG */
2045    if (paspi->button == Button1) {
2046       IntPoint *vs=paspi->vs, *vs2=paspi->vs2;
2047       char *smooth=paspi->smooth, *smooth2=paspi->smooth2;
2048 
2049       memset(smooth, 0, sizeof(5*sizeof(char)));
2050       memset(smooth2, 0, sizeof(5*sizeof(char)));
2051 
2052       if (paspi->before) {
2053          /* Add a point between the current and the previous point */
2054          if (!paspi->sssi.prev_valid) {
2055             /* first point of poly */
2056             paspi->num_vs = 0;
2057 
2058             vs2[0].x = new_x;
2059             vs2[0].y = new_y;
2060             if (paspi->sssi.ipt.later_valid) {
2061                paspi->num_vs2 = 3;
2062                vs2[1].x = (paspi->sssi.ipt.hinge_pt.x<<1) -
2063                      paspi->sssi.ipt.later_smooth_pt.x;
2064                vs2[1].y = (paspi->sssi.ipt.hinge_pt.y<<1) -
2065                      paspi->sssi.ipt.later_smooth_pt.y;
2066                smooth2[1] = TRUE;
2067                paspi->extra_smooth2 = TRUE;
2068                MARKHO(drawWindow, revDefaultGC, OFFSET_X(vs2[1].x),
2069                      OFFSET_Y(vs2[1].y));
2070             } else {
2071                paspi->num_vs2 = 2;
2072             }
2073             vs2[paspi->num_vs2-1].x = paspi->sssi.ipt.hinge_pt.x;
2074             vs2[paspi->num_vs2-1].y = paspi->sssi.ipt.hinge_pt.y;
2075          } else if (!paspi->sssi.next_valid) {
2076             /* last point of poly */
2077             vs[0].x = new_x;
2078             vs[0].y = new_y;
2079             if (paspi->sssi.ipt.earlier_valid) {
2080                paspi->num_vs = 3;
2081                vs[1].x = paspi->sssi.ipt.earlier_smooth_pt.x;
2082                vs[1].y = paspi->sssi.ipt.earlier_smooth_pt.y;
2083                smooth[1] = TRUE;
2084             } else {
2085                paspi->num_vs = 2;
2086             }
2087             vs[paspi->num_vs-1].x = paspi->sssi.ipt.hinge_pt.x;
2088             vs[paspi->num_vs-1].y = paspi->sssi.ipt.hinge_pt.y;
2089 
2090             vs2[0].x = new_x;
2091             vs2[0].y = new_y;
2092             if (paspi->sssi.ipt_prev.later_valid) {
2093                paspi->num_vs2 = 3;
2094                vs2[1].x = paspi->sssi.ipt_prev.later_smooth_pt.x;
2095                vs2[1].y = paspi->sssi.ipt_prev.later_smooth_pt.y;
2096                smooth2[1] = TRUE;
2097             } else {
2098                paspi->num_vs2 = 2;
2099             }
2100             vs2[paspi->num_vs2-1].x = paspi->sssi.ipt_prev.hinge_pt.x;
2101             vs2[paspi->num_vs2-1].y = paspi->sssi.ipt_prev.hinge_pt.y;
2102          } else {
2103             vs[0].x = new_x;
2104             vs[0].y = new_y;
2105             if (paspi->sssi.ipt_prev.later_valid) {
2106                paspi->num_vs = 3;
2107                vs[1].x = paspi->sssi.ipt_prev.later_smooth_pt.x;
2108                vs[1].y = paspi->sssi.ipt_prev.later_smooth_pt.y;
2109                smooth[1] = TRUE;
2110             } else {
2111                paspi->num_vs = 2;
2112             }
2113             vs[paspi->num_vs-1].x = paspi->sssi.ipt_prev.hinge_pt.x;
2114             vs[paspi->num_vs-1].y = paspi->sssi.ipt_prev.hinge_pt.y;
2115 
2116             vs2[0].x = new_x;
2117             vs2[0].y = new_y;
2118             if (paspi->sssi.ipt.earlier_valid) {
2119                paspi->num_vs2 = 3;
2120                vs2[1].x = paspi->sssi.ipt.earlier_smooth_pt.x;
2121                vs2[1].y = paspi->sssi.ipt.earlier_smooth_pt.y;
2122                smooth2[1] = TRUE;
2123             } else {
2124                paspi->num_vs2 = 2;
2125             }
2126             vs2[paspi->num_vs2-1].x = paspi->sssi.ipt.hinge_pt.x;
2127             vs2[paspi->num_vs2-1].y = paspi->sssi.ipt.hinge_pt.y;
2128          }
2129       } else {
2130          /* Add a point between the current and the next point */
2131          if (!paspi->sssi.prev_valid) {
2132             /* first point of poly */
2133             vs[0].x = new_x;
2134             vs[0].y = new_y;
2135             if (paspi->sssi.ipt.later_valid) {
2136                paspi->num_vs = 3;
2137                vs[1].x = paspi->sssi.ipt.later_smooth_pt.x;
2138                vs[1].y = paspi->sssi.ipt.later_smooth_pt.y;
2139                smooth[1] = TRUE;
2140             } else {
2141                paspi->num_vs = 2;
2142             }
2143             vs[paspi->num_vs-1].x = paspi->sssi.ipt.hinge_pt.x;
2144             vs[paspi->num_vs-1].y = paspi->sssi.ipt.hinge_pt.y;
2145 
2146             vs2[0].x = new_x;
2147             vs2[0].y = new_y;
2148             if (paspi->sssi.ipt_next.earlier_valid) {
2149                paspi->num_vs2 = 3;
2150                vs2[1].x = paspi->sssi.ipt_next.earlier_smooth_pt.x;
2151                vs2[1].y = paspi->sssi.ipt_next.earlier_smooth_pt.y;
2152                smooth2[1] = TRUE;
2153             } else {
2154                paspi->num_vs2 = 2;
2155             }
2156             vs2[paspi->num_vs2-1].x = paspi->sssi.ipt_next.hinge_pt.x;
2157             vs2[paspi->num_vs2-1].y = paspi->sssi.ipt_next.hinge_pt.y;
2158          } else if (!paspi->sssi.next_valid) {
2159             /* last point of poly */
2160             vs[0].x = new_x;
2161             vs[0].y = new_y;
2162             if (paspi->sssi.ipt.earlier_valid) {
2163                paspi->num_vs = 3;
2164                vs[1].x = (paspi->sssi.ipt.hinge_pt.x<<1) -
2165                      paspi->sssi.ipt.earlier_smooth_pt.x;
2166                vs[1].y = (paspi->sssi.ipt.hinge_pt.y<<1) -
2167                      paspi->sssi.ipt.earlier_smooth_pt.y;
2168                smooth[1] = TRUE;
2169                paspi->extra_smooth = TRUE;
2170                MARKHO(drawWindow, revDefaultGC, OFFSET_X(vs[1].x),
2171                      OFFSET_Y(vs[1].y));
2172             } else {
2173                paspi->num_vs = 2;
2174             }
2175             vs[paspi->num_vs-1].x = paspi->sssi.ipt.hinge_pt.x;
2176             vs[paspi->num_vs-1].y = paspi->sssi.ipt.hinge_pt.y;
2177 
2178             paspi->num_vs2 = 0;
2179          } else {
2180             vs[0].x = new_x;
2181             vs[0].y = new_y;
2182             if (paspi->sssi.ipt.later_valid) {
2183                paspi->num_vs = 3;
2184                vs[1].x = paspi->sssi.ipt.later_smooth_pt.x;
2185                vs[1].y = paspi->sssi.ipt.later_smooth_pt.y;
2186                smooth[1] = TRUE;
2187             } else {
2188                paspi->num_vs = 2;
2189             }
2190             vs[paspi->num_vs-1].x = paspi->sssi.ipt.hinge_pt.x;
2191             vs[paspi->num_vs-1].y = paspi->sssi.ipt.hinge_pt.y;
2192 
2193             vs2[0].x = new_x;
2194             vs2[0].y = new_y;
2195             if (paspi->sssi.ipt_next.earlier_valid) {
2196                paspi->num_vs2 = 3;
2197                vs2[1].x = paspi->sssi.ipt_next.earlier_smooth_pt.x;
2198                vs2[1].y = paspi->sssi.ipt_next.earlier_smooth_pt.y;
2199                smooth2[1] = TRUE;
2200             } else {
2201                paspi->num_vs2 = 2;
2202             }
2203             vs2[paspi->num_vs2-1].x = paspi->sssi.ipt_next.hinge_pt.x;
2204             vs2[paspi->num_vs2-1].y = paspi->sssi.ipt_next.hinge_pt.y;
2205          }
2206       }
2207    } else {
2208       IntPoint *vs=paspi->vs, *vs2=paspi->vs2;
2209       char *smooth=paspi->smooth, *smooth2=paspi->smooth2;
2210 
2211       memset(smooth, 0, sizeof(5*sizeof(char)));
2212       memset(smooth2, 0, sizeof(5*sizeof(char)));
2213 
2214       if (!paspi->sssi.prev_valid) {
2215          /* first point of poly */
2216          paspi->num_vs = 0;
2217 
2218          vs2[0].x = paspi->sssi.ipt.hinge_pt.x;
2219          vs2[0].y = paspi->sssi.ipt.hinge_pt.y;
2220          vs2[1].x = new_x;
2221          vs2[1].y = new_y;
2222          smooth2[1] = TRUE;
2223          if (paspi->sssi.ipt_next.earlier_valid) {
2224             paspi->num_vs2 = 4;
2225             vs2[2].x = paspi->sssi.ipt_next.earlier_smooth_pt.x;
2226             vs2[2].y = paspi->sssi.ipt_next.earlier_smooth_pt.y;
2227             smooth2[2] = TRUE;
2228          } else {
2229             paspi->num_vs2 = 3;
2230          }
2231          vs2[paspi->num_vs2-1].x = paspi->sssi.ipt_next.hinge_pt.x;
2232          vs2[paspi->num_vs2-1].y = paspi->sssi.ipt_next.hinge_pt.y;
2233       } else if (!paspi->sssi.next_valid) {
2234          /* last point of poly */
2235          vs[0].x = paspi->sssi.ipt.hinge_pt.x;
2236          vs[0].y = paspi->sssi.ipt.hinge_pt.y;
2237          vs[1].x = new_x;
2238          vs[1].y = new_y;
2239          smooth[1] = TRUE;
2240          if (paspi->sssi.ipt_prev.later_valid) {
2241             paspi->num_vs = 4;
2242             vs[2].x = paspi->sssi.ipt_prev.later_smooth_pt.x;
2243             vs[2].y = paspi->sssi.ipt_prev.later_smooth_pt.y;
2244             smooth[2] = TRUE;
2245          } else {
2246             paspi->num_vs = 3;
2247          }
2248          vs[paspi->num_vs-1].x = paspi->sssi.ipt_prev.hinge_pt.x;
2249          vs[paspi->num_vs-1].y = paspi->sssi.ipt_prev.hinge_pt.y;
2250 
2251          paspi->num_vs2 = 0;
2252       } else {
2253          int new_x_prime=(paspi->sssi.ipt.hinge_pt.x<<1)-new_x;
2254          int new_y_prime=(paspi->sssi.ipt.hinge_pt.y<<1)-new_y;
2255 
2256          vs[0].x = paspi->sssi.ipt.hinge_pt.x;
2257          vs[0].y = paspi->sssi.ipt.hinge_pt.y;
2258          if (paspi->before) {
2259             vs[1].x = new_x;
2260             vs[1].y = new_y;
2261          } else {
2262             vs[1].x = new_x_prime;
2263             vs[1].y = new_y_prime;
2264          }
2265          smooth[1] = TRUE;
2266          if (paspi->sssi.ipt_prev.later_valid) {
2267             paspi->num_vs = 4;
2268             vs[2].x = paspi->sssi.ipt_prev.later_smooth_pt.x;
2269             vs[2].y = paspi->sssi.ipt_prev.later_smooth_pt.y;
2270             smooth[2] = TRUE;
2271          } else {
2272             paspi->num_vs = 3;
2273          }
2274          vs[paspi->num_vs-1].x = paspi->sssi.ipt_prev.hinge_pt.x;
2275          vs[paspi->num_vs-1].y = paspi->sssi.ipt_prev.hinge_pt.y;
2276 
2277          vs2[0].x = paspi->sssi.ipt.hinge_pt.x;
2278          vs2[0].y = paspi->sssi.ipt.hinge_pt.y;
2279          if (paspi->before) {
2280             vs2[1].x = new_x_prime;
2281             vs2[1].y = new_y_prime;
2282          } else {
2283             vs2[1].x = new_x;
2284             vs2[1].y = new_y;
2285          }
2286          smooth2[1] = TRUE;
2287          if (paspi->sssi.ipt_next.earlier_valid) {
2288             paspi->num_vs2 = 4;
2289             vs2[2].x = paspi->sssi.ipt_next.earlier_smooth_pt.x;
2290             vs2[2].y = paspi->sssi.ipt_next.earlier_smooth_pt.y;
2291             smooth2[2] = TRUE;
2292          } else {
2293             paspi->num_vs2 = 3;
2294          }
2295          vs2[paspi->num_vs2-1].x = paspi->sssi.ipt_next.hinge_pt.x;
2296          vs2[paspi->num_vs2-1].y = paspi->sssi.ipt_next.hinge_pt.y;
2297       }
2298    }
2299 }
2300 
2301 static
ContinueAddStructuredPolyOrPolygonPointRestMoved(paspi)2302 void ContinueAddStructuredPolyOrPolygonPointRestMoved(paspi)
2303    AddStructuredPointInfo *paspi;
2304 {
2305    if (paspi->button == Button1) {
2306       MARKHR(drawWindow, revDefaultGC, OFFSET_X(paspi->ruler_abs_x),
2307             OFFSET_Y(paspi->ruler_abs_y));
2308       paspi->vs[0].x = paspi->vs2[0].x = paspi->ruler_abs_x;
2309       paspi->vs[0].y = paspi->vs2[0].y = paspi->ruler_abs_y;
2310    } else {
2311       XGCValues values;
2312 
2313       XSetDashes(mainDisplay, revDefaultGC, 0, dashList[8], dashListLength[8]);
2314       if (!paspi->sssi.prev_valid) {
2315          /* first point of poly */
2316          paspi->vs2[1].x = paspi->ruler_abs_x;
2317          paspi->vs2[1].y = paspi->ruler_abs_y;
2318          paspi->dashed_vs[0].x = OFFSET_X(paspi->vs2[1].x);
2319          paspi->dashed_vs[0].y = OFFSET_Y(paspi->vs2[1].y);
2320          paspi->dashed_vs[1].x = OFFSET_X(paspi->vs2[0].x);
2321          paspi->dashed_vs[1].y = OFFSET_Y(paspi->vs2[0].y);
2322          MARKHO(drawWindow, revDefaultGC, paspi->dashed_vs[0].x,
2323                paspi->dashed_vs[0].y);
2324 
2325          values.line_style = LineOnOffDash;
2326          XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
2327          MyDashedLine(drawWindow, revDefaultGC, paspi->dashed_vs, 2);
2328          values.line_style = LineSolid;
2329          XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
2330       } else if (!paspi->sssi.next_valid) {
2331          /* last point of poly */
2332          paspi->vs[1].x = paspi->ruler_abs_x;
2333          paspi->vs[1].y = paspi->ruler_abs_y;
2334          paspi->dashed_vs[0].x = OFFSET_X(paspi->vs[1].x);
2335          paspi->dashed_vs[0].y = OFFSET_Y(paspi->vs[1].y);
2336          paspi->dashed_vs[1].x = OFFSET_X(paspi->vs[0].x);
2337          paspi->dashed_vs[1].y = OFFSET_Y(paspi->vs[0].y);
2338          MARKHO(drawWindow, revDefaultGC, paspi->dashed_vs[0].x,
2339                paspi->dashed_vs[0].y);
2340 
2341          values.line_style = LineOnOffDash;
2342          XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
2343          MyDashedLine(drawWindow, revDefaultGC, paspi->dashed_vs, 2);
2344          values.line_style = LineSolid;
2345          XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
2346       } else {
2347          if (paspi->before) {
2348             paspi->vs[1].x = paspi->ruler_abs_x;
2349             paspi->vs[1].y = paspi->ruler_abs_y;
2350             paspi->vs2[1].x = (paspi->vs[0].x<<1) - paspi->ruler_abs_x;
2351             paspi->vs2[1].y = (paspi->vs[0].y<<1) - paspi->ruler_abs_y;
2352             paspi->dashed_vs[0].x = OFFSET_X(paspi->vs[1].x);
2353             paspi->dashed_vs[0].y = OFFSET_Y(paspi->vs[1].y);
2354             paspi->dashed_vs[1].x = OFFSET_X(paspi->vs2[1].x);
2355             paspi->dashed_vs[1].y = OFFSET_Y(paspi->vs2[1].y);
2356             MARKHO(drawWindow, revDefaultGC, paspi->dashed_vs[0].x,
2357                   paspi->dashed_vs[0].y);
2358             MARKHO(drawWindow, revDefaultGC, paspi->dashed_vs[1].x,
2359                   paspi->dashed_vs[1].y);
2360 
2361             values.line_style = LineOnOffDash;
2362             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
2363             MyDashedLine(drawWindow, revDefaultGC, paspi->dashed_vs, 2);
2364             values.line_style = LineSolid;
2365             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
2366          } else {
2367             paspi->vs2[1].x = paspi->ruler_abs_x;
2368             paspi->vs2[1].y = paspi->ruler_abs_y;
2369             paspi->vs[1].x = (paspi->vs2[0].x<<1) - paspi->ruler_abs_x;
2370             paspi->vs[1].y = (paspi->vs2[0].y<<1) - paspi->ruler_abs_y;
2371             paspi->dashed_vs[0].x = OFFSET_X(paspi->vs2[1].x);
2372             paspi->dashed_vs[0].y = OFFSET_Y(paspi->vs2[1].y);
2373             paspi->dashed_vs[1].x = OFFSET_X(paspi->vs[1].x);
2374             paspi->dashed_vs[1].y = OFFSET_Y(paspi->vs[1].y);
2375             MARKHO(drawWindow, revDefaultGC, paspi->dashed_vs[0].x,
2376                   paspi->dashed_vs[0].y);
2377             MARKHO(drawWindow, revDefaultGC, paspi->dashed_vs[1].x,
2378                   paspi->dashed_vs[1].y);
2379 
2380             values.line_style = LineOnOffDash;
2381             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
2382             MyDashedLine(drawWindow, revDefaultGC, paspi->dashed_vs, 2);
2383             values.line_style = LineSolid;
2384             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
2385          }
2386       }
2387    }
2388    if (paspi->num_vs > 0) {
2389       paspi->sv = MakeMultiSplinePolyVertex(LT_STRUCT_SPLINE, &paspi->sn,
2390             paspi->smooth, drawOrigX, drawOrigY, paspi->num_vs, paspi->vs);
2391       XDrawLines(mainDisplay, drawWindow, revDefaultGC, paspi->sv, paspi->sn,
2392             CoordModeOrigin);
2393    }
2394    if (paspi->num_vs2 > 0) {
2395       paspi->sv2 = MakeMultiSplinePolyVertex(LT_STRUCT_SPLINE, &paspi->sn2,
2396             paspi->smooth2, drawOrigX, drawOrigY, paspi->num_vs2, paspi->vs2);
2397       XDrawLines(mainDisplay, drawWindow, revDefaultGC, paspi->sv2, paspi->sn2,
2398             CoordModeOrigin);
2399    }
2400    MarkRulers(OFFSET_X(paspi->ruler_abs_x), OFFSET_Y(paspi->ruler_abs_y));
2401    AddPointMeasureCursor(ADDPOINT_DOSHOW, ABS_SIZE(paspi->dx_off),
2402          ABS_SIZE(paspi->dy_off), paspi->ruler_abs_x, paspi->ruler_abs_y);
2403 }
2404 
2405 static
ContinueAddStructuredPolyOrPolygonPointFinished(paspi)2406 int ContinueAddStructuredPolyOrPolygonPointFinished(paspi)
2407    AddStructuredPointInfo *paspi;
2408 {
2409    int i=0, tmp_x=0, tmp_y=0, new_abs_x=0, new_abs_y=0, orig_hinge_index=0;
2410    int ok=TRUE;
2411    IntPoint *obj_vlist=NULL;
2412    struct ObjRec *obj_ptr=paspi->obj_ptr;
2413    struct PolyRec *poly_ptr=paspi->poly_ptr;
2414    struct PolygonRec *polygon_ptr=paspi->polygon_ptr;
2415 
2416    if (paspi->extra_smooth) {
2417       MARKHO(drawWindow, revDefaultGC, OFFSET_X(paspi->vs[1].x),
2418             OFFSET_Y(paspi->vs[1].y));
2419    }
2420    if (paspi->extra_smooth2) {
2421       MARKHO(drawWindow, revDefaultGC, OFFSET_X(paspi->vs2[1].x),
2422             OFFSET_Y(paspi->vs2[1].y));
2423    }
2424    if (!paspi->already_moved) {
2425       ok = FALSE;
2426    } else {
2427       if (paspi->ruler_abs_x == paspi->sssi.ipt.hinge_pt.x &&
2428             paspi->ruler_abs_y == paspi->sssi.ipt.hinge_pt.y) {
2429          ok = FALSE;
2430       }
2431       if (paspi->before) {
2432          if (paspi->ruler_abs_x == paspi->sssi.ipt_prev.hinge_pt.x &&
2433                paspi->ruler_abs_y == paspi->sssi.ipt_prev.hinge_pt.y) {
2434             ok = FALSE;
2435          }
2436       } else {
2437          if (paspi->ruler_abs_x == paspi->sssi.ipt_next.hinge_pt.x &&
2438                paspi->ruler_abs_y == paspi->sssi.ipt_next.hinge_pt.y) {
2439             ok = FALSE;
2440          }
2441       }
2442    }
2443    if (!ok) {
2444       free(paspi->ssvlist);
2445       free(paspi->vlist);
2446 
2447       return FALSE;
2448    }
2449    if (poly_ptr != NULL) {
2450       obj_vlist = poly_ptr->vlist;
2451    } else if (polygon_ptr != NULL) {
2452       obj_vlist = polygon_ptr->vlist;
2453    }
2454    HighLightReverse();
2455    if (paspi->button == Button1) {
2456       /* add a hinge point */
2457       IntPoint *new_vlist=(IntPoint*)malloc((paspi->n+4)*sizeof(IntPoint));
2458 
2459       if (new_vlist == NULL) FailAllocMessage();
2460       memset(new_vlist, 0, (paspi->n+4)*sizeof(IntPoint));
2461       obj_ptr = paspi->obj_ptr;
2462       new_abs_x = paspi->ruler_abs_x;
2463       new_abs_y = paspi->ruler_abs_y;
2464       if (obj_ptr->ctm != NULL) {
2465          ReverseTransformPointThroughCTM(new_abs_x-obj_ptr->x,
2466                new_abs_y-obj_ptr->y, obj_ptr->ctm, &tmp_x, &tmp_y);
2467          new_abs_x = obj_ptr->x+tmp_x;
2468          new_abs_y = obj_ptr->y+tmp_y;
2469       }
2470       if (paspi->before) {
2471          if (!paspi->sssi.prev_valid) {
2472             /* first point of poly */
2473             new_vlist[0].x = new_vlist[1].x = new_abs_x;
2474             new_vlist[0].y = new_vlist[1].y = new_abs_y;
2475             if (paspi->sssi.ipt.later_valid) {
2476                new_vlist[2].x = (obj_vlist[0].x<<1) -
2477                      obj_vlist[1].x;
2478                new_vlist[2].y = (obj_vlist[0].y<<1) -
2479                      obj_vlist[1].y;
2480             } else {
2481                new_vlist[2].x = obj_vlist[0].x;
2482                new_vlist[2].y = obj_vlist[0].y;
2483             }
2484             for (i=0; i < paspi->n; i++) {
2485                new_vlist[i+3].x = obj_vlist[i].x;
2486                new_vlist[i+3].y = obj_vlist[i].y;
2487             }
2488          } else if (!paspi->sssi.next_valid) {
2489             /* last point of poly */
2490             for (i=0; i < paspi->n-2; i++) {
2491                new_vlist[i].x = obj_vlist[i].x;
2492                new_vlist[i].y = obj_vlist[i].y;
2493             }
2494             new_vlist[paspi->n-2].x = new_vlist[paspi->n-1].x =
2495                   new_vlist[paspi->n].x = new_abs_x;
2496             new_vlist[paspi->n-2].y = new_vlist[paspi->n-1].y =
2497                   new_vlist[paspi->n].y = new_abs_y;
2498             new_vlist[paspi->n+1].x = obj_vlist[paspi->n-2].x;
2499             new_vlist[paspi->n+1].y = obj_vlist[paspi->n-2].y;
2500             new_vlist[paspi->n+2].x = obj_vlist[paspi->n-1].x;
2501             new_vlist[paspi->n+2].y = obj_vlist[paspi->n-1].y;
2502          } else {
2503             orig_hinge_index = paspi->sssi.orig_hinge_index;
2504             if (polygon_ptr != NULL && orig_hinge_index == 0) {
2505                orig_hinge_index = polygon_ptr->n-1;
2506             }
2507             for (i=0; i < orig_hinge_index-1; i++) {
2508                new_vlist[i].x = obj_vlist[i].x;
2509                new_vlist[i].y = obj_vlist[i].y;
2510             }
2511             new_vlist[orig_hinge_index-1].x = new_vlist[orig_hinge_index].x =
2512                   new_vlist[orig_hinge_index+1].x = new_abs_x;
2513             new_vlist[orig_hinge_index-1].y = new_vlist[orig_hinge_index].y =
2514                   new_vlist[orig_hinge_index+1].y = new_abs_y;
2515             for (i=orig_hinge_index-1; i < paspi->n; i++) {
2516                new_vlist[i+3].x = obj_vlist[i].x;
2517                new_vlist[i+3].y = obj_vlist[i].y;
2518             }
2519          }
2520       } else {
2521          if (!paspi->sssi.prev_valid) {
2522             /* first point of poly */
2523             for (i=0; i < 2; i++) {
2524                new_vlist[i].x = obj_vlist[i].x;
2525                new_vlist[i].y = obj_vlist[i].y;
2526             }
2527             new_vlist[2].x = new_vlist[3].x = new_vlist[4].x = new_abs_x;
2528             new_vlist[2].y = new_vlist[3].y = new_vlist[4].y = new_abs_y;
2529             for (i=2; i < paspi->n; i++) {
2530                new_vlist[i+3].x = obj_vlist[i].x;
2531                new_vlist[i+3].y = obj_vlist[i].y;
2532             }
2533          } else if (!paspi->sssi.next_valid) {
2534             /* last point of poly */
2535             for (i=0; i < paspi->n; i++) {
2536                new_vlist[i].x = obj_vlist[i].x;
2537                new_vlist[i].y = obj_vlist[i].y;
2538             }
2539             if (paspi->sssi.ipt.earlier_valid) {
2540                new_vlist[paspi->n].x =
2541                      (obj_vlist[paspi->n-1].x<<1) -
2542                      obj_vlist[paspi->n-2].x;
2543                new_vlist[paspi->n].y =
2544                      (obj_vlist[paspi->n-1].y<<1) -
2545                      obj_vlist[paspi->n-2].y;
2546             } else {
2547                new_vlist[paspi->n].x = obj_vlist[paspi->n-1].x;
2548                new_vlist[paspi->n].y = obj_vlist[paspi->n-1].y;
2549             }
2550             new_vlist[paspi->n+1].x = new_vlist[paspi->n+2].x = new_abs_x;
2551             new_vlist[paspi->n+1].y = new_vlist[paspi->n+2].y = new_abs_y;
2552          } else {
2553             orig_hinge_index = paspi->sssi.orig_hinge_index;
2554             for (i=0; i < orig_hinge_index+2; i++) {
2555                new_vlist[i].x = obj_vlist[i].x;
2556                new_vlist[i].y = obj_vlist[i].y;
2557             }
2558             new_vlist[orig_hinge_index+2].x = new_vlist[orig_hinge_index+3].x =
2559                   new_vlist[orig_hinge_index+4].x = new_abs_x;
2560             new_vlist[orig_hinge_index+2].y = new_vlist[orig_hinge_index+3].y =
2561                   new_vlist[orig_hinge_index+4].y = new_abs_y;
2562             for (i=orig_hinge_index+2; i < paspi->n; i++) {
2563                new_vlist[i+3].x = obj_vlist[i].x;
2564                new_vlist[i+3].y = obj_vlist[i].y;
2565             }
2566          }
2567       }
2568       free(obj_vlist);
2569       if (poly_ptr != NULL) {
2570          paspi->poly_ptr->vlist = new_vlist;
2571          paspi->poly_ptr->n += 3;
2572       } else if (polygon_ptr != NULL) {
2573          paspi->polygon_ptr->vlist = new_vlist;
2574          paspi->polygon_ptr->n += 3;
2575       }
2576    } else { /* paspi->button == Button2 */
2577       obj_ptr = paspi->obj_ptr;
2578       new_abs_x = paspi->ruler_abs_x;
2579       new_abs_y = paspi->ruler_abs_y;
2580       if (obj_ptr->ctm != NULL) {
2581          ReverseTransformPointThroughCTM(new_abs_x-obj_ptr->x,
2582                new_abs_y-obj_ptr->y, obj_ptr->ctm, &tmp_x, &tmp_y);
2583          new_abs_x = obj_ptr->x+tmp_x;
2584          new_abs_y = obj_ptr->y+tmp_y;
2585       }
2586       if (!paspi->sssi.prev_valid) {
2587          /* first point of poly */
2588          obj_vlist[1].x = new_abs_x;
2589          obj_vlist[1].y = new_abs_y;
2590       } else if (!paspi->sssi.next_valid) {
2591          /* last point of poly */
2592          obj_vlist[paspi->n-2].x = new_abs_x;
2593          obj_vlist[paspi->n-2].y = new_abs_y;
2594       } else {
2595          orig_hinge_index = paspi->sssi.orig_hinge_index;
2596          if (paspi->before) {
2597             if (polygon_ptr != NULL && orig_hinge_index == 0) {
2598                obj_vlist[paspi->n-2].x = new_abs_x;
2599                obj_vlist[paspi->n-2].y = new_abs_y;
2600                obj_vlist[orig_hinge_index+1].x =
2601                      (obj_vlist[orig_hinge_index].x<<1) - new_abs_x;
2602                obj_vlist[orig_hinge_index+1].y =
2603                      (obj_vlist[orig_hinge_index].y<<1) - new_abs_y;
2604             } else {
2605                obj_vlist[orig_hinge_index-1].x = new_abs_x;
2606                obj_vlist[orig_hinge_index-1].y = new_abs_y;
2607                obj_vlist[orig_hinge_index+1].x =
2608                      (obj_vlist[orig_hinge_index].x<<1) - new_abs_x;
2609                obj_vlist[orig_hinge_index+1].y =
2610                      (obj_vlist[orig_hinge_index].y<<1) - new_abs_y;
2611             }
2612          } else {
2613             if (polygon_ptr != NULL && orig_hinge_index == 0) {
2614                obj_vlist[orig_hinge_index+1].x = new_abs_x;
2615                obj_vlist[orig_hinge_index+1].y = new_abs_y;
2616                obj_vlist[paspi->n-2].x =
2617                      (obj_vlist[orig_hinge_index].x<<1) - new_abs_x;
2618                obj_vlist[paspi->n-2].y =
2619                      (obj_vlist[orig_hinge_index].y<<1) - new_abs_y;
2620             } else {
2621                obj_vlist[orig_hinge_index+1].x = new_abs_x;
2622                obj_vlist[orig_hinge_index+1].y = new_abs_y;
2623                obj_vlist[orig_hinge_index-1].x =
2624                      (obj_vlist[orig_hinge_index].x<<1) - new_abs_x;
2625                obj_vlist[orig_hinge_index-1].y =
2626                      (obj_vlist[orig_hinge_index].y<<1) - new_abs_y;
2627             }
2628          }
2629       }
2630    }
2631    AdjObjSplineVs(obj_ptr);
2632    if (poly_ptr != NULL) {
2633       UpdPolyBBox(obj_ptr, poly_ptr->n, poly_ptr->vlist);
2634    } else if (polygon_ptr != NULL) {
2635       UpdPolyBBox(obj_ptr, polygon_ptr->n, polygon_ptr->vlist);
2636    }
2637    AdjObjBBox(obj_ptr);
2638 
2639    UpdSelBBox();
2640    RedrawAreas(botObj,
2641          paspi->sel_ltx-GRID_ABS_SIZE(1), paspi->sel_lty-GRID_ABS_SIZE(1),
2642          paspi->sel_rbx+GRID_ABS_SIZE(1), paspi->sel_rby+GRID_ABS_SIZE(1),
2643          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
2644          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
2645    HighLightForward();
2646    SetFileModified(TRUE);
2647    justDupped = FALSE;
2648 
2649    free(paspi->ssvlist);
2650    free(paspi->vlist);
2651 
2652    return TRUE;
2653 }
2654 
2655 static
EraseHighLightForAddStructuredPolyPoint(paspi,abs_dx,abs_dy,measure_cursor_mode)2656 void EraseHighLightForAddStructuredPolyPoint(paspi, abs_dx, abs_dy,
2657       measure_cursor_mode)
2658    AddStructuredPointInfo *paspi;
2659    int abs_dx, abs_dy, measure_cursor_mode;
2660 {
2661    if (paspi->sv != NULL) {
2662       XDrawLines(mainDisplay, drawWindow, revDefaultGC, paspi->sv, paspi->sn,
2663             CoordModeOrigin);
2664       free(paspi->sv);
2665       paspi->sv = NULL;
2666    }
2667    if (paspi->sv2 != NULL) {
2668       XDrawLines(mainDisplay, drawWindow, revDefaultGC, paspi->sv2, paspi->sn2,
2669             CoordModeOrigin);
2670       free(paspi->sv2);
2671       paspi->sv2 = NULL;
2672    }
2673    AddPointMeasureCursor(measure_cursor_mode, abs_dx, abs_dy, paspi->tx+abs_dx,
2674          paspi->ty+abs_dy);
2675    if (paspi->button == Button1) {
2676       MARKHR(drawWindow, revDefaultGC, OFFSET_X(paspi->ruler_abs_x),
2677             OFFSET_Y(paspi->ruler_abs_y));
2678    } else {
2679       XGCValues values;
2680 
2681       if (paspi->num_vs > 0) {
2682          MARKHO(drawWindow, revDefaultGC, OFFSET_X(paspi->vs[1].x),
2683                OFFSET_Y(paspi->vs[1].y));
2684       }
2685       if (paspi->num_vs2 > 0) {
2686          MARKHO(drawWindow, revDefaultGC, OFFSET_X(paspi->vs2[1].x),
2687                OFFSET_Y(paspi->vs2[1].y));
2688       }
2689       XSetDashes(mainDisplay, revDefaultGC, 0, dashList[8], dashListLength[8]);
2690       values.line_style = LineOnOffDash;
2691       XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
2692       MyDashedLine(drawWindow, revDefaultGC, paspi->dashed_vs, 2);
2693       values.line_style = LineSolid;
2694       XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
2695    }
2696 }
2697 
2698 static
ContinueAddStructuredPolyOrPolygonPoint(obj_ptr,button,start_mouse_x,start_mouse_y,index,poly_ptr,polygon_ptr,pn_last_mouse_x,pn_last_mouse_y)2699 int ContinueAddStructuredPolyOrPolygonPoint(obj_ptr, button, start_mouse_x,
2700       start_mouse_y, index, poly_ptr, polygon_ptr, pn_last_mouse_x,
2701       pn_last_mouse_y)
2702    struct ObjRec *obj_ptr;
2703    unsigned int button;
2704    int start_mouse_x, start_mouse_y, index, *pn_last_mouse_x, *pn_last_mouse_y;
2705    struct PolyRec *poly_ptr;
2706    struct PolygonRec *polygon_ptr;
2707    /* (start_mouse_x,start_mouse_y) is the mouse's origin in screen offsets */
2708 {
2709    AddStructuredPointInfo aspi;
2710    int done=FALSE;
2711 
2712    memset(&aspi, 0, sizeof(AddStructuredPointInfo));
2713    aspi.before = INVALID;
2714    aspi.obj_ptr = obj_ptr;
2715    aspi.button = button;
2716    aspi.start_mouse_x = start_mouse_x;
2717    aspi.start_mouse_y = start_mouse_y;
2718    aspi.index = index;
2719    if (poly_ptr != NULL) {
2720       aspi.poly_ptr = poly_ptr;
2721    } else if (polygon_ptr != NULL) {
2722       aspi.polygon_ptr = polygon_ptr;
2723    } else {
2724       /* should not come here */
2725       return FALSE;
2726    }
2727    if (!ContinueAddStructuredPolyOrPolygonPointSetup(&aspi)) {
2728       return FALSE;
2729    }
2730    while (!done) {
2731       XEvent input, ev;
2732 
2733       XNextEvent(mainDisplay, &input);
2734 
2735       if (input.type == Expose || input.type == VisibilityNotify) {
2736          ExposeEventHandler(&input, TRUE);
2737       } else if (input.type == MotionNotify || input.type == KeyPress ||
2738             input.type == KeyRelease) {
2739          if (input.type == KeyPress || input.type == KeyRelease) {
2740             aspi.mouse_x = aspi.grid_x;
2741             aspi.mouse_y = aspi.grid_y;
2742          } else {
2743             aspi.mouse_x = input.xmotion.x;
2744             aspi.mouse_y = input.xmotion.y;
2745          }
2746          if (shiftForDiagMouseMove && DiagEventCheck(&input)) {
2747             if (input.type == KeyRelease) {
2748                aspi.mouse_x = input.xkey.x;
2749                aspi.mouse_y = input.xkey.y;
2750             } else {
2751                DiagGridXY(aspi.orig_grid_x, aspi.orig_grid_y, &aspi.mouse_x,
2752                      &aspi.mouse_y);
2753             }
2754          }
2755          GridXY(aspi.mouse_x, aspi.mouse_y, &aspi.grid_x, &aspi.grid_y);
2756          /* erase */
2757          EraseHighLightForAddStructuredPolyPoint(&aspi, ABS_SIZE(aspi.dx_off),
2758                ABS_SIZE(aspi.dy_off), ADDPOINT_DOSHOW);
2759          aspi.dx_off = aspi.grid_x - aspi.orig_grid_x;
2760          aspi.dy_off = aspi.grid_y - aspi.orig_grid_y;
2761          aspi.ruler_abs_x = aspi.tx + ABS_SIZE(aspi.dx_off);
2762          aspi.ruler_abs_y = aspi.ty + ABS_SIZE(aspi.dy_off);
2763          MarkRulers(OFFSET_X(aspi.ruler_abs_x), OFFSET_Y(aspi.ruler_abs_y));
2764 
2765          if (!aspi.already_moved) {
2766             ContinueAddStructuredPolyOrPolygonPointFirstMoved(&aspi);
2767          }
2768          ContinueAddStructuredPolyOrPolygonPointRestMoved(&aspi);
2769          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
2770       } else if (input.type == ButtonRelease) {
2771          done = TRUE;
2772          *pn_last_mouse_x = aspi.mouse_x;
2773          *pn_last_mouse_y = aspi.mouse_y;
2774          /* erase */
2775          EraseHighLightForAddStructuredPolyPoint(&aspi, ABS_SIZE(aspi.dx_off),
2776                ABS_SIZE(aspi.dy_off), ADDPOINT_DOSHOW);
2777 
2778          return ContinueAddStructuredPolyOrPolygonPointFinished(&aspi);
2779       }
2780    }
2781    return TRUE;
2782 }
2783 
2784 static
ContinueAddPolyPoint(ObjPtr,MouseX,MouseY,Index,PolyPtr,LastMouseX,LastMouseY)2785 int ContinueAddPolyPoint(ObjPtr, MouseX, MouseY, Index, PolyPtr,
2786       LastMouseX, LastMouseY)
2787    struct ObjRec *ObjPtr;
2788    int MouseX, MouseY, Index;
2789    struct PolyRec *PolyPtr;
2790    int *LastMouseX, *LastMouseY;
2791    /*
2792     * (MouseX,MouseY) is the mouse's origin in screen offsets
2793     *
2794     * This routine is only called for LT_STRAIGHT, LT_SPLINE, or LT_INTSPLINE.
2795     */
2796 {
2797    int n=PolyPtr->n, sn=0, curved=PolyPtr->curved;
2798    int already_moved=FALSE, done=FALSE, before=INVALID;
2799    XPoint v[3], *sv=NULL;
2800    IntPoint *vs=PolyPtr->vlist, *pv=NULL, *cntrlv=NULL;
2801    int prev_x, prev_y, prev_tx, prev_ty, x, y, tx, ty, tmp_x, tmp_y;
2802    int next_x, next_y, next_tx, next_ty;
2803    int orig_x, orig_y, grid_x, grid_y, new_mouse_x, new_mouse_y;
2804    int sel_ltx, sel_lty, sel_rbx, sel_rby, num=0, i, intn=0;
2805    char *smooth=PolyPtr->smooth, *tmp_smooth=NULL;
2806    double prev_angle, next_angle, prev_dist, next_dist, dx, dy;
2807 
2808 #ifdef _TGIF_DBG /* debug, do not translate */
2809    TgAssert(curved != LT_STRUCT_SPLINE,
2810          "curved == LT_STRUCT_SPLINE in ContinueAddPolyPoint()", NULL);
2811 #endif /* _TGIF_DBG */
2812    memset(v, 0, sizeof(XPoint)*3);
2813    sel_ltx = selLtX; sel_lty = selLtY;
2814    sel_rbx = selRbX; sel_rby = selRbY;
2815 
2816    x = tx = vs[Index].x;
2817    y = ty = vs[Index].y;
2818    if (ObjPtr->ctm != NULL) {
2819       TransformPointThroughCTM(x-ObjPtr->x, y-ObjPtr->y, ObjPtr->ctm,
2820             &tmp_x, &tmp_y);
2821       tx = ObjPtr->x+tmp_x;
2822       ty = ObjPtr->y+tmp_y;
2823    }
2824    if (curved == LT_INTSPLINE || smooth == NULL || !smooth[Index]) {
2825       MARK(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
2826    } else {
2827       MARKO(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
2828    }
2829    if (Index == 0) {
2830       next_x = next_tx = vs[1].x;
2831       next_y = next_ty = vs[1].y;
2832       prev_x = prev_tx = 2*x-next_x;
2833       prev_y = prev_ty = 2*y-next_y;
2834    } else if (Index == n-1) {
2835       prev_x = prev_tx = vs[n-2].x;
2836       prev_y = prev_ty = vs[n-2].y;
2837       next_x = next_tx = 2*x-prev_x;
2838       next_y = next_ty = 2*y-prev_y;
2839    } else {
2840       prev_x = prev_tx = vs[Index-1].x;
2841       prev_y = prev_ty = vs[Index-1].y;
2842       next_x = next_tx = vs[Index+1].x;
2843       next_y = next_ty = vs[Index+1].y;
2844    }
2845    if (ObjPtr->ctm != NULL) {
2846       TransformPointThroughCTM(next_x-ObjPtr->x, next_y-ObjPtr->y,
2847             ObjPtr->ctm, &tmp_x, &tmp_y);
2848       next_tx = ObjPtr->x+tmp_x;
2849       next_ty = ObjPtr->y+tmp_y;
2850       TransformPointThroughCTM(prev_x-ObjPtr->x, prev_y-ObjPtr->y,
2851             ObjPtr->ctm, &tmp_x, &tmp_y);
2852       prev_tx = ObjPtr->x+tmp_x;
2853       prev_ty = ObjPtr->y+tmp_y;
2854    }
2855    dx = (double)(prev_tx - tx);
2856    dy = (double)(prev_ty - ty);
2857    prev_dist = dx*dx+dy*dy;
2858    dx = (double)(next_tx - tx);
2859    dy = (double)(next_ty - ty);
2860    next_dist = dx*dx+dy*dy;
2861 
2862    prev_angle = (prev_tx==tx) ? ((prev_ty>=ty) ? M_PI/2.0 : -M_PI/2.0) :
2863          atan2((double)(prev_ty-ty), (double)(prev_tx-tx));
2864    next_angle = (next_tx==tx) ? ((next_ty>=ty) ? M_PI/2.0 : -M_PI/2.0) :
2865          atan2((double)(next_ty-ty), (double)(next_tx-tx));
2866    pv = (IntPoint*)malloc((n+2)*sizeof(IntPoint));
2867    if (pv == NULL) FailAllocMessage();
2868    if (curved != LT_INTSPLINE && smooth != NULL) {
2869       tmp_smooth = (char*)malloc((n+2)*sizeof(char));
2870       if (tmp_smooth == NULL) FailAllocMessage();
2871    }
2872    if (ObjPtr->ctm == NULL) {
2873       for (i = 0; i <= Index; i++) {
2874          pv[i].x = vs[i].x;
2875          pv[i].y = vs[i].y;
2876          if (tmp_smooth != NULL) tmp_smooth[i] = smooth[i];
2877       }
2878       for (i = Index; i < n; i++) {
2879          pv[i+1].x = vs[i].x;
2880          pv[i+1].y = vs[i].y;
2881          if (tmp_smooth != NULL) tmp_smooth[i+1] = smooth[i];
2882       }
2883    } else {
2884       for (i = 0; i <= Index; i++) {
2885          TransformPointThroughCTM(vs[i].x-ObjPtr->x, vs[i].y-ObjPtr->y,
2886                ObjPtr->ctm, &tmp_x, &tmp_y);
2887          pv[i].x = ObjPtr->x+tmp_x;
2888          pv[i].y = ObjPtr->y+tmp_y;
2889          if (tmp_smooth != NULL) tmp_smooth[i] = smooth[i];
2890       }
2891       for (i = Index; i < n; i++) {
2892          TransformPointThroughCTM(vs[i].x-ObjPtr->x, vs[i].y-ObjPtr->y,
2893                ObjPtr->ctm, &tmp_x, &tmp_y);
2894          pv[i+1].x = ObjPtr->x+tmp_x;
2895          pv[i+1].y = ObjPtr->y+tmp_y;
2896          if (tmp_smooth != NULL) tmp_smooth[i+1] = smooth[i];
2897       }
2898    }
2899    GridXY(MouseX, MouseY, &orig_x, &orig_y);
2900    grid_x = orig_x;
2901    grid_y = orig_y;
2902    new_mouse_x = MouseX; new_mouse_y = MouseY;
2903    AddPointMeasureCursor(ADDPOINT_STARTSHOW, 0, 0, tx, ty);
2904 
2905    while (!done) {
2906       double new_angle, theta_1, theta_2;
2907       XEvent input, ev;
2908 
2909       XNextEvent(mainDisplay, &input);
2910 
2911       if (input.type == Expose || input.type == VisibilityNotify) {
2912          ExposeEventHandler(&input, TRUE);
2913       } else if (input.type == MotionNotify || input.type == KeyPress ||
2914             input.type == KeyRelease) {
2915          int new_x, new_y;
2916 
2917          if (input.type == KeyPress || input.type == KeyRelease) {
2918             new_mouse_x = grid_x;
2919             new_mouse_y = grid_y;
2920          } else {
2921             new_mouse_x = input.xmotion.x;
2922             new_mouse_y = input.xmotion.y;
2923          }
2924          if (shiftForDiagMouseMove && DiagEventCheck(&input)) {
2925             if (input.type == KeyRelease) {
2926                new_mouse_x = input.xkey.x;
2927                new_mouse_y = input.xkey.y;
2928             } else {
2929                DiagGridXY(orig_x, orig_y, &new_mouse_x, &new_mouse_y);
2930             }
2931          }
2932          GridXY(new_mouse_x, new_mouse_y, &grid_x, &grid_y);
2933          new_x = ABS_SIZE(new_mouse_x-MouseX) + tx;
2934          new_y = ABS_SIZE(new_mouse_y-MouseY) + ty;
2935          if (!already_moved) {
2936             double new_prev_dist, new_next_dist;
2937 
2938             already_moved = TRUE;
2939 
2940             dx = (double)(prev_tx - new_mouse_x);
2941             dy = (double)(prev_ty - new_mouse_y);
2942             new_prev_dist = dx*dx+dy*dy;
2943             dx = (double)(next_tx - new_mouse_x);
2944             dy = (double)(next_ty - new_mouse_y);
2945             new_next_dist = dx*dx+dy*dy;
2946 
2947             before = DetermineBefore(prev_dist, next_dist, new_prev_dist,
2948                   new_next_dist);
2949             if (before == INVALID) {
2950                new_angle = (new_x==tx) ? ((new_y>=ty) ? M_PI/2.0 : -M_PI/2.0) :
2951                      atan2((double)(new_y-ty), (double)(new_x-tx));
2952                theta_1 = fabs(prev_angle - new_angle);
2953                theta_2 = fabs(next_angle - new_angle);
2954                if (theta_1 > M_PI) theta_1 = 2*M_PI-theta_1;
2955                if (theta_2 > M_PI) theta_2 = 2*M_PI-theta_2;
2956                before = (theta_1 <= theta_2);
2957             }
2958             if (before) {
2959                /* Add a point between the current and the previous point */
2960                if (Index == 0) {
2961                   num = 2;
2962                   v[0].x = OFFSET_X(tx); v[0].y = OFFSET_Y(ty);
2963                   v[1].x = OFFSET_X(tx); v[1].y = OFFSET_Y(ty);
2964                } else {
2965                   num = 3;
2966                   v[0].x = OFFSET_X(prev_tx); v[0].y = OFFSET_Y(prev_ty);
2967                   v[1].x = OFFSET_X(tx);      v[1].y = OFFSET_Y(ty);
2968                   v[2].x = OFFSET_X(tx);      v[2].y = OFFSET_Y(ty);
2969                }
2970             } else {
2971                /* Add a point between the current and the next point */
2972                if (Index == n-1) {
2973                   num = 2;
2974                   v[0].x = OFFSET_X(tx);      v[0].y = OFFSET_Y(ty);
2975                   v[1].x = OFFSET_X(tx);      v[1].y = OFFSET_Y(ty);
2976                } else {
2977                   num = 3;
2978                   v[0].x = OFFSET_X(tx);      v[0].y = OFFSET_Y(ty);
2979                   v[1].x = OFFSET_X(tx);      v[1].y = OFFSET_Y(ty);
2980                   v[2].x = OFFSET_X(next_tx); v[2].y = OFFSET_Y(next_ty);
2981                }
2982             }
2983             switch (curved) {
2984             case LT_STRAIGHT:
2985             case LT_SPLINE:
2986                sv = MakeMultiSplinePolyVertex(curved, &sn, tmp_smooth,
2987                      drawOrigX, drawOrigY, n+1, pv);
2988                break;
2989             case LT_INTSPLINE:
2990                sv = MakeIntSplinePolyVertex(&sn, &intn, &cntrlv,
2991                      drawOrigX, drawOrigY, n+1, pv);
2992                break;
2993             case LT_STRUCT_SPLINE:
2994                /* should never get here */
2995                break;
2996             }
2997             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
2998                   CoordModeOrigin);
2999          } else {
3000             AddPointMeasureCursor(ADDPOINT_DOSHOW, abs(ABS_X(v[1].x)-tx),
3001                   abs(ABS_Y(v[1].y)-ty), ABS_X(v[1].x), ABS_Y(v[1].y));
3002             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
3003                   CoordModeOrigin);
3004             v[1].x = OFFSET_X(tx) + grid_x - orig_x;
3005             v[1].y = OFFSET_Y(ty) + grid_y - orig_y;
3006             free(sv);
3007             if (before) {
3008                pv[Index].x = tx + ABS_SIZE(grid_x-orig_x);
3009                pv[Index].y = ty + ABS_SIZE(grid_y-orig_y);
3010             } else {
3011                pv[Index+1].x = tx + ABS_SIZE(grid_x-orig_x);
3012                pv[Index+1].y = ty + ABS_SIZE(grid_y-orig_y);
3013             }
3014             switch (curved) {
3015             case LT_STRAIGHT:
3016             case LT_SPLINE:
3017                sv = MakeMultiSplinePolyVertex(curved, &sn, tmp_smooth,
3018                      drawOrigX, drawOrigY, n+1, pv);
3019                break;
3020             case LT_INTSPLINE:
3021                free(cntrlv);
3022                sv = MakeIntSplinePolyVertex(&sn, &intn, &cntrlv,
3023                      drawOrigX, drawOrigY, n+1, pv);
3024                break;
3025             case LT_STRUCT_SPLINE:
3026                /* should never get here */
3027                break;
3028             }
3029             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
3030                   CoordModeOrigin);
3031             MarkRulers(v[1].x, v[1].y);
3032             AddPointMeasureCursor(ADDPOINT_DOSHOW, abs(ABS_X(v[1].x)-tx),
3033                   abs(ABS_Y(v[1].y)-ty), ABS_X(v[1].x), ABS_Y(v[1].y));
3034          }
3035          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
3036       } else if (input.type == ButtonRelease) {
3037          done = TRUE;
3038          *LastMouseX = new_mouse_x; *LastMouseY = new_mouse_y;
3039          if (curved == LT_INTSPLINE || smooth == NULL || !smooth[Index]) {
3040             MARK(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
3041          } else {
3042             MARKO(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
3043          }
3044          if (already_moved) {
3045             AddPointMeasureCursor(ADDPOINT_ENDSHOW, abs(ABS_X(v[1].x)-tx),
3046                   abs(ABS_Y(v[1].y)-ty), ABS_X(v[1].x), ABS_Y(v[1].y));
3047          } else {
3048             AddPointMeasureCursor(ADDPOINT_STARTSHOW, 0, 0, tx, ty);
3049          }
3050          if (!already_moved) {
3051             return FALSE;
3052          } else {
3053             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
3054                   CoordModeOrigin);
3055             if (grid_x == orig_x && grid_y == orig_y) {
3056                return FALSE;
3057             }
3058          }
3059          HighLightReverse();
3060          vs = (IntPoint*)realloc(vs, (n+2)*sizeof(IntPoint));
3061          if (vs == NULL) return FailAllocMessage();
3062          if (smooth != NULL) {
3063             smooth = (char*)realloc(smooth, (n+2)*sizeof(char));
3064             if (smooth == NULL) return FailAllocMessage();
3065          }
3066          PolyPtr->vlist = vs;
3067          PolyPtr->smooth = smooth;
3068          if (before) {
3069             for (i = n-1; i >= Index; i--) {
3070                vs[i+1] = vs[i];
3071                if (smooth != NULL) smooth[i+1] = smooth[i];
3072             }
3073             if (ObjPtr->ctm == NULL) {
3074                vs[Index].x = x + ABS_SIZE(grid_x-orig_x);
3075                vs[Index].y = y + ABS_SIZE(grid_y-orig_y);
3076             } else {
3077                vs[Index].x = tx + ABS_SIZE(grid_x-orig_x);
3078                vs[Index].y = ty + ABS_SIZE(grid_y-orig_y);
3079                ReverseTransformPointThroughCTM(vs[Index].x-ObjPtr->x,
3080                         vs[Index].y-ObjPtr->y, ObjPtr->ctm, &tmp_x, &tmp_y);
3081                vs[Index].x = ObjPtr->x+tmp_x;
3082                vs[Index].y = ObjPtr->y+tmp_y;
3083             }
3084             if (smooth != NULL) smooth[Index] = smooth[Index+1];
3085          } else {
3086             for (i = n-1; i > Index; i--) {
3087                vs[i+1] = vs[i];
3088                if (smooth != NULL) smooth[i+1] = smooth[i];
3089             }
3090             if (ObjPtr->ctm == NULL) {
3091                vs[Index+1].x = x + ABS_SIZE(grid_x-orig_x);
3092                vs[Index+1].y = y + ABS_SIZE(grid_y-orig_y);
3093             } else {
3094                vs[Index+1].x = tx + ABS_SIZE(grid_x-orig_x);
3095                vs[Index+1].y = ty + ABS_SIZE(grid_y-orig_y);
3096                ReverseTransformPointThroughCTM(vs[Index+1].x-ObjPtr->x,
3097                         vs[Index+1].y-ObjPtr->y, ObjPtr->ctm, &tmp_x, &tmp_y);
3098                vs[Index+1].x = ObjPtr->x+tmp_x;
3099                vs[Index+1].y = ObjPtr->y+tmp_y;
3100             }
3101             if (smooth != NULL) smooth[Index+1] = smooth[Index];
3102          }
3103          if (sv != NULL) {
3104             free(sv);
3105             sv = NULL;
3106          }
3107          if (pv != NULL) {
3108             free(pv);
3109             pv = NULL;
3110          }
3111          if (tmp_smooth != NULL) free(tmp_smooth);
3112          if (curved == LT_INTSPLINE && cntrlv != NULL) free(cntrlv);
3113          PolyPtr->n++;
3114          n++;
3115          AdjObjSplineVs(ObjPtr);
3116          if (curved != LT_INTSPLINE) {
3117             UpdPolyBBox(ObjPtr, PolyPtr->n, PolyPtr->vlist);
3118          } else {
3119             UpdPolyBBox(ObjPtr, PolyPtr->intn, PolyPtr->intvlist);
3120          }
3121          AdjObjBBox(ObjPtr);
3122 
3123          UpdSelBBox();
3124          RedrawAreas(botObj,
3125                sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
3126                sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
3127                selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
3128                selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
3129          HighLightForward();
3130          SetFileModified(TRUE);
3131          justDupped = FALSE;
3132       } else if (input.type == KeyPress) {
3133          if (KeyPressEventIsEscape(&input.xkey)) {
3134             done = TRUE;
3135             *LastMouseX = new_mouse_x; *LastMouseY = new_mouse_y;
3136             if (curved == LT_INTSPLINE || smooth == NULL || !smooth[Index]) {
3137                MARK(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
3138             } else {
3139                MARKO(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
3140             }
3141             if (already_moved) {
3142                AddPointMeasureCursor(ADDPOINT_ENDSHOW, abs(ABS_X(v[1].x)-tx),
3143                      abs(ABS_Y(v[1].y)-ty), ABS_X(v[1].x), ABS_Y(v[1].y));
3144             } else {
3145                AddPointMeasureCursor(ADDPOINT_STARTSHOW, 0, 0, tx, ty);
3146             }
3147             if (already_moved) {
3148                XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
3149                      CoordModeOrigin);
3150                if (grid_x == orig_x && grid_y == orig_y) {
3151                   return FALSE;
3152                }
3153             }
3154             return FALSE;
3155          }
3156       }
3157    }
3158    return TRUE;
3159 }
3160 
3161 static
ContinueAddPolygonPoint(ObjPtr,MouseX,MouseY,Index,PolygonPtr,LastMouseX,LastMouseY)3162 int ContinueAddPolygonPoint(ObjPtr, MouseX, MouseY, Index, PolygonPtr,
3163       LastMouseX, LastMouseY)
3164    struct ObjRec *ObjPtr;
3165    int MouseX, MouseY, Index;
3166    struct PolygonRec *PolygonPtr;
3167    int *LastMouseX, *LastMouseY;
3168    /*
3169     * (MouseX,MouseY) is the mouse's origin in screen offsets
3170     *
3171     * This routine is only called for LT_STRAIGHT, LT_SPLINE, or LT_INTSPLINE.
3172     */
3173 {
3174    int n=PolygonPtr->n, sn=0, curved=PolygonPtr->curved;
3175    int already_moved=FALSE, done=FALSE, before=FALSE;
3176    XPoint v[3], *sv=NULL;
3177    IntPoint *vs=PolygonPtr->vlist, *pv=NULL, *cntrlv=NULL;
3178    int prev_x, prev_y, prev_tx, prev_ty, x, y, tx, ty, tmp_x, tmp_y;
3179    int next_x, next_y, next_tx, next_ty;
3180    int orig_x, orig_y, grid_x, grid_y, new_mouse_x, new_mouse_y;
3181    int sel_ltx, sel_lty, sel_rbx, sel_rby, i, intn=0;
3182    char *smooth=PolygonPtr->smooth, *tmp_smooth=NULL;
3183    double prev_angle, next_angle, prev_dist, next_dist, dx, dy;
3184 
3185    memset(v, 0, sizeof(XPoint)*3);
3186 #ifdef _TGIF_DBG /* debug, do not translate */
3187    TgAssert(curved != LT_STRUCT_SPLINE,
3188          "curved == LT_STRUCT_SPLINE in ContinueAddPolygonPoint()", NULL);
3189 #endif /* _TGIF_DBG */
3190    sel_ltx = selLtX; sel_lty = selLtY;
3191    sel_rbx = selRbX; sel_rby = selRbY;
3192 
3193    x = tx = vs[Index].x;
3194    y = ty = vs[Index].y;
3195    if (ObjPtr->ctm != NULL) {
3196       TransformPointThroughCTM(x-ObjPtr->x, y-ObjPtr->y, ObjPtr->ctm,
3197             &tmp_x, &tmp_y);
3198       tx = ObjPtr->x+tmp_x;
3199       ty = ObjPtr->y+tmp_y;
3200    }
3201    if (curved == LT_INTSPLINE || smooth == NULL || !smooth[Index]) {
3202       MARK(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
3203    } else {
3204       MARKO(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
3205    }
3206    if (Index == 0 || Index == n-1) {
3207       next_x = next_tx = vs[1].x;
3208       next_y = next_ty = vs[1].y;
3209       prev_x = prev_tx = vs[n-2].x;
3210       prev_y = prev_ty = vs[n-2].y;
3211    } else {
3212       prev_x = prev_tx = vs[Index-1].x;
3213       prev_y = prev_ty = vs[Index-1].y;
3214       next_x = next_tx = vs[Index+1].x;
3215       next_y = next_ty = vs[Index+1].y;
3216    }
3217    if (ObjPtr->ctm != NULL) {
3218       TransformPointThroughCTM(next_x-ObjPtr->x, next_y-ObjPtr->y,
3219             ObjPtr->ctm, &tmp_x, &tmp_y);
3220       next_tx = ObjPtr->x+tmp_x;
3221       next_ty = ObjPtr->y+tmp_y;
3222       TransformPointThroughCTM(prev_x-ObjPtr->x, prev_y-ObjPtr->y,
3223             ObjPtr->ctm, &tmp_x, &tmp_y);
3224       prev_tx = ObjPtr->x+tmp_x;
3225       prev_ty = ObjPtr->y+tmp_y;
3226    }
3227    dx = (double)(prev_tx - tx);
3228    dy = (double)(prev_ty - ty);
3229    prev_dist = dx*dx+dy*dy;
3230    dx = (double)(next_tx - tx);
3231    dy = (double)(next_ty - ty);
3232    next_dist = dx*dx+dy*dy;
3233 
3234    prev_angle = (prev_tx==tx) ? ((prev_ty>=ty) ? M_PI/2.0 : -M_PI/2.0) :
3235          atan2((double)(prev_ty-ty), (double)(prev_tx-tx));
3236    next_angle = (next_tx==tx) ? ((next_ty>=ty) ? M_PI/2.0 : -M_PI/2.0) :
3237          atan2((double)(next_ty-ty), (double)(next_tx-tx));
3238    pv = (IntPoint *)malloc((n+2)*sizeof(IntPoint));
3239    if (pv == NULL) FailAllocMessage();
3240    if (curved != LT_INTSPLINE && smooth != NULL) {
3241       tmp_smooth = (char*)malloc((n+2)*sizeof(char));
3242       if (tmp_smooth == NULL) FailAllocMessage();
3243    }
3244    if (ObjPtr->ctm == NULL) {
3245       for (i = 0; i <= Index; i++) {
3246          pv[i].x = vs[i].x;
3247          pv[i].y = vs[i].y;
3248          if (tmp_smooth != NULL) tmp_smooth[i] = smooth[i];
3249       }
3250       for (i = Index; i < n; i++) {
3251          pv[i+1].x = vs[i].x;
3252          pv[i+1].y = vs[i].y;
3253          if (tmp_smooth != NULL) tmp_smooth[i+1] = smooth[i];
3254       }
3255    } else {
3256       for (i = 0; i <= Index; i++) {
3257          TransformPointThroughCTM(vs[i].x-ObjPtr->x, vs[i].y-ObjPtr->y,
3258                ObjPtr->ctm, &tmp_x, &tmp_y);
3259          pv[i].x = ObjPtr->x+tmp_x;
3260          pv[i].y = ObjPtr->y+tmp_y;
3261          if (tmp_smooth != NULL) tmp_smooth[i] = smooth[i];
3262       }
3263       for (i = Index; i < n; i++) {
3264          TransformPointThroughCTM(vs[i].x-ObjPtr->x, vs[i].y-ObjPtr->y,
3265                ObjPtr->ctm, &tmp_x, &tmp_y);
3266          pv[i+1].x = ObjPtr->x+tmp_x;
3267          pv[i+1].y = ObjPtr->y+tmp_y;
3268          if (tmp_smooth != NULL) tmp_smooth[i+1] = smooth[i];
3269       }
3270    }
3271    GridXY(MouseX, MouseY, &orig_x, &orig_y);
3272    grid_x = orig_x;
3273    grid_y = orig_y;
3274    new_mouse_x = MouseX; new_mouse_y = MouseY;
3275    AddPointMeasureCursor(ADDPOINT_STARTSHOW, 0, 0, tx, ty);
3276 
3277    while (!done) {
3278       double new_angle, theta_1, theta_2;
3279       XEvent input, ev;
3280 
3281       XNextEvent(mainDisplay, &input);
3282 
3283       if (input.type == Expose || input.type == VisibilityNotify) {
3284          ExposeEventHandler(&input, TRUE);
3285       } else if (input.type == MotionNotify || input.type == KeyPress ||
3286             input.type == KeyRelease) {
3287          int new_x, new_y;
3288 
3289          if (input.type == KeyPress || input.type == KeyRelease) {
3290             new_mouse_x = grid_x;
3291             new_mouse_y = grid_y;
3292          } else {
3293             new_mouse_x = input.xmotion.x;
3294             new_mouse_y = input.xmotion.y;
3295          }
3296          if (shiftForDiagMouseMove && DiagEventCheck(&input)) {
3297             if (input.type == KeyRelease) {
3298                new_mouse_x = input.xkey.x;
3299                new_mouse_y = input.xkey.y;
3300             } else {
3301                DiagGridXY(orig_x, orig_y, &new_mouse_x, &new_mouse_y);
3302             }
3303          }
3304          GridXY(new_mouse_x, new_mouse_y, &grid_x, &grid_y);
3305          new_x = ABS_SIZE(new_mouse_x-MouseX) + tx;
3306          new_y = ABS_SIZE(new_mouse_y-MouseY) + ty;
3307          if (!already_moved) {
3308             double new_prev_dist, new_next_dist;
3309 
3310             already_moved = TRUE;
3311 
3312             dx = (double)(prev_tx - new_mouse_x);
3313             dy = (double)(prev_ty - new_mouse_y);
3314             new_prev_dist = dx*dx+dy*dy;
3315             dx = (double)(next_tx - new_mouse_x);
3316             dy = (double)(next_ty - new_mouse_y);
3317             new_next_dist = dx*dx+dy*dy;
3318 
3319             before = DetermineBefore(prev_dist, next_dist, new_prev_dist,
3320                   new_next_dist);
3321             if (before == INVALID) {
3322                new_angle = (new_x==tx) ? ((new_y>=ty) ? M_PI/2.0 : -M_PI/2.0) :
3323                      atan2((double)(new_y-ty), (double)(new_x-tx));
3324                theta_1 = fabs(prev_angle - new_angle);
3325                theta_2 = fabs(next_angle - new_angle);
3326                if (theta_1 > M_PI) theta_1 = 2*M_PI-theta_1;
3327                if (theta_2 > M_PI) theta_2 = 2*M_PI-theta_2;
3328                before = (theta_1 <= theta_2);
3329             }
3330             if (before) {
3331                /* Add a point between the current and the previous point */
3332                v[0].x = OFFSET_X(prev_tx); v[0].y = OFFSET_Y(prev_ty);
3333                v[1].x = OFFSET_X(tx);      v[1].y = OFFSET_Y(ty);
3334                v[2].x = OFFSET_X(tx);      v[2].y = OFFSET_Y(ty);
3335             } else {
3336                /* Add a point between the current and the next point */
3337                v[0].x = OFFSET_X(tx);      v[0].y = OFFSET_Y(ty);
3338                v[1].x = OFFSET_X(tx);      v[1].y = OFFSET_Y(ty);
3339                v[2].x = OFFSET_X(next_tx); v[2].y = OFFSET_Y(next_ty);
3340             }
3341             switch (curved) {
3342             case LT_STRAIGHT:
3343             case LT_SPLINE:
3344                sv = MakeMultiSplinePolygonVertex(curved, &sn, tmp_smooth,
3345                      drawOrigX, drawOrigY, n+1, pv);
3346                break;
3347             case LT_INTSPLINE:
3348                sv = MakeIntSplinePolygonVertex(&sn, &intn, &cntrlv,
3349                      drawOrigX, drawOrigY, n+1, pv);
3350                break;
3351             case LT_STRUCT_SPLINE:
3352                /* should never get here */
3353                break;
3354             }
3355             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
3356                   CoordModeOrigin);
3357          } else {
3358             AddPointMeasureCursor(ADDPOINT_DOSHOW, abs(ABS_X(v[1].x)-tx),
3359                   abs(ABS_Y(v[1].y)-ty), ABS_X(v[1].x), ABS_Y(v[1].y));
3360             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
3361                   CoordModeOrigin);
3362             v[1].x = OFFSET_X(tx) + grid_x - orig_x;
3363             v[1].y = OFFSET_Y(ty) + grid_y - orig_y;
3364             if (sv != NULL) {
3365                free(sv);
3366                sv = NULL;
3367             }
3368             if (before) {
3369                pv[Index].x = tx + ABS_SIZE(grid_x-orig_x);
3370                pv[Index].y = ty + ABS_SIZE(grid_y-orig_y);
3371                if (Index == 0) {
3372                   pv[n].x = tx + ABS_SIZE(grid_x-orig_x);
3373                   pv[n].y = ty + ABS_SIZE(grid_y-orig_y);
3374                }
3375             } else {
3376                pv[Index+1].x = tx + ABS_SIZE(grid_x-orig_x);
3377                pv[Index+1].y = ty + ABS_SIZE(grid_y-orig_y);
3378                if (Index == n-1) {
3379                   pv[0].x = tx + ABS_SIZE(grid_x-orig_x);
3380                   pv[0].y = ty + ABS_SIZE(grid_y-orig_y);
3381                }
3382             }
3383             switch (curved) {
3384             case LT_STRAIGHT:
3385             case LT_SPLINE:
3386                sv = MakeMultiSplinePolygonVertex(curved, &sn, tmp_smooth,
3387                      drawOrigX, drawOrigY, n+1, pv);
3388                break;
3389             case LT_INTSPLINE:
3390                free(cntrlv);
3391                sv = MakeIntSplinePolygonVertex(&sn, &intn, &cntrlv,
3392                      drawOrigX, drawOrigY, n+1, pv);
3393                break;
3394             case LT_STRUCT_SPLINE:
3395                /* should never get here */
3396                break;
3397             }
3398             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
3399                   CoordModeOrigin);
3400             MarkRulers(v[1].x, v[1].y);
3401             AddPointMeasureCursor(ADDPOINT_DOSHOW, abs(ABS_X(v[1].x)-tx),
3402                   abs(ABS_Y(v[1].y)-ty), ABS_X(v[1].x), ABS_Y(v[1].y));
3403          }
3404          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
3405       } else if (input.type == ButtonRelease) {
3406          done = TRUE;
3407          *LastMouseX = new_mouse_x; *LastMouseY = new_mouse_y;
3408          if (curved == LT_INTSPLINE || smooth == NULL || !smooth[Index]) {
3409             MARK(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
3410          } else {
3411             MARKO(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
3412          }
3413          if (already_moved) {
3414             AddPointMeasureCursor(ADDPOINT_ENDSHOW, abs(ABS_X(v[1].x)-tx),
3415                   abs(ABS_Y(v[1].y)-ty), ABS_X(v[1].x), ABS_Y(v[1].y));
3416          } else {
3417             AddPointMeasureCursor(ADDPOINT_STARTSHOW, 0, 0, tx, ty);
3418          }
3419          if (!already_moved) {
3420             return FALSE;
3421          } else {
3422             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
3423                   CoordModeOrigin);
3424             if (grid_x == orig_x && grid_y == orig_y) {
3425                return FALSE;
3426             }
3427          }
3428          HighLightReverse();
3429          vs = (IntPoint*)realloc(vs, (n+2)*sizeof(IntPoint));
3430          if (vs == NULL) return FailAllocMessage();
3431          if (smooth != NULL) {
3432             smooth = (char*)realloc(smooth, (n+2)*sizeof(char));
3433             if (smooth == NULL) return FailAllocMessage();
3434          }
3435          PolygonPtr->vlist = vs;
3436          PolygonPtr->smooth = smooth;
3437          if (Index == 0 || Index == n-1) {
3438             if (before) {
3439                vs[n].x = vs[n-1].x;
3440                vs[n].y = vs[n-1].y;
3441                if (ObjPtr->ctm == NULL) {
3442                   vs[n-1].x = x + ABS_SIZE(grid_x-orig_x);
3443                   vs[n-1].y = y + ABS_SIZE(grid_y-orig_y);
3444                } else {
3445                   vs[n-1].x = tx + ABS_SIZE(grid_x-orig_x);
3446                   vs[n-1].y = ty + ABS_SIZE(grid_y-orig_y);
3447                   ReverseTransformPointThroughCTM(vs[n-1].x-ObjPtr->x,
3448                         vs[n-1].y-ObjPtr->y, ObjPtr->ctm, &tmp_x, &tmp_y);
3449                   vs[n-1].x = ObjPtr->x+tmp_x;
3450                   vs[n-1].y = ObjPtr->y+tmp_y;
3451                }
3452                if (smooth != NULL) smooth[n] = smooth[n-1];
3453             } else {
3454                for (i = n-1; i > 0; i--) {
3455                   vs[i+1].x = vs[i].x;
3456                   vs[i+1].y = vs[i].y;
3457                   if (smooth != NULL) smooth[i+1] = smooth[i];
3458                }
3459                if (ObjPtr->ctm == NULL) {
3460                   vs[1].x = x + ABS_SIZE(grid_x-orig_x);
3461                   vs[1].y = y + ABS_SIZE(grid_y-orig_y);
3462                } else {
3463                   vs[1].x = tx + ABS_SIZE(grid_x-orig_x);
3464                   vs[1].y = ty + ABS_SIZE(grid_y-orig_y);
3465                   ReverseTransformPointThroughCTM(vs[1].x-ObjPtr->x,
3466                         vs[1].y-ObjPtr->y, ObjPtr->ctm, &tmp_x, &tmp_y);
3467                   vs[1].x = ObjPtr->x+tmp_x;
3468                   vs[1].y = ObjPtr->y+tmp_y;
3469                }
3470                if (smooth != NULL) smooth[1] = smooth[0];
3471             }
3472          } else {
3473             if (before) {
3474                for (i = n-1; i >= Index; i--) {
3475                   vs[i+1].x = vs[i].x;
3476                   vs[i+1].y = vs[i].y;
3477                   if (smooth != NULL) smooth[i+1] = smooth[i];
3478                }
3479                if (ObjPtr->ctm == NULL) {
3480                   vs[Index].x = x + ABS_SIZE(grid_x-orig_x);
3481                   vs[Index].y = y + ABS_SIZE(grid_y-orig_y);
3482                } else {
3483                   vs[Index].x = tx + ABS_SIZE(grid_x-orig_x);
3484                   vs[Index].y = ty + ABS_SIZE(grid_y-orig_y);
3485                   ReverseTransformPointThroughCTM(vs[Index].x-ObjPtr->x,
3486                         vs[Index].y-ObjPtr->y, ObjPtr->ctm, &tmp_x, &tmp_y);
3487                   vs[Index].x = ObjPtr->x+tmp_x;
3488                   vs[Index].y = ObjPtr->y+tmp_y;
3489                }
3490                if (smooth != NULL) smooth[Index] = smooth[Index+1];
3491             } else {
3492                for (i = n-1; i > Index; i--) {
3493                   vs[i+1].x = vs[i].x;
3494                   vs[i+1].y = vs[i].y;
3495                   if (smooth != NULL) smooth[i+1] = smooth[i];
3496                }
3497                if (ObjPtr->ctm == NULL) {
3498                   vs[Index+1].x = x + ABS_SIZE(grid_x-orig_x);
3499                   vs[Index+1].y = y + ABS_SIZE(grid_y-orig_y);
3500                } else {
3501                   vs[Index+1].x = tx + ABS_SIZE(grid_x-orig_x);
3502                   vs[Index+1].y = ty + ABS_SIZE(grid_y-orig_y);
3503                   ReverseTransformPointThroughCTM(vs[Index+1].x-ObjPtr->x,
3504                         vs[Index+1].y-ObjPtr->y, ObjPtr->ctm, &tmp_x, &tmp_y);
3505                   vs[Index+1].x = ObjPtr->x+tmp_x;
3506                   vs[Index+1].y = ObjPtr->y+tmp_y;
3507                }
3508                if (smooth != NULL) smooth[Index+1] = smooth[Index];
3509             }
3510          }
3511          if (sv != NULL) { free(sv); sv = NULL; }
3512          if (pv != NULL) { free(pv); pv = NULL; }
3513          if (tmp_smooth != NULL) { free(tmp_smooth); tmp_smooth = NULL; }
3514          if (curved == LT_INTSPLINE && cntrlv != NULL) {
3515             free(cntrlv);
3516             cntrlv = NULL;
3517          }
3518          PolygonPtr->n++;
3519          n++;
3520          AdjObjSplineVs(ObjPtr);
3521          if (curved != LT_INTSPLINE) {
3522             UpdPolyBBox(ObjPtr, PolygonPtr->n, PolygonPtr->vlist);
3523          } else {
3524             UpdPolyBBox(ObjPtr, PolygonPtr->intn, PolygonPtr->intvlist);
3525          }
3526          AdjObjBBox(ObjPtr);
3527 
3528          UpdSelBBox();
3529          RedrawAreas(botObj,
3530                sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
3531                sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
3532                selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
3533                selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
3534          HighLightForward();
3535          SetFileModified(TRUE);
3536          justDupped = FALSE;
3537       } else if (input.type == KeyPress) {
3538          if (KeyPressEventIsEscape(&input.xkey)) {
3539             done = TRUE;
3540             *LastMouseX = new_mouse_x; *LastMouseY = new_mouse_y;
3541             if (curved == LT_INTSPLINE || smooth == NULL || !smooth[Index]) {
3542                MARK(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
3543             } else {
3544                MARKO(drawWindow, revDefaultGC, OFFSET_X(tx), OFFSET_Y(ty));
3545             }
3546             if (already_moved) {
3547             AddPointMeasureCursor(ADDPOINT_ENDSHOW, abs(ABS_X(v[1].x)-tx),
3548                   abs(ABS_Y(v[1].y)-ty), ABS_X(v[1].x), ABS_Y(v[1].y));
3549             } else {
3550                AddPointMeasureCursor(ADDPOINT_STARTSHOW, 0, 0, tx, ty);
3551             }
3552             if (already_moved) {
3553                XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
3554                      CoordModeOrigin);
3555                if (grid_x == orig_x && grid_y == orig_y) {
3556                   return FALSE;
3557                }
3558             }
3559             return FALSE;
3560          }
3561       }
3562    }
3563    return TRUE;
3564 }
3565 
AddPoint()3566 void AddPoint()
3567 {
3568    int adding=TRUE, pt_added=FALSE, root_x=0, root_y=0, old_x=0, old_y=0;
3569    int curved=(-1);
3570    struct ObjRec *obj_ptr;
3571    struct PolyRec *poly_ptr=NULL;
3572    struct PolygonRec *polygon_ptr=NULL;
3573    unsigned int status=0;
3574    Window root_win=None, child_win=None;
3575 
3576    if (!(topSel != NULL && topSel == botSel &&
3577          (topSel->obj->type == OBJ_POLY || topSel->obj->type == OBJ_POLYGON))) {
3578       MsgBox(TgLoadString(STID_SELECT_ONLY_ONE_POLY_POLYGON), TOOL_NAME,
3579             INFO_MB);
3580       return;
3581    } else if (topSel->obj->locked) {
3582       MsgBox(TgLoadString(STID_CANNOT_ADD_PT_FOR_LOCKED), TOOL_NAME, INFO_MB);
3583       return;
3584    } else if (AutoRetractedArrowAttr(topSel->obj, TRUE)) {
3585       MsgBox(TgLoadString(STID_CANNOT_ADD_PT_FOR_AUTO_ARROW), TOOL_NAME,
3586             INFO_MB);
3587       return;
3588    }
3589    if (curChoice == VERTEXMODE) {
3590       HighLightReverse();
3591       JustRemoveAllVSel();
3592       HighLightForward();
3593    }
3594    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
3595    obj_ptr = topSel->obj;
3596    switch (obj_ptr->type) {
3597    case OBJ_POLY:
3598       poly_ptr = obj_ptr->detail.p;
3599       curved = poly_ptr->curved;
3600       break;
3601    case OBJ_POLYGON:
3602       polygon_ptr = obj_ptr->detail.g;
3603       curved = polygon_ptr->curved;
3604       break;
3605    }
3606    SaveStatusStrings();
3607    if (curved == LT_STRUCT_SPLINE) {
3608       SetMouseStatus(TgLoadString(STID_ADD_HINGE_VERTEX),
3609             TgLoadString(STID_ADD_A_SMOOTH_VERTEX),
3610             TgLoadCachedString(CSTID_FINISH));
3611       TwoLineMsg(TgLoadString(STID_LEFT_ADD_HINGE_MID_ADD_SMOOTH),
3612             TgLoadString(STID_CLICK_RIGHT_BUTTON_TO_QUIT));
3613    } else {
3614       SetMouseStatus(TgLoadCachedString(CSTID_ADD_A_VERTEX),
3615             TgLoadCachedString(CSTID_FINISH), TgLoadCachedString(CSTID_FINISH));
3616       TwoLineMsg(TgLoadString(STID_LEFT_BTN_TO_ADD_PTS),
3617             TgLoadString(STID_CLICK_OTHER_BUTTON_TO_QUIT));
3618    }
3619    if (!debugNoPointerGrab) {
3620       XGrabPointer(mainDisplay, drawWindow, False,
3621             PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
3622             GrabModeAsync, GrabModeAsync, None, defaultCursor, CurrentTime);
3623    }
3624    XQueryPointer(mainDisplay, drawWindow, &root_win, &child_win,
3625          &root_x, &root_y, &old_x, &old_y, &status);
3626    XSetFont(mainDisplay, revDefaultGC, defaultFontPtr->fid);
3627    /* do not translate -- program constants */
3628    XDrawString(mainDisplay, drawWindow, revDefaultGC,
3629          old_x+4, old_y+defaultFontAsc, "ADD", 3);
3630    MarkRulers(old_x, old_y);
3631 
3632    while (adding) {
3633       XEvent input;
3634 
3635       XNextEvent(mainDisplay, &input);
3636 
3637       if (input.type == Expose || input.type == VisibilityNotify) {
3638          ExposeEventHandler(&input, TRUE);
3639       } else if (input.type == ButtonPress) {
3640          int index=0;
3641 
3642          /* erase */
3643          XDrawString(mainDisplay, drawWindow, revDefaultGC, old_x+4,
3644                old_y+defaultFontAsc, "ADD", 3);
3645          if (curved == LT_STRUCT_SPLINE) {
3646             if (input.xbutton.button == Button1 ||
3647                   input.xbutton.button == Button2) {
3648                if (obj_ptr->type == OBJ_POLY &&
3649                      PtInPolyMark(obj_ptr, input.xbutton.x, input.xbutton.y,
3650                      poly_ptr->ssn, poly_ptr->ssvlist, &index)) {
3651                   if (ContinueAddStructuredPolyOrPolygonPoint(obj_ptr,
3652                         input.xbutton.button, input.xbutton.x, input.xbutton.y,
3653                         index, poly_ptr, NULL, &old_x, &old_y)) {
3654                      pt_added = TRUE;
3655                   }
3656                } else if (obj_ptr->type == OBJ_POLYGON &&
3657                      PtInPolyMark(obj_ptr, input.xbutton.x, input.xbutton.y,
3658                      polygon_ptr->ssn-1, polygon_ptr->ssvlist, &index)) {
3659                   if (ContinueAddStructuredPolyOrPolygonPoint(obj_ptr,
3660                         input.xbutton.button, input.xbutton.x, input.xbutton.y,
3661                         index, NULL, polygon_ptr, &old_x, &old_y)) {
3662                      pt_added = TRUE;
3663                   }
3664                }
3665                XDrawString(mainDisplay, drawWindow, revDefaultGC, old_x+4,
3666                      old_y+defaultFontAsc, "ADD", 3);
3667             } else {
3668                XUngrabPointer(mainDisplay, CurrentTime);
3669                Msg("");
3670                adding = FALSE;
3671             }
3672          } else if (input.xbutton.button == Button1) {
3673             if (obj_ptr->type == OBJ_POLY &&
3674                   PtInPolyMark(obj_ptr, input.xbutton.x, input.xbutton.y,
3675                   poly_ptr->n, poly_ptr->vlist, &index)) {
3676                if (ContinueAddPolyPoint(obj_ptr, input.xbutton.x,
3677                      input.xbutton.y, index, poly_ptr, &old_x, &old_y)) {
3678                   pt_added = TRUE;
3679                }
3680             } else if (obj_ptr->type == OBJ_POLYGON &&
3681                   PtInPolyMark(obj_ptr, input.xbutton.x, input.xbutton.y,
3682                   polygon_ptr->n-1, polygon_ptr->vlist, &index)) {
3683                if (ContinueAddPolygonPoint(obj_ptr, input.xbutton.x,
3684                      input.xbutton.y, index, polygon_ptr, &old_x, &old_y)) {
3685                   pt_added = TRUE;
3686                }
3687             }
3688             XDrawString(mainDisplay, drawWindow, revDefaultGC,
3689                   old_x+4, old_y+defaultFontAsc, "ADD", 3);
3690          } else {
3691             XUngrabPointer(mainDisplay, CurrentTime);
3692             Msg("");
3693             adding = FALSE;
3694          }
3695       } else if (input.type == MotionNotify) {
3696          XEvent ev;
3697 
3698          /* erase */
3699          XDrawString(mainDisplay, drawWindow, revDefaultGC,
3700                old_x+4, old_y+defaultFontAsc, "ADD", 3);
3701          old_x = input.xmotion.x;
3702          old_y = input.xmotion.y;
3703          /* draw */
3704          XDrawString(mainDisplay, drawWindow, revDefaultGC,
3705                old_x+4, old_y+defaultFontAsc, "ADD", 3);
3706          MarkRulers(old_x, old_y);
3707          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
3708       }
3709    }
3710    RestoreStatusStrings();
3711    if (pt_added) {
3712       RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
3713    } else {
3714       AbortPrepareCmd(CMD_REPLACE);
3715    }
3716 }
3717 
FlushUndoBuffer()3718 void FlushUndoBuffer()
3719 {
3720    CleanUpMsg();
3721    CleanUpCmds();
3722    if (FlushColormap()) {
3723       Msg(TgLoadString(STID_UNDO_BUF_AND_CMAP_FLUSHED));
3724       sprintf(gszMsgBox, TgLoadString(STID_NUM_COLORS_ALLOCATED), maxColors);
3725       Msg(gszMsgBox);
3726    } else {
3727       Msg(TgLoadString(STID_UNDO_BUF_FLUSHED));
3728    }
3729 }
3730 
RestoreImageWH()3731 void RestoreImageWH()
3732 {
3733    struct XBmRec *xbm_ptr=NULL;
3734    struct XPmRec *xpm_ptr=NULL;
3735    int w, h, image_w=0, image_h=0;
3736    int ltx, lty, rbx, rby;
3737 
3738    if (topSel == NULL) {
3739       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
3740       return;
3741    } else if (topSel != botSel || (!(topSel->obj->type==OBJ_XBM ||
3742          topSel->obj->type==OBJ_XPM))) {
3743       MsgBox(TgLoadString(STID_SEL_ONE_XBM_OR_XPM_TO_RESTORE), TOOL_NAME,
3744             INFO_MB);
3745       return;
3746    } else if (topSel->obj->locked) {
3747       MsgBox(TgLoadString(STID_CANNOT_RESTORE_LOCKED), TOOL_NAME, INFO_MB);
3748       return;
3749    }
3750    w = topSel->obj->obbox.rbx - topSel->obj->obbox.ltx;
3751    h = topSel->obj->obbox.rby - topSel->obj->obbox.lty;
3752    switch (topSel->obj->type) {
3753    case OBJ_XBM:
3754       xbm_ptr = topSel->obj->detail.xbm;
3755       if (xbm_ptr->real_type==XBM_EPS && xbm_ptr->bitmap==None) {
3756          image_w = xbm_ptr->eps_w;
3757          image_h = xbm_ptr->eps_h;
3758       } else {
3759          image_w = xbm_ptr->image_w;
3760          image_h = xbm_ptr->image_h;
3761       }
3762       if (w == image_w && h == image_h) return;
3763       break;
3764    case OBJ_XPM:
3765       xpm_ptr = topSel->obj->detail.xpm;
3766       image_w = xpm_ptr->image_w; image_h = xpm_ptr->image_h;
3767       if (w == image_w && h == image_h) return;
3768       break;
3769    }
3770    ltx = selLtX; lty = selLtY; rbx = selRbX; rby = selRbY;
3771    HighLightReverse();
3772 
3773    PrepareToReplaceAnObj(topSel->obj);
3774    if (topSel->obj->ctm == NULL) {
3775       topSel->obj->obbox.rbx = topSel->obj->obbox.ltx+image_w;
3776       topSel->obj->obbox.rby = topSel->obj->obbox.lty+image_h;
3777    } else {
3778       topSel->obj->obbox.rbx = topSel->obj->obbox.ltx+image_w;
3779       topSel->obj->obbox.rby = topSel->obj->obbox.lty+image_h;
3780       free(topSel->obj->ctm);
3781       topSel->obj->ctm = NULL;
3782    }
3783    topSel->obj->x = topSel->obj->obbox.ltx;
3784    topSel->obj->y = topSel->obj->obbox.lty;
3785    switch (topSel->obj->type) {
3786    case OBJ_XBM:
3787       if (xbm_ptr->cached_bitmap != None) {
3788          XFreePixmap(mainDisplay, xbm_ptr->cached_bitmap);
3789       }
3790       xbm_ptr->cached_bitmap = None;
3791       xbm_ptr->cached_zoom = 0;
3792       break;
3793    case OBJ_XPM:
3794       if (xpm_ptr->cached_pixmap != None) {
3795          XFreePixmap(mainDisplay, xpm_ptr->cached_pixmap);
3796       }
3797       xpm_ptr->cached_pixmap = None;
3798       if (xpm_ptr->cached_bitmap != None) {
3799          XFreePixmap(mainDisplay, xpm_ptr->cached_bitmap);
3800       }
3801       xpm_ptr->cached_bitmap = None;
3802       if (xpm_ptr->clip_mask != None) {
3803          XFreePixmap(mainDisplay, xpm_ptr->clip_mask);
3804       }
3805       xpm_ptr->clip_mask = None;
3806       xpm_ptr->cached_zoom = 0;
3807       xpm_ptr->cached_color = (-1);
3808       break;
3809    }
3810    AdjObjBBox(topSel->obj);
3811    RecordReplaceAnObj(topSel->obj);
3812 
3813    UpdSelBBox();
3814    RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
3815          rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
3816          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
3817          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
3818    HighLightForward();
3819    SetFileModified(TRUE);
3820    justDupped = FALSE;
3821 }
3822 
CutMaps()3823 void CutMaps()
3824 {
3825    if (topSel == NULL || topSel != botSel) {
3826       MsgBox(TgLoadString(STID_SEL_ONE_XBM_OR_XPM_TO_CUT), TOOL_NAME,
3827             INFO_MB);
3828       return;
3829    } else if (topSel->obj->locked) {
3830       MsgBox(TgLoadString(STID_CANNOT_CUT_LOCKED), TOOL_NAME, INFO_MB);
3831       return;
3832    } else if (topSel->obj->ctm != NULL) {
3833       MsgBox(TgLoadString(STID_CANNOT_CUT_TRANSFORMED_X_OBJ), TOOL_NAME,
3834             INFO_MB);
3835       return;
3836    }
3837    switch (topSel->obj->type) {
3838    case OBJ_XBM: CutXBitmap(); break;
3839    case OBJ_XPM: CutXPixmap(NULL, NULL, NULL, NULL, NULL); break;
3840    default:
3841       MsgBox(TgLoadString(STID_SEL_ONE_XBM_OR_XPM_TO_CUT), TOOL_NAME,
3842             INFO_MB);
3843       break;
3844    }
3845 }
3846 
BreakUpMaps()3847 void BreakUpMaps()
3848 {
3849    int cols=0, rows=0, cols_and_rows=TRUE, ok=TRUE, image_w=0, image_h=0;
3850    char spec[MAXSTRING+1], *dup_spec=NULL, *part1=NULL, *part2=NULL;
3851    struct ObjRec *obj_ptr=NULL;
3852 
3853    if (topSel == NULL || topSel != botSel ||
3854          (topSel->obj->type != OBJ_XBM && topSel->obj->type != OBJ_XPM)) {
3855       MsgBox(TgLoadString(STID_SEL_ONE_XBM_OR_XPM_TO_BREAKUP), TOOL_NAME,
3856             INFO_MB);
3857       return;
3858    } else if (topSel->obj->ctm != NULL) {
3859       MsgBox(TgLoadString(STID_CANNOT_BREAK_XFORMED_X_OBJ), TOOL_NAME,
3860             INFO_MB);
3861       return;
3862    } else if (topSel->obj->locked) {
3863       MsgBox(TgLoadString(STID_CANNOT_BREAKUP_LOCKED), TOOL_NAME,
3864             INFO_MB);
3865       return;
3866    }
3867    obj_ptr = topSel->obj;
3868 
3869    switch (obj_ptr->type) {
3870    case OBJ_XBM:
3871       image_w=obj_ptr->detail.xbm->image_w;
3872       image_h=obj_ptr->detail.xbm->image_h;
3873       break;
3874    case OBJ_XPM:
3875       image_w=obj_ptr->detail.xpm->image_w;
3876       image_h=obj_ptr->detail.xpm->image_h;
3877       break;
3878    default: return;
3879    }
3880    sprintf(gszMsgBox, TgLoadString(STID_ENTER_NUM_ROWCOL_TO_BREAK),
3881          image_w, image_h);
3882    *spec = '\0';
3883    if (Dialog(gszMsgBox, TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), spec) ==
3884          INVALID) {
3885       return;
3886    }
3887    UtilTrimBlanks(spec);
3888    if (*spec == '\0') return;
3889    if ((dup_spec=UtilStrDup(spec)) == NULL) {
3890       FailAllocMessage();
3891       return;
3892    }
3893    if (*dup_spec == '=') cols_and_rows = FALSE;
3894 
3895    /* do not translate -- program constants */
3896    if ((part1=strtok(dup_spec, " ,xX=[]")) != NULL &&
3897          (part2=strtok(NULL, " ,xX=[]")) != NULL) {
3898       cols = atoi(part1);
3899       rows = atoi(part2);
3900       if (cols > 0 && rows > 0) {
3901          switch (obj_ptr->type) {
3902          case OBJ_XBM:
3903             BreakUpXBitmap(obj_ptr, cols_and_rows, cols, rows);
3904             break;
3905          case OBJ_XPM:
3906             BreakUpXPixmap(obj_ptr, cols_and_rows, cols, rows);
3907             break;
3908          }
3909       } else {
3910          ok = FALSE;
3911       }
3912    } else {
3913       ok = FALSE;
3914    }
3915    if (!ok) {
3916       sprintf(gszMsgBox, TgLoadString(STID_INVALID_ROWCOL_SPEC_REENTER),
3917             dup_spec);
3918       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3919    }
3920    free(dup_spec);
3921 }
3922 
3923 /* ----------------------- RemoveTransparentPixel ----------------------- */
3924 
3925 static
ReproducePixmap(pixel_to_use,orig_pixmap,orig_image,orig_bitmap,orig_bitmap_image,w,h,pixmap_return,image_return,bitmap_return,bitmap_image_return)3926 int ReproducePixmap(pixel_to_use, orig_pixmap, orig_image, orig_bitmap,
3927       orig_bitmap_image, w, h, pixmap_return, image_return, bitmap_return,
3928       bitmap_image_return)
3929    int pixel_to_use, w, h;
3930    Pixmap orig_pixmap, orig_bitmap, *pixmap_return, *bitmap_return;
3931    XImage *orig_image, *orig_bitmap_image, **image_return, **bitmap_image_return;
3932 {
3933    int i=0;
3934    XImage *src_image=NULL, *src_bitmap_image=NULL;
3935 
3936    SetWatchCursor(drawWindow);
3937    SetWatchCursor(mainWindow);
3938 
3939    *pixmap_return = XCreatePixmap(mainDisplay, mainWindow, w, h, mainDepth);
3940    *bitmap_return = XCreatePixmap(mainDisplay, mainWindow, w, h, 1);
3941 
3942    XFillRectangle(mainDisplay, *pixmap_return, xpmGC, 0, 0, w, h);
3943    XSetForeground(mainDisplay, xbmGC, 1);
3944    XFillRectangle(mainDisplay, *bitmap_return, xbmGC, 0, 0, w, h);
3945    XSetForeground(mainDisplay, xbmGC, 0);
3946 
3947    *image_return = (*pixmap_return==None ? NULL : XGetImage(mainDisplay,
3948          *pixmap_return, 0, 0, w, h, AllPlanes, ZPixmap));
3949    *bitmap_image_return = (*bitmap_return==None ? NULL : XGetImage(mainDisplay,
3950          *bitmap_return, 0, 0, w, h, 1, ZPixmap));
3951    if (orig_image != NULL) {
3952       src_image = orig_image;
3953    } else {
3954       src_image = XGetImage(mainDisplay, orig_pixmap, 0, 0, w, h, AllPlanes, ZPixmap);
3955    }
3956    if (orig_bitmap_image != NULL) {
3957       src_bitmap_image = orig_bitmap_image;
3958    } else {
3959       src_bitmap_image = XGetImage(mainDisplay, orig_bitmap, 0, 0, w, h, 1, ZPixmap);
3960    }
3961    if (*pixmap_return == None || *bitmap_return == None || *image_return==NULL ||
3962          *bitmap_image_return==NULL || src_image==NULL || src_bitmap_image==NULL) {
3963       if (*pixmap_return == None) {
3964          FailAllocPixmapMessage(w, h);
3965       } else if (*bitmap_return == None) {
3966          FailAllocBitmapMessage(w, h);
3967       } else {
3968          MsgBox(TgLoadString(STID_XGETIMAGE_MAY_RUN_OUT_VMEM), TOOL_NAME,
3969                INFO_MB);
3970       }
3971       if (*pixmap_return != None) XFreePixmap(mainDisplay, *pixmap_return);
3972       if (*bitmap_return != None) XFreePixmap(mainDisplay, *bitmap_return);
3973       if (*image_return != NULL) XDestroyImage(*image_return);
3974       if (*bitmap_image_return != NULL) XDestroyImage(*bitmap_image_return);
3975       if (orig_image != NULL) XDestroyImage(src_image);
3976       if (orig_bitmap_image != NULL) XDestroyImage(src_bitmap_image);
3977       if (orig_image == NULL && src_image != NULL) XDestroyImage(src_image);
3978       if (orig_bitmap_image == NULL && src_bitmap_image != NULL) XDestroyImage(src_bitmap_image);
3979 
3980       *pixmap_return = *bitmap_return = None;
3981       *image_return = *bitmap_image_return = NULL;
3982       SetDefaultCursor(mainWindow);
3983       SetDefaultCursor(drawWindow);
3984       return FALSE;
3985    }
3986    for (i=0; i < h; i++) {
3987       int j=0;
3988 
3989       for (j=0; j < w; j++) {
3990          if (src_bitmap_image == NULL) {
3991             XPutPixel(*image_return, j, i, XGetPixel(src_image, j, i));
3992          } else {
3993             if (XGetPixel(src_bitmap_image, j, i) != 0) {
3994                XPutPixel(*image_return, j, i, XGetPixel(src_image, j, i));
3995             } else {
3996                XPutPixel(*image_return, j, i, pixel_to_use);
3997             }
3998          }
3999       }
4000    }
4001    XPutImage(mainDisplay, *pixmap_return, xpmGC, *image_return, 0, 0, 0, 0, w, h);
4002    XPutImage(mainDisplay, *bitmap_return, xbmGC, *bitmap_image_return, 0, 0, 0, 0, w, h);
4003    SetDefaultCursor(mainWindow);
4004    SetDefaultCursor(drawWindow);
4005 
4006    if (orig_image == NULL && src_image != NULL) XDestroyImage(src_image);
4007    if (orig_bitmap_image == NULL && src_bitmap_image != NULL) XDestroyImage(src_bitmap_image);
4008 
4009    return TRUE;
4010 }
4011 
4012 static
RemoveObjTransPixel(obj_ptr,ptci)4013 int RemoveObjTransPixel(obj_ptr, ptci)
4014    struct ObjRec *obj_ptr;
4015    TrueColorInfo *ptci;
4016 {
4017    struct XPmRec *xpm_ptr=obj_ptr->detail.xpm;
4018    Pixmap dest_pixmap=None, dest_bitmap=None;
4019    XImage *dest_image=NULL, *dest_bitmap_image=NULL;
4020    int index=(-1);
4021    unsigned char trans_color_r='\0', trans_color_g='\0', trans_color_b='\0';
4022 
4023    switch (xpm_ptr->real_type) {
4024    case XPM_XPM:
4025       if (ObjHasIndexedTransPixel(obj_ptr, &index)) {
4026          xpm_ptr->pixels[index] = colorPixels[colorIndex];
4027          UtilFree(xpm_ptr->color_str[index]);
4028          xpm_ptr->color_str[index] = UtilStrDup(colorMenuItems[colorIndex]);
4029          if (xpm_ptr->color_str[index] == NULL) FailAllocMessage();
4030          if (!ReproducePixmap(colorPixels[colorIndex], xpm_ptr->pixmap, xpm_ptr->image, xpm_ptr->bitmap,
4031                xpm_ptr->bitmap_image, xpm_ptr->image_w, xpm_ptr->image_h,
4032                &dest_pixmap, &dest_image, &dest_bitmap, &dest_bitmap_image)) {
4033             return FALSE;
4034          }
4035          xpm_ptr->pixmap = dest_pixmap;
4036          xpm_ptr->image = dest_image;
4037          xpm_ptr->bitmap = dest_bitmap;
4038          xpm_ptr->bitmap_image = dest_bitmap_image;
4039          return TRUE;
4040       }
4041       break;
4042    case XPM_JPEG: break;
4043    case PPM_TRUE:
4044       if (ObjHasTrueColorTransPixel(obj_ptr, &trans_color_r, &trans_color_g, &trans_color_b)) {
4045          unsigned int r=(unsigned int)trans_color_r;
4046          unsigned int g=(unsigned int)trans_color_g;
4047          unsigned int b=(unsigned int)trans_color_b;
4048          int pixel_to_use=((r << ptci->r_shift) & mainVisual->red_mask) |
4049                  ((g << ptci->g_shift) & mainVisual->green_mask) |
4050                  ((b << ptci->b_shift) & mainVisual->blue_mask) ;
4051 
4052          if (!ReproducePixmap(pixel_to_use, xpm_ptr->pixmap, xpm_ptr->image, xpm_ptr->bitmap,
4053                xpm_ptr->bitmap_image, xpm_ptr->image_w, xpm_ptr->image_h,
4054                &dest_pixmap, &dest_image, &dest_bitmap, &dest_bitmap_image)) {
4055             return FALSE;
4056          }
4057          xpm_ptr->pixmap = dest_pixmap;
4058          xpm_ptr->image = dest_image;
4059          xpm_ptr->bitmap = dest_bitmap;
4060          xpm_ptr->bitmap_image = dest_bitmap_image;
4061          xpm_ptr->has_transparent_color = FALSE;
4062          xpm_ptr->transparent_color[0] = (unsigned char)0;
4063          xpm_ptr->transparent_color[1] = (unsigned char)0;
4064          xpm_ptr->transparent_color[2] = (unsigned char)0;
4065          return TRUE;
4066       }
4067       break;
4068    }
4069    /* no change */
4070    return FALSE;
4071 }
4072 
RemoveTransparentPixel()4073 void RemoveTransparentPixel()
4074 {
4075    struct ObjRec *obj_ptr=NULL;
4076    struct SelRec *sel_ptr=NULL, *tmp_top_sel=NULL, *tmp_bot_sel=NULL;
4077    int count=0, ltx=0, lty=0, rbx=0, rby=0;
4078    TrueColorInfo tci;
4079 
4080    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
4081       obj_ptr = sel_ptr->obj;
4082       if (obj_ptr->type == OBJ_XPM && ObjHasTransPixel(obj_ptr)) {
4083          AddObjIntoSel(obj_ptr, tmp_bot_sel, NULL, &tmp_top_sel, &tmp_bot_sel);
4084          count++;
4085       }
4086    }
4087    if (count == 0) {
4088       MsgBox(TgLoadString(STID_SEL_ONE_XPM_TRANSPIX), TOOL_NAME, INFO_MB);
4089       return;
4090    }
4091    if (fullTrueColorMode && !SetupTrueColorInfo(&tci)) return;
4092 
4093    HighLightReverse();
4094    RemoveAllSel();
4095    topSel = tmp_top_sel;
4096    botSel = tmp_bot_sel;
4097    tmp_top_sel = tmp_bot_sel = NULL;
4098    ltx = selLtX; lty = selLtY; rbx = selRbX; rby = selRbY;
4099 
4100    StartCompositeCmd();
4101    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
4102       obj_ptr = sel_ptr->obj;
4103       PrepareToReplaceAnObj(obj_ptr);
4104 
4105       if (RemoveObjTransPixel(obj_ptr, &tci)) {
4106          RecordReplaceAnObj(obj_ptr);
4107          AdjObjCache(obj_ptr);
4108       } else {
4109          AbortPrepareCmd(CMD_REPLACE);
4110       }
4111    }
4112    EndCompositeCmd();
4113 
4114    UpdSelBBox();
4115    RedrawAnArea(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
4116       rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1));
4117    HighLightForward();
4118    SetFileModified(TRUE);
4119    justDupped = FALSE;
4120 }
4121 
4122 /* ----------------------- LayoutOnArc ----------------------- */
4123 
4124 typedef struct BoxInfoRec {
4125    struct ObjRec *obj;
4126    int w, h;
4127    int valid_v;
4128    double half_w, angle_in_radian, angle_to_rotate;
4129    XPoint v[5];
4130 } *BoxInfoPtr;
4131 
4132 #define LAYOUT_DIR_NONE 0
4133 #define LAYOUT_DIR_S 1 /* convex */
4134 #define LAYOUT_DIR_N 2 /* concave */
4135 
4136 int gnLayoutDirection=LAYOUT_DIR_NONE;
4137 
4138 static
RotateXY(x,y,angle_in_radian,new_x,new_y)4139 void RotateXY(x, y, angle_in_radian, new_x, new_y)
4140    int x, y;
4141    double angle_in_radian;
4142    short *new_x, *new_y;
4143 {
4144    if (x == 0 && y == 0) {
4145       *new_x = 0;
4146       *new_y = 0;
4147    } else {
4148       double sin_val = sin(angle_in_radian);
4149       double cos_val = cos(angle_in_radian);
4150 
4151       *new_x = (short)round(x*cos_val - y*sin_val);
4152       *new_y = (short)round(x*sin_val + y*cos_val);
4153    }
4154 }
4155 
4156 static
RotateBBoxByRadian(bbox,angle_in_radian,v)4157 void RotateBBoxByRadian(bbox, angle_in_radian, v)
4158    struct BBRec *bbox;
4159    double angle_in_radian;
4160    XPoint *v; /* array of 5 points */
4161 {
4162    RotateXY(bbox->ltx, bbox->lty, angle_in_radian, &(v[0].x), &(v[0].y));
4163    RotateXY(bbox->rbx, bbox->lty, angle_in_radian, &(v[1].x), &(v[1].y));
4164    RotateXY(bbox->rbx, bbox->rby, angle_in_radian, &(v[2].x), &(v[2].y));
4165    RotateXY(bbox->ltx, bbox->rby, angle_in_radian, &(v[3].x), &(v[3].y));
4166    v[4].x = v[0].x; v[4].y = v[0].y;
4167 }
4168 
4169 #define SET_LAYOUT_VS (FALSE)
4170 #define FINALIZE_LAYOUT (TRUE)
4171 
4172 static
HighLightOrFinalLayout(arc_ptr,box_info_ptr,grid_x,grid_y,finalize)4173 int HighLightOrFinalLayout(arc_ptr, box_info_ptr, grid_x, grid_y, finalize)
4174    struct ArcRec *arc_ptr;
4175    struct BoxInfoRec *box_info_ptr;
4176    int grid_x, grid_y, finalize;
4177 {
4178    int i, num_objects=numObjSelected-1, circular=FALSE;
4179    int abs_cx, abs_cy, abs_x=ABS_X(grid_x), abs_y=ABS_Y(grid_y), dx, dy;
4180    double abs_radius, total_arc_radian, total_radian=0, inc_radian=0, angle;
4181 
4182    if (abs(arc_ptr->angle2) == (360<<6)) {
4183       circular = TRUE;
4184    }
4185    abs_cx = arc_ptr->xc;
4186    abs_cy = arc_ptr->yc;
4187    dx = abs_x - abs_cx;
4188    dy = abs_y - abs_cy;
4189    if (dx == 0 && dy == 0) {
4190       box_info_ptr[0].valid_v = FALSE;
4191       return FALSE;
4192    }
4193    box_info_ptr[0].valid_v = TRUE;
4194    total_arc_radian = ((double)arc_ptr->angle2)*M_PI/((double)180.0*64.0);
4195    abs_radius = (double)sqrt((double)(dx*dx+dy*dy));
4196    for (i=0; i < num_objects; i++) {
4197       if (!finalize) {
4198          box_info_ptr[i].angle_in_radian = atan2(box_info_ptr[i].half_w,
4199                abs_radius) * ((double)2.0);
4200       }
4201       total_radian += fabs(box_info_ptr[i].angle_in_radian);
4202    }
4203    switch (gnLayoutDirection) {
4204    case LAYOUT_DIR_S:
4205       if (circular) {
4206          inc_radian = (total_arc_radian-total_radian)/((double)num_objects);
4207       } else {
4208          inc_radian = (total_arc_radian-total_radian)/((double)(num_objects-1));
4209       }
4210       break;
4211    case LAYOUT_DIR_N:
4212       if (circular) {
4213          inc_radian = (total_arc_radian+total_radian)/((double)num_objects);
4214       } else {
4215          inc_radian = (total_arc_radian+total_radian)/((double)(num_objects-1));
4216       }
4217       break;
4218    }
4219    angle = arc_ptr->angle1*M_PI/((double)180.0*64.0);
4220    for (i=0; i < num_objects; i++, angle+=inc_radian) {
4221       struct ObjRec *obj_ptr=box_info_ptr[i].obj;
4222       int x=0, y=0, w=0, h=0, orig_x=0, orig_y=0;
4223       XPoint v[5];
4224       struct BBRec bbox;
4225       double half_way_angle=(double)0.0;
4226 
4227       w = box_info_ptr[i].w;
4228       h = box_info_ptr[i].h;
4229       if (finalize) {
4230          if (obj_ptr->type == OBJ_TEXT &&
4231                obj_ptr->detail.t->minilines.just != JUST_C) {
4232             ChangeObjTextJust(obj_ptr, JUST_C);
4233          }
4234       }
4235       /*
4236        * half_way_angle measures angle in the arc sense (see the beginning
4237        *     of "arc.c").
4238        * the RoateaBBoxByRadian() rotates things in the drawing sense
4239        *     (the one that's vertically flipped w.r.t. PostScript)
4240        */
4241       switch (gnLayoutDirection) {
4242       case LAYOUT_DIR_S:
4243          half_way_angle = angle+(box_info_ptr[i].angle_in_radian/2.0);
4244          if (finalize) {
4245             orig_x = ((obj_ptr->obbox.rbx+obj_ptr->obbox.ltx)>>1);
4246             orig_y = obj_ptr->obbox.lty;
4247             RotateObjForLayout(obj_ptr, box_info_ptr[i].angle_to_rotate,
4248                   CORNER_BOTTOM);
4249             x = abs_cx + (int)((abs_radius-h)*cos(half_way_angle));
4250             y = abs_cy - (int)((abs_radius-h)*sin(half_way_angle));
4251          } else {
4252             SetBBRec(&bbox, -(w>>1), -h, w-(w>>1), 0);
4253             box_info_ptr[i].angle_to_rotate = (-half_way_angle-(M_PI/2.0));
4254             RotateBBoxByRadian(&bbox, box_info_ptr[i].angle_to_rotate, v);
4255             x = abs_cx + (int)(abs_radius*cos(half_way_angle));
4256             y = abs_cy - (int)(abs_radius*sin(half_way_angle));
4257          }
4258          break;
4259       case LAYOUT_DIR_N:
4260          half_way_angle = angle-(box_info_ptr[i].angle_in_radian/2.0);
4261          if (finalize) {
4262             orig_x = ((obj_ptr->obbox.rbx+obj_ptr->obbox.ltx)>>1);
4263             orig_y = obj_ptr->obbox.rby;
4264             RotateObjForLayout(obj_ptr, box_info_ptr[i].angle_to_rotate,
4265                   CORNER_TOP);
4266             x = abs_cx + (int)((abs_radius-h)*cos(half_way_angle));
4267             y = abs_cy - (int)((abs_radius-h)*sin(half_way_angle));
4268          } else {
4269             SetBBRec(&bbox, -(w>>1), 0, w-(w>>1), h);
4270             box_info_ptr[i].angle_to_rotate = (-half_way_angle+(M_PI/2.0));
4271             RotateBBoxByRadian(&bbox, box_info_ptr[i].angle_to_rotate, v);
4272             x = abs_cx + (int)(abs_radius*cos(half_way_angle));
4273             y = abs_cy - (int)(abs_radius*sin(half_way_angle));
4274          }
4275          break;
4276       }
4277       if (finalize) {
4278          MoveObj(obj_ptr, x-orig_x, y-orig_y);
4279       } else {
4280          box_info_ptr[i].v[0].x = OFFSET_X(x + v[0].x);
4281          box_info_ptr[i].v[0].y = OFFSET_Y(y + v[0].y);
4282          box_info_ptr[i].v[1].x = OFFSET_X(x + v[1].x);
4283          box_info_ptr[i].v[1].y = OFFSET_Y(y + v[1].y);
4284          box_info_ptr[i].v[2].x = OFFSET_X(x + v[2].x);
4285          box_info_ptr[i].v[2].y = OFFSET_Y(y + v[2].y);
4286          box_info_ptr[i].v[3].x = OFFSET_X(x + v[3].x);
4287          box_info_ptr[i].v[3].y = OFFSET_Y(y + v[3].y);
4288          box_info_ptr[i].v[4].x = box_info_ptr[i].v[0].x;
4289          box_info_ptr[i].v[4].y = box_info_ptr[i].v[0].y;
4290       }
4291       switch (gnLayoutDirection) {
4292       case LAYOUT_DIR_S:
4293          angle += box_info_ptr[i].angle_in_radian;
4294          break;
4295       case LAYOUT_DIR_N:
4296          angle -= box_info_ptr[i].angle_in_radian;
4297          break;
4298       }
4299    }
4300    return TRUE;
4301 }
4302 
4303 #define NO_GENERATE 0
4304 #define GENERATE 1
4305 
4306 static
HighLightLayout(arc_ptr,box_info_ptr,grid_x,grid_y,generate)4307 void HighLightLayout(arc_ptr, box_info_ptr, grid_x, grid_y, generate)
4308    struct ArcRec *arc_ptr;
4309    struct BoxInfoRec *box_info_ptr;
4310    int grid_x, grid_y, generate;
4311 {
4312    int i, num_objects=numObjSelected-1;
4313 
4314    if (generate) {
4315       HighLightOrFinalLayout(arc_ptr, box_info_ptr, grid_x, grid_y,
4316             SET_LAYOUT_VS);
4317    }
4318    if (box_info_ptr[0].valid_v) {
4319       for (i=0; i < num_objects; i++) {
4320          XDrawLines(mainDisplay, drawWindow, revDefaultGC, box_info_ptr[i].v, 5,
4321                CoordModeOrigin);
4322       }
4323    }
4324 }
4325 
4326 static
DoLayoutOnArc(arc_obj,box_info_ptr)4327 void DoLayoutOnArc(arc_obj, box_info_ptr)
4328    struct ObjRec *arc_obj;
4329    struct BoxInfoRec *box_info_ptr;
4330 {
4331    struct ArcRec *arc_ptr=arc_obj->detail.a;
4332    int done=FALSE, something_changed=FALSE, grid_x=0, grid_y=0;
4333 
4334    SetMouseStatus(TgLoadCachedString(CSTID_START_LAYOUT_ON_ARC),
4335          TgLoadCachedString(CSTID_FINISH), TgLoadCachedString(CSTID_FINISH));
4336    if (!debugNoPointerGrab) {
4337       XGrabPointer(mainDisplay, drawWindow, FALSE,
4338             PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
4339             GrabModeAsync, GrabModeAsync, None, handCursor, CurrentTime);
4340    }
4341    while (!done) {
4342       XEvent input;
4343 
4344       XNextEvent(mainDisplay, &input);
4345       switch (input.type) {
4346       case Expose: ExposeEventHandler(&input, TRUE); break;
4347       case VisibilityNotify: ExposeEventHandler(&input, TRUE); break;
4348       case ButtonPress:
4349          if (input.xbutton.button == Button1) {
4350             SetMouseStatus(TgLoadCachedString(CSTID_END_LAYOUT_ON_ARC), "", "");
4351             GridXY(input.xbutton.x, input.xbutton.y, &grid_x, &grid_y);
4352             HighLightLayout(arc_ptr, box_info_ptr, grid_x, grid_y, GENERATE);
4353             something_changed = TRUE;
4354          } else {
4355             XUngrabPointer(mainDisplay, CurrentTime);
4356             XSync(mainDisplay, False);
4357             done = TRUE;
4358          }
4359          break;
4360       case MotionNotify:
4361          if (something_changed) {
4362             HighLightLayout(arc_ptr, box_info_ptr, grid_x, grid_y, NO_GENERATE);
4363             GridXY(input.xmotion.x, input.xmotion.y, &grid_x, &grid_y);
4364             HighLightLayout(arc_ptr, box_info_ptr, grid_x, grid_y, GENERATE);
4365          }
4366          break;
4367       case ButtonRelease:
4368          XUngrabPointer(mainDisplay, CurrentTime);
4369          XSync(mainDisplay, False);
4370          done = TRUE;
4371          HighLightLayout(arc_ptr, box_info_ptr, grid_x, grid_y, NO_GENERATE);
4372       }
4373    }
4374    if (something_changed && box_info_ptr[0].valid_v) {
4375       int ltx=selLtX, lty=selLtY, rbx=selRbX, rby=selRbY;
4376 
4377       HighLightReverse();
4378       PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
4379       if (HighLightOrFinalLayout(arc_ptr, box_info_ptr, grid_x, grid_y,
4380             FINALIZE_LAYOUT)) {
4381          RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
4382          UpdSelBBox();
4383          RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
4384                rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
4385                selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
4386                selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
4387          SetFileModified(TRUE);
4388          justDupped = FALSE;
4389       } else {
4390          AbortPrepareCmd(CMD_REPLACE);
4391       }
4392       HighLightForward();
4393    }
4394 }
4395 
4396 static
ObjsAlreadySorted(box_info_ptr,min_index,max_index)4397 int ObjsAlreadySorted(box_info_ptr, min_index, max_index)
4398    struct BoxInfoRec *box_info_ptr;
4399    int min_index, max_index;
4400 {
4401    int i;
4402 
4403    for (i=min_index; i < max_index; i++) {
4404       int d=box_info_ptr[i].obj->obbox.ltx-box_info_ptr[i+1].obj->obbox.ltx;
4405 
4406       if (d > 0) {
4407          return FALSE;
4408       } else if (d == 0 &&
4409             box_info_ptr[i].obj->obbox.lty > box_info_ptr[i+1].obj->obbox.lty) {
4410          return FALSE;
4411       }
4412    }
4413    return TRUE;
4414 }
4415 
4416 static
QuickSortObjs(box_info_ptr,min_index,max_index,level)4417 void QuickSortObjs(box_info_ptr, min_index, max_index, level)
4418    struct BoxInfoRec *box_info_ptr;
4419    int min_index, max_index, level;
4420 {
4421    int i, j, pivot_index, pivot_x_value, pivot_y_value, something_swapped;
4422    struct ObjRec *tmp_obj;
4423 
4424    if (min_index > max_index) return;
4425    if (ObjsAlreadySorted(box_info_ptr, min_index, max_index)) return;
4426    pivot_index = max_index;
4427    pivot_x_value = box_info_ptr[pivot_index].obj->obbox.ltx;
4428    pivot_y_value = box_info_ptr[pivot_index].obj->obbox.lty;
4429    i = min_index;
4430    j = max_index-1;
4431    something_swapped = FALSE;
4432    do {
4433       int d;
4434 
4435       while (TRUE) {
4436          d = box_info_ptr[i].obj->obbox.ltx-pivot_x_value;
4437          if (d < 0 || (d == 0 &&
4438                box_info_ptr[i].obj->obbox.lty < pivot_y_value)) {
4439             i++;
4440          } else {
4441             break;
4442          }
4443       }
4444       while (j > i) {
4445          d = box_info_ptr[j].obj->obbox.ltx-pivot_x_value;
4446          if (d > 0 || (d == 0 &&
4447                box_info_ptr[j].obj->obbox.lty > pivot_y_value)) {
4448             j--;
4449          } else {
4450             break;
4451          }
4452       }
4453       if (j > i) {
4454          tmp_obj = box_info_ptr[j].obj;
4455          box_info_ptr[j].obj = box_info_ptr[i].obj;
4456          box_info_ptr[i].obj = tmp_obj;
4457          something_swapped = TRUE;
4458          if (j == i+1) break;
4459          i++; j--;
4460       } else {
4461          break;
4462       }
4463    } while (TRUE);
4464    if (i == max_index) {
4465       /* pivot corresponds to the largest */
4466       if (something_swapped) {
4467 #ifdef _TGIF_DBG /* debug, do not translate */
4468          fprintf(stderr, "Huh? min_index=%1d, max_index=%1d, level=%1d\n",
4469                min_index, max_index, level);
4470 #endif /* _TGIF_DBG */
4471       } else {
4472          QuickSortObjs(box_info_ptr, min_index, j, level+1);
4473       }
4474    } else if (j > i) {
4475       tmp_obj = box_info_ptr[max_index].obj;
4476       box_info_ptr[max_index].obj = box_info_ptr[j].obj;
4477       box_info_ptr[j].obj = tmp_obj;
4478       QuickSortObjs(box_info_ptr, min_index, j-1, level+1);
4479       QuickSortObjs(box_info_ptr, j+1, max_index, level+1);
4480    } else {
4481       tmp_obj = box_info_ptr[max_index].obj;
4482       box_info_ptr[max_index].obj = box_info_ptr[i].obj;
4483       box_info_ptr[i].obj = tmp_obj;
4484       QuickSortObjs(box_info_ptr, min_index, i-1, level+1);
4485       QuickSortObjs(box_info_ptr, i+1, max_index, level+1);
4486    }
4487 }
4488 
4489 static
DecideLayoutDirection(obj_ptr)4490 int DecideLayoutDirection(obj_ptr)
4491    struct ObjRec *obj_ptr;
4492 {
4493    struct ArcRec *arc_ptr=obj_ptr->detail.a;
4494    struct BBRec *p_obbox=(&obj_ptr->obbox);
4495    int cx=arc_ptr->xc, cy=arc_ptr->yc, h_slack, v_slack;
4496    char spec[MAXSTRING+1];
4497 
4498    h_slack = ((p_obbox->ltx+p_obbox->rbx)>>1) - cx;
4499    v_slack = ((p_obbox->lty+p_obbox->rby)>>1) - cy;
4500    if (h_slack == 0) {
4501       if (v_slack == 0) {
4502          return LAYOUT_DIR_S;
4503       } else if (v_slack > 0) {
4504          return LAYOUT_DIR_S;
4505       } else {
4506          return LAYOUT_DIR_N;
4507       }
4508    }
4509    *spec = '\0';
4510    if (Dialog(TgLoadString(STID_ENTER_CONCAVE_OR_CONVEX), NULL, spec) ==
4511          INVALID) {
4512       return LAYOUT_DIR_NONE;
4513    }
4514    UtilTrimBlanks(spec);
4515    /* do not translate -- program constants */
4516    if (UtilStrICmp(spec, "concave") == 0) {
4517       return LAYOUT_DIR_N;
4518    } else if (UtilStrICmp(spec, "convex") == 0) {
4519       return LAYOUT_DIR_S;
4520    } else {
4521       switch (*spec) {
4522       case 'c': case 'C': return LAYOUT_DIR_N;
4523       case 'v': case 'V': return LAYOUT_DIR_S;
4524       }
4525    }
4526    sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC), spec);
4527    MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4528    return LAYOUT_DIR_NONE;
4529 }
4530 
LayoutOnArc()4531 void LayoutOnArc()
4532 {
4533    struct SelRec *sel_ptr;
4534    struct ObjRec *arc_obj=NULL;
4535    int arc_obj_count=0, i, something_locked=FALSE;
4536    struct BoxInfoRec *box_info_ptr;
4537 
4538    if (curChoice != NOTHING) {
4539       MsgBox(TgLoadString(STID_SEL_AN_ARC_OBJ), TOOL_NAME, INFO_MB);
4540       return;
4541    }
4542    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
4543       struct ObjRec *obj_ptr=sel_ptr->obj;
4544 
4545       if (obj_ptr->type == OBJ_ARC) {
4546          arc_obj_count++;
4547          arc_obj = obj_ptr;
4548       } else if (obj_ptr->locked) {
4549          something_locked = TRUE;
4550       }
4551    }
4552    if (arc_obj_count == 0) {
4553       MsgBox(TgLoadString(STID_NO_ARC_OBJ_SELECTED), TOOL_NAME, INFO_MB);
4554       return;
4555    } else if (arc_obj_count > 1) {
4556       MsgBox(TgLoadString(STID_TOO_MANY_ARC_SEL_ONLY_ONE_ARC), TOOL_NAME,
4557             INFO_MB);
4558       return;
4559    } else if (numObjSelected == 1) {
4560       MsgBox(TgLoadString(STID_NO_OTHER_OBJ_FOR_LAYOUTONARC), TOOL_NAME,
4561             INFO_MB);
4562       return;
4563    } else if (something_locked) {
4564       MsgBox(TgLoadString(STID_CANNOT_LAYOUTONARC_LOCKED), TOOL_NAME, INFO_MB);
4565       return;
4566    }
4567    if (arc_obj->ctm != NULL || arc_obj->detail.a->w != arc_obj->detail.a->h) {
4568       MsgBox(TgLoadString(STID_ARC_XFORMED_FOR_LAYOUTONARC), TOOL_NAME,
4569             INFO_MB);
4570       return;
4571    }
4572    gnLayoutDirection = DecideLayoutDirection(arc_obj);
4573    if (gnLayoutDirection == LAYOUT_DIR_NONE) {
4574       return;
4575    }
4576    box_info_ptr = (struct BoxInfoRec *)malloc(
4577          (numObjSelected-1)*sizeof(struct BoxInfoRec));
4578    if (box_info_ptr == NULL) {
4579       FailAllocMessage();
4580       return;
4581    }
4582    for (i=0, sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
4583       struct ObjRec *obj_ptr=sel_ptr->obj;
4584 
4585       if (obj_ptr->type != OBJ_ARC) {
4586          box_info_ptr[i].obj = obj_ptr;
4587          i++;
4588       }
4589    }
4590    QuickSortObjs(box_info_ptr, 0, numObjSelected-2, 0);
4591    for (i=0; i < numObjSelected-1; i++) {
4592       struct ObjRec *obj_ptr=box_info_ptr[i].obj;
4593 
4594       box_info_ptr[i].w = obj_ptr->obbox.rbx-obj_ptr->obbox.ltx;
4595       box_info_ptr[i].h = obj_ptr->obbox.rby-obj_ptr->obbox.lty;
4596       box_info_ptr[i].half_w = (double)(box_info_ptr[i].w>>1);
4597       box_info_ptr[i].valid_v = FALSE;
4598    }
4599    SaveStatusStrings();
4600    DoLayoutOnArc(arc_obj, box_info_ptr);
4601    RestoreStatusStrings();
4602    free(box_info_ptr);
4603 }
4604 
4605 /* ----------------------- PreciseRotate ----------------------- */
4606 
4607 static
FinishPreciseRotate(angle_spec,pivot_x,pivot_y)4608 int FinishPreciseRotate(angle_spec, pivot_x, pivot_y)
4609    double angle_spec;
4610    int pivot_x, pivot_y;
4611 {
4612    struct SelRec *sel_ptr;
4613    double angle_in_radian=angle_spec*M_PI/180.0;
4614    double sin_val=sin(angle_in_radian);
4615    double cos_val=cos(angle_in_radian);
4616 
4617    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
4618       struct ObjRec *obj_ptr=sel_ptr->obj;
4619       int orig_x=((obj_ptr->obbox.ltx+obj_ptr->obbox.rbx)>>1);
4620       int orig_y=obj_ptr->obbox.lty;
4621       int x=0, y=0, dx=orig_x-pivot_x, dy=orig_y-pivot_y;
4622 
4623       if (dx != 0 || dy != 0) {
4624          x = (short)round(dx*cos_val - dy*sin_val);
4625          y = (short)round(dx*sin_val + dy*cos_val);
4626       }
4627       x += pivot_x;
4628       y += pivot_y;
4629       /* RotateObjForLayout() rotates about center-top */
4630       RotateObjForLayout(obj_ptr, angle_in_radian, CORNER_BOTTOM);
4631       MoveObj(obj_ptr, x-orig_x, y-orig_y);
4632    }
4633    return TRUE;
4634 }
4635 
PreciseRotate()4636 void PreciseRotate()
4637 {
4638    char spec[MAXSTRING+1];
4639    double angle_spec;
4640    int arc_count=0, pivot_x=0, pivot_y=0, ltx, lty, rbx, rby;
4641    struct SelRec *sel_ptr;
4642    struct ObjRec *arc_obj=NULL;
4643 
4644    if (curChoice == VERTEXMODE) {
4645       MsgBox(TgLoadString(STID_ROT_NOT_AVAIL_ON_VERTEX_MODE), TOOL_NAME,
4646             INFO_MB);
4647       return;
4648    } else if (curChoice != NOTHING || topSel == NULL) {
4649       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
4650       return;
4651    }
4652    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
4653       if (sel_ptr->obj->type == OBJ_ARC) {
4654          arc_obj = sel_ptr->obj;
4655          arc_count++;
4656       }
4657    }
4658    if (!autoRotatePivot && rotatePivotAbsXYValid) {
4659       pivot_x = rotatePivotAbsX;
4660       pivot_y = rotatePivotAbsY;
4661    } else {
4662       if (arc_count == 1) {
4663          if (arc_obj->ctm == NULL) {
4664             pivot_x = arc_obj->detail.a->xc;
4665             pivot_y = arc_obj->detail.a->yc;
4666          } else {
4667             struct ArcRec *arc_ptr=arc_obj->detail.a;
4668             int x, y;
4669 
4670             TransformPointThroughCTM(arc_ptr->xc-arc_obj->x,
4671                   arc_ptr->yc-arc_obj->y, arc_obj->ctm, &x, &y);
4672             pivot_x = x + arc_obj->x;
4673             pivot_y = y + arc_obj->y;
4674          }
4675       } else {
4676          pivot_x = (selObjLtX+selObjRbX)>>1;
4677          pivot_y = (selObjLtY+selObjRbY)>>1;
4678       }
4679    }
4680    *spec = '\0';
4681    Dialog(TgLoadString(STID_ENTER_AN_ANGLE_IN_DEGREES), NULL, spec);
4682    UtilTrimBlanks(spec);
4683    if (*spec == '\0') return;
4684    if (sscanf(spec, "%lf", &angle_spec) != 1) {
4685       sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC_NUM_EXPECTED), spec);
4686       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4687       return;
4688    }
4689    if (fabs(angle_spec) < (1.0e-5)) return;
4690 
4691    ltx = selLtX; lty = selLtY; rbx = selRbX; rby = selRbY;
4692    HighLightReverse();
4693    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
4694    if (FinishPreciseRotate(angle_spec, pivot_x, pivot_y)) {
4695       RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
4696       UpdSelBBox();
4697       RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
4698             rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
4699             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
4700             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
4701       SetFileModified(TRUE);
4702       justDupped = FALSE;
4703    } else {
4704       AbortPrepareCmd(CMD_REPLACE);
4705    }
4706    HighLightForward();
4707 }
4708 
RotateAllSelObj(angle_spec)4709 void RotateAllSelObj(angle_spec)
4710    double angle_spec;
4711 {
4712    int arc_count=0, pivot_x=0, pivot_y=0, ltx, lty, rbx, rby;
4713    struct SelRec *sel_ptr;
4714    struct ObjRec *arc_obj=NULL;
4715 
4716    if (curChoice != NOTHING || topSel == NULL) {
4717       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
4718       return;
4719    }
4720    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
4721       if (sel_ptr->obj->type == OBJ_ARC) {
4722          arc_obj = sel_ptr->obj;
4723          arc_count++;
4724       }
4725    }
4726    if (arc_count == 1) {
4727       if (arc_obj->ctm == NULL) {
4728          pivot_x = arc_obj->detail.a->xc;
4729          pivot_y = arc_obj->detail.a->yc;
4730       } else {
4731          struct ArcRec *arc_ptr=arc_obj->detail.a;
4732          int x, y;
4733 
4734          TransformPointThroughCTM(arc_ptr->xc-arc_obj->x,
4735                arc_ptr->yc-arc_obj->y, arc_obj->ctm, &x, &y);
4736          pivot_x = x + arc_obj->x;
4737          pivot_y = y + arc_obj->y;
4738       }
4739    } else {
4740       pivot_x = (selObjLtX+selObjRbX)>>1;
4741       pivot_y = (selObjLtY+selObjRbY)>>1;
4742    }
4743    if (fabs(angle_spec) < (1.0e-5)) return;
4744 
4745    ltx = selLtX; lty = selLtY; rbx = selRbX; rby = selRbY;
4746    HighLightReverse();
4747    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
4748    FinishPreciseRotate(angle_spec, pivot_x, pivot_y);
4749    RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
4750    UpdSelBBox();
4751    RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
4752          rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
4753          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
4754          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
4755    HighLightForward();
4756    SetFileModified(TRUE);
4757    justDupped = FALSE;
4758 }
4759 
NoTransform()4760 void NoTransform()
4761 {
4762    struct SelRec *sel_ptr;
4763    int changed=FALSE, grouped=FALSE;
4764    int ltx=selLtX, lty=selLtY, rbx=selRbX, rby=selRbY;
4765 
4766    if (topSel == NULL) {
4767       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
4768       return;
4769    }
4770    HighLightReverse();
4771    StartCompositeCmd();
4772    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
4773       struct ObjRec *obj_ptr=sel_ptr->obj;
4774 
4775       if (obj_ptr->ctm != NULL) {
4776          int cx=0, cy=0;
4777 
4778          switch (obj_ptr->type) {
4779          case OBJ_GROUP:
4780          case OBJ_SYM:
4781          case OBJ_ICON:
4782          case OBJ_PIN:
4783             grouped = TRUE;
4784             break;
4785 
4786          default:
4787             cx = ((obj_ptr->obbox.ltx+obj_ptr->obbox.rbx)>>1);
4788             cy = ((obj_ptr->obbox.lty+obj_ptr->obbox.rby)>>1);
4789 
4790             changed = TRUE;
4791             PrepareToReplaceAnObj(obj_ptr);
4792             free(obj_ptr->ctm);
4793             obj_ptr->ctm = NULL;
4794             memcpy(&obj_ptr->obbox, &obj_ptr->orig_obbox, sizeof(struct BBRec));
4795             if (obj_ptr->type == OBJ_TEXT) {
4796                memcpy(&obj_ptr->bbox, &obj_ptr->detail.t->orig_bbox,
4797                      sizeof(struct BBRec));
4798             }
4799             AdjObjSplineVs(obj_ptr);
4800             AdjObjCache(obj_ptr);
4801             AdjObjBBox(obj_ptr);
4802             MoveObj(obj_ptr,
4803                   cx-((obj_ptr->obbox.ltx+obj_ptr->obbox.rbx)>>1),
4804                   cy-((obj_ptr->obbox.lty+obj_ptr->obbox.rby)>>1));
4805             RecordReplaceAnObj(obj_ptr);
4806             break;
4807          }
4808       } else {
4809          switch (obj_ptr->type) {
4810          case OBJ_GROUP:
4811          case OBJ_SYM:
4812          case OBJ_ICON:
4813          case OBJ_PIN:
4814             grouped = TRUE;
4815             break;
4816 
4817          default: break;
4818          }
4819       }
4820    }
4821    EndCompositeCmd();
4822    if (changed) {
4823       UpdSelBBox();
4824       RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
4825             rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
4826             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
4827             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
4828       HighLightForward();
4829       SetFileModified(TRUE);
4830       justDupped = FALSE;
4831 
4832       if (grouped) {
4833          Msg(TgLoadString(STID_SOME_GROUP_OBJ_NOT_MODIFIED));
4834       }
4835    } else {
4836       if (grouped) {
4837          MsgBox(TgLoadString(STID_CANNOT_REM_XFORM_FOR_GROUPED), TOOL_NAME,
4838                INFO_MB);
4839       }
4840       HighLightForward();
4841    }
4842 }
4843 
SetEditTextSize()4844 void SetEditTextSize()
4845 {
4846    char spec[MAXSTRING+1];
4847    int size=0;
4848 
4849    MakeQuiescent();
4850    *spec = '\0';
4851    sprintf(gszMsgBox, TgLoadString(STID_ENTER_EDIT_TEXT_SIZE), editTextSize);
4852    *spec = '\0';
4853    Dialog(gszMsgBox, NULL, spec);
4854    UtilTrimBlanks(spec);
4855    if (*spec == '\0') return;
4856    size = atoi(spec);
4857    if (size != 0 && (size < 4 || size > 34)) {
4858       sprintf(gszMsgBox, TgLoadString(STID_EDIT_TEXT_SIZE_OUT_OF_RANGE), spec);
4859       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
4860    } else {
4861       SetEditTextSizeValue(size);
4862 
4863       if (editTextSize == 0) {
4864          Msg(TgLoadString(STID_ACTUAL_EDIT_TEXT_SIZE));
4865       } else {
4866          sprintf(gszMsgBox, TgLoadString(STID_USE_SPECIFIED_EDIT_TEXT_SIZE),
4867                editTextSize);
4868          Msg(gszMsgBox);
4869       }
4870    }
4871 }
4872 
4873 static
UpdateOuterInnerSelForFind(obj_ptr)4874 void UpdateOuterInnerSelForFind(obj_ptr)
4875    struct ObjRec *obj_ptr;
4876    /* outerSel is at the top of the chain and innerSel is at the bottom */
4877 {
4878    AddObjIntoSel(obj_ptr, NULL, outerSelForFind, &outerSelForFind,
4879          &innerSelForFind);
4880 }
4881 
4882 static
DoFind(obj_ptr)4883 struct ObjRec *DoFind(obj_ptr)
4884    struct ObjRec *obj_ptr;
4885 {
4886    int found_starting_point=TRUE;
4887    MiniLinesInfo *minilines=NULL;
4888    struct ObjRec *ptr=NULL;
4889    struct ObjRec *return_obj=NULL;
4890 
4891    if (obj_ptr->type != OBJ_TEXT) {
4892       struct AttrRec *attr_ptr;
4893 
4894       for (attr_ptr=obj_ptr->fattr; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
4895          if (attr_ptr->shown) {
4896             return_obj = DoFind(attr_ptr->obj);
4897             if (return_obj != NULL) {
4898                return return_obj;
4899             }
4900          }
4901       }
4902    }
4903    switch (obj_ptr->type) {
4904    case OBJ_SYM:
4905    case OBJ_GROUP:
4906    case OBJ_ICON:
4907       for (ptr=obj_ptr->detail.r->last; ptr != NULL; ptr=ptr->prev) {
4908          ptr->tmp_parent = obj_ptr;
4909          return_obj = DoFind(ptr);
4910          if (return_obj != NULL) {
4911             UpdateOuterInnerSelForFind(obj_ptr);
4912             return return_obj;
4913          }
4914       }
4915       break;
4916    case OBJ_PIN:
4917       ptr = GetPinObj(obj_ptr);
4918       ptr->tmp_parent = obj_ptr;
4919       return_obj = DoFind(ptr);
4920       if (return_obj != NULL) {
4921          return return_obj;
4922       }
4923       break;
4924    case OBJ_TEXT:
4925       found_starting_point = TRUE;
4926       minilines = (&obj_ptr->detail.t->minilines);
4927 
4928       SaveCursorPositionInCurText();
4929 
4930       curStrBlock = minilines->first->first_block;
4931       textCurIndex = 0;
4932       ResetOnCursorKey(FALSE);
4933       SetTextHighlight();
4934       UpdatePinnedMenu(MENU_EDIT);
4935 
4936       if (FindStringInMiniLines(minilines, &found_starting_point,
4937             gpszSearch, gnSearchLen, gnSearchCaseSensitive,
4938             &gpFoundStartStrBlock, &gnFoundStartCharIndex,
4939             &gpFoundEndStrBlock, &gnFoundEndCharIndex)) {
4940          return obj_ptr;
4941       }
4942       RestoreCursorPositionInCurText();
4943       break;
4944    default: break;
4945    }
4946    return NULL;
4947 }
4948 
4949 static int gnFoundStartingPoint=FALSE;
4950 
4951 static
DoFindAlready(obj_ptr,bottom_half,pn_give_up)4952 struct ObjRec *DoFindAlready(obj_ptr, bottom_half, pn_give_up)
4953    struct ObjRec *obj_ptr;
4954    int bottom_half; /* TRUE if searching from curTextObj to topObj */
4955    int *pn_give_up;
4956 {
4957    struct ObjRec *ptr=NULL;
4958    struct ObjRec *return_obj=NULL;
4959 
4960    if (obj_ptr->type != OBJ_TEXT) {
4961       struct AttrRec *attr_ptr;
4962 
4963       for (attr_ptr=obj_ptr->fattr; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
4964          if (attr_ptr->shown) {
4965             return_obj = DoFindAlready(attr_ptr->obj, bottom_half, pn_give_up);
4966             if (return_obj != NULL) {
4967                return return_obj;
4968             }
4969             if (pn_give_up != NULL && *pn_give_up) {
4970                return NULL;
4971             }
4972          }
4973       }
4974    }
4975    switch (obj_ptr->type) {
4976    case OBJ_SYM:
4977    case OBJ_GROUP:
4978    case OBJ_ICON:
4979       for (ptr=obj_ptr->detail.r->last; ptr != NULL; ptr=ptr->prev) {
4980          ptr->tmp_parent = obj_ptr;
4981          return_obj = DoFindAlready(ptr, bottom_half, pn_give_up);
4982          if (return_obj != NULL) {
4983             UpdateOuterInnerSelForFind(obj_ptr);
4984             return return_obj;
4985          }
4986          if (pn_give_up != NULL && *pn_give_up) {
4987             return NULL;
4988          }
4989       }
4990       break;
4991    case OBJ_PIN:
4992       ptr = GetPinObj(obj_ptr);
4993       ptr->tmp_parent = obj_ptr;
4994       return_obj = DoFindAlready(ptr, bottom_half, pn_give_up);
4995       if (return_obj != NULL) {
4996          return return_obj;
4997       }
4998       if (pn_give_up != NULL && *pn_give_up) {
4999          return NULL;
5000       }
5001       break;
5002    case OBJ_TEXT:
5003       if ((bottom_half && gnFoundStartingPoint) ||
5004             (!bottom_half && obj_ptr != curTextObj)) {
5005          int found_starting_point=TRUE;
5006          MiniLinesInfo *minilines=(&obj_ptr->detail.t->minilines);
5007 
5008          SaveCursorPositionInCurText();
5009 
5010          curStrBlock = minilines->first->first_block;
5011          textCurIndex = 0;
5012          ResetOnCursorKey(FALSE);
5013          SetTextHighlight();
5014          UpdatePinnedMenu(MENU_EDIT);
5015 
5016          if (FindStringInMiniLines(minilines, &found_starting_point,
5017                gpszSearch, gnSearchLen, gnSearchCaseSensitive,
5018                &gpFoundStartStrBlock, &gnFoundStartCharIndex,
5019                &gpFoundEndStrBlock, &gnFoundEndCharIndex)) {
5020             return obj_ptr;
5021          }
5022          RestoreCursorPositionInCurText();
5023       } else if (bottom_half) {
5024          if (obj_ptr == curTextObj) {
5025             gnFoundStartingPoint = TRUE;
5026          }
5027       } else {
5028          if (obj_ptr == curTextObj) {
5029             if (pn_give_up != NULL) *pn_give_up = TRUE;
5030          }
5031       }
5032       break;
5033    default: break;
5034    }
5035    return NULL;
5036 }
5037 
5038 static
Find()5039 void Find()
5040 {
5041    int wrapped=FALSE;
5042    struct ObjRec *obj_ptr=NULL;
5043    struct ObjRec *found_obj=NULL;
5044 
5045    CleanOuterInnerSelForFind();
5046    UpdatePinnedMenu(MENU_EDIT);
5047 
5048    if (gpszSearch == NULL) return;
5049    gnSearchLen = strlen(gpszSearch);
5050 
5051    gnFoundStartCharIndex = 0;
5052    gpFoundStartStrBlock = NULL;
5053 
5054    SetWatchCursor(drawWindow);
5055    SetWatchCursor(mainWindow);
5056 
5057    if (curChoice == DRAWTEXT) {
5058       if (curTextObj != NULL && FindTextInCurTextObj(gpszSearch, gnSearchLen,
5059             gnSearchCaseSensitive, &gpFoundStartStrBlock,
5060             &gnFoundStartCharIndex, &gpFoundEndStrBlock,
5061             &gnFoundEndCharIndex)) {
5062          found_obj = curTextObj;
5063          CopyOuterSelToOuterSelForFind();
5064       }
5065       if (found_obj == NULL) {
5066          gnFoundStartingPoint = FALSE;
5067          for (obj_ptr=botObj; obj_ptr != NULL; obj_ptr=obj_ptr->prev) {
5068             obj_ptr->tmp_parent = NULL;
5069             if (!ObjInVisibleLayer(obj_ptr)) {
5070                continue;
5071             }
5072             found_obj = DoFindAlready(obj_ptr, TRUE, NULL);
5073             if (found_obj != NULL) {
5074                break;
5075             }
5076          }
5077          if (found_obj == NULL && gnFoundStartingPoint) {
5078             wrapped = TRUE;
5079             for (obj_ptr=botObj; obj_ptr != NULL; obj_ptr=obj_ptr->prev) {
5080                int give_up=FALSE;
5081 
5082                obj_ptr->tmp_parent = NULL;
5083                if (!ObjInVisibleLayer(obj_ptr)) {
5084                   continue;
5085                }
5086                found_obj = DoFindAlready(obj_ptr, FALSE, &give_up);
5087                if (found_obj != NULL) {
5088                   break;
5089                }
5090                if (give_up) {
5091                   break;
5092                }
5093             }
5094          }
5095       }
5096    }
5097    if (found_obj == NULL) {
5098       if (curChoice == DRAWTEXT) {
5099          wrapped = TRUE;
5100       }
5101       for (obj_ptr=botObj; obj_ptr != NULL; obj_ptr=obj_ptr->prev) {
5102          obj_ptr->tmp_parent = NULL;
5103          if (!ObjInVisibleLayer(obj_ptr)) {
5104             continue;
5105          }
5106          found_obj = DoFind(obj_ptr);
5107          if (found_obj != NULL) {
5108             break;
5109          }
5110       }
5111       if (found_obj != NULL) {
5112          if (curChoice != DRAWTEXT) {
5113             SetCurChoice(DRAWTEXT);
5114          }
5115       }
5116    }
5117    SetDefaultCursor(mainWindow);
5118    ShowCursor();
5119 
5120    if (found_obj == NULL) {
5121       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_FIND_NAMED_STRING),
5122             gpszSearch);
5123       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5124    } else {
5125       HighLightText(found_obj, gpFoundStartStrBlock, gnFoundStartCharIndex,
5126             gpFoundEndStrBlock, gnFoundEndCharIndex);
5127       if (wrapped) {
5128          Msg(TgLoadString(STID_FIND_CMD_WRAPPED));
5129       }
5130    }
5131 }
5132 
FindCaseSensitive()5133 void FindCaseSensitive()
5134 {
5135    char spec[MAXSTRING];
5136 
5137    *spec = '\0';
5138    Dialog(TgLoadString(STID_ENTER_CASE_STR_TO_FIND), NULL, spec);
5139    if (*spec == '\0') {
5140       return;
5141    }
5142    if (gpszSearch != NULL) free(gpszSearch);
5143    if ((gpszSearch=UtilStrDup(spec)) == NULL) FailAllocMessage();
5144 
5145    gnSearchCaseSensitive = TRUE;
5146    if (curChoice != DRAWTEXT) {
5147       MakeQuiescent();
5148    }
5149    Find();
5150 }
5151 
FindNoCase()5152 void FindNoCase()
5153 {
5154    char spec[MAXSTRING];
5155 
5156    *spec = '\0';
5157    Dialog(TgLoadString(STID_ENTER_NOCASE_STR_TO_FIND), NULL, spec);
5158    if (*spec == '\0') {
5159       return;
5160    }
5161    if (gpszSearch != NULL) free(gpszSearch);
5162    if ((gpszSearch=UtilStrDup(spec)) == NULL) FailAllocMessage();
5163 
5164    gnSearchCaseSensitive = FALSE;
5165    if (curChoice != DRAWTEXT) {
5166       MakeQuiescent();
5167    }
5168    Find();
5169 }
5170 
FindAgain()5171 void FindAgain()
5172 {
5173    if (gpszSearch == NULL) {
5174       MsgBox(TgLoadString(STID_NO_PREVIOUS_FIND), TOOL_NAME, INFO_MB);
5175    } else {
5176       Find();
5177    }
5178 }
5179 
5180 /* ----------------------- EditMenu ----------------------- */
5181 
RefreshEditMenu(menu)5182 int RefreshEditMenu(menu)
5183    TgMenu *menu;
5184 {
5185    int ok=TRUE;
5186    StrSegInfo ssi;
5187 
5188    memset(&ssi, 0, sizeof(StrSegInfo));
5189 
5190    /* CopyPlainTextAsObject */
5191    ok &= TgEnableMenuItemById(menu, CMDID_COPYPLAINTEXTASOBJECT,
5192          (curChoice == DRAWTEXT && textHighlight));
5193 
5194    /* CopyUTF8String */
5195    ok &= TgEnableMenuItemById(menu, CMDID_COPYUTF8,
5196          (curChoice == DRAWTEXT && textHighlight &&
5197          CanCopyHighLightedTextAsUTF8Strings(NULL)));
5198    /* PasteUTF8String */
5199    ok &= TgEnableMenuItemById(menu, CMDID_PASTEUTF8,
5200          (curChoice == DRAWTEXT && textCursorShown &&
5201          CanPasteUTF8StringIntoText(&ssi)));
5202 
5203    /* ImageProc submenu */
5204    ok &= TgEnableMenuItemById(menu, MENU_IMAGEPROC, CanPerformImageProc());
5205 
5206    /* FindAgain */
5207    ok &= TgEnableMenuItemById(menu, CMDID_FINDAGAIN, (gpszSearch != NULL));
5208 
5209    return ok;
5210 }
5211 
CreateEditMenu(parent_menu,x,y,menu_info,status_str_xlated)5212 TgMenu *CreateEditMenu(parent_menu, x, y, menu_info, status_str_xlated)
5213    TgMenu *parent_menu;
5214    int x, y;
5215    TgMenuInfo *menu_info;
5216    int status_str_xlated; /* ignored, always 0 */
5217 {
5218    TgMenu *menu=TgCreateMenuFromMenuInfo(parent_menu, x, y, menu_info, FALSE);
5219 
5220    if (menu != NULL) {
5221       if (!RefreshEditMenu(menu)) {
5222          return TgDestroyMenu(menu, TRUE);
5223       }
5224       menu->refresh_proc = ((RefreshMenuFunc*)RefreshEditMenu);
5225    }
5226    return menu;
5227 }
5228 
EditMenu(X,Y,TrackMenubar)5229 int EditMenu(X, Y, TrackMenubar)
5230    int X, Y, TrackMenubar;
5231 {
5232    int rc=INVALID;
5233    TgMenu *menu=(editMenuInfo.create_proc)(NULL, X, Y, &editMenuInfo, INVALID);
5234 
5235    activeMenu = MENU_EDIT;
5236    if (menu != NULL) {
5237       menu->track_menubar = TrackMenubar;
5238 
5239       rc = TgMenuLoop(menu);
5240       TgDestroyMenu(menu, TRUE);
5241    }
5242    return rc;
5243 }
5244 
5245 /* ======================= ArrangeMenu Related ======================= */
5246 
FrontProc()5247 void FrontProc()
5248 {
5249    if (topSel != NULL) {
5250       HighLightReverse();
5251       MoveSelToTop();
5252       RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5253             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
5254       HighLightForward();
5255       SetFileModified(TRUE);
5256    }
5257 }
5258 
BackProc()5259 void BackProc()
5260 {
5261    if (topSel != NULL) {
5262       HighLightReverse();
5263       MoveSelToBot();
5264       RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5265             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
5266       HighLightForward();
5267       SetFileModified(TRUE);
5268    }
5269 }
5270 
AlignObjsTop()5271 void AlignObjsTop()
5272 {
5273    register int saved_h_align = horiAlign, saved_v_align = vertAlign;
5274 
5275    horiAlign = ALIGN_N; vertAlign = ALIGN_T;
5276    AlignSelObjs();
5277    horiAlign = saved_h_align; vertAlign = saved_v_align;
5278 }
5279 
AlignObjsMiddle()5280 void AlignObjsMiddle()
5281 {
5282    register int saved_h_align = horiAlign, saved_v_align = vertAlign;
5283 
5284    horiAlign = ALIGN_N; vertAlign = ALIGN_M;
5285    AlignSelObjs();
5286    horiAlign = saved_h_align; vertAlign = saved_v_align;
5287 }
5288 
AlignObjsBottom()5289 void AlignObjsBottom()
5290 {
5291    register int saved_h_align = horiAlign, saved_v_align = vertAlign;
5292 
5293    horiAlign = ALIGN_N; vertAlign = ALIGN_B;
5294    AlignSelObjs();
5295    horiAlign = saved_h_align; vertAlign = saved_v_align;
5296 }
5297 
AlignObjsLeft()5298 void AlignObjsLeft()
5299 {
5300    register int saved_h_align = horiAlign, saved_v_align = vertAlign;
5301 
5302    horiAlign = ALIGN_L; vertAlign = ALIGN_N;
5303    AlignSelObjs();
5304    horiAlign = saved_h_align; vertAlign = saved_v_align;
5305 }
5306 
AlignObjsCenter()5307 void AlignObjsCenter()
5308 {
5309    register int saved_h_align = horiAlign, saved_v_align = vertAlign;
5310 
5311    horiAlign = ALIGN_C; vertAlign = ALIGN_N;
5312    AlignSelObjs();
5313    horiAlign = saved_h_align; vertAlign = saved_v_align;
5314 }
5315 
AlignObjsRight()5316 void AlignObjsRight()
5317 {
5318    register int saved_h_align = horiAlign, saved_v_align = vertAlign;
5319 
5320    horiAlign = ALIGN_R; vertAlign = ALIGN_N;
5321    AlignSelObjs();
5322    horiAlign = saved_h_align; vertAlign = saved_v_align;
5323 }
5324 
5325 static
Abut(Dir)5326 void Abut(Dir)
5327    int Dir;
5328 {
5329    struct ObjRec *obj_ptr=NULL;
5330    struct SelRec *sel_ptr=NULL;
5331    struct SelRec *top_sort=NULL, *bot_sort=NULL, *sort_ptr=NULL;
5332    struct SelRec *new_sort_ptr=NULL, *tmp_sel_ptr=NULL, *next_sort=NULL;
5333    struct ObjRec *sorted_obj=NULL, *locked_obj=NULL;
5334    struct SubCmdRec *sub_cmd=NULL;
5335    int sel_ltx=0, sel_lty=0, sel_rbx=0, sel_rby=0, rbx=0, rby=0;
5336    int found=FALSE, delta=0, dx=0, dy=0;
5337 
5338    if (topSel == NULL) {
5339       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
5340       return;
5341    } else if (curChoice == VERTEXMODE) {
5342       MsgBox(TgLoadString(STID_CANNOT_ABUT_IN_VERTEX_MODE), TOOL_NAME, INFO_MB);
5343       return;
5344    } else if (numObjLocked > 1) {
5345       MsgBox(TgLoadString(STID_CANNOT_ABUT_LOCKED), TOOL_NAME, INFO_MB);
5346       return;
5347    }
5348    HighLightReverse();
5349    StartCompositeCmd();
5350    sel_ltx = selLtX; sel_lty = selLtY;
5351    sel_rbx = selRbX; sel_rby = selRbY;
5352 
5353    top_sort = (struct SelRec *)malloc(sizeof(struct SelRec));
5354    if (top_sort == NULL) FailAllocMessage();
5355    top_sort->next = top_sort->prev = NULL;
5356 
5357    top_sort->obj = sorted_obj = botSel->obj;
5358    if (botSel->obj->locked) locked_obj = botSel->obj;
5359    for (sel_ptr=botSel->prev; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
5360       obj_ptr = sel_ptr->obj;
5361       if (obj_ptr->locked) locked_obj = obj_ptr;
5362       switch (Dir) {
5363       case ABUT_HORIZONTAL:
5364          if (obj_ptr->obbox.ltx < sorted_obj->obbox.ltx ||
5365                (obj_ptr->obbox.ltx == sorted_obj->obbox.ltx &&
5366                obj_ptr->obbox.lty < sorted_obj->obbox.lty)) {
5367             top_sort->obj = sorted_obj = sel_ptr->obj;
5368          }
5369          break;
5370       case ABUT_VERTICAL:
5371          if (obj_ptr->obbox.lty < sorted_obj->obbox.lty ||
5372                (obj_ptr->obbox.lty == sorted_obj->obbox.lty &&
5373                obj_ptr->obbox.ltx < sorted_obj->obbox.ltx)) {
5374             top_sort->obj = sorted_obj = sel_ptr->obj;
5375          }
5376          break;
5377       }
5378    }
5379    bot_sort = top_sort;
5380 
5381    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
5382       if (sel_ptr->obj == top_sort->obj) continue;
5383 
5384       obj_ptr = sel_ptr->obj;
5385       new_sort_ptr = (struct SelRec *)malloc(sizeof(struct SelRec));
5386       if (new_sort_ptr == NULL) FailAllocMessage();
5387       new_sort_ptr->obj = obj_ptr;
5388       found = FALSE;
5389       for (sort_ptr=top_sort->next; sort_ptr!=NULL; sort_ptr=sort_ptr->next) {
5390          switch (Dir) {
5391          case ABUT_HORIZONTAL:
5392             if (sort_ptr->obj->obbox.ltx > obj_ptr->obbox.ltx ||
5393                   (sort_ptr->obj->obbox.ltx == obj_ptr->obbox.ltx &&
5394                   sort_ptr->obj->obbox.lty > obj_ptr->obbox.lty)) {
5395                found = TRUE;
5396             }
5397             break;
5398          case ABUT_VERTICAL:
5399             if (sort_ptr->obj->obbox.lty > obj_ptr->obbox.lty ||
5400                   (sort_ptr->obj->obbox.lty == obj_ptr->obbox.lty &&
5401                   sort_ptr->obj->obbox.ltx > obj_ptr->obbox.ltx)) {
5402                found = TRUE;
5403             }
5404             break;
5405          }
5406          if (found) break;
5407       }
5408       new_sort_ptr->next = sort_ptr;
5409       if (sort_ptr == NULL) {
5410          new_sort_ptr->prev = bot_sort;
5411          bot_sort->next = new_sort_ptr;
5412          bot_sort = new_sort_ptr;
5413       } else {
5414          new_sort_ptr->prev = sort_ptr->prev;
5415          sort_ptr->prev->next = new_sort_ptr;
5416          sort_ptr->prev = new_sort_ptr;
5417       }
5418    }
5419 
5420    tmp_sel_ptr = (struct SelRec *)malloc(sizeof(struct SelRec));
5421    if (tmp_sel_ptr == NULL) FailAllocMessage();
5422    tmp_sel_ptr->next = tmp_sel_ptr->prev = NULL;
5423 
5424    if (locked_obj != NULL) {
5425       switch (Dir) {
5426       case ABUT_HORIZONTAL:
5427          rbx = top_sort->obj->obbox.rbx;
5428          for (sort_ptr=top_sort; sort_ptr->obj!=locked_obj &&
5429                sort_ptr->next!=NULL; sort_ptr=next_sort) {
5430             next_sort = sort_ptr->next;
5431             delta = rbx-next_sort->obj->obbox.ltx;
5432             dx = (-delta);
5433             rbx = next_sort->obj->obbox.rbx+delta;
5434          }
5435          break;
5436       case ABUT_VERTICAL:
5437          rby = top_sort->obj->obbox.rby;
5438          for (sort_ptr=top_sort; sort_ptr->obj!=locked_obj &&
5439                sort_ptr->next!=NULL; sort_ptr=next_sort) {
5440             next_sort = sort_ptr->next;
5441             delta = rby-next_sort->obj->obbox.lty;
5442             dy = (-delta);
5443             rby = next_sort->obj->obbox.rby+delta;
5444          }
5445          break;
5446       }
5447    }
5448    sub_cmd = (struct SubCmdRec *)malloc(sizeof(struct SubCmdRec));
5449    if (sub_cmd == NULL) FailAllocMessage();
5450    memset(sub_cmd, 0, sizeof(struct SubCmdRec));
5451 
5452    rbx = top_sort->obj->obbox.rbx;
5453    rby = top_sort->obj->obbox.rby;
5454    found = (locked_obj == NULL);
5455    if (!found && locked_obj != top_sort->obj) {
5456       tmp_sel_ptr->obj = top_sort->obj;
5457       switch (Dir) {
5458       case ABUT_HORIZONTAL:
5459          sub_cmd->detail.move.dx = dx;
5460          sub_cmd->detail.move.dy = 0;
5461          PrepareToRecord(CMD_MOVE, tmp_sel_ptr, tmp_sel_ptr, 1);
5462          RecordCmd(CMD_MOVE, sub_cmd, NULL, NULL, 0);
5463          MoveObj(top_sort->obj, dx, 0);
5464          break;
5465       case ABUT_VERTICAL:
5466          sub_cmd->detail.move.dx = 0;
5467          sub_cmd->detail.move.dy = dy;
5468          PrepareToRecord(CMD_MOVE, tmp_sel_ptr, tmp_sel_ptr, 1);
5469          RecordCmd(CMD_MOVE, sub_cmd, NULL, NULL, 0);
5470          MoveObj(top_sort->obj, 0, dy);
5471          break;
5472       }
5473    }
5474    for (sort_ptr=top_sort; sort_ptr->next!=NULL; sort_ptr=next_sort) {
5475       next_sort = sort_ptr->next;
5476       tmp_sel_ptr->obj = next_sort->obj;
5477       switch (Dir) {
5478       case ABUT_HORIZONTAL:
5479          delta = rbx-next_sort->obj->obbox.ltx;
5480          rbx = next_sort->obj->obbox.rbx+delta;
5481          if (!found) delta += dx;
5482          sub_cmd->detail.move.dx = delta;
5483          sub_cmd->detail.move.dy = 0;
5484          PrepareToRecord(CMD_MOVE, tmp_sel_ptr, tmp_sel_ptr, 1);
5485          RecordCmd(CMD_MOVE, sub_cmd, NULL, NULL, 0);
5486          MoveObj(next_sort->obj, delta, 0);
5487          break;
5488       case ABUT_VERTICAL:
5489          delta = rby-next_sort->obj->obbox.lty;
5490          rby = next_sort->obj->obbox.rby+delta;
5491          if (!found) delta += dy;
5492          sub_cmd->detail.move.dx = 0;
5493          sub_cmd->detail.move.dy = delta;
5494          PrepareToRecord(CMD_MOVE, tmp_sel_ptr, tmp_sel_ptr, 1);
5495          RecordCmd(CMD_MOVE, sub_cmd, NULL, NULL, 0);
5496          MoveObj(next_sort->obj, 0, delta);
5497          break;
5498       }
5499       free(sort_ptr);
5500    }
5501    EndCompositeCmd();
5502    free(sort_ptr);
5503    free(sub_cmd);
5504    free(tmp_sel_ptr);
5505 
5506    UpdSelBBox();
5507    RedrawAreas(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5508          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1),
5509          sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
5510          sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1));
5511    HighLightForward();
5512    justDupped = FALSE;
5513    switch (Dir) {
5514    case ABUT_HORIZONTAL: Msg(TgLoadString(STID_ABUTTED_HORI)); break;
5515    case ABUT_VERTICAL: Msg(TgLoadString(STID_ABUTTED_VERT)); break;
5516    }
5517    SetFileModified(TRUE);
5518 }
5519 
AbutHorizontal()5520 void AbutHorizontal()
5521 {
5522    Abut(ABUT_HORIZONTAL);
5523 }
5524 
AbutVertical()5525 void AbutVertical()
5526 {
5527    Abut(ABUT_VERTICAL);
5528 }
5529 
RefreshArrangeMenu(menu)5530 void RefreshArrangeMenu(menu)
5531    TgMenu *menu;
5532 {
5533 }
5534 
ArrangeMenu(X,Y,TrackMenubar)5535 int ArrangeMenu(X, Y, TrackMenubar)
5536    int X, Y, TrackMenubar;
5537 {
5538    int rc=INVALID;
5539    TgMenu *menu=(arrangeMenuInfo.create_proc)(NULL, X, Y, &arrangeMenuInfo,
5540          FALSE);
5541 
5542    activeMenu = MENU_ARRANGE;
5543    if (menu != NULL) {
5544       menu->track_menubar = TrackMenubar;
5545 
5546       rc = TgMenuLoop(menu);
5547       TgDestroyMenu(menu, TRUE);
5548    }
5549    return rc;
5550 }
5551 
RefreshPropertiesMenu(menu)5552 int RefreshPropertiesMenu(menu)
5553    TgMenu *menu;
5554 {
5555    int ok=TRUE;
5556 
5557    /* UseAltEditTextBgColor */
5558    ok &= TgSetMenuItemCheckById(menu, CMDID_TOGGLEALTEDITTEXTBGCOLOR,
5559          useAltEditTextBgColor);
5560    /* AddColor */
5561    ok &= TgEnableMenuItemById(menu, CMDID_ADDCOLOR, colorDisplay);
5562 
5563    return ok;
5564 }
5565 
CreatePropertiesMenu(parent_menu,x,y,menu_info,status_str_xlated)5566 TgMenu *CreatePropertiesMenu(parent_menu, x, y, menu_info, status_str_xlated)
5567    TgMenu *parent_menu;
5568    int x, y;
5569    TgMenuInfo *menu_info;
5570    int status_str_xlated; /* ignored, always 0 */
5571 {
5572    TgMenu *menu=TgCreateMenuFromMenuInfo(parent_menu, x, y, menu_info, FALSE);
5573 
5574    if (menu != NULL) {
5575       if (!RefreshPropertiesMenu(menu)) {
5576          return TgDestroyMenu(menu, TRUE);
5577       }
5578       menu->refresh_proc = ((RefreshMenuFunc*)RefreshPropertiesMenu);
5579    }
5580    return menu;
5581 }
5582 
PropertiesMenu(X,Y,TrackMenubar)5583 int PropertiesMenu(X, Y, TrackMenubar)
5584    int X, Y, TrackMenubar;
5585 {
5586    int rc=INVALID;
5587    TgMenu *menu=(propertiesMenuInfo.create_proc)(NULL, X, Y,
5588          &propertiesMenuInfo, INVALID);
5589 
5590    activeMenu = MENU_PROPERTIES;
5591    if (menu != NULL) {
5592       menu->track_menubar = TrackMenubar;
5593 
5594       rc = TgMenuLoop(menu);
5595       TgDestroyMenu(menu, TRUE);
5596    }
5597    return rc;
5598 }
5599 
UpdateSymbols()5600 void UpdateSymbols()
5601 {
5602    int dx=0, dy=0, changed=FALSE;
5603    int sel_ltx, sel_lty, sel_rbx, sel_rby, file_type=INVALID;
5604    char path_name[MAXPATHLENGTH], sym_name[MAXPATHLENGTH];
5605    struct ObjRec *obj_ptr, *new_obj_ptr;
5606    struct SelRec *sel_ptr;
5607    struct GroupRec *icon_ptr;
5608 
5609    if (topSel == NULL) {
5610       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
5611       return;
5612    }
5613    tmpTopObj = tmpBotObj = NULL;
5614    tmpTopSel = tmpBotSel = NULL;
5615 
5616    sel_ltx = selLtX; sel_lty = selLtY;
5617    sel_rbx = selRbX; sel_rby = selRbY;
5618 
5619    HighLightReverse();
5620 
5621    StartCompositeCmd();
5622    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
5623       obj_ptr = sel_ptr->obj;
5624       if (obj_ptr->type != OBJ_ICON && obj_ptr->type != OBJ_PIN) {
5625          continue;
5626       }
5627       icon_ptr = obj_ptr->detail.r;
5628       strcpy(sym_name, icon_ptr->s);
5629       if (obj_ptr->type == OBJ_ICON) {
5630          file_type = SYM_FILE_TYPE;
5631       } else {
5632          file_type = PIN_FILE_TYPE;
5633       }
5634       if (GetSymbolPath(icon_ptr->s, obj_ptr->type==OBJ_PIN, path_name)) {
5635          if ((new_obj_ptr=GetObjRepresentation(path_name, sym_name,
5636                file_type)) != NULL) {
5637             PrepareToReplaceAnObj(obj_ptr);
5638             if (icon_ptr->flip != NO_FLIP) {
5639                if (icon_ptr->flip & HORI_EVEN) FlipIconHorizontal(new_obj_ptr);
5640                if (icon_ptr->flip & VERT_EVEN) FlipIconVertical(new_obj_ptr);
5641                if (icon_ptr->flip & (HORI_ODD | VERT_ODD)) {
5642                   RotateIconClockWise(new_obj_ptr);
5643                   if (icon_ptr->flip & HORI_ODD) {
5644                      FlipIconHorizontal(new_obj_ptr);
5645                   }
5646                   if (icon_ptr->flip & VERT_ODD) {
5647                      FlipIconVertical(new_obj_ptr);
5648                   }
5649                   RotateIconCounter(new_obj_ptr);
5650                }
5651             }
5652             switch (horiAlign) {
5653             case ALIGN_L:
5654                dx = obj_ptr->obbox.ltx - new_obj_ptr->obbox.ltx;
5655                break;
5656             case ALIGN_N:
5657             case ALIGN_S:
5658             case ALIGN_C:
5659                dx = (int)(((obj_ptr->obbox.ltx+obj_ptr->obbox.rbx) -
5660                      (new_obj_ptr->obbox.ltx+new_obj_ptr->obbox.rbx))/2);
5661                break;
5662             case ALIGN_R:
5663                dx = obj_ptr->obbox.rbx - new_obj_ptr->obbox.rbx;
5664                break;
5665             }
5666             switch (vertAlign) {
5667             case ALIGN_T:
5668                dy = obj_ptr->obbox.lty - new_obj_ptr->obbox.lty;
5669                break;
5670             case ALIGN_N:
5671             case ALIGN_S:
5672             case ALIGN_M:
5673                dy = (int)(((obj_ptr->obbox.lty+obj_ptr->obbox.rby) -
5674                      (new_obj_ptr->obbox.lty+new_obj_ptr->obbox.rby))/2);
5675                break;
5676             case ALIGN_B:
5677                dy = obj_ptr->obbox.rby - new_obj_ptr->obbox.rby;
5678                break;
5679             }
5680             MoveObj(new_obj_ptr, dx, dy);
5681 
5682             changed = TRUE;
5683 
5684             UnlinkObj(obj_ptr);
5685             CopyAndUpdateAttrs(new_obj_ptr, obj_ptr);
5686             ExpandCurSelBBoxes(new_obj_ptr);
5687 
5688             sel_ptr->obj = new_obj_ptr;
5689             AssignNewObjIds(new_obj_ptr);
5690             AddObj(NULL, topObj, new_obj_ptr);
5691             RecordReplaceAnObj(new_obj_ptr);
5692             FreeObj(obj_ptr);
5693          }
5694       }
5695    }
5696    EndCompositeCmd();
5697 
5698    if (changed) {
5699       UpdSelBBox();
5700       RedrawAreas(botObj, sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
5701             sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
5702             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5703             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
5704       SetFileModified(TRUE);
5705       justDupped = FALSE;
5706       Msg(TgLoadString(STID_ICONS_BROUGHT_UP_TO_DATE));
5707    }
5708    HighLightForward();
5709 }
5710 
SizeToWidest()5711 void SizeToWidest()
5712 {
5713    struct SelRec *sel_ptr=NULL;
5714    int abs_w=0;
5715 
5716    if (topSel == NULL || topSel == botSel) {
5717       MsgBox(TgLoadString(STID_SEL_AT_LEAST_TWO_OBJS), TOOL_NAME, INFO_MB);
5718       return;
5719    }
5720    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
5721       struct ObjRec *obj_ptr=sel_ptr->obj;
5722       int w=obj_ptr->obbox.rbx-obj_ptr->obbox.ltx;
5723 
5724       if (w > abs_w) abs_w = w;
5725    }
5726    if (abs_w <= 1) {
5727       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_SIZE_OF_GIVEN_WIDTH), abs_w);
5728       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5729       return;
5730    }
5731    SizeAllSelToGivenWidth(abs_w);
5732 }
5733 
SizeToNarrowest()5734 void SizeToNarrowest()
5735 {
5736    struct SelRec *sel_ptr=NULL;
5737    int abs_w=0;
5738 
5739    if (topSel == NULL || topSel == botSel) {
5740       MsgBox(TgLoadString(STID_SEL_AT_LEAST_TWO_OBJS), TOOL_NAME, INFO_MB);
5741       return;
5742    }
5743    abs_w = topSel->obj->obbox.rbx-topSel->obj->obbox.ltx;
5744    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
5745       struct ObjRec *obj_ptr=sel_ptr->obj;
5746       int w=obj_ptr->obbox.rbx-obj_ptr->obbox.ltx;
5747 
5748       if (w < abs_w) abs_w = w;
5749    }
5750    if (abs_w <= 1) {
5751       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_SIZE_OF_GIVEN_WIDTH), abs_w);
5752       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5753       return;
5754    }
5755    SizeAllSelToGivenWidth(abs_w);
5756 }
5757 
SizeToTallest()5758 void SizeToTallest()
5759 {
5760    struct SelRec *sel_ptr=NULL;
5761    int abs_h=0;
5762 
5763    if (topSel == NULL || topSel == botSel) {
5764       MsgBox(TgLoadString(STID_SEL_AT_LEAST_TWO_OBJS), TOOL_NAME, INFO_MB);
5765       return;
5766    }
5767    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
5768       struct ObjRec *obj_ptr=sel_ptr->obj;
5769       int h=obj_ptr->obbox.rby-obj_ptr->obbox.lty;
5770 
5771       if (h > abs_h) abs_h = h;
5772    }
5773    if (abs_h <= 1) {
5774       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_SIZE_OF_GIVEN_HEIGHT), abs_h);
5775       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5776       return;
5777    }
5778    SizeAllSelToGivenHeight(abs_h);
5779 }
5780 
SizeToShortest()5781 void SizeToShortest()
5782 {
5783    struct SelRec *sel_ptr=NULL;
5784    int abs_h=0;
5785 
5786    if (topSel == NULL || topSel == botSel) {
5787       MsgBox(TgLoadString(STID_SEL_AT_LEAST_TWO_OBJS), TOOL_NAME, INFO_MB);
5788       return;
5789    }
5790    abs_h = topSel->obj->obbox.rby-topSel->obj->obbox.lty;
5791    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
5792       struct ObjRec *obj_ptr=sel_ptr->obj;
5793       int h=obj_ptr->obbox.rby-obj_ptr->obbox.lty;
5794 
5795       if (h < abs_h) abs_h = h;
5796    }
5797    if (abs_h <= 1) {
5798       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_SIZE_OF_GIVEN_HEIGHT), abs_h);
5799       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5800       return;
5801    }
5802    SizeAllSelToGivenHeight(abs_h);
5803 }
5804 
SizeToGivenWidthHeight()5805 void SizeToGivenWidthHeight()
5806 {
5807    int abs_w=0, abs_h=0;
5808    char spec[MAXSTRING];
5809 
5810    if (topSel == NULL) {
5811       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
5812       return;
5813    }
5814    *spec = '\0';
5815    if (Dialog(TgLoadString(STID_SPECIFY_WIDTH_HEIGHT), NULL, spec) == INVALID) {
5816       return;
5817    }
5818    UtilTrimBlanks(spec);
5819    if (*spec == '\0') return;
5820 
5821    if (!ParseWHSpec(spec, &abs_w, &abs_h)) {
5822       sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC), spec);
5823       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5824       return;
5825    }
5826    if (abs_w <= 1) {
5827       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_SIZE_OF_GIVEN_WIDTH), abs_w);
5828       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5829       return;
5830    }
5831    if (abs_h <= 1) {
5832       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_SIZE_OF_GIVEN_HEIGHT), abs_h);
5833       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5834       return;
5835    }
5836    SizeAllSelToGivenWidthHeight(abs_w, abs_h);
5837 }
5838 
SizeToGivenWidth()5839 void SizeToGivenWidth()
5840 {
5841    int abs_w=0;
5842    char spec[MAXSTRING];
5843 
5844    if (topSel == NULL) {
5845       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
5846       return;
5847    }
5848    *spec = '\0';
5849    if (Dialog(TgLoadString(STID_SPECIFY_WIDTH), NULL, spec) == INVALID) return;
5850    UtilTrimBlanks(spec);
5851    if (*spec == '\0') return;
5852 
5853    if (!GetDimension(spec, FALSE, &abs_w)) {
5854       sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC), spec);
5855       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5856       return;
5857    }
5858    if (abs_w <= 1) {
5859       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_SIZE_OF_GIVEN_WIDTH), abs_w);
5860       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5861       return;
5862    }
5863    SizeAllSelToGivenWidth(abs_w);
5864 }
5865 
SizeToGivenHeight()5866 void SizeToGivenHeight()
5867 {
5868    int abs_h=0;
5869    char spec[MAXSTRING];
5870 
5871    if (topSel == NULL) {
5872       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
5873       return;
5874    }
5875    *spec = '\0';
5876    if (Dialog(TgLoadString(STID_SPECIFY_HEIGHT), NULL, spec) == INVALID) return;
5877    UtilTrimBlanks(spec);
5878    if (*spec == '\0') return;
5879 
5880    if (!GetDimension(spec, FALSE, &abs_h)) {
5881       sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC), spec);
5882       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5883       return;
5884    }
5885    if (abs_h <= 1) {
5886       sprintf(gszMsgBox, TgLoadString(STID_CANNOT_SIZE_OF_GIVEN_HEIGHT), abs_h);
5887       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5888       return;
5889    }
5890    SizeAllSelToGivenHeight(abs_h);
5891 }
5892 
5893 /* ----------------------- Generic Object Shadow ----------------------- */
5894 
SetObjectShadowColor()5895 void SetObjectShadowColor()
5896 {
5897    char spec[MAXSTRING], buf[MAXSTRING];
5898    int new_alloc=0;
5899 
5900    *spec = '\0';
5901    sprintf(buf, TgLoadString(STID_ENTER_A_COLOR_FOR_OBJ_SHADOW),
5902          objShadowColorStr);
5903    UtilStrCpyN(spec, sizeof(spec), objShadowColorStr);
5904    if (Dialog(buf, TgLoadString(STID_PRESS_ENTER_FOR_DEF_COLOR), spec) ==
5905          INVALID) {
5906       return;
5907    }
5908    UtilTrimBlanks(spec);
5909    if (*spec == '\0') {
5910       strcpy(objShadowColorStr, "#c0c0c0");
5911       sprintf(gszMsgBox, TgLoadString(STID_OBJ_SHADOW_COLOR_SET_TO_NAMED),
5912             objShadowColorStr);
5913       Msg(gszMsgBox);
5914       return;
5915    }
5916    if (QuickFindColorIndex(NULL, spec, &new_alloc, FALSE) == INVALID) {
5917       sprintf(gszMsgBox, TgLoadString(STID_FAIL_ALLOC_NAMED_COLOR), spec);
5918       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5919       return;
5920    }
5921    UtilStrCpyN(objShadowColorStr, sizeof(objShadowColorStr), spec);
5922 
5923    sprintf(gszMsgBox, TgLoadString(STID_OBJ_SHADOW_COLOR_SET_TO_NAMED),
5924          objShadowColorStr);
5925    Msg(gszMsgBox);
5926 }
5927 
SetObjectShadowOffsets()5928 void SetObjectShadowOffsets()
5929 {
5930    char spec[MAXSTRING];
5931 
5932    *spec = '\0';
5933    sprintf(gszMsgBox, TgLoadString(STID_ENTER_XY_OFFSET_OBJ_SHADOW),
5934          objShadowXOffset, objShadowYOffset);
5935    if (Dialog(gszMsgBox, NULL, spec) == INVALID) return;
5936    UtilTrimBlanks(spec);
5937    if (*spec == '\0') return;
5938 
5939    if (ParseXYSpec(spec, &objShadowXOffset, &objShadowYOffset)) {
5940       sprintf(gszMsgBox, TgLoadString(STID_OBJ_SHADOW_XY_OFFSETS_SET_TO),
5941             objShadowXOffset, objShadowYOffset);
5942       Msg(gszMsgBox);
5943    }
5944 }
5945 
5946 static
DoAddObjectShadow()5947 void DoAddObjectShadow()
5948 {
5949    struct SelRec *sel_ptr=NULL;
5950    int new_alloc=0, color_index=QuickFindColorIndex(NULL, objShadowColorStr,
5951          &new_alloc, FALSE);
5952 
5953    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
5954       struct ObjRec *obj_ptr=sel_ptr->obj, *dup_obj_ptr=NULL, *group_obj=NULL;
5955       struct GroupRec *group_ptr=NULL;
5956       struct BBRec obbox, bbox;
5957       int locked=obj_ptr->locked;
5958 
5959       dup_obj_ptr = DupObj(obj_ptr);
5960       AdjObjSplineVs(dup_obj_ptr);
5961       MoveObj(dup_obj_ptr, objShadowXOffset, objShadowYOffset);
5962       ChangeObjColor(dup_obj_ptr, color_index);
5963       UnionRect(&(obj_ptr->obbox), &(dup_obj_ptr->obbox), &obbox);
5964       UnionRect(&(obj_ptr->bbox), &(dup_obj_ptr->bbox), &bbox);
5965 
5966       group_obj = JustCreateGroupObj();
5967       group_ptr = group_obj->detail.r;
5968 
5969       group_obj->prev = obj_ptr->prev;
5970       group_obj->next = obj_ptr->next;
5971       if (obj_ptr == topObj) {
5972          curPage->top = topObj = group_obj;
5973       } else {
5974          obj_ptr->prev->next = group_obj;
5975       }
5976       if (obj_ptr == botObj) {
5977          curPage->bot = botObj = group_obj;
5978       } else {
5979          obj_ptr->next->prev = group_obj;
5980       }
5981       obj_ptr->prev = NULL;
5982       obj_ptr->next = dup_obj_ptr;
5983       dup_obj_ptr->prev = obj_ptr;
5984       dup_obj_ptr->next = NULL;
5985       group_ptr->first = obj_ptr;
5986       group_ptr->last = dup_obj_ptr;
5987 
5988       obj_ptr->x = min(obj_ptr->x, dup_obj_ptr->x);
5989       obj_ptr->y = min(obj_ptr->y, dup_obj_ptr->y);
5990 
5991       memcpy(&group_obj->obbox, &obbox, sizeof(struct BBRec));
5992       memcpy(&group_obj->bbox, &bbox, sizeof(struct BBRec));
5993 
5994       group_obj->locked = locked;
5995 
5996       sel_ptr->obj = group_obj;
5997    }
5998    UpdSelBBox();
5999 }
6000 
AddObjectShadow()6001 void AddObjectShadow()
6002 {
6003    if (curChoice != NOTHING || topSel == NULL) {
6004       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
6005       return;
6006    }
6007    HighLightReverse();
6008    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
6009    DoAddObjectShadow();
6010    RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
6011    RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
6012          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
6013    HighLightForward();
6014    SetFileModified(TRUE);
6015    justDupped = FALSE;
6016 }
6017 
6018 static
DoRemoveObjectShadow()6019 void DoRemoveObjectShadow()
6020 {
6021    struct SelRec *sel_ptr=NULL;
6022 
6023    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
6024       struct ObjRec *obj_ptr=sel_ptr->obj;
6025 
6026       if (obj_ptr->type == OBJ_GROUP) {
6027          struct GroupRec *group_ptr=obj_ptr->detail.r;
6028          struct ObjRec *first_obj=group_ptr->first;
6029 
6030          if (first_obj != NULL) {
6031             struct ObjRec *second_obj=first_obj->next;
6032 
6033             if (second_obj->next == NULL) {
6034                int first_w=(first_obj->obbox.rbx-first_obj->obbox.ltx);
6035                int first_h=(first_obj->obbox.rby-first_obj->obbox.lty);
6036                int second_w=(second_obj->obbox.rbx-second_obj->obbox.ltx);
6037                int second_h=(second_obj->obbox.rby-second_obj->obbox.lty);
6038 
6039                if (first_w == second_w && first_h == second_h) {
6040                   group_ptr->first = group_ptr->last = NULL;
6041 
6042                   first_obj->prev = obj_ptr->prev;
6043                   first_obj->next = obj_ptr->next;
6044                   if (obj_ptr == topObj) {
6045                      curPage->top = topObj = first_obj;
6046                   } else {
6047                      obj_ptr->prev->next = first_obj;
6048                   }
6049                   if (obj_ptr == botObj) {
6050                      curPage->bot = botObj = first_obj;
6051                   } else {
6052                      obj_ptr->next->prev = first_obj;
6053                   }
6054                   obj_ptr->prev = NULL;
6055                   obj_ptr->next = NULL;
6056 
6057                   FreeObj(second_obj);
6058                   FreeObj(obj_ptr);
6059 
6060                   sel_ptr->obj = first_obj;
6061                }
6062             }
6063          }
6064       }
6065    }
6066 }
6067 
6068 static
CountObjectWithShadow()6069 int CountObjectWithShadow()
6070 {
6071    struct SelRec *sel_ptr=NULL;
6072    int count=0;
6073 
6074    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
6075       struct ObjRec *obj_ptr=sel_ptr->obj;
6076 
6077       if (obj_ptr->type == OBJ_GROUP) {
6078          struct GroupRec *group_ptr=obj_ptr->detail.r;
6079          struct ObjRec *first_obj=group_ptr->first;
6080 
6081          if (first_obj != NULL) {
6082             struct ObjRec *second_obj=first_obj->next;
6083 
6084             if (second_obj->next == NULL) {
6085                int first_w=(first_obj->obbox.rbx-first_obj->obbox.ltx);
6086                int first_h=(first_obj->obbox.rby-first_obj->obbox.lty);
6087                int second_w=(second_obj->obbox.rbx-second_obj->obbox.ltx);
6088                int second_h=(second_obj->obbox.rby-second_obj->obbox.lty);
6089 
6090                if (first_w == second_w && first_h == second_h) {
6091                   count++;
6092                }
6093             }
6094          }
6095       }
6096    }
6097    return count;
6098 }
6099 
RemoveObjectShadow()6100 void RemoveObjectShadow()
6101 {
6102    if (curChoice != NOTHING || topSel == NULL) {
6103       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
6104       return;
6105    }
6106    if (CountObjectWithShadow() == 0) {
6107       sprintf(gszMsgBox, TgLoadString(STID_NO_OBJ_SHADOW_FOUND));
6108       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6109       return;
6110    }
6111    HighLightReverse();
6112    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
6113    DoRemoveObjectShadow();
6114    RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
6115    RedrawAnArea(botObj, selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
6116          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
6117    HighLightForward();
6118    SetFileModified(TRUE);
6119    justDupped = FALSE;
6120 }
6121 
ReadObjectShadowInfo(buf)6122 int ReadObjectShadowInfo(buf)
6123    char *buf;
6124 {
6125    int x=0, y=0;
6126    char color_str[40], *psz=NULL;
6127 
6128    if (importingFile) return TRUE;
6129 
6130    psz = FindChar((int)'(', buf);
6131    *color_str = '\0';
6132    psz = ParseStr(psz, (int)',', color_str, sizeof(color_str));
6133    InitScan(psz, "\t\n, []");
6134    if (GETINT("objshadow_info", x, "x offset") == INVALID ||
6135        GETINT("objshadow_info", y, "y offset") == INVALID) {
6136       return FALSE;
6137    }
6138    UtilTrimBlanks(color_str);
6139    if (!ignoreObjectShadowInfoInFile) {
6140       UtilStrCpyN(objShadowColorStr, sizeof(objShadowColorStr), color_str);
6141       objShadowXOffset = x;
6142       objShadowYOffset = y;
6143    }
6144 
6145    return TRUE;
6146 }
6147 
6148 /* ----------------------- ExtendSegment ----------------------- */
6149 
6150 static
ConsecutiveVerticesSelected(pn_index0,pn_index1)6151 int ConsecutiveVerticesSelected(pn_index0, pn_index1)
6152    int *pn_index0, *pn_index1;
6153 {
6154    int index0=topVSel->v_index[0], index1=topVSel->v_index[1];
6155 
6156    if (index0+1 == index1) {
6157       *pn_index0 = index0;
6158       *pn_index1 = index1;
6159       return TRUE;
6160    } else if (index1+1 == index0) {
6161       *pn_index0 = index1;
6162       *pn_index1 = index0;
6163       return TRUE;
6164    }
6165    return FALSE;
6166 }
6167 
ExtendSegment()6168 void ExtendSegment()
6169 {
6170    char *c_ptr=NULL, sz_value[MAXSTRING+1];
6171    char sz_spec[MAXSTRING+1], sz_spec_copy[MAXSTRING+1];
6172    double dval=(double)0;
6173    struct ObjRec *obj_ptr=NULL;
6174    struct PolyRec *poly_ptr=NULL;
6175    int dx=0, dy=0, x0=0, y0=0, x1=0, y1=0;
6176    double dnew_x=0, dnew_y=0;
6177    double ddx=(double)0, ddy=(double)0;
6178    struct AttrRec *name_attr=NULL, *on_reshape_attr=NULL;
6179    int ltx, lty, rbx, rby, auto_center_attr=FALSE;
6180    int has_on_reshape=FALSE, two_point_polyline=TRUE, index0=0, index1=1;
6181 
6182    if (curChoice != NOTHING && curChoice != VERTEXMODE) {
6183       UtilStrCpyN(gszMsgBox, sizeof(gszMsgBox),
6184             TgLoadString(STID_TWO_VERTEX_POLY_ONLY));
6185       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6186       return;
6187    } else if (curChoice == NOTHING) {
6188       if (topSel == NULL || topSel != botSel ||
6189             topSel->obj->type != OBJ_POLY || topSel->obj->detail.p->n != 2) {
6190          UtilStrCpyN(gszMsgBox, sizeof(gszMsgBox),
6191                TgLoadString(STID_TWO_VERTEX_POLY_ONLY));
6192          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6193          return;
6194       }
6195       obj_ptr = topSel->obj;
6196    } else if (curChoice == VERTEXMODE) {
6197       if (topVSel == NULL) {
6198          if (topSel == NULL || topSel != botSel ||
6199                topSel->obj->type != OBJ_POLY || topSel->obj->detail.p->n != 2) {
6200             UtilStrCpyN(gszMsgBox, sizeof(gszMsgBox),
6201                   TgLoadString(STID_TWO_VERTEX_POLY_ONLY));
6202             MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6203             return;
6204          }
6205          obj_ptr = topSel->obj;
6206       } else if (topVSel == botVSel && topVSel->n == 1 &&
6207             topVSel->obj->type == OBJ_POLY &&
6208             topVSel->obj->detail.p->n == 2) {
6209          /* treat this as if only the poly object is selected */
6210          obj_ptr = topVSel->obj;
6211       } else if (topVSel == botVSel && topVSel->n == 2 &&
6212             topVSel->obj->type == OBJ_POLY &&
6213             ConsecutiveVerticesSelected(&index0, &index1)) {
6214          /* treat this as if only the poly object is selected */
6215          obj_ptr = topVSel->obj;
6216          two_point_polyline = FALSE;
6217       } else {
6218          UtilStrCpyN(gszMsgBox, sizeof(gszMsgBox),
6219                TgLoadString(STID_TWO_CONSEC_VERTICES_ONLY));
6220          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6221          return;
6222       }
6223    }
6224    ltx = obj_ptr->bbox.ltx; lty = obj_ptr->bbox.lty;
6225    rbx = obj_ptr->bbox.rbx; rby = obj_ptr->bbox.rby;
6226    poly_ptr = obj_ptr->detail.p;
6227 
6228    *sz_spec = '\0';
6229    Dialog(TgLoadString(STID_ENTER_MULT_FACTOR),
6230          TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), sz_spec);
6231    UtilTrimBlanks(sz_spec);
6232    if (*sz_spec == '\0') return;
6233 
6234    strcpy(sz_spec_copy, sz_spec);
6235    if ((c_ptr=strtok(sz_spec, " ,\t\n\r")) == NULL) return;
6236    if (*c_ptr == '+') {
6237       UtilStrCpyN(sz_value, sizeof(sz_value), &c_ptr[1]);
6238    } else {
6239       UtilStrCpyN(sz_value, sizeof(sz_value), c_ptr);
6240    }
6241    if (sscanf(sz_value, "%lf", &dval) != 1) {
6242       sprintf(gszMsgBox, TgLoadString(STID_FAIL_TO_PARSE_FOR_A_VAL),
6243             sz_spec_copy);
6244       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6245       return;
6246    }
6247    if (obj_ptr->ctm == NULL) {
6248       dx = poly_ptr->vlist[index1].x - poly_ptr->vlist[index0].x;
6249       dy = poly_ptr->vlist[index1].y - poly_ptr->vlist[index0].y;
6250       ddx = ((double)dx) * dval;
6251       ddy = ((double)dy) * dval;
6252       dx = round(ddx);
6253       dy = round(ddy);
6254       if (dx == 0 && dy == 0) {
6255          UtilStrCpyN(gszMsgBox, sizeof(gszMsgBox),
6256                TgLoadString(STID_RES_LINESEG_HAVE_ZERO_LEN));
6257          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6258          return;
6259       }
6260    } else {
6261       TransformPointThroughCTM(poly_ptr->vlist[index0].x-obj_ptr->x,
6262             poly_ptr->vlist[index0].y-obj_ptr->y, obj_ptr->ctm, &x0, &y0);
6263       TransformPointThroughCTM(poly_ptr->vlist[index1].x-obj_ptr->x,
6264             poly_ptr->vlist[index1].y-obj_ptr->y, obj_ptr->ctm, &x1, &y1);
6265       dx = x1 - x0;
6266       dy = y1 - y0;
6267       ddx = ((double)dx) * dval;
6268       ddy = ((double)dy) * dval;
6269       dx = round(ddx);
6270       dy = round(ddy);
6271       if (dx == 0 && dy == 0) {
6272          UtilStrCpyN(gszMsgBox, sizeof(gszMsgBox),
6273                TgLoadString(STID_RES_LINESEG_HAVE_ZERO_LEN));
6274          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
6275          return;
6276       }
6277    }
6278    auto_center_attr = AutoCenterAttr(obj_ptr);
6279    has_on_reshape = HasOnReshape(obj_ptr, &name_attr);
6280 
6281    if (has_on_reshape && name_attr != NULL) {
6282       on_reshape_attr = FindAttrWithName(obj_ptr, "on_reshape=", NULL);
6283    }
6284    HighLightReverse();
6285    if (on_reshape_attr != NULL) {
6286       StartCompositeCmd();
6287    }
6288    PrepareToReplaceAnObj(obj_ptr);
6289    if (obj_ptr->ctm == NULL) {
6290       if (dval > (double)0) {
6291          poly_ptr->vlist[index1].x = poly_ptr->vlist[index0].x + dx;
6292          poly_ptr->vlist[index1].y = poly_ptr->vlist[index0].y + dy;
6293       } else {
6294          poly_ptr->vlist[index0].x = poly_ptr->vlist[index1].x + dx;
6295          poly_ptr->vlist[index0].y = poly_ptr->vlist[index1].y + dy;
6296       }
6297       if (!two_point_polyline) {
6298          topVSel->x[0] = poly_ptr->vlist[index0].x;
6299          topVSel->y[0] = poly_ptr->vlist[index0].y;
6300          topVSel->x[1] = poly_ptr->vlist[index1].x;
6301          topVSel->y[1] = poly_ptr->vlist[index1].y;
6302       }
6303    } else {
6304       if (dval > (double)0) {
6305          x1 = x0 + obj_ptr->x + ddx;
6306          y1 = y0 + obj_ptr->y + ddy;
6307          ReverseTransformDoublePointThroughCTM(((double)x0)+ddx,
6308                ((double)y0)+ddy, obj_ptr->ctm, &dnew_x, &dnew_y);
6309          poly_ptr->vlist[index1].x = round(dnew_x) + obj_ptr->x;
6310          poly_ptr->vlist[index1].y = round(dnew_y) + obj_ptr->y;
6311       } else {
6312          x0 = x1 + obj_ptr->x + dx;
6313          y0 = y1 + obj_ptr->y + dy;
6314          ReverseTransformDoublePointThroughCTM(((double)x1)+ddx,
6315                ((double)y1)+ddy, obj_ptr->ctm, &dnew_x, &dnew_y);
6316          poly_ptr->vlist[index0].x = round(dnew_x) + obj_ptr->x;
6317          poly_ptr->vlist[index0].y = round(dnew_y) + obj_ptr->y;
6318       }
6319       if (!two_point_polyline) {
6320          int new_x=0, new_y=0;
6321 
6322          TransformPointThroughCTM(poly_ptr->vlist[index0].x-obj_ptr->x,
6323                poly_ptr->vlist[index0].y-obj_ptr->y, obj_ptr->ctm, &new_x,
6324                &new_y);
6325          topVSel->x[0] = obj_ptr->x + new_x;
6326          topVSel->y[0] = obj_ptr->y + new_y;
6327          TransformPointThroughCTM(poly_ptr->vlist[index1].x-obj_ptr->x,
6328                poly_ptr->vlist[index1].y-obj_ptr->y, obj_ptr->ctm, &new_x,
6329                &new_y);
6330          topVSel->x[1] = obj_ptr->x + new_x;
6331          topVSel->y[1] = obj_ptr->y + new_y;
6332       }
6333    }
6334    AdjObjSplineVs(obj_ptr);
6335    if (poly_ptr->curved != LT_INTSPLINE) {
6336       UpdPolyBBox(obj_ptr, poly_ptr->n, poly_ptr->vlist);
6337    } else {
6338       UpdPolyBBox(obj_ptr, poly_ptr->intn, poly_ptr->intvlist);
6339    }
6340    if (auto_center_attr) {
6341       struct AttrRec *attr_ptr=obj_ptr->fattr;
6342       int modified=FALSE;
6343 
6344       for ( ; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
6345          if (attr_ptr->shown) {
6346             struct BBRec bbox;
6347 
6348             CenterObjInOBBox(attr_ptr->obj, obj_ptr->obbox, &bbox);
6349             if (bbox.ltx < ltx) ltx = bbox.ltx;
6350             if (bbox.lty < lty) lty = bbox.lty;
6351             if (bbox.rbx > rbx) rbx = bbox.rbx;
6352             if (bbox.rby > rby) rby = bbox.rby;
6353             modified = TRUE;
6354          }
6355       }
6356       if (modified) AdjObjBBox(obj_ptr);
6357    }
6358    RecordReplaceAnObj(obj_ptr);
6359    if (on_reshape_attr != NULL) {
6360       DoExec(on_reshape_attr, obj_ptr);
6361    }
6362    if (on_reshape_attr != NULL) {
6363       EndCompositeCmd();
6364    }
6365    UpdSelBBox();
6366    RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
6367          rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
6368          obj_ptr->bbox.ltx-GRID_ABS_SIZE(1),
6369          obj_ptr->bbox.lty-GRID_ABS_SIZE(1),
6370          obj_ptr->bbox.rbx+GRID_ABS_SIZE(1),
6371          obj_ptr->bbox.rby+GRID_ABS_SIZE(1));
6372    SetFileModified(TRUE);
6373    justDupped = FALSE;
6374 
6375    HighLightForward();
6376 }
6377 
6378 /* -------------------- ToggleTighterStructuredSplines() -------------------- */
6379 
ToggleTighterStructuredSplines()6380 void ToggleTighterStructuredSplines()
6381 {
6382    tighterStructSplines = !tighterStructSplines;
6383    AdjSplineVs();
6384    SetFileModified(TRUE);
6385    UpdSelBBox();
6386    justDupped = FALSE;
6387    ClearAndRedrawDrawWindow();
6388    UtilStrCpyN(gszMsgBox, sizeof(gszMsgBox), TgLoadString(tighterStructSplines ?
6389          STID_WILL_USE_TIGHTER_SPLINES : STID_WILL_USE_LOOSER_SPLINES));
6390    Msg(gszMsgBox);
6391 }
6392 
6393 /* ----------------------- CreatePolySplineMenu() ----------------------- */
6394 
RefreshPolySplineMenu(menu)6395 int RefreshPolySplineMenu(menu)
6396    TgMenu *menu;
6397 {
6398    int ok=TRUE;
6399 
6400    /* UseTighterSplines */
6401 #ifdef NOT_DEFINED
6402    ok &= TgSetMenuItemCheckById(menu, CMDID_TOGGLETIGHTERSPLINES,
6403          tighterStructSplines);
6404 #endif /* NOT_DEFINED */
6405 
6406    return ok;
6407 }
6408 
CreatePolySplineMenu(parent_menu,x,y,menu_info,status_str_xlated)6409 TgMenu *CreatePolySplineMenu(parent_menu, x, y, menu_info, status_str_xlated)
6410    TgMenu *parent_menu;
6411    int x, y;
6412    TgMenuInfo *menu_info;
6413    int status_str_xlated; /* ignored, always 0 */
6414 {
6415    TgMenu *menu=TgCreateMenuFromMenuInfo(parent_menu, x, y, menu_info, FALSE);
6416 
6417    if (menu != NULL) {
6418       if (!RefreshPolySplineMenu(menu)) {
6419          return TgDestroyMenu(menu, TRUE);
6420       }
6421       menu->refresh_proc = ((RefreshMenuFunc*)RefreshPolySplineMenu);
6422    }
6423    return menu;
6424 }
6425 
6426