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