1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "cvundoes.h"
31 #include "fontforgeui.h"
32 #include "spiro.h"
33 #include "splinefit.h"
34 #include "splineutil.h"
35 #include "splineutil2.h"
36 #include "utype.h"
37 
38 #include <math.h>
39 extern void BackTrace( const char* msg );
40 
41 int stop_at_join = false;
42 extern int interpCPsOnMotion;
43 
CVAnySel(CharView * cv,int * anyp,int * anyr,int * anyi,int * anya)44 int CVAnySel(CharView *cv, int *anyp, int *anyr, int *anyi, int *anya) {
45     int anypoints = 0, anyrefs=0, anyimages=0, anyanchor=0;
46     SplinePointList *spl;
47     Spline *spline, *first;
48     RefChar *rf;
49     ImageList *il;
50     AnchorPoint *ap;
51     int i;
52 
53     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL && !anypoints; spl = spl->next ) {
54         anypoints = SplinePointListCheckSelected1(spl, cv->b.sc->inspiro && hasspiro(), NULL, true);
55     }
56     for ( rf=cv->b.layerheads[cv->b.drawmode]->refs; rf!=NULL && !anyrefs; rf=rf->next )
57 	if ( rf->selected ) anyrefs = true;
58     if ( cv->b.drawmode==dm_fore ) {
59 	if ( cv->showanchor && anya!=NULL )
60 	    for ( ap=cv->b.sc->anchor; ap!=NULL && !anyanchor; ap=ap->next )
61 		if ( ap->selected ) anyanchor = true;
62     }
63     for ( il=cv->b.layerheads[cv->b.drawmode]->images; il!=NULL && !anyimages; il=il->next )
64 	if ( il->selected ) anyimages = true;
65     if ( anyp!=NULL ) *anyp = anypoints;
66     if ( anyr!=NULL ) *anyr = anyrefs;
67     if ( anyi!=NULL ) *anyi = anyimages;
68     if ( anya!=NULL ) *anya = anyanchor;
69 return( anypoints || anyrefs || anyimages || anyanchor );
70 }
71 
CVAnySelPoints(CharView * cv)72 int CVAnySelPoints(CharView *cv) {
73     /* if there are any points selected */
74     SplinePointList *spl;
75     Spline *spline, *first;
76     int i;
77 
78     for ( spl= cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl=spl->next ) {
79 	if ( cv->b.sc->inspiro && hasspiro()) {
80 	    for ( i=0; i<spl->spiro_cnt-1; ++i )
81 		if ( SPIRO_SELECTED(&spl->spiros[i]))
82 return( true );
83 	} else {
84 	    if ( spl->first->selected )
85 return( true );
86 	    first = NULL;
87 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
88 		if ( spline->to->selected )
89 return( true );
90 		if ( first==NULL ) first = spline;
91 	    }
92 	}
93     }
94 return( false );
95 }
96 
97 GList_Glib*
CVGetSelectedPoints(CharView * cv)98 CVGetSelectedPoints(CharView *cv)
99 {
100     GList_Glib* ret = 0;
101     /* if there are any points selected */
102     SplinePointList *spl;
103     Spline *spline, *first;
104     int i;
105 
106     for ( spl= cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl=spl->next )
107     {
108 	if ( cv->b.sc->inspiro && hasspiro())
109 	{
110 	    for ( i=0; i<spl->spiro_cnt-1; ++i )
111 		if ( SPIRO_SELECTED(&spl->spiros[i]))
112 		    ret = g_list_append( ret, &spl->spiros[i] );
113 	}
114 	else
115 	{
116 	    if ( spl->first->selected )
117 		ret = g_list_append( ret, spl->first );
118 	    first = NULL;
119 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next )
120 	    {
121 		if ( spline->to->selected )
122 		    ret = g_list_append( ret, spline->to );
123 		if ( first==NULL ) first = spline;
124 	    }
125 	}
126     }
127     return ret;
128 }
129 
130 
131 
CVClearSel(CharView * cv)132 int CVClearSel(CharView *cv) {
133     SplinePointList *spl;
134     int i;
135     Spline *spline, *first;
136     RefChar *rf;
137     ImageList *img;
138     int needsupdate = 0;
139     AnchorPoint *ap;
140 
141     SplinePointListFree(cv->p.pretransform_spl);
142     cv->p.pretransform_spl = NULL;
143 
144     cv->lastselpt = NULL; cv->lastselcp = NULL;
145     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next )
146     {
147 	if ( spl->first->selected )
148 	{
149 	    needsupdate = true;
150 	    spl->first->selected = false;
151 	    spl->first->nextcpselected = false;
152 	    spl->first->prevcpselected = false;
153 	}
154 	first = NULL;
155 	for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next )
156 	{
157 	    if ( spline->to->selected )
158 	    {
159 		needsupdate = true;
160 		spline->to->selected = false;
161 		spline->to->nextcpselected = false;
162 		spline->to->prevcpselected = false;
163 	    }
164 	    if ( first==NULL )
165 		first = spline;
166 	}
167 	for ( i=0 ; i<spl->spiro_cnt-1; ++i )
168 	    if ( SPIRO_SELECTED(&spl->spiros[i]))
169 	    {
170 		needsupdate = true;
171 		SPIRO_DESELECT(&spl->spiros[i]);
172 	    }
173     }
174     for ( rf=cv->b.layerheads[cv->b.drawmode]->refs; rf!=NULL; rf = rf->next )
175 	if ( rf->selected )
176 	{
177 	    needsupdate = true;
178 	    rf->selected = false;
179 	}
180     if ( cv->b.drawmode == dm_fore )
181 	for ( ap=cv->b.sc->anchor; ap!=NULL; ap = ap->next )
182 	    if ( ap->selected )
183 	    {
184 		if ( cv->showanchor )
185 		    needsupdate = true;
186 		ap->selected = false;
187 	    }
188     for ( img=cv->b.layerheads[cv->b.drawmode]->images; img!=NULL; img = img->next )
189 	if ( img->selected )
190 	{
191 	    needsupdate = true;
192 	    img->selected = false;
193 	}
194     if ( cv->p.nextcp || cv->p.prevcp || cv->widthsel || cv->vwidthsel ||
195 	    cv->icsel || cv->tah_sel )
196     {
197 	needsupdate = true;
198     }
199     cv->p.nextcp = cv->p.prevcp = false;
200     cv->widthsel = cv->vwidthsel = cv->lbearingsel = cv->icsel = cv->tah_sel = false;
201 
202     needsupdate = 1;
203     return( needsupdate );
204 }
205 
CVSetSel(CharView * cv,int mask)206 int CVSetSel(CharView *cv,int mask) {
207     SplinePointList *spl;
208     Spline *spline, *first;
209     RefChar *rf;
210     ImageList *img;
211     int needsupdate = 0;
212     AnchorPoint *ap;
213     RefChar *usemymetrics = HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv));
214     int i;
215 
216     cv->lastselpt = NULL; cv->lastselcp = NULL;
217     if ( mask&1 ) {
218 	if ( !cv->b.sc->inspiro || !hasspiro()) {
219 	    for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
220 		if ( !spl->first->selected ) { needsupdate = true; spl->first->selected = true; }
221 		first = NULL;
222 		for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
223 		    if ( !spline->to->selected )
224 			{ needsupdate = true; spline->to->selected = true; }
225 		    cv->lastselpt = spline->to;
226 		    if ( first==NULL ) first = spline;
227 		}
228 	    }
229 	} else {
230 	    for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
231 		for ( i=0; i<spl->spiro_cnt-1; ++i ) {
232 		    if ( !SPIRO_SELECTED(&spl->spiros[i])) {
233 			needsupdate = true;
234 			SPIRO_SELECT(&spl->spiros[i]);
235 		    }
236 		    cv->lastselcp = &spl->spiros[i];
237 		}
238 	    }
239 	}
240 	for ( rf=cv->b.layerheads[cv->b.drawmode]->refs; rf!=NULL; rf = rf->next )
241 	    if ( !rf->selected ) { needsupdate = true; rf->selected = true; }
242 	for ( img=cv->b.layerheads[cv->b.drawmode]->images; img!=NULL; img = img->next )
243 	    if ( !img->selected ) { needsupdate = true; img->selected = true; }
244     }
245     if ( (mask&2) && cv->showanchor ) {
246 	for ( ap=cv->b.sc->anchor; ap!=NULL; ap=ap->next )
247 	    if ( !ap->selected ) { needsupdate = true; ap->selected = true; }
248     }
249     if ( cv->p.nextcp || cv->p.prevcp )
250 	needsupdate = true;
251     cv->p.nextcp = cv->p.prevcp = false;
252     if ( cv->showhmetrics && !cv->widthsel && (mask&4) && usemymetrics==NULL ) {
253 	cv->widthsel = needsupdate = true;
254 	cv->oldwidth = cv->b.sc->width;
255     }
256     if ( cv->showvmetrics && cv->b.sc->parent->hasvmetrics && !cv->vwidthsel && (mask&4) && usemymetrics==NULL ) {
257 	cv->vwidthsel = needsupdate = true;
258 	cv->oldvwidth = cv->b.sc->vwidth;
259     }
260 return( needsupdate );
261 }
262 
CVInvertSel(CharView * cv)263 void CVInvertSel(CharView *cv) {
264     SplinePointList *spl;
265     Spline *spline, *first;
266     RefChar *rf;
267     ImageList *img;
268     int i;
269 
270     cv->lastselpt = NULL; cv->lastselcp = NULL;
271 
272     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
273 	if ( cv->b.sc->inspiro && hasspiro()) {
274 	    for ( i=0; i<spl->spiro_cnt-1; ++i )
275 		spl->spiros[i].ty ^= 0x80;
276 	} else {
277 	    spl->first->selected = !spl->first->selected;
278 	    first = NULL;
279 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
280 		spline->to->selected = !spline->to->selected;
281 		if ( spline->to->selected )
282 		    cv->lastselpt = spline->to;
283 		if ( first==NULL ) first = spline;
284 	    }
285 	    /* in circular case, first point is toggled twice in above code	*/
286 	    /* so fix it here						*/
287 	    if ( spline==first && spline != NULL)
288 		spl->first->selected = !spl->first->selected;
289 	}
290     }
291     for ( rf=cv->b.layerheads[cv->b.drawmode]->refs; rf!=NULL; rf = rf->next )
292         rf->selected = !rf->selected;
293     for ( img=cv->b.layerheads[cv->b.drawmode]->images; img!=NULL; img = img->next )
294         img->selected = !img->selected;
295     cv->p.nextcp = cv->p.prevcp = false;
296 }
297 
CVAllSelected(CharView * cv)298 int CVAllSelected(CharView *cv) {
299     SplinePointList *spl;
300     Spline *spline, *first;
301     RefChar *rf;
302     ImageList *img;
303     int i;
304 
305     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
306 	if ( cv->b.sc->inspiro && hasspiro()) {
307 	    for ( i=0; i<spl->spiro_cnt-1; ++i )
308 		if ( !SPIRO_SELECTED(&spl->spiros[i]))
309 return( false );
310 	} else {
311 	    if ( !spl->first->selected )
312 return( false );
313 	    first = NULL;
314 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
315 		if ( !spline->to->selected )
316 return( false );
317 		if ( first==NULL ) first = spline;
318 	    }
319 	}
320     }
321     for ( rf=cv->b.layerheads[cv->b.drawmode]->refs; rf!=NULL; rf = rf->next )
322 	if ( !rf->selected )
323 return( false );
324     for ( img=cv->b.layerheads[cv->b.drawmode]->images; img!=NULL; img = img->next )
325 	if ( !img->selected )
326 return( false );
327 return( true );
328 }
329 
SplineSetFindSelBounds(SplinePointList * spl,DBounds * bounds,int nosel,int inspiro)330 static void SplineSetFindSelBounds(SplinePointList *spl, DBounds *bounds,
331 	int nosel, int inspiro) {
332     SplinePoint *sp, *first;
333     int i;
334 
335     for ( ; spl!=NULL; spl = spl->next ) {
336 	if ( !inspiro ) {
337 	    first = NULL;
338 	    for ( sp = spl->first; sp!=first; sp = sp->next->to ) {
339 		if ( nosel || sp->selected ) {
340 		    if ( bounds->minx==0 && bounds->maxx==0 &&
341 			 bounds->miny==0 && bounds->maxy == 0 ) {
342 			bounds->minx = bounds->maxx = sp->me.x;
343 			bounds->miny = bounds->maxy = sp->me.y;
344 		    } else {
345 			if ( sp->me.x<bounds->minx ) bounds->minx = sp->me.x;
346 			if ( sp->me.x>bounds->maxx ) bounds->maxx = sp->me.x;
347 			if ( sp->me.y<bounds->miny ) bounds->miny = sp->me.y;
348 			if ( sp->me.y>bounds->maxy ) bounds->maxy = sp->me.y;
349 		    }
350 		}
351 		if ( first==NULL ) first = sp;
352 		if ( sp->next==NULL )
353 	    break;
354 	    }
355 	} else {
356 	    for ( i=0; i<spl->spiro_cnt-1; ++i ) {
357 		if ( nosel || SPIRO_SELECTED(&spl->spiros[i])) {
358 		    if ( bounds->minx==0 && bounds->maxx==0 &&
359 			 bounds->miny==0 && bounds->maxy == 0 ) {
360 			bounds->minx = bounds->maxx = spl->spiros[i].x;
361 			bounds->miny = bounds->maxy = spl->spiros[i].y;
362 		    } else {
363 			if ( spl->spiros[i].x<bounds->minx ) bounds->minx = spl->spiros[i].x;
364 			if ( spl->spiros[i].x>bounds->maxx ) bounds->maxx = spl->spiros[i].x;
365 			if ( spl->spiros[i].y<bounds->miny ) bounds->miny = spl->spiros[i].y;
366 			if ( spl->spiros[i].y>bounds->maxy ) bounds->maxy = spl->spiros[i].y;
367 		    }
368 		}
369 	    }
370 	}
371     }
372 }
373 
CVFindCenter(CharView * cv,BasePoint * bp,int nosel)374 void CVFindCenter(CharView *cv, BasePoint *bp, int nosel) {
375     DBounds b;
376     ImageList *img;
377 
378     b.minx = b.miny = b.maxx = b.maxy = 0;
379     SplineSetFindSelBounds(cv->b.layerheads[cv->b.drawmode]->splines,&b,nosel,cv->b.sc->inspiro&& hasspiro());
380     if ( cv->b.drawmode==dm_fore ) {
381 	RefChar *rf;
382 	for ( rf=cv->b.layerheads[cv->b.drawmode]->refs; rf!=NULL; rf=rf->next ) {
383 	    if ( nosel || rf->selected ) {
384 		if ( b.minx==0 && b.maxx==0 )
385 		    b = rf->bb;
386 		else {
387 		    if ( rf->bb.minx<b.minx ) b.minx = rf->bb.minx;
388 		    if ( rf->bb.miny<b.miny ) b.miny = rf->bb.miny;
389 		    if ( rf->bb.maxx>b.maxx ) b.maxx = rf->bb.maxx;
390 		    if ( rf->bb.maxy>b.maxy ) b.maxy = rf->bb.maxy;
391 		}
392 	    }
393 	}
394     }
395     for ( img=cv->b.layerheads[cv->b.drawmode]->images; img!=NULL; img=img->next ) {
396 	if ( nosel || img->selected ) {
397 	    if ( b.minx==0 && b.maxx==0 )
398 		b = img->bb;
399 	    else {
400 		if ( img->bb.minx<b.minx ) b.minx = img->bb.minx;
401 		if ( img->bb.miny<b.miny ) b.miny = img->bb.miny;
402 		if ( img->bb.maxx>b.maxx ) b.maxx = img->bb.maxx;
403 		if ( img->bb.maxy>b.maxy ) b.maxy = img->bb.maxy;
404 	    }
405 	}
406     }
407     bp->x = (b.minx+b.maxx)/2;
408     bp->y = (b.miny+b.maxy)/2;
409 }
410 
OnBB(CharView * cv,DBounds * bb,real fudge)411 static int OnBB(CharView *cv, DBounds *bb, real fudge) {
412 
413     if ( cv->info.y < bb->miny-fudge || cv->info.y > bb->maxy+fudge ||
414 	    cv->info.x < bb->minx-fudge || cv->info.x > bb->maxx+fudge )
415 return( ee_none );
416 
417     cv->expandorigin.x = (cv->info.x-bb->minx)<(bb->maxx-cv->info.x) ?
418 		bb->maxx : bb->minx;
419     cv->expandorigin.y = (cv->info.y-bb->miny)<(bb->maxy-cv->info.y) ?
420 		bb->maxy : bb->miny;
421     cv->expandwidth = cv->expandorigin.x==bb->maxx? bb->minx-bb->maxx : bb->maxx-bb->minx;
422     cv->expandheight = cv->expandorigin.y==bb->maxy? bb->miny-bb->maxy : bb->maxy-bb->miny;
423 
424     if (( cv->info.x < bb->minx + fudge && cv->info.y < bb->miny+ 4*fudge ) ||
425 	    ( cv->info.x < bb->minx + 4*fudge && cv->info.y < bb->miny+ fudge )) {
426 return( ee_sw );
427     }
428     if (( cv->info.x < bb->minx + fudge && cv->info.y > bb->maxy- 4*fudge ) ||
429 	    ( cv->info.x < bb->minx + 4*fudge && cv->info.y > bb->maxy- fudge ))
430 return( ee_nw );
431     if (( cv->info.x > bb->maxx - fudge && cv->info.y < bb->miny+ 4*fudge ) ||
432 	    ( cv->info.x > bb->maxx - 4*fudge && cv->info.y < bb->miny+ fudge ))
433 return( ee_se );
434     if (( cv->info.x > bb->maxx - fudge && cv->info.y > bb->maxy- 4*fudge ) ||
435 	    ( cv->info.x > bb->maxx - 4*fudge && cv->info.y > bb->maxy- fudge ))
436 return( ee_ne );
437     if ( cv->info.x < bb->minx + fudge )
438 return( ee_right );
439     if ( cv->info.x > bb->maxx - fudge )
440 return( ee_left );
441     if ( cv->info.y < bb->miny + fudge )
442 return( ee_down );
443     if ( cv->info.y > bb->maxy - fudge )
444 return( ee_up );
445 
446 return( ee_none );
447 }
448 
SetCur(CharView * cv)449 static void SetCur(CharView *cv) {
450     static GCursor cursors[ee_max];
451 
452     if ( cursors[ee_nw]==0 ) {
453 	cursors[ee_none] = ct_mypointer;
454 	cursors[ee_nw]   = cursors[ee_se] = ct_nwse; cursors[ee_ne] = cursors[ee_sw] = ct_nesw;
455 	cursors[ee_left] = cursors[ee_right] = ct_leftright;
456 	cursors[ee_up]   = cursors[ee_down] = ct_updown;
457     }
458     GDrawSetCursor(cv->v,cursors[cv->expandedge]);
459 }
460 
NearCaret(SplineChar * sc,real x,real fudge)461 static int NearCaret(SplineChar *sc,real x,real fudge ) {
462     PST *pst;
463     int i;
464 
465     for ( pst=sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next );
466     if ( pst==NULL )
467 return( -1 );
468     for ( i=0; i<pst->u.lcaret.cnt; ++i ) {
469 	if ( x>pst->u.lcaret.carets[i]-fudge && x<pst->u.lcaret.carets[i]+fudge )
470 return( i );
471     }
472 return( -1 );
473 }
474 
CVNearRBearingLine(CharView * cv,real x,real fudge)475 int CVNearRBearingLine( CharView* cv, real x, real fudge )
476 {
477     RefChar *usemymetrics = HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv));
478     return( cv->showhmetrics
479 	    && x>cv->b.sc->width-fudge
480 	    && x<cv->b.sc->width+fudge
481 	    && !cv->b.container
482 	    && !usemymetrics );
483 }
CVNearLBearingLine(CharView * cv,real x,real fudge)484 int CVNearLBearingLine( CharView* cv, real x, real fudge )
485 {
486     RefChar *usemymetrics = HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv));
487     return( cv->showhmetrics
488 	    && x>0-fudge
489 	    && x<0+fudge
490 	    && !cv->b.container && !usemymetrics );
491 }
492 
493 
CVCheckResizeCursors(CharView * cv)494 void CVCheckResizeCursors(CharView *cv) {
495     CharViewTab* tab = CVGetActiveTab(cv);
496     RefChar *ref;
497     ImageList *img;
498     int old_ee = cv->expandedge;
499     real fudge = 3.5/tab->scale;
500 
501     cv->expandedge = ee_none;
502     if ( cv->b.drawmode!=dm_grid ) {
503 	for ( ref=cv->b.layerheads[cv->b.drawmode]->refs; ref!=NULL; ref=ref->next ) if ( ref->selected ) {
504 	    if (( cv->expandedge = OnBB(cv,&ref->bb,fudge))!=ee_none )
505 	break;
506 	}
507 	if ( cv->expandedge == ee_none ) {
508 	    RefChar *usemymetrics = HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv));
509 	    /* if ( cv->showhmetrics && cv->info.x > cv->b.sc->width-fudge && */
510 	    /* 	 cv->info.x<cv->b.sc->width+fudge && cv->b.container==NULL && */
511 	    /* 	 usemymetrics==NULL ) */
512       if ( cv->showhmetrics && NearCaret(cv->b.sc,cv->info.x,fudge)!=-1 &&
513 		    usemymetrics==NULL )
514 		cv->expandedge = ee_right;
515 	    else if( CVNearRBearingLine( cv, cv->info.x, fudge ))
516 		cv->expandedge = ee_right;
517 	    else if( CVNearLBearingLine( cv, cv->info.x, fudge ))
518 		cv->expandedge = ee_left;
519 	    if ( cv->showvmetrics && cv->b.sc->parent->hasvmetrics && cv->b.container==NULL &&
520 		    cv->info.y > -cv->b.sc->vwidth - fudge + cv->b.sc->parent->ascent &&
521 		    cv->info.y < -cv->b.sc->vwidth + fudge + cv->b.sc->parent->ascent)
522 		cv->expandedge = ee_down;
523 	}
524     }
525     for ( img=cv->b.layerheads[cv->b.drawmode]->images; img!=NULL; img=img->next ) if ( img->selected ) {
526 	if (( cv->expandedge = OnBB(cv,&img->bb,fudge))!=ee_none )
527     break;
528     }
529     if ( cv->expandedge!=old_ee )
530 	SetCur(cv);
531 }
532 
CVPreserveMaybeState(CharView * cv,int isTState)533 Undoes *CVPreserveMaybeState(CharView *cv, int isTState) {
534     if( isTState )
535 	return CVPreserveTState( cv );
536     return CVPreserveState( &cv->b );
537 }
538 
CVPreserveTState(CharView * cv)539 Undoes *CVPreserveTState(CharView *cv) {
540     int anyrefs;
541 
542     cv->p.transany = CVAnySel(cv,NULL,&anyrefs,NULL,NULL);
543     cv->p.transanyrefs = anyrefs;
544 
545 return( _CVPreserveTState(&cv->b,&cv->p));
546 }
547 
CVRestoreTOriginalState(CharView * cv)548 void CVRestoreTOriginalState(CharView *cv) {
549     _CVRestoreTOriginalState(&cv->b,&cv->p);
550 }
551 
CVUndoCleanup(CharView * cv)552 void CVUndoCleanup(CharView *cv) {
553     _CVUndoCleanup(&cv->b,&cv->p);
554 }
555 
ImgRefEdgeSelected(CharView * cv,FindSel * fs,GEvent * event)556 static int ImgRefEdgeSelected(CharView *cv, FindSel *fs,GEvent *event) {
557     RefChar *ref;
558     ImageList *img;
559     int update;
560 
561     cv->expandedge = ee_none;
562     /* Check the bounding box of references if meta is up, or if they didn't */
563     /*  click on a reference edge. Point being to allow people to select */
564     /*  macron or other reference which fills the bounding box */
565     if ( !(event->u.mouse.state&ksm_meta) ||
566 	    (fs->p->ref!=NULL && !fs->p->ref->selected)) {
567 	for ( ref=cv->b.layerheads[cv->b.drawmode]->refs; ref!=NULL; ref=ref->next ) if ( ref->selected ) {
568 	    if (( cv->expandedge = OnBB(cv,&ref->bb,fs->fudge))!=ee_none ) {
569 		ref->selected = false;
570 		update = CVClearSel(cv);
571 		ref->selected = true;
572 		if ( update )
573 		    SCUpdateAll(cv->b.sc);
574 		CVPreserveTState(cv);
575 		cv->p.ref = ref;
576 		SetCur(cv);
577 return( true );
578 	    }
579 	}
580     }
581     for ( img=cv->b.layerheads[cv->b.drawmode]->images; img!=NULL; img=img->next ) if ( img->selected ) {
582 	if (( cv->expandedge = OnBB(cv,&img->bb,fs->fudge))!=ee_none ) {
583 	    img->selected = false;
584 	    update = CVClearSel(cv);
585 	    img->selected = true;
586 	    if ( update )
587 		SCUpdateAll(cv->b.sc);
588 	    CVPreserveTState(cv);
589 	    cv->p.img = img;
590 	    SetCur(cv);
591 return( true );
592 	}
593     }
594 return( false );
595 }
596 
CVUnselectAllBCP(CharView * cv)597 void CVUnselectAllBCP( CharView *cv )
598 {
599     CVFindAndVisitSelectedControlPoints( cv, false,
600 					 FE_unselectBCP, 0 );
601 
602     // This should happen, but it effects the single selection with mouse
603     // codepaths in bad ways as at 2013.Aug
604     /* cv->p.nextcp = 0; */
605     /* cv->p.prevcp = 0; */
606 
607 }
608 
CVMouseDownPointer(CharView * cv,FindSel * fs,GEvent * event)609 void CVMouseDownPointer(CharView *cv, FindSel *fs, GEvent *event) {
610     int needsupdate = false;
611     int dowidth, dovwidth, doic, dotah, nearcaret;
612     int dolbearing;
613     RefChar *usemymetrics = HasUseMyMetrics(cv->b.sc,CVLayer((CharViewBase *) cv));
614     int i;
615 
616     cv->p.splineAdjacentPointsSelected = 0;
617     if( cv->p.spline )
618     {
619 	cv->p.splineAdjacentPointsSelected =
620 	    cv->p.spline->from && cv->p.spline->to
621 	    && cv->p.spline->from->selected && cv->p.spline->to->selected;
622     }
623 
624     if ( cv->pressed==NULL )
625 	cv->pressed = GDrawRequestTimer(cv->v,200,100,NULL);
626     cv->last_c.x = cv->info.x; cv->last_c.y = cv->info.y;
627     /* don't clear the selection if the things we clicked on were already */
628     /*  selected, or if the user held the shift key down */
629     if ( ImgRefEdgeSelected(cv,fs,event))
630 return;
631     dowidth    = CVNearRBearingLine( cv, cv->p.cx, fs->fudge );
632     dolbearing = CVNearLBearingLine( cv, cv->p.cx, fs->fudge );
633     doic = ( cv->showhmetrics && cv->b.sc->italic_correction!=TEX_UNDEF &&
634 		cv->b.sc->italic_correction!=0 &&
635 		cv->p.cx>cv->b.sc->width+cv->b.sc->italic_correction-fs->fudge &&
636 		cv->p.cx<cv->b.sc->width+cv->b.sc->italic_correction+fs->fudge &&
637 		cv->b.container==NULL );
638     dotah = ( cv->showhmetrics && cv->b.sc->top_accent_horiz!=TEX_UNDEF &&
639 		cv->p.cx>cv->b.sc->top_accent_horiz-fs->fudge &&
640 		cv->p.cx<cv->b.sc->top_accent_horiz+fs->fudge &&
641 		cv->b.container==NULL );
642     dovwidth = ( cv->showvmetrics && cv->b.sc->parent->hasvmetrics && cv->b.container == NULL &&
643 		cv->p.cy > -cv->b.sc->vwidth + cv->b.sc->parent->ascent - fs->fudge &&
644 		cv->p.cy < -cv->b.sc->vwidth + cv->b.sc->parent->ascent + fs->fudge &&
645 		usemymetrics==NULL );
646     cv->nearcaret = nearcaret = -1;
647     if ( cv->showhmetrics ) nearcaret = NearCaret(cv->b.sc,cv->p.cx,fs->fudge);
648     if ( (fs->p->sp==NULL    || !fs->p->sp->selected) &&
649 	 (fs->p->spiro==NULL || !SPIRO_SELECTED(fs->p->spiro)) &&
650 	 (fs->p->ref==NULL   || !fs->p->ref->selected) &&
651 	 (fs->p->img==NULL   || !fs->p->img->selected) &&
652 	 (fs->p->ap==NULL    || !fs->p->ap->selected) &&
653 	 (!dowidth    || !cv->widthsel) &&
654 	 (!dolbearing || !cv->lbearingsel) &&
655 	 (!dovwidth   || !cv->vwidthsel) &&
656 	 (!doic  || !cv->icsel) &&
657 	 (!dotah || !cv->tah_sel) &&
658 	 !(event->u.mouse.state&ksm_shift))
659     {
660 	needsupdate = CVClearSel(cv);
661     }
662 
663     if ( !fs->p->anysel )
664     {
665 	/* Nothing else... unless they clicked on the width line, check that */
666 	if ( dowidth )
667 	{
668 	    if ( event->u.mouse.state&ksm_shift )
669 		cv->widthsel = !cv->widthsel;
670 	    else
671 		cv->widthsel = true;
672 	    if ( cv->widthsel ) {
673 		cv->oldwidth = cv->b.sc->width;
674 		fs->p->cx = cv->b.sc->width;
675 		CVInfoDraw(cv,cv->gw);
676 		fs->p->anysel = true;
677 		cv->expandedge = ee_right;
678 	    } else
679 		cv->expandedge = ee_none;
680 	    SetCur(cv);
681 	    needsupdate = true;
682 	}
683 	if ( nearcaret!=-1 )
684 	{
685 	    PST *pst;
686 	    for ( pst=cv->b.sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next );
687 	    cv->lcarets = pst;
688 	    cv->nearcaret = nearcaret;
689 	    cv->expandedge = ee_right;
690 	    SetCur(cv);
691 	}
692 	else if ( dolbearing )
693 	{
694 	    if ( event->u.mouse.state&ksm_shift )
695 		cv->lbearingsel = !cv->lbearingsel;
696 	    else
697 		cv->lbearingsel = true;
698 	    if ( cv->lbearingsel ) {
699 //		cv->oldlbearing = cv->b.sc->lbearing;
700 		fs->p->cx = 0;;
701 		CVInfoDraw(cv,cv->gw);
702 		fs->p->anysel = true;
703 		cv->expandedge = ee_left;
704 	    } else
705 		cv->expandedge = ee_none;
706 	    SetCur(cv);
707 	    needsupdate = true;
708 	}
709 	else if ( dovwidth )
710 	{
711 	    if ( event->u.mouse.state&ksm_shift )
712 		cv->vwidthsel = !cv->vwidthsel;
713 	    else
714 		cv->vwidthsel = true;
715 	    if ( cv->vwidthsel ) {
716 		cv->oldvwidth = cv->b.sc->vwidth;
717 		fs->p->cy = /*cv->b.sc->parent->vertical_origin*/-cv->b.sc->vwidth;
718 		CVInfoDraw(cv,cv->gw);
719 		fs->p->anysel = true;
720 		cv->expandedge = ee_down;
721 	    } else
722 		cv->expandedge = ee_none;
723 	    SetCur(cv);
724 	    needsupdate = true;
725 	}
726 	else if ( doic )
727 	{
728 	    if ( event->u.mouse.state&ksm_shift )
729 		cv->icsel = !cv->icsel;
730 	    else
731 		cv->icsel = true;
732 	    if ( cv->icsel ) {
733 		cv->oldic = cv->b.sc->italic_correction+cv->b.sc->width;
734 		fs->p->cx = cv->b.sc->italic_correction+cv->b.sc->width;
735 		CVInfoDraw(cv,cv->gw);
736 		fs->p->anysel = true;
737 		cv->expandedge = ee_right;
738 	    } else
739 		cv->expandedge = ee_none;
740 	    SetCur(cv);
741 	    needsupdate = true;
742 	}
743 	else if ( dotah )
744 	{
745 	    if ( event->u.mouse.state&ksm_shift )
746 		cv->tah_sel = !cv->tah_sel;
747 	    else
748 		cv->tah_sel = true;
749 	    if ( cv->tah_sel ) {
750 		cv->oldtah = cv->b.sc->top_accent_horiz;
751 		fs->p->cx = cv->b.sc->top_accent_horiz;
752 		CVInfoDraw(cv,cv->gw);
753 		fs->p->anysel = true;
754 		cv->expandedge = ee_right;
755 	    } else
756 		cv->expandedge = ee_none;
757 	    SetCur(cv);
758 	    needsupdate = true;
759 	}
760     }
761     else if ( event->u.mouse.clicks<=1 && !(event->u.mouse.state&ksm_shift))
762     {
763 	/* printf("CVMouseDownPointer(2) not shifting\n"); */
764 	/* printf("CVMouseDownPointer(2) cv->p.sp:%p\n", cv->p.sp ); */
765 	/* printf("CVMouseDownPointer(2) n:%p p:%p sp:%p spline:%p ap:%p\n", */
766 	/*        fs->p->nextcp,fs->p->prevcp, fs->p->sp, fs->p->spline, fs->p->ap ); */
767 	/* printf("CVMouseDownPointer(2) spl:%p\n", fs->p->spl ); */
768 	/* SPLFirstVisit( fs->p->spl->first, SPLFirstVisitorDebugSelectionState, 0 ); */
769 	CVUnselectAllBCP( cv );
770 
771 	if ( fs->p->nextcp || fs->p->prevcp ) {
772 	    CPStartInfo(cv,event);
773 	    /* Needs update to draw control points selected */
774 	    needsupdate = true;
775 	} else if ( fs->p->sp!=NULL ) {
776 	    if ( !fs->p->sp->selected ) needsupdate = true;
777 	    fs->p->sp->selected = true;
778 	} else if ( fs->p->spiro!=NULL ) {
779 	    if ( !SPIRO_SELECTED(fs->p->spiro) ) needsupdate = true;
780 	    SPIRO_SELECT( fs->p->spiro );
781 	} else if ( fs->p->spline!=NULL && (!cv->b.sc->inspiro || !hasspiro())) {
782 	    if ( !fs->p->spline->to->selected &&
783 		    !fs->p->spline->from->selected ) needsupdate = true;
784 	    fs->p->spline->to->selected = true;
785 	    fs->p->spline->from->selected = true;
786 	} else if ( fs->p->img!=NULL ) {
787 	    if ( !fs->p->img->selected ) needsupdate = true;
788 	    fs->p->img->selected = true;
789 	} else if ( fs->p->ref!=NULL ) {
790 	    if ( !fs->p->ref->selected ) needsupdate = true;
791 	    fs->p->ref->selected = true;
792 	} else if ( fs->p->ap!=NULL ) {
793 	    if ( !fs->p->ap->selected ) needsupdate = true;
794 	    fs->p->ap->selected = true;
795 	}
796     }
797     else if ( event->u.mouse.clicks<=1 )
798     {
799 	/* printf("CVMouseDownPointer(3) with shift... n:%p p:%p sp:%p spline:%p ap:%p\n", */
800 	/*        fs->p->nextcp,fs->p->prevcp, fs->p->sp, fs->p->spline, fs->p->ap ); */
801 	/* printf("CVMouseDownPointer(3) spl:%p\n", fs->p->spl ); */
802 	/* SPLFirstVisit( fs->p->spl->first, SPLFirstVisitorDebugSelectionState, 0 ); */
803 
804 	if ( fs->p->nextcp || fs->p->prevcp ) {
805 	    /* Needs update to draw control points selected */
806 	    needsupdate = true;
807 	} else if ( fs->p->sp!=NULL ) {
808 	    needsupdate = true;
809 	    fs->p->sp->selected = !fs->p->sp->selected;
810 	} else if ( fs->p->spiro!=NULL ) {
811 	    needsupdate = true;
812 	    fs->p->spiro->ty ^= 0x80;
813 	} else if ( fs->p->spline!=NULL && (!cv->b.sc->inspiro || !hasspiro())) {
814 	    needsupdate = true;
815 	    fs->p->spline->to->selected = !fs->p->spline->to->selected;
816 	    fs->p->spline->from->selected = !fs->p->spline->from->selected;
817 	} else if ( fs->p->img!=NULL ) {
818 	    needsupdate = true;
819 	    fs->p->img->selected = !fs->p->img->selected;
820 	} else if ( fs->p->ref!=NULL ) {
821 	    needsupdate = true;
822 	    fs->p->ref->selected = !fs->p->ref->selected;
823 	} else if ( fs->p->ap!=NULL ) {
824 	    needsupdate = true;
825 	    fs->p->ap->selected = !fs->p->ap->selected;
826 	}
827     }
828     else if ( event->u.mouse.clicks==2 )
829     {
830 	/* printf("mouse down click==2\n"); */
831 	CPEndInfo(cv);
832 	if ( fs->p->spl!=NULL ) {
833 	    if ( cv->b.sc->inspiro && hasspiro()) {
834 		for ( i=0; i<fs->p->spl->spiro_cnt-1; ++i ) {
835 		    if ( !SPIRO_SELECTED(&fs->p->spl->spiros[i])) {
836 			needsupdate = true;
837 			SPIRO_SELECT(&fs->p->spl->spiros[i]);
838 		    }
839 		}
840 	    } else {
841 		Spline *spline, *first;
842 		if ( !fs->p->spl->first->selected ) { needsupdate = true; fs->p->spl->first->selected = true; }
843 		first = NULL;
844 		for ( spline = fs->p->spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
845 		    if ( !spline->to->selected )
846 			{ needsupdate = true; spline->to->selected = true; }
847 		    if ( first==NULL ) first = spline;
848 		}
849 	    }
850 	} else if ( fs->p->ref!=NULL || fs->p->img!=NULL ) {
851 	    /* Double clicking on a referenced character doesn't do much */
852 	} else if ( fs->p->ap!=NULL ) {
853 	    /* Select all Anchor Points at this location */
854 	    AnchorPoint *ap;
855 	    for ( ap=cv->b.sc->anchor; ap!=NULL; ap=ap->next )
856 		if ( ap->me.x==fs->p->ap->me.x && ap->me.y==fs->p->ap->me.y )
857 		    if ( !ap->selected ) {
858 			ap->selected = true;
859 			needsupdate = true;
860 		    }
861 	}
862     }
863     else if ( event->u.mouse.clicks==3 )
864     {
865 	/* printf("mouse down click==3\n"); */
866 	if ( CVSetSel(cv,1)) needsupdate = true;
867 		/* don't select width or anchor points for three clicks */
868 		/*  but select all points, refs */
869     }
870     else
871     {
872 	/* printf("mouse down ELSE\n"); */
873 	/* Select everything */
874 	if ( CVSetSel(cv,-1)) needsupdate = true;
875     }
876 
877 
878     if ( needsupdate )
879 	SCUpdateAll(cv->b.sc);
880 
881     /* lastselpt is set by our caller */
882 }
883 
CVRectSelect(CharView * cv,real newx,real newy)884 static int CVRectSelect(CharView *cv, real newx, real newy) {
885     int any=false;
886     DBounds old, new;
887     RefChar *rf;
888     ImageList *img;
889     Spline *spline, *first;
890     SplinePointList *spl;
891     BasePoint *bp;
892     AnchorPoint *ap;
893     DBounds bb;
894     int i;
895 
896     if ( cv->p.cx<=cv->p.ex ) {
897 	old.minx = cv->p.cx;
898 	old.maxx = cv->p.ex;
899     } else {
900 	old.minx = cv->p.ex;
901 	old.maxx = cv->p.cx;
902     }
903     if ( cv->p.cy<=cv->p.ey ) {
904 	old.miny = cv->p.cy;
905 	old.maxy = cv->p.ey;
906     } else {
907 	old.miny = cv->p.ey;
908 	old.maxy = cv->p.cy;
909     }
910 
911     if ( cv->p.cx<=newx ) {
912 	new.minx = cv->p.cx;
913 	new.maxx = newx;
914     } else {
915 	new.minx = newx;
916 	new.maxx = cv->p.cx;
917     }
918     if ( cv->p.cy<=newy ) {
919 	new.miny = cv->p.cy;
920 	new.maxy = newy;
921     } else {
922 	new.miny = newy;
923 	new.maxy = cv->p.cy;
924     }
925 
926     for ( rf = cv->b.layerheads[cv->b.drawmode]->refs; rf!=NULL; rf=rf->next ) {
927 	if (( rf->bb.minx>=old.minx && rf->bb.maxx<old.maxx &&
928 		    rf->bb.miny>=old.miny && rf->bb.maxy<old.maxy ) !=
929 		( rf->bb.minx>=new.minx && rf->bb.maxx<new.maxx &&
930 		    rf->bb.miny>=new.miny && rf->bb.maxy<new.maxy )) {
931 	    rf->selected = !rf->selected;
932 	    any = true;
933 	}
934     }
935     if ( cv->b.drawmode==dm_fore ) {
936 	if ( cv->showanchor ) for ( ap=cv->b.sc->anchor ; ap!=NULL; ap=ap->next ) {
937 	    bp = &ap->me;
938 	    if (( bp->x>=old.minx && bp->x<old.maxx &&
939 			bp->y>=old.miny && bp->y<old.maxy ) !=
940 		    ( bp->x>=new.minx && bp->x<new.maxx &&
941 			bp->y>=new.miny && bp->y<new.maxy )) {
942 		ap->selected = !ap->selected;
943 		any = true;
944 	    }
945 	}
946     }
947 
948     for ( img = cv->b.layerheads[cv->b.drawmode]->images; img!=NULL; img=img->next ) {
949 	bb.minx = img->xoff;
950 	bb.miny = img->yoff;
951 	bb.maxx = img->xoff+GImageGetWidth(img->image)*img->xscale;
952 	bb.maxy = img->yoff+GImageGetHeight(img->image)*img->yscale;
953 	if (( bb.minx>=old.minx && bb.maxx<old.maxx &&
954 		    bb.miny>=old.miny && bb.maxy<old.maxy ) !=
955 		( bb.minx>=new.minx && bb.maxx<new.maxx &&
956 		    bb.miny>=new.miny && bb.maxy<new.maxy )) {
957 	    img->selected = !img->selected;
958 	    any = true;
959 	}
960     }
961 
962     for ( spl = cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL; spl = spl->next ) {
963 	if ( !cv->b.sc->inspiro || !hasspiro()) {
964 	    first = NULL;
965 	    if ( spl->first->prev==NULL ) {
966 		bp = &spl->first->me;
967 		if (( bp->x>=old.minx && bp->x<old.maxx &&
968 			    bp->y>=old.miny && bp->y<old.maxy ) !=
969 			( bp->x>=new.minx && bp->x<new.maxx &&
970 			    bp->y>=new.miny && bp->y<new.maxy )) {
971 		    spl->first->selected = !spl->first->selected;
972 		    if ( spl->first->selected )
973 			cv->lastselpt = spl->first;
974 		    else if ( spl->first==cv->lastselpt )
975 			cv->lastselpt = NULL;
976 		    any = true;
977 		}
978 	    }
979 	    for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
980 		bp = &spline->to->me;
981 		if (( bp->x>=old.minx && bp->x<old.maxx &&
982 			    bp->y>=old.miny && bp->y<old.maxy ) !=
983 			( bp->x>=new.minx && bp->x<new.maxx &&
984 			    bp->y>=new.miny && bp->y<new.maxy )) {
985 		    spline->to->selected = !spline->to->selected;
986 		    if ( spline->to->selected )
987 			cv->lastselpt = spline->to;
988 		    else if ( spline->to==cv->lastselpt )
989 			cv->lastselpt = NULL;
990 		    any = true;
991 		}
992 		if ( first==NULL ) first = spline;
993 	    }
994 	} else {
995 	    for ( i=0; i<spl->spiro_cnt-1; ++i ) {
996 		if (( spl->spiros[i].x>=old.minx && spl->spiros[i].x<old.maxx &&
997 			    spl->spiros[i].y>=old.miny && spl->spiros[i].y<old.maxy ) !=
998 			( spl->spiros[i].x>=new.minx && spl->spiros[i].x<new.maxx &&
999 			    spl->spiros[i].y>=new.miny && spl->spiros[i].y<new.maxy )) {
1000 		    spl->spiros[i].ty ^= 0x80;
1001 		    if ( SPIRO_SELECTED(&spl->spiros[i]))
1002 			cv->lastselcp = &spl->spiros[i];
1003 		    any = true;
1004 		}
1005 	    }
1006 	}
1007     }
1008 return( any );
1009 }
1010 
CVAdjustControl(CharView * cv,BasePoint * cp,BasePoint * to)1011 void CVAdjustControl(CharView *cv,BasePoint *cp, BasePoint *to) {
1012     SplinePoint *sp = cv->p.sp;
1013 
1014     SPAdjustControl(sp,cp,to,cv->b.layerheads[cv->b.drawmode]->order2);
1015     CVSetCharChanged(cv,true);
1016 }
1017 
isSplinePointPartOfGuide(SplineFont * sf,SplinePoint * sp)1018 bool isSplinePointPartOfGuide( SplineFont *sf, SplinePoint *sp )
1019 {
1020     if( !sp || !sf )
1021 	return 0;
1022     if( !sf->grid.splines )
1023 	return 0;
1024 
1025     SplinePointList* spl = sf->grid.splines;
1026     return SplinePointListContainsPoint( spl, sp );
1027 }
1028 
CVAdjustSpline(CharView * cv)1029 static void CVAdjustSpline(CharView *cv) {
1030     Spline *old = cv->p.spline;
1031     FitPoint fp[5];
1032     real t;
1033     Spline1D *oldx = &old->splines[0], *oldy = &old->splines[1];
1034     int oldfrompointtype, oldtopointtype;
1035 
1036     if ( cv->b.layerheads[cv->b.drawmode]->order2 )
1037 return;
1038 
1039     //
1040     // Click + drag on a guide moves the guide to where your mouse is at
1041     //
1042     if( cv->b.drawmode == dm_grid
1043 	&& isSplinePointPartOfGuide( cv->b.sc->parent, cv->p.spline->from )
1044 	&& isSplinePointPartOfGuide( cv->b.sc->parent, cv->p.spline->to ) )
1045     {
1046 	if( 0 == cv->p.spline->splines[0].a
1047 	    && 0 == cv->p.spline->splines[0].b
1048 	    && 0 == cv->p.spline->splines[1].a
1049 	    && 0 == cv->p.spline->splines[1].b
1050 	    && ( (cv->p.spline->splines[0].c && cv->p.spline->from->me.y == cv->p.spline->to->me.y)
1051 		 || (cv->p.spline->splines[1].c && cv->p.spline->from->me.x == cv->p.spline->to->me.x )))
1052 	{
1053 	    if( cv->p.spline->from->me.y == cv->p.spline->to->me.y )
1054 	    {
1055 		int newy = cv->info.y;
1056 		cv->p.spline->from->me.y = newy;
1057 		cv->p.spline->to->me.y = newy;
1058 		cv->p.spline->splines[1].d = newy;
1059 	    }
1060 	    else
1061 	    {
1062 		int newx = cv->info.x;
1063 		cv->p.spline->from->me.x = newx;
1064 		cv->p.spline->to->me.x = newx;
1065 		cv->p.spline->splines[0].d = newx;
1066 	    }
1067 	    CVSetCharChanged(cv,true);
1068 	    return;
1069 	}
1070     }
1071 
1072 
1073     fp[0].p.x = cv->info.x; fp[0].p.y = cv->info.y; fp[0].t = cv->p.t;
1074     t = cv->p.t/10;
1075     fp[1].p.x = ((oldx->a*t+oldx->b)*t+oldx->c)*t + oldx->d;
1076     fp[1].p.y = ((oldy->a*t+oldy->b)*t+oldy->c)*t + oldy->d;
1077     fp[1].t = t;
1078     t = 1-(1-cv->p.t)/10;
1079     fp[2].p.x = ((oldx->a*t+oldx->b)*t+oldx->c)*t + oldx->d;
1080     fp[2].p.y = ((oldy->a*t+oldy->b)*t+oldy->c)*t + oldy->d;
1081     fp[2].t = t;
1082     fp[3] = fp[0];		/* Give more weight to this point than to the others */
1083     fp[4] = fp[0];		/*  ditto */
1084     cv->p.spline = ApproximateSplineFromPoints(old->from,old->to,fp,5,old->order2);
1085 
1086     /* don't change hvcurves to corners */
1087     oldfrompointtype = old->from->pointtype;
1088     oldtopointtype = old->to->pointtype;
1089     old->from->pointtype = old->to->pointtype = pt_corner;
1090     if ( oldfrompointtype == pt_hvcurve )
1091         SPChangePointType(old->from, pt_hvcurve);
1092     if ( oldtopointtype == pt_hvcurve )
1093         SPChangePointType(old->to, pt_hvcurve);
1094     //
1095     // dont go changing pt_curve points into pt_corner without explicit consent.
1096     //
1097     if( oldfrompointtype == pt_curve || oldfrompointtype == pt_tangent )
1098     {
1099 	old->from->pointtype = oldfrompointtype;
1100 	SPTouchControl( old->from, &old->from->nextcp, cv->b.layerheads[cv->b.drawmode]->order2 );
1101     }
1102     if( oldtopointtype == pt_curve || oldtopointtype == pt_tangent )
1103     {
1104 	old->to->pointtype = oldtopointtype;
1105 	SPTouchControl( old->to, &old->to->prevcp, cv->b.layerheads[cv->b.drawmode]->order2 );
1106     }
1107 
1108     old->from->nextcpdef = old->to->prevcpdef = false;
1109     SplineFree(old);
1110     CVSetCharChanged(cv,true);
1111 }
1112 
Nearish(real a,real fudge)1113 static int Nearish(real a,real fudge) {
1114 return( a>-fudge && a<fudge );
1115 }
1116 
1117 /* Are any of the selected points open (that is are they missing either a next*/
1118 /*  or a prev spline so that at least one direction is free to make a new link)*/
1119 /*  And did any of those selected points move on top of a point which was not */
1120 /*  selected but which was also open? if so then merge all cases where this */
1121 /*  happened (could be more than one) */
1122 /* However if two things merge we must start all over again because we will have */
1123 /*  freed one of the splinesets in the merger */
1124 /* This is very similar for spiros (because start and end points of open contours */
1125 /*  are the same in both representations). The only complication is checking */
1126 /*  that they are selected */
CVCheckMerges(CharView * cv)1127 static int CVCheckMerges(CharView *cv ) {
1128     CharViewTab* tab = CVGetActiveTab(cv);
1129     SplineSet *activess, *mergess;
1130     real fudge = 2/tab->scale;
1131     int cnt= -1;
1132     int firstsel, lastsel;
1133     int mfirstsel, mlastsel;
1134     int inspiro = cv->b.sc->inspiro && hasspiro();
1135 
1136   restart:
1137     ++cnt;
1138     for ( activess=cv->b.layerheads[cv->b.drawmode]->splines; activess!=NULL; activess=activess->next ) {
1139 	if ( activess->first->prev!=NULL )
1140     continue;		/* Closed contour, can't merge with anything */
1141 	firstsel = (inspiro && SPIRO_SELECTED(&activess->spiros[0])) ||
1142 		    (!inspiro && activess->first->selected);
1143 	lastsel = (inspiro && SPIRO_SELECTED(&activess->spiros[activess->spiro_cnt-2])) ||
1144 		    (!inspiro && activess->last->selected);
1145 	if ( firstsel || lastsel ) {
1146 	    for ( mergess = cv->b.layerheads[cv->b.drawmode]->splines; mergess!=NULL; mergess=mergess->next ) {
1147 		if ( mergess->first->prev!=NULL )
1148 	    continue;		/* Closed contour, can't merge with anything */
1149 		mfirstsel = (inspiro && SPIRO_SELECTED(&mergess->spiros[0])) ||
1150 			    (!inspiro && mergess->first->selected);
1151 		mlastsel = (inspiro && SPIRO_SELECTED(&mergess->spiros[mergess->spiro_cnt-2])) ||
1152 			    (!inspiro && mergess->last->selected);
1153 		if ( !mfirstsel || !mlastsel ) {
1154 		    if ( !mfirstsel && firstsel &&
1155 			    Nearish(mergess->first->me.x-activess->first->me.x,fudge) &&
1156 			    Nearish(mergess->first->me.y-activess->first->me.y,fudge)) {
1157 			CVMergeSplineSets(cv,activess->first,activess,
1158 				mergess->first,mergess);
1159   goto restart;
1160 		    }
1161 		    if ( !mlastsel && firstsel &&
1162 			    Nearish(mergess->last->me.x-activess->first->me.x,fudge) &&
1163 			    Nearish(mergess->last->me.y-activess->first->me.y,fudge)) {
1164 			CVMergeSplineSets(cv,activess->first,activess,
1165 				mergess->last,mergess);
1166   goto restart;
1167 		    }
1168 		    if ( !mfirstsel && lastsel &&
1169 			    Nearish(mergess->first->me.x-activess->last->me.x,fudge) &&
1170 			    Nearish(mergess->first->me.y-activess->last->me.y,fudge)) {
1171 			CVMergeSplineSets(cv,activess->last,activess,
1172 				mergess->first,mergess);
1173   goto restart;
1174 		    }
1175 		    if ( !mlastsel && lastsel &&
1176 			    Nearish(mergess->last->me.x-activess->last->me.x,fudge) &&
1177 			    Nearish(mergess->last->me.y-activess->last->me.y,fudge)) {
1178 			CVMergeSplineSets(cv,activess->last,activess,
1179 				mergess->last,mergess);
1180   goto restart;
1181 		    }
1182 		}
1183 	    }
1184 	}
1185     }
1186 return( cnt>0 && stop_at_join );
1187 }
1188 
adjustLBearing(CharView * cv,SplineChar * sc,real val)1189 static void adjustLBearing( CharView *cv, SplineChar *sc, real val )
1190 {
1191     DBounds bb;
1192     SplineCharFindBounds(sc,&bb);
1193     if ( val != 0 )
1194     {
1195 	enum fvtrans_flags flags = fvt_alllayers | fvt_nopreserve;
1196 	real transform[6];
1197 	transform[0] = transform[3] = 1.0;
1198 	transform[1] = transform[2] = transform[5] = 0;
1199 	transform[4] = val;
1200 	// With no recent change, assume CVPreserveState was called.
1201 	// Remove it, as it doesn't preserve hints or other layers
1202 	// Instead, delegate to FVTrans to do this.
1203 	if (!cv->recentchange) {
1204 	    CVRemoveTopUndo(&cv->b);
1205 	    flags &= ~fvt_nopreserve;
1206 	}
1207 //	printf("adjustLBearing val:%f min:%f v-min:%f\n",val,bb.minx,(bb.minx+val));
1208 	FVTrans( (FontViewBase *) cv->b.fv, sc, transform, NULL, flags );
1209 	// We copy and adapt some code from FVTrans in order to adjust the CharView carets.
1210 	// We omit the fvt_scalepstpos for FVTrans since other CharView code seems to skip updating the SplineChar.
1211 	PST *pst;
1212 	for ( pst = cv->b.sc->possub; pst!=NULL; pst=pst->next ) {
1213 	    if ( pst->type == pst_lcaret ) {
1214 		int j;
1215 		for ( j=0; j<pst->u.lcaret.cnt; ++j )
1216 		    pst->u.lcaret.carets[j] = rint(pst->u.lcaret.carets[j]+val);
1217 	    }
1218 	}
1219     }
1220 }
1221 
1222 /* Move the selection and return whether we did a merge */
CVMoveSelection(CharView * cv,real dx,real dy,uint32 input_state)1223 int CVMoveSelection(CharView *cv, real dx, real dy, uint32 input_state) {
1224     CharViewTab* tab = CVGetActiveTab(cv);
1225     real transform[6];
1226     RefChar *refs;
1227     ImageList *img;
1228     AnchorPoint *ap;
1229     double fudge;
1230     extern float snapdistance;
1231     int i,j;
1232     int changed = false, outlinechanged = false;
1233     SplinePointList *spl;
1234     SplinePoint *sp;
1235 
1236     transform[0] = transform[3] = 1.0;
1237     transform[1] = transform[2] = 0.0;
1238     transform[4] = dx; transform[5] = dy;
1239     if ( transform[4]==0 && transform[5]==0 )
1240 return(false);
1241     for ( spl=cv->b.layerheads[cv->b.drawmode]->splines; spl!=NULL && !outlinechanged; spl=spl->next ) {
1242 	if ( cv->b.sc->inspiro && hasspiro()) {
1243 	    for ( i=0; i<spl->spiro_cnt-1; ++i )
1244 		if ( SPIRO_SELECTED(&spl->spiros[i])) {
1245 		    outlinechanged = true;
1246 	    break;
1247 		}
1248 	} else {
1249 	    for ( sp=spl->first ;; ) {
1250 		if ( sp->selected ) {
1251 		    outlinechanged = true;
1252 	    break;
1253 		}
1254 		if ( sp->next==NULL )
1255 	    break;
1256 		sp = sp->next->to;
1257 		if ( sp==spl->first )
1258 	    break;
1259 	    }
1260 	}
1261     }
1262 
1263     enum transformPointMask tpmask = 0;
1264     tpmask |= tpmask_dontFixControlPoints;
1265 
1266     if ( cv->b.sc->inspiro && hasspiro())
1267 	SplinePointListSpiroTransform(cv->b.layerheads[cv->b.drawmode]->splines,transform,false);
1268     else
1269     {
1270 	bool interp = CVShouldInterpolateCPsOnMotion( cv );
1271 
1272 	SplinePointListTransformExtended(cv->b.layerheads[cv->b.drawmode]->splines,transform,
1273 					 interp ? tpt_OnlySelectedInterpCPs : tpt_OnlySelected,
1274 					 tpmask );
1275 
1276 	if( interp )
1277 	{
1278 	    bool preserveState = false;
1279 	    CVVisitAllControlPoints( cv, preserveState,
1280 	    			     FE_touchControlPoint,
1281 	    			     (void*)(intptr_t)cv->b.layerheads[cv->b.drawmode]->order2 );
1282 	}
1283     }
1284 
1285 
1286     for ( refs = cv->b.layerheads[cv->b.drawmode]->refs; refs!=NULL; refs=refs->next ) if ( refs->selected ) {
1287 	refs->transform[4] += transform[4];
1288 	refs->transform[5] += transform[5];
1289 	refs->bb.minx += transform[4]; refs->bb.maxx += transform[4];
1290 	refs->bb.miny += transform[5]; refs->bb.maxy += transform[5];
1291 	for ( j=0; j<refs->layer_cnt; ++j )
1292 	    SplinePointListTransform(refs->layers[j].splines,transform,tpt_AllPoints);
1293 	outlinechanged = true;
1294     }
1295     if ( CVLayer( (CharViewBase *) cv) > ly_back ) {
1296 	if ( cv->showanchor ) {
1297 	    for ( ap=cv->b.sc->anchor; ap!=NULL; ap=ap->next ) if ( ap->selected ) {
1298 		ap->me.x += transform[4];
1299 		ap->me.y += transform[5];
1300 		changed = true;
1301 	    }
1302 	}
1303     }
1304     for ( img = cv->b.layerheads[cv->b.drawmode]->images; img!=NULL; img=img->next ) if ( img->selected ) {
1305 	img->xoff += transform[4];
1306 	img->yoff += transform[5];
1307 	img->bb.minx += transform[4]; img->bb.maxx += transform[4];
1308 	img->bb.miny += transform[5]; img->bb.maxy += transform[5];
1309 	SCOutOfDateBackground(cv->b.sc);
1310 	changed = true;
1311     }
1312     fudge = snapdistance/tab->scale/2;
1313     if ( cv->widthsel ) {
1314 	if ( cv->b.sc->width+dx>0 && ((int16) (cv->b.sc->width+dx))<0 )
1315 	    cv->b.sc->width = 32767;
1316 	else if ( cv->b.sc->width+dx<0 && ((int16) (cv->b.sc->width+dx))>0 )
1317 	    cv->b.sc->width = -32768;
1318 	else
1319 	    cv->b.sc->width += dx;
1320 	if ( cv->b.sc->width>=-fudge && cv->b.sc->width<fudge )
1321 	    cv->b.sc->width = 0;
1322 	changed = true;
1323     }
1324     if ( cv->lbearingsel ) {
1325 
1326 //	printf("lbearing dx:%f\n", dx );
1327 	adjustLBearing( cv, cv->b.sc, dx );
1328 	changed = true;
1329     }
1330     if ( cv->vwidthsel ) {
1331 	if ( cv->b.sc->vwidth-dy>0 && ((int16) (cv->b.sc->vwidth-dy))<0 )
1332 	    cv->b.sc->vwidth = 32767;
1333 	else if ( cv->b.sc->vwidth-dy<0 && ((int16) (cv->b.sc->vwidth-dy))>0 )
1334 	    cv->b.sc->vwidth = -32768;
1335 	else
1336 	    cv->b.sc->vwidth -= dy;
1337 	if ( cv->b.sc->vwidth>=-fudge && cv->b.sc->vwidth<fudge )
1338 	    cv->b.sc->vwidth = 0;
1339 	changed = true;
1340     }
1341     if ( cv->icsel ) {
1342 	if ( cv->b.sc->italic_correction+dx>0 && ((int16) (cv->b.sc->italic_correction+dx))<0 )
1343 	    cv->b.sc->italic_correction = 32767-1;
1344 	else if ( cv->b.sc->italic_correction+dx<0 && ((int16) (cv->b.sc->italic_correction+dx))>0 )
1345 	    cv->b.sc->italic_correction = -32768;
1346 	else
1347 	    cv->b.sc->italic_correction += dx;
1348 	if ( cv->b.sc->italic_correction>=-fudge && cv->b.sc->italic_correction<fudge )
1349 	    cv->b.sc->italic_correction = 0;
1350 	changed = true;
1351     }
1352     if ( cv->tah_sel ) {
1353 	if ( cv->b.sc->top_accent_horiz+dx>0 && ((int16) (cv->b.sc->top_accent_horiz+dx))<0 )
1354 	    cv->b.sc->top_accent_horiz = 32767-1;
1355 	else if ( cv->b.sc->top_accent_horiz+dx<0 && ((int16) (cv->b.sc->top_accent_horiz+dx))>0 )
1356 	    cv->b.sc->top_accent_horiz = -32768;
1357 	else
1358 	    cv->b.sc->top_accent_horiz += dx;
1359 	if ( cv->b.sc->top_accent_horiz>=-fudge && cv->b.sc->top_accent_horiz<fudge )
1360 	    cv->b.sc->top_accent_horiz = 0;
1361 	changed = true;
1362     }
1363     if ( outlinechanged )
1364 	CVSetCharChanged(cv,true);
1365     else if ( changed )
1366 	CVSetCharChanged(cv,2);
1367     if ( input_state&ksm_meta )
1368 return( false );			/* Don't merge if the meta key is down */
1369 
1370 return( CVCheckMerges( cv ));
1371 }
1372 
CVExpandEdge(CharView * cv)1373 static int CVExpandEdge(CharView *cv) {
1374     real transform[6];
1375     real xscale=1.0, yscale=1.0;
1376 
1377     CVRestoreTOriginalState(cv);
1378     if ( cv->expandedge != ee_up && cv->expandedge != ee_down )
1379 	xscale = (cv->info.x-cv->expandorigin.x)/cv->expandwidth;
1380     if ( cv->expandedge != ee_left && cv->expandedge != ee_right )
1381 	yscale = (cv->info.y-cv->expandorigin.y)/cv->expandheight;
1382     transform[0] = xscale; transform[3] = yscale;
1383     transform[1] = transform[2] = 0;
1384     transform[4] = (1-xscale)*cv->expandorigin.x;
1385     transform[5] = (1-yscale)*cv->expandorigin.y;
1386     CVSetCharChanged(cv,true);
1387     CVTransFunc(cv,transform,false);
1388 return( true );
1389 }
1390 
touchControlPointsVisitor(void * key,void * value,SplinePoint * sp,BasePoint * which,bool isnext,void * udata)1391 static void touchControlPointsVisitor ( void* key,
1392 				 void* value,
1393 				 SplinePoint* sp,
1394 				 BasePoint *which,
1395 				 bool isnext,
1396 				 void* udata )
1397 {
1398     SPTouchControl( sp, which, (int)(intptr_t)udata );
1399 }
1400 
1401 #ifndef MAX
1402 #define MAX(x,y)   (((x) > (y)) ? (x) : (y))
1403 #endif
1404 #ifndef MIN
1405 #define MIN(x,y)   (((x) < (y)) ? (x) : (y))
1406 #endif
1407 
nearest_point_on_line_segment(BasePoint p1,BasePoint p2,BasePoint p3)1408 BasePoint nearest_point_on_line_segment(BasePoint p1, BasePoint p2, BasePoint p3) {
1409 	double x_diff = p2.x - p1.x;
1410 	double y_diff = p2.y - p1.y;
1411 	double slope_1 = (p2.y - p1.y) / (p2.x - p1.x);
1412 	double inverse_slope_1 = (p2.x - p1.x) / (p2.y - p1.y);
1413 	BasePoint output_1;
1414 	output_1.x = 0;
1415 	output_1.y = 0;
1416 	// Accuracy may be important, so we try to use the longer dimension.
1417 	if (x_diff == 0) {
1418 		// The line is vertical.
1419 		output_1.x = p1.x;
1420 		output_1.y = p3.y;
1421 	} else if (y_diff == 0) {
1422 		// The line is horizontal.
1423 		output_1.x = p3.x;
1424 		output_1.y = p1.y;
1425 	} else {
1426 		output_1.x = (p3.x/slope_1 + slope_1*p1.x + p3.y - p1.y)/(slope_1 + 1/slope_1);
1427 		output_1.y = (p3.y/inverse_slope_1 + inverse_slope_1*p1.y + p3.x - p1.x)/(inverse_slope_1 + 1/inverse_slope_1);
1428 	}
1429 	// We are using the segment between p1 and p2, not the line through them, so we must crop.
1430 	double min_x = MIN(p1.x, p2.x), max_x = MAX(p1.x, p2.x), min_y = MIN(p1.y, p2.y), max_y = MAX(p1.y, p2.y);
1431 	if (output_1.x < min_x) output_1.x = min_x;
1432 	if (output_1.x > max_x) output_1.x = max_x;
1433 	if (output_1.y < min_y) output_1.y = min_y;
1434 	if (output_1.y > max_y) output_1.y = max_y;
1435 	// Note that it is not necessary to conform these points to the line since the corners of the box are on the line.
1436 	return output_1;
1437 }
1438 
CVMouseMovePointer(CharView * cv,GEvent * event)1439 int CVMouseMovePointer(CharView *cv, GEvent *event) {
1440     extern float arrowAmount;
1441     int needsupdate = false;
1442     int did_a_merge = false;
1443     int touch_control_points = false;
1444 
1445 
1446     /* if we haven't moved from the original location (ever) then this is a noop */
1447     if ( !cv->p.rubberbanding && !cv->recentchange &&
1448 	    RealNear(cv->info.x,cv->p.cx) && RealNear(cv->info.y,cv->p.cy) )
1449 return( false );
1450 
1451     /* This can happen if they depress on a control point, move it, then use */
1452     /*  the arrow keys to move the point itself, and then try to move the cp */
1453     /*  again (mouse still depressed) */
1454     if (( cv->p.nextcp || cv->p.prevcp ) && cv->p.sp==NULL )
1455 	cv->p.nextcp = cv->p.prevcp = false;
1456 
1457     /* I used to have special cases for moving width lines, but that's now */
1458     /*  done by move selection */
1459     if ( cv->expandedge!=ee_none && !cv->widthsel && !cv->vwidthsel && !cv->lbearingsel
1460 	 && cv->nearcaret==-1 && !cv->icsel && !cv->tah_sel )
1461     {
1462 	if( !cv->changedActiveGlyph )
1463 	{
1464 	    needsupdate = CVExpandEdge(cv);
1465 	}
1466     }
1467     else if ( cv->nearcaret!=-1 && cv->lcarets!=NULL ) {
1468 	if ( cv->info.x!=cv->last_c.x ) {
1469 	    if ( !cv->recentchange ) SCPreserveLayer(cv->b.sc,CVLayer((CharViewBase *) cv),2);
1470 	    cv->lcarets->u.lcaret.carets[cv->nearcaret] += cv->info.x-cv->last_c.x;
1471 	    if ( cv->lcarets->u.lcaret.carets[cv->nearcaret]<0 )
1472 		cv->lcarets->u.lcaret.carets[cv->nearcaret] = 0;
1473 	    needsupdate = true;
1474 	    CVSetCharChanged(cv,true);
1475 	}
1476     } else if ( !cv->p.anysel ) {
1477 	if ( !cv->p.rubberbanding ) {
1478 	    cv->p.ex = cv->p.cx;
1479 	    cv->p.ey = cv->p.cy;
1480 	}
1481 	needsupdate = CVRectSelect(cv,cv->info.x,cv->info.y);
1482 	/* printf("moving2 cx:%g cy:%g\n", cv->p.cx, cv->p.cy ); */
1483 	cv->p.ex = cv->info.x;
1484 	cv->p.ey = cv->info.y;
1485 	cv->p.rubberbanding = true;
1486     }
1487     else if ( cv->p.nextcp )
1488     {
1489 	if ( !cv->recentchange )
1490 	    CVPreserveState(&cv->b);
1491 
1492 	FE_adjustBCPByDeltaData d;
1493 	memset( &d, 0, sizeof(FE_adjustBCPByDeltaData));
1494 	d.cv = cv;
1495 	d.dx = (cv->info.x - cv->p.sp->nextcp.x) * arrowAmount;
1496 	d.dy = (cv->info.y - cv->p.sp->nextcp.y) * arrowAmount;
1497 	visitSelectedControlPointsVisitor func = FE_adjustBCPByDelta;
1498 	/* printf("move sp:%p ncp:%p \n", */
1499 	/*        cv->p.sp, &(cv->p.sp->nextcp)  ); */
1500 	/* printf("move me.x:%f me.y:%f\n", cv->p.sp->me.x, cv->p.sp->me.y ); */
1501 	/* printf("move ncp.x:%f ncp.y:%f ix:%f iy:%f\n", */
1502 	/*        cv->p.sp->nextcp.x, cv->p.sp->nextcp.y, */
1503 	/*        cv->info.x, cv->info.y ); */
1504 	/* printf("move dx:%f dy:%f\n",  d.dx, d.dy ); */
1505 	/* printf("move dx:%f \n", cv->info.x - cv->p.sp->nextcp.x ); */
1506 	if( cv->activeModifierAlt )
1507 	    func = FE_adjustBCPByDeltaWhilePreservingBCPAngle;
1508 
1509 	CVFindAndVisitSelectedControlPoints( cv, false, func, &d );
1510 	CPUpdateInfo(cv,event);
1511 	needsupdate = true;
1512     }
1513     else if ( cv->p.prevcp )
1514     {
1515 	if ( !cv->recentchange )
1516 	    CVPreserveState(&cv->b);
1517 
1518 	FE_adjustBCPByDeltaData d;
1519 	memset( &d, 0, sizeof(FE_adjustBCPByDeltaData));
1520 	d.cv = cv;
1521 	d.dx = (cv->info.x - cv->p.sp->prevcp.x) * arrowAmount;
1522 	d.dy = (cv->info.y - cv->p.sp->prevcp.y) * arrowAmount;
1523 	visitSelectedControlPointsVisitor func = FE_adjustBCPByDelta;
1524 
1525 	if( cv->activeModifierAlt )
1526 	    func = FE_adjustBCPByDeltaWhilePreservingBCPAngle;
1527 
1528 	CVFindAndVisitSelectedControlPoints( cv, false, func, &d );
1529 	CPUpdateInfo(cv,event);
1530 	needsupdate = true;
1531     }
1532     else if ( cv->p.spline
1533 		&& !cv->p.splineAdjacentPointsSelected
1534 		&& (!cv->b.sc->inspiro || !hasspiro()))
1535     {
1536 	if ( !cv->recentchange )
1537 	    CVPreserveState(&cv->b);
1538 
1539 	CVAdjustSpline(cv);
1540 	CVSetCharChanged(cv,true);
1541 	needsupdate = true;
1542 	touch_control_points = true;
1543     }
1544     else
1545     {
1546 	if ( !cv->recentchange )
1547 	    CVPreserveState(&cv->b);
1548 
1549 	BasePoint tmpp1;
1550 	BasePoint tmpp2;
1551 	tmpp1.x = cv->info.x;
1552 	tmpp1.y = cv->info.y;
1553 	tmpp2.x = cv->info.x;
1554 	tmpp2.y = cv->info.y;
1555 	BasePoint cachecp1 = (cv->p.sp ? cv->p.sp->prevcp : (BasePoint){0, 0});
1556 	BasePoint cachecp2 = (cv->p.sp ? cv->p.sp->nextcp : (BasePoint){0, 0});
1557 	double xadj = cv->info.x-cv->last_c.x;
1558 	double yadj = cv->info.y-cv->last_c.y;
1559 	touch_control_points = true;
1560 	// The modifier is wrong.
1561 	if (cv->p.anysel && cv->p.sp && event->u.mouse.state & ksm_control) {
1562 		// Identify the individual point clicked. Find its control points. Move the selected point on a line between those control points.
1563 		tmpp1 = nearest_point_on_line_segment((BasePoint){cv->p.sp->prevcp.x,cv->p.sp->prevcp.y}, \
1564 			(BasePoint){cv->p.sp->nextcp.x,cv->p.sp->nextcp.y}, (BasePoint){cv->info.x, cv->info.y});
1565 		// We also need to rebase the original point onto that line segment so that the movement is exactly along the line even if the original click is not.
1566 		tmpp2 = nearest_point_on_line_segment((BasePoint){cv->p.sp->prevcp.x,cv->p.sp->prevcp.y}, \
1567 			(BasePoint){cv->p.sp->nextcp.x,cv->p.sp->nextcp.y}, (BasePoint){cv->last_c.x, cv->last_c.y});
1568 		xadj = tmpp1.x-tmpp2.x;
1569 		yadj = tmpp1.y-tmpp2.y;
1570 		touch_control_points = false; // We will need to move the control points back (but only for the point dragged).
1571 	}
1572 
1573 	did_a_merge = CVMoveSelection(cv,
1574 		xadj,yadj,
1575 		event->u.mouse.state);
1576 	// Rather than create a new set of functions for moving points without their control points, we instead just restore them if we did not want them moved.
1577 	if (cv->p.sp && touch_control_points == false) {
1578 		cv->p.sp->prevcp = cachecp1;
1579 		cv->p.sp->nextcp = cachecp2;
1580 		touch_control_points = true;
1581     		AdjustControls(cv->p.sp);
1582 	}
1583 	needsupdate = true;
1584 
1585     }
1586 
1587 
1588     if ( needsupdate )
1589     {
1590 	SCUpdateAll(cv->b.sc);
1591 	CVGridHandlePossibleFitChar( cv );
1592     }
1593 
1594     if ( touch_control_points )
1595     {
1596 	// We should really only need to visit the Adjacent CP
1597 	// visiting all is a hammer left below in case it might be needed.
1598 	CVVisitAdjacentToSelectedControlPoints( cv, false,
1599 						touchControlPointsVisitor,
1600 						(void*)(intptr_t)cv->b.layerheads[cv->b.drawmode]->order2 );
1601 	/* CVVisitAllControlPoints( cv, false, */
1602 	/* 			 touchControlPointsVisitor, */
1603 	/* 			 (void*)cv->b.layerheads[cv->b.drawmode]->order2 ); */
1604 
1605 	GDrawRequestExpose(cv->v,NULL,false);
1606     } else if (!needsupdate) {
1607         // Needed to update the rubber rect
1608         GDrawRequestExpose(cv->v,NULL,false);
1609     }
1610 
1611     cv->last_c.x = cv->info.x; cv->last_c.y = cv->info.y;
1612 return( did_a_merge );
1613 }
1614 
1615 
1616 
1617 
CVMouseUpPointer(CharView * cv)1618 void CVMouseUpPointer(CharView *cv ) {
1619     static char *buts[3];
1620     buts[0] = _("_Yes");
1621     buts[1] = _("_No");
1622     buts[2] = NULL;
1623 
1624     if ( cv->widthsel )
1625     {
1626 	if ( cv->b.sc->width<0 && cv->oldwidth>=0 ) {
1627 	    if ( gwwv_ask(_("Negative Width"), (const char **) buts, 0, 1, _("Negative character widths are not allowed in TrueType.\nDo you really want a negative width?") )==1 )
1628 		cv->b.sc->width = cv->oldwidth;
1629 	}
1630 	SCSynchronizeWidth(cv->b.sc,cv->b.sc->width,cv->oldwidth,NULL);
1631 	cv->expandedge = ee_none;
1632 	GDrawSetCursor(cv->v,ct_mypointer);
1633     }
1634     if ( cv->lbearingsel )
1635     {
1636 //	printf("oldlbearing:%f\n", cv->oldlbearing );
1637 	cv->expandedge = ee_none;
1638 	GDrawSetCursor(cv->v,ct_mypointer);
1639     }
1640     if ( cv->vwidthsel )
1641     {
1642 	if ( cv->b.sc->vwidth<0 && cv->oldvwidth>=0 ) {
1643 	    if ( gwwv_ask(_("Negative Width"), (const char **) buts, 0, 1, _("Negative character widths are not allowed in TrueType.\nDo you really want a negative width?") )==1 )
1644 		cv->b.sc->vwidth = cv->oldvwidth;
1645 	}
1646 	cv->expandedge = ee_none;
1647 	GDrawSetCursor(cv->v,ct_mypointer);
1648     }
1649     if ( cv->nearcaret!=-1 && cv->lcarets!=NULL )
1650     {
1651 	cv->nearcaret = -1;
1652 	cv->expandedge = ee_none;
1653 	cv->lcarets = NULL;
1654 	GDrawSetCursor(cv->v,ct_mypointer);
1655     }
1656     if( cv->changedActiveGlyph )
1657     {
1658 	cv->changedActiveGlyph = 0;
1659     }
1660     else
1661     {
1662 	if ( cv->expandedge!=ee_none )
1663 	{
1664 	    CVUndoCleanup(cv);
1665 	    cv->expandedge = ee_none;
1666 	    GDrawSetCursor(cv->v,ct_mypointer);
1667 	}
1668 	else if ( CVAllSelected(cv) && cv->b.drawmode==dm_fore && cv->p.spline==NULL
1669 		  && !cv->p.prevcp && !cv->p.nextcp && cv->info.y==cv->p.cy )
1670 	{
1671 	    SCUndoSetLBearingChange(cv->b.sc,(int) rint(cv->info.x-cv->p.cx));
1672 	    SCSynchronizeLBearing(cv->b.sc,cv->info.x-cv->p.cx,CVLayer((CharViewBase *) cv));
1673 	}
1674     }
1675     GDrawRequestExpose(cv->v, NULL, false);
1676     CPEndInfo(cv);
1677 }
1678 
1679 /* ************************************************************************** */
1680 /*  ************************** Select Point Dialog *************************  */
1681 /* ************************************************************************** */
1682 
SpiroClosest(BasePoint * base,spiro_cp * sp1,spiro_cp * sp2)1683 static spiro_cp *SpiroClosest(BasePoint *base,spiro_cp *sp1, spiro_cp *sp2) {
1684     double dx1, dy1, dx2, dy2;
1685     if ( sp1==NULL )
1686 return( sp2 );
1687     if ( sp2==NULL )
1688 return( sp1 );
1689     if ( (dx1 = sp1->x-base->x)<0 ) dx1 = -dx1;
1690     if ( (dy1 = sp1->y-base->y)<0 ) dy1 = -dy1;
1691     if ( (dx2 = sp2->x-base->x)<0 ) dx2 = -dx2;
1692     if ( (dy2 = sp2->y-base->y)<0 ) dy2 = -dy2;
1693     if ( dx1+dy1 < dx2+dy2 )
1694 return( sp1 );
1695     else
1696 return( sp2 );
1697 }
1698 
Closest(BasePoint * base,SplinePoint * sp1,SplinePoint * sp2)1699 static SplinePoint *Closest(BasePoint *base,SplinePoint *sp1, SplinePoint *sp2) {
1700     double dx1, dy1, dx2, dy2;
1701     if ( sp1==NULL )
1702 return( sp2 );
1703     if ( sp2==NULL )
1704 return( sp1 );
1705     if ( (dx1 = sp1->me.x-base->x)<0 ) dx1 = -dx1;
1706     if ( (dy1 = sp1->me.y-base->y)<0 ) dy1 = -dy1;
1707     if ( (dx2 = sp2->me.x-base->x)<0 ) dx2 = -dx2;
1708     if ( (dy2 = sp2->me.y-base->y)<0 ) dy2 = -dy2;
1709     if ( dx1+dy1 < dx2+dy2 )
1710 return( sp1 );
1711     else
1712 return( sp2 );
1713 }
1714 
SelectPointsWithin(CharView * cv,BasePoint * base,double fuzz,BasePoint * bounds)1715 static int SelectPointsWithin(CharView *cv, BasePoint *base, double fuzz, BasePoint *bounds) {
1716     SplineSet *ss;
1717     SplinePoint *sp;
1718     SplinePoint *any = NULL;
1719     int i;
1720     spiro_cp *anycp = NULL;
1721 
1722     CVClearSel(cv);
1723     if ( cv->b.sc->inspiro && hasspiro()) {
1724 	for ( ss= cv->b.layerheads[cv->b.drawmode]->splines; ss!=NULL; ss=ss->next ) {
1725 	    for ( i=0; i<ss->spiro_cnt-1; ++i ) {
1726 		spiro_cp *cp = &ss->spiros[i];
1727 		if ( bounds!=NULL ) {
1728 		    if ( cp->x >= base->x && cp->x <= base->x+bounds->x &&
1729 			    cp->y >= base->y && cp->y <= base->y+bounds->y ) {
1730 			SPIRO_SELECT(cp);
1731 			anycp = cp;
1732 		    }
1733 		} else if ( fuzz>0 ) {
1734 		    if ( RealWithin(cp->x,base->x,fuzz) && RealWithin(cp->y,base->y,fuzz)) {
1735 			SPIRO_SELECT(cp);
1736 			anycp = SpiroClosest(base,anycp,cp);
1737 		    }
1738 		} else {
1739 		    if ( RealNear(cp->x,base->x) && RealNear(cp->y,base->y)) {
1740 			SPIRO_SELECT(cp);
1741 			anycp = SpiroClosest(base,anycp,cp);
1742       goto cpdone;
1743 		    }
1744 		}
1745 	    }
1746 	}
1747       cpdone:
1748 	if ( any==NULL ) {
1749 	    CVShowPoint(cv,base);
1750 	    GDrawBeep(NULL);
1751 	} else {
1752 	    BasePoint here;
1753 	    here.x = anycp->x;
1754 	    here.y = anycp->y;
1755 	    CVShowPoint(cv,&here);
1756 	}
1757 	SCUpdateAll(cv->b.sc);
1758 return( anycp!=NULL );
1759     } else {
1760 	for ( ss= cv->b.layerheads[cv->b.drawmode]->splines; ss!=NULL; ss=ss->next ) {
1761 	    for ( sp=ss->first; ; ) {
1762 		if ( bounds!=NULL ) {
1763 		    if ( sp->me.x >= base->x && sp->me.x <= base->x+bounds->x &&
1764 			    sp->me.y >= base->y && sp->me.y <= base->y+bounds->y ) {
1765 			sp->selected = true;
1766 			any = sp;
1767 		    }
1768 		} else if ( fuzz>0 ) {
1769 		    if ( RealWithin(sp->me.x,base->x,fuzz) && RealWithin(sp->me.y,base->y,fuzz)) {
1770 			sp->selected = true;
1771 			any = Closest(base,any,sp);
1772 		    }
1773 		} else {
1774 		    if ( RealNear(sp->me.x,base->x) && RealNear(sp->me.y,base->y)) {
1775 			sp->selected = true;
1776 			any = Closest(base,any,sp);
1777       goto done;
1778 		    }
1779 		}
1780 		if ( sp->next==NULL )
1781 	    break;
1782 		sp = sp->next->to;
1783 		if ( sp==ss->first )
1784 	    break;
1785 	    }
1786 	}
1787       done:
1788 	if ( any==NULL ) {
1789 	    CVShowPoint(cv,base);
1790 	    GDrawBeep(NULL);
1791 	} else
1792 	    CVShowPoint(cv,&any->me);
1793 	SCUpdateAll(cv->b.sc);
1794 return( any!=NULL );
1795     }
1796 }
1797 
1798 #define CID_X	1001
1799 #define CID_Y	1002
1800 #define CID_Width	1003
1801 #define CID_Height	1004
1802 #define CID_Fuzz	1005
1803 #define CID_Exact	1006
1804 #define CID_Within	1007
1805 #define CID_Fuzzy	1008
1806 
1807 struct selpt {
1808     int done;
1809     CharView *cv;
1810     GWindow gw;
1811 };
1812 
SPA_DoCancel(struct selpt * pa)1813 static void SPA_DoCancel(struct selpt *pa) {
1814     pa->done = true;
1815 }
1816 
SPA_OK(GGadget * g,GEvent * e)1817 static int SPA_OK(GGadget *g, GEvent *e) {
1818     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1819 	GWindow gw = GGadgetGetWindow(g);
1820 	struct selpt *pa = GDrawGetUserData(gw);
1821 	CharView *cv = pa->cv;
1822 	BasePoint base, bounds;
1823 	double fuzz;
1824 	int err = false, ret;
1825 
1826 /* GT: X is a coordinate */
1827 	base.x = GetReal8(gw,CID_X,_("X"),&err);
1828 /* GT: Y is a coordinate */
1829 	base.y = GetReal8(gw,CID_Y,_("Y"),&err);
1830 	if ( err )
1831 return( true );
1832 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Exact)) )
1833 	    ret = SelectPointsWithin(cv, &base, 0, NULL );
1834 	else if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Fuzzy)) ) {
1835 	    fuzz = GetReal8(gw,CID_Fuzz,_("Search Radius"),&err);
1836 	    if ( err )
1837 return( true );
1838 	    ret = SelectPointsWithin(cv, &base, fuzz, NULL );
1839 	} else {
1840 	    bounds.x = GetReal8(gw,CID_Width,_("Width"),&err);
1841 	    bounds.y = GetReal8(gw,CID_Height,_("Height"),&err);
1842 	    if ( err )
1843 return( true );
1844 	    if ( bounds.x<0 ) {
1845 		base.x += bounds.x;
1846 		bounds.x = -bounds.x;
1847 	    }
1848 	    if ( bounds.y<0 ) {
1849 		base.y += bounds.y;
1850 		bounds.y = -bounds.y;
1851 	    }
1852 	    ret = SelectPointsWithin(cv, &base, 0, &bounds );
1853 	}
1854 	pa->done = ret;
1855     }
1856 return( true );
1857 }
1858 
SPA_Cancel(GGadget * g,GEvent * e)1859 static int SPA_Cancel(GGadget *g, GEvent *e) {
1860     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1861 	struct selpt *pa = GDrawGetUserData(GGadgetGetWindow(g));
1862 	SPA_DoCancel(pa);
1863     }
1864 return( true );
1865 }
1866 
SPA_Radius(GGadget * g,GEvent * e)1867 static int SPA_Radius(GGadget *g, GEvent *e) {
1868     if ( e->type==et_controlevent ) {
1869 	GWindow gw = GGadgetGetWindow(g);
1870 	GGadgetSetChecked(GWidgetGetControl(gw,CID_Fuzzy),true);
1871     }
1872 return( true );
1873 }
1874 
SPA_Rect(GGadget * g,GEvent * e)1875 static int SPA_Rect(GGadget *g, GEvent *e) {
1876     if ( e->type==et_controlevent ) {
1877 	GWindow gw = GGadgetGetWindow(g);
1878 	GGadgetSetChecked(GWidgetGetControl(gw,CID_Within),true);
1879     }
1880 return( true );
1881 }
1882 
spa_e_h(GWindow gw,GEvent * event)1883 static int spa_e_h(GWindow gw, GEvent *event) {
1884     if ( event->type==et_close ) {
1885 	SPA_DoCancel( GDrawGetUserData(gw));
1886     } else if ( event->type == et_char ) {
1887 return( false );
1888     }
1889 return( true );
1890 }
1891 
CVSelectPointAt(CharView * cv)1892 void CVSelectPointAt(CharView *cv) {
1893     GRect pos;
1894     GWindow gw;
1895     GWindowAttrs wattrs;
1896     GGadgetCreateData gcd[18];
1897     GTextInfo label[18];
1898     static struct selpt pa;
1899     int k;
1900     int first = false;
1901 
1902     pa.done = false;
1903     pa.cv = cv;
1904 
1905     if ( pa.gw==NULL ) {
1906 	memset(&wattrs,0,sizeof(wattrs));
1907 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
1908 	wattrs.event_masks = ~(1<<et_charup);
1909 	wattrs.restrict_input_to_me = 1;
1910 	wattrs.undercursor = 1;
1911 	wattrs.cursor = ct_pointer;
1912 	wattrs.utf8_window_title = _("Select Point(s) at...");
1913 	wattrs.is_dlg = true;
1914 	pos.x = pos.y = 0;
1915 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,190));
1916 	pos.height = GDrawPointsToPixels(NULL,175);
1917 	pa.gw = gw = GDrawCreateTopWindow(NULL,&pos,spa_e_h,&pa,&wattrs);
1918 
1919 	memset(&label,0,sizeof(label));
1920 	memset(&gcd,0,sizeof(gcd));
1921 
1922 	k = 0;
1923 	label[k].text = (unichar_t *) _("_X:");
1924 	label[k].text_is_1byte = true;
1925 	label[k].text_in_resource = true;
1926 	gcd[k].gd.label = &label[k];
1927 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 8+4;
1928 	gcd[k].gd.flags = gg_enabled|gg_visible;
1929 	gcd[k++].creator = GLabelCreate;
1930 
1931 	gcd[k].gd.pos.x = 30; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;  gcd[k].gd.pos.width = 40;
1932 	gcd[k].gd.flags = gg_enabled|gg_visible;
1933 	gcd[k].gd.cid = CID_X;
1934 	gcd[k++].creator = GTextFieldCreate;
1935 
1936 	label[k].text = (unichar_t *) _("_Y:");
1937 	label[k].text_is_1byte = true;
1938 	label[k].text_in_resource = true;
1939 	gcd[k].gd.label = &label[k];
1940 	gcd[k].gd.pos.x = 80; gcd[k].gd.pos.y = gcd[k-2].gd.pos.y;
1941 	gcd[k].gd.flags = gg_enabled|gg_visible;
1942 	gcd[k++].creator = GLabelCreate;
1943 
1944 	gcd[k].gd.pos.x = 100; gcd[k].gd.pos.y = gcd[k-2].gd.pos.y;  gcd[k].gd.pos.width = 40;
1945 	gcd[k].gd.flags = gg_enabled|gg_visible;
1946 	gcd[k].gd.cid = CID_Y;
1947 	gcd[k++].creator = GTextFieldCreate;
1948 
1949 	label[k].text = (unichar_t *) _("_Exact");
1950 	label[k].text_is_1byte = true;
1951 	label[k].text_in_resource = true;
1952 	gcd[k].gd.label = &label[k];
1953 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+23;
1954 	gcd[k].gd.flags = gg_enabled|gg_visible|gg_cb_on;
1955 	gcd[k].gd.cid = CID_Exact;
1956 	gcd[k++].creator = GRadioCreate;
1957 
1958 	label[k].text = (unichar_t *) _("_Around");
1959 	label[k].text_is_1byte = true;
1960 	label[k].text_in_resource = true;
1961 	gcd[k].gd.label = &label[k];
1962 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+13;
1963 	gcd[k].gd.flags = gg_enabled|gg_visible;
1964 	gcd[k].gd.cid = CID_Fuzzy;
1965 	gcd[k++].creator = GRadioCreate;
1966 
1967 	label[k].text = (unichar_t *) _("W_ithin Rectangle");
1968 	label[k].text_is_1byte = true;
1969 	label[k].text_in_resource = true;
1970 	gcd[k].gd.label = &label[k];
1971 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16+24;
1972 	gcd[k].gd.flags = gg_enabled|gg_visible;
1973 	gcd[k].gd.cid = CID_Within;
1974 	gcd[k++].creator = GRadioCreate;
1975 
1976 	label[k].text = (unichar_t *) _("_Radius:");
1977 	label[k].text_is_1byte = true;
1978 	label[k].text_in_resource = true;
1979 	gcd[k].gd.label = &label[k];
1980 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-2].gd.pos.y+17+4;
1981 	gcd[k].gd.flags = gg_enabled|gg_visible;
1982 	gcd[k++].creator = GLabelCreate;
1983 
1984 	label[k].text = (unichar_t *) _("3");
1985 	label[k].text_is_1byte = true;
1986 	label[k].text_in_resource = true;
1987 	gcd[k].gd.label = &label[k];
1988 	gcd[k].gd.pos.x = 52; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;  gcd[k].gd.pos.width = 40;
1989 	gcd[k].gd.flags = gg_enabled|gg_visible;
1990 	gcd[k].gd.cid = CID_Fuzz;
1991 	gcd[k].gd.handle_controlevent = SPA_Radius;
1992 	gcd[k++].creator = GTextFieldCreate;
1993 
1994 	label[k].text = (unichar_t *) _("_Width:");
1995 	label[k].text_is_1byte = true;
1996 	label[k].text_in_resource = true;
1997 	gcd[k].gd.label = &label[k];
1998 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-3].gd.pos.y+17+4;
1999 	gcd[k].gd.flags = gg_enabled|gg_visible;
2000 	gcd[k++].creator = GLabelCreate;
2001 
2002 	gcd[k].gd.pos.x = 52; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;  gcd[k].gd.pos.width = 40;
2003 	gcd[k].gd.flags = gg_enabled|gg_visible;
2004 	gcd[k].gd.cid = CID_Width;
2005 	gcd[k].gd.handle_controlevent = SPA_Rect;
2006 	gcd[k++].creator = GTextFieldCreate;
2007 
2008 	label[k].text = (unichar_t *) _("_Height:");
2009 	label[k].text_is_1byte = true;
2010 	label[k].text_in_resource = true;
2011 	gcd[k].gd.label = &label[k];
2012 	gcd[k].gd.pos.x = 100; gcd[k].gd.pos.y = gcd[k-2].gd.pos.y;
2013 	gcd[k].gd.flags = gg_enabled|gg_visible;
2014 	gcd[k++].creator = GLabelCreate;
2015 
2016 	gcd[k].gd.pos.x = 138; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;  gcd[k].gd.pos.width = 40;
2017 	gcd[k].gd.flags = gg_enabled|gg_visible;
2018 	gcd[k].gd.cid = CID_Height;
2019 	gcd[k].gd.handle_controlevent = SPA_Rect;
2020 	gcd[k++].creator = GTextFieldCreate;
2021 
2022 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+28;
2023 	gcd[k].gd.pos.width = GDrawPixelsToPoints(gw,pos.width)-10;
2024 	gcd[k].gd.flags = gg_enabled|gg_visible;
2025 	gcd[k++].creator = GLineCreate;
2026 
2027 	gcd[k].gd.pos.x = 20-3; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+8;
2028 	gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
2029 	gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
2030 	label[k].text = (unichar_t *) _("_OK");
2031 	label[k].text_is_1byte = true;
2032 	label[k].text_in_resource = true;
2033 	gcd[k].gd.label = &label[k];
2034 	gcd[k].gd.handle_controlevent = SPA_OK;
2035 	gcd[k++].creator = GButtonCreate;
2036 
2037 	gcd[k].gd.pos.x = -20; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
2038 	gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
2039 	gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
2040 	label[k].text = (unichar_t *) _("_Cancel");
2041 	label[k].text_is_1byte = true;
2042 	label[k].text_in_resource = true;
2043 	gcd[k].gd.label = &label[k];
2044 	gcd[k].gd.handle_controlevent = SPA_Cancel;
2045 	gcd[k++].creator = GButtonCreate;
2046 
2047 	gcd[k].gd.pos.x = 2; gcd[k].gd.pos.y = 2;
2048 	gcd[k].gd.pos.width = pos.width-4; gcd[k].gd.pos.height = pos.height-4;
2049 	gcd[k].gd.flags = gg_enabled|gg_visible|gg_pos_in_pixels;
2050 	gcd[k++].creator = GGroupCreate;
2051 
2052 	GGadgetsCreate(gw,gcd);
2053 	first = true;
2054     } else {
2055 	GTextFieldSelect(GWidgetGetControl(pa.gw,CID_X),0,-1);
2056     }
2057     GWidgetIndicateFocusGadget(GWidgetGetControl(pa.gw,CID_X));
2058     GDrawSetVisible(pa.gw,true);
2059     GDrawProcessOneEvent(NULL);
2060     if ( first )
2061 	GGadgetSetChecked(GWidgetGetControl(gw,CID_Exact),true);
2062     while ( !pa.done )
2063 	GDrawProcessOneEvent(NULL);
2064     GDrawSetVisible(pa.gw,false);
2065 
2066 }
2067