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 "fvfonts.h"
33 #include "gkeysym.h"
34 #include "search.h"
35 #include "splineutil.h"
36 #include "ustring.h"
37 #include "utype.h"
38 
39 #include <math.h>
40 
41 static SearchView *searcher=NULL;
42 
43 #define CID_Allow	1000
44 #define CID_Flipping	1001
45 #define CID_Scaling	1002
46 #define CID_Rotating	1003
47 #define CID_Selected	1004
48 #define CID_Find	1005
49 #define CID_FindAll	1006
50 #define CID_Replace	1007
51 #define CID_ReplaceAll	1008
52 #define CID_Cancel	1009
53 #define CID_TopBox	1010
54 #define CID_Fuzzy	1011
55 #define CID_Endpoints	1012
56 
57 static double old_fudge = .001;
58 
SVSelectSC(SearchView * sv)59 static void SVSelectSC(SearchView *sv) {
60     SplineChar *sc = sv->sd.curchar;
61     SplinePointList *spl;
62     SplinePoint *sp;
63     RefChar *rf;
64     int i;
65     int layer = sv->sd.fv->active_layer;
66 
67     /* Deselect all */;
68     for ( spl = sc->layers[layer].splines; spl!=NULL; spl = spl->next ) {
69 	for ( sp=spl->first ;; ) {
70 	    sp->selected = false;
71 	    if ( sp->next == NULL )
72 	break;
73 	    sp = sp->next->to;
74 	    if ( sp==spl->first )
75 	break;
76 	}
77     }
78     for ( rf=sc->layers[layer].refs; rf!=NULL; rf = rf->next )
79 	if ( rf->selected ) rf->selected = false;
80 
81     if ( sv->sd.subpatternsearch ) {
82 	spl = sv->sd.matched_spl;
83 	for ( sp = sv->sd.matched_sp; ; ) {
84 	    sp->selected = true;
85 	    if ( sp->next == NULL || sv->sd.last_sp==NULL || sp==sv->sd.last_sp )
86 	break;
87 	    sp = sp->next->to;
88 	    /* Ok to wrap back to first */
89 	}
90     } else {
91 	for ( rf=sc->layers[layer].refs, i=0; rf!=NULL; rf=rf->next, ++i )
92 	    if ( sv->sd.matched_refs&(1<<i) )
93 		rf->selected = true;
94 	for ( spl = sc->layers[layer].splines,i=0; spl!=NULL; spl = spl->next, ++i ) {
95 	    if ( sv->sd.matched_ss&(1<<i) ) {
96 		for ( sp=spl->first ;; ) {
97 		    sp->selected = true;
98 		    if ( sp->next == NULL )
99 		break;
100 		    sp = sp->next->to;
101 		    if ( sp==spl->first )
102 		break;
103 		}
104 	    }
105 	}
106     }
107     SCUpdateAll(sc);
108     sc->changed_since_search = false;
109 }
110 
DoFindOne(SearchView * sv,int startafter)111 static int DoFindOne(SearchView *sv,int startafter) {
112     int i, gid;
113     SplineChar *startcur = sv->sd.curchar;
114 
115     /* It is possible that some idiot deleted the current character since */
116     /*  the last search... do some mild checks */
117     if ( sv->sd.curchar!=NULL &&
118 	    sv->sd.curchar->parent == sv->sd.fv->sf &&
119 	    sv->sd.curchar->orig_pos>=0 && sv->sd.curchar->orig_pos<sv->sd.fv->sf->glyphcnt &&
120 	    sv->sd.curchar==sv->sd.fv->sf->glyphs[sv->sd.curchar->orig_pos] )
121 	/* Looks ok */;
122     else
123 	sv->sd.curchar=startcur=NULL;
124 
125     if ( !sv->sd.subpatternsearch ) startafter = false;
126 
127     if ( sv->showsfindnext && sv->sd.curchar!=NULL )
128 	i = sv->sd.fv->map->backmap[sv->sd.curchar->orig_pos]+1-startafter;
129     else {
130 	startafter = false;
131 	if ( !sv->sd.onlyselected )
132 	    i = 0;
133 	else {
134 	    for ( i=0; i<sv->sd.fv->map->enccount; ++i )
135 		if ( sv->sd.fv->selected[i] && (gid=sv->sd.fv->map->map[i])!=-1 &&
136 			sv->sd.fv->sf->glyphs[gid]!=NULL )
137 	    break;
138 	}
139     }
140 
141     for ( ; i<sv->sd.fv->map->enccount; ++i ) {
142 	if (( !sv->sd.onlyselected || sv->sd.fv->selected[i]) && (gid=sv->sd.fv->map->map[i])!=-1 &&
143 		sv->sd.fv->sf->glyphs[gid]!=NULL ) {
144 	    SCSplinePointsUntick(sv->sd.fv->sf->glyphs[gid],sv->sd.fv->active_layer);
145 	    if ( SearchChar(&sv->sd,gid,startafter) )
146     break;
147 	}
148 	startafter = false;
149     }
150     if ( i>=sv->sd.fv->map->enccount ) {
151 	ff_post_notice(_("Not Found"),sv->showsfindnext?_("The search pattern was not found again in the font %.100s"):_("The search pattern was not found in the font %.100s"),sv->sd.fv->sf->fontname);
152 	sv->sd.curchar = startcur;
153 	GGadgetSetTitle8(GWidgetGetControl(sv->gw,CID_Find),_("Find"));
154 	sv->showsfindnext = false;
155 return( false );
156     }
157     SVSelectSC(sv);
158     if ( sv->lastcv!=NULL && sv->lastcv->b.sc==startcur && sv->lastcv->b.fv== sv->sd.fv ) {
159 	CVChangeSC(sv->lastcv,sv->sd.curchar);
160 	GDrawSetVisible(sv->lastcv->gw,true);
161 	GDrawRaise(sv->lastcv->gw);
162     } else
163 	sv->lastcv = CharViewCreate(sv->sd.curchar,(FontView *) sv->sd.fv,-1);
164     GGadgetSetTitle8(GWidgetGetControl(sv->gw,CID_Find),_("Find Next"));
165     sv->showsfindnext = true;
166 return( true );
167 }
168 
DoFindAll(SearchView * sv)169 static void DoFindAll(SearchView *sv) {
170     int any;
171 
172     any = _DoFindAll(&sv->sd);
173     GDrawRequestExpose(((FontView *) (sv->sd.fv))->v,NULL,false);
174     if ( !any )
175 	ff_post_notice(_("Not Found"),_("The search pattern was not found in the font %.100s"),sv->sd.fv->sf->fontname);
176 }
177 
pathpointcnt(SplineSet * ss)178 static int pathpointcnt(SplineSet *ss) {
179     SplinePoint *sp;
180     int cnt;
181 
182     if ( ss==NULL )		/* No paths */
183 return( 0 );
184     if ( ss->next!=NULL )	/* more than one path */
185 return( -1 );
186     if ( ss->first->prev!=NULL )/* single path, but it is closed */
187 return( -2 );
188     for ( sp=ss->first, cnt=1; sp->next!=NULL; sp=sp->next->to, ++cnt );
189 return( cnt );
190 }
191 
SVParseDlg(SearchView * sv,int check_replace)192 static int SVParseDlg(SearchView *sv, int check_replace ) {
193     int err=false;
194     double fudge;
195 
196     fudge = GetReal8(sv->gw,CID_Fuzzy,_("Match Fuzziness:"),&err);
197     if ( err )
198 return( false );
199     old_fudge = fudge;
200 
201     sv->sd.tryreverse = true;
202     sv->sd.tryflips = GGadgetIsChecked(GWidgetGetControl(sv->gw,CID_Flipping));
203     sv->sd.tryscale = GGadgetIsChecked(GWidgetGetControl(sv->gw,CID_Scaling));
204     sv->sd.tryrotate = GGadgetIsChecked(GWidgetGetControl(sv->gw,CID_Rotating));
205     sv->sd.endpoints = GGadgetIsChecked(GWidgetGetControl(sv->gw,CID_Endpoints));
206     sv->sd.onlyselected = GGadgetIsChecked(GWidgetGetControl(sv->gw,CID_Selected));
207 
208     SVResetPaths(&sv->sd);
209     if ( pathpointcnt(sv->sd.path)==0 )
210 	ff_post_error(_("Bad search pattern"),_("Nothing to match."));
211     else if ( sv->sd.endpoints ) {
212 	if ( pathpointcnt(sv->sd.path)<3 ||
213 		(check_replace && pathpointcnt(sv->sd.replacepath)<3 && pathpointcnt(sv->sd.replacepath)!=0 )) {
214 	    if ( pathpointcnt(sv->sd.path)<0 )
215 		ff_post_error(_("Bad search pattern"),_("When \"Endpoints specify minimum length and direction only\" is checked, the search pattern must be a single open contour."));
216 	    else if ( pathpointcnt(sv->sd.path)<3 )
217 		ff_post_error(_("Bad search pattern"),_("When \"Endpoints specify minimum length and direction only\" is checked, the search pattern must be a single open contour with at least 3 points on it (otherwise there is nothing to match)."));
218 	    else
219 		ff_post_error(_("Bad replace pattern"),_("When \"Endpoints specify minimum length and direction only\" is checked, the replace pattern must be a single open contour with at least 3 points on it."));
220 return( false );
221 	}
222     } else if ( pathpointcnt(sv->sd.path)>0 ) {
223 	/* It might make sense not to do a sub-pattern search if the */
224 	/*  replace pattern is closed... but that's kind of weird */
225 	if ( check_replace && pathpointcnt(sv->sd.replacepath)<0 ) {
226 	    ff_post_error(_("Bad replace pattern"),_("When the search path is a single open contour, the replace pattern must also be."));
227 return( false );
228 	}
229     }
230 
231     sv->sd.fudge = fudge;
232     sv->sd.fudge_percent = sv->sd.tryrotate ? .01 : .001;
233 return( true );
234 }
235 
SV_Find(GGadget * g,GEvent * e)236 static int SV_Find(GGadget *g, GEvent *e) {
237     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
238 	SearchView *sv = (SearchView *) ((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container;
239 	if ( !SVParseDlg(sv,false))
240 return( true );
241 	sv->sd.findall = sv->sd.replaceall = false;
242 	DoFindOne(sv,false);
243     }
244 return( true );
245 }
246 
SV_FindAll(GGadget * g,GEvent * e)247 static int SV_FindAll(GGadget *g, GEvent *e) {
248     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
249 	SearchView *sv = (SearchView *) ((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container;
250 	if ( !SVParseDlg(sv,false))
251 return( true );
252 	sv->sd.findall = true;
253 	sv->sd.replaceall = false;
254 	DoFindAll(sv);
255     }
256 return( true );
257 }
258 
SV_RplFind(GGadget * g,GEvent * e)259 static int SV_RplFind(GGadget *g, GEvent *e) {
260     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
261 	SearchView *sv = (SearchView *) ((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container;
262 	RefChar *rf;
263 	if ( !SVParseDlg(sv,true))
264 return( true );
265 	sv->sd.findall = sv->sd.replaceall = false;
266 	for ( rf=sv->sd.sc_rpl.layers[ly_fore].refs; rf!=NULL; rf = rf->next ) {
267 	    if ( SCDependsOnSC(rf->sc,sv->sd.curchar)) {
268 		ff_post_error(_("Self-referential glyph"),_("Attempt to make a glyph that refers to itself"));
269 return( true );
270 	    }
271 	}
272 	DoRpl(&sv->sd);
273 	DoFindOne(sv,sv->sd.subpatternsearch);
274     }
275 return( true );
276 }
277 
SV_RplAll(GGadget * g,GEvent * e)278 static int SV_RplAll(GGadget *g, GEvent *e) {
279     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
280 	SearchView *sv = (SearchView *) ((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container;
281 	if ( !SVParseDlg(sv,true))
282 return( true );
283 	sv->sd.findall = false;
284 	sv->sd.replaceall = true;
285 	DoFindAll(sv);
286     }
287 return( true );
288 }
289 
290 /* ************************************************************************** */
291 
SV_DoClose(struct cvcontainer * cvc)292 void SV_DoClose(struct cvcontainer *cvc) {
293     GDrawSetVisible(((SearchView *) cvc)->gw,false);
294 }
295 
SV_Cancel(GGadget * g,GEvent * e)296 static int SV_Cancel(GGadget *g, GEvent *e) {
297     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate )
298 	SV_DoClose(((CharViewBase *) GDrawGetUserData(GGadgetGetWindow(g)))->container);
299 return( true );
300 }
301 
SVResize(SearchView * sv,GEvent * event)302 static void SVResize(SearchView *sv, GEvent *event) {
303     int width, height;
304 
305     if ( !event->u.resize.sized )
306 return;
307 
308     GGadgetMove(GWidgetGetControl(sv->gw,CID_TopBox),4,4);
309     GGadgetResize(GWidgetGetControl(sv->gw,CID_TopBox),
310 	    event->u.resize.size.width-8,
311 	    event->u.resize.size.height-12);
312 
313     width = (event->u.resize.size.width-40)/2;
314     height = (event->u.resize.size.height-sv->cv_y-sv->button_height-8);
315     if ( width<70 || height<80 ) {
316 	if ( width<70 ) width = 70;
317 	width = 2*width+40;
318 	if ( height<80 ) height = 80;
319 	height += sv->cv_y+sv->button_height+8;
320 	GDrawResize(sv->gw,width,height);
321 return;
322     }
323     if ( width!=sv->cv_width || height!=sv->cv_height ) {
324 	GDrawResize(sv->cv_srch.gw,width,height);
325 	GDrawResize(sv->cv_rpl.gw,width,height);
326 	sv->cv_width = width; sv->cv_height = height;
327 	sv->rpl_x = 30+width;
328 	GDrawMove(sv->cv_rpl.gw,sv->rpl_x,sv->cv_y);
329     }
330 
331     GDrawSync(NULL);
332     GDrawProcessPendingEvents(NULL);
333     GDrawRequestExpose(sv->gw,NULL,false);
334 }
335 
SVMakeActive(SearchView * sv,CharView * cv)336 void SVMakeActive(SearchView *sv,CharView *cv) {
337     GRect r;
338     if ( sv==NULL )
339 return;
340     sv->cv_srch.inactive = sv->cv_rpl.inactive = true;
341     cv->inactive = false;
342     GDrawSetUserData(sv->gw,cv);
343     GDrawRequestExpose(sv->cv_srch.v,NULL,false);
344     GDrawRequestExpose(sv->cv_rpl.v,NULL,false);
345     GDrawGetSize(sv->gw,&r);
346     r.x = 0;
347     r.y = sv->mbh;
348     r.height = sv->fh+10;
349     GDrawRequestExpose(sv->gw,&r,false);
350 }
351 
SVChar(SearchView * sv,GEvent * event)352 static void SVChar(SearchView *sv, GEvent *event) {
353     if ( event->u.chr.keysym==GK_Tab || event->u.chr.keysym==GK_BackTab )
354 	SVMakeActive(sv,sv->cv_srch.inactive ? &sv->cv_srch : &sv->cv_rpl);
355     else
356 	CVChar(sv->cv_srch.inactive ? &sv->cv_rpl : &sv->cv_srch,event);
357 }
358 
SVDraw(SearchView * sv,GWindow pixmap,GEvent * event)359 static void SVDraw(SearchView *sv, GWindow pixmap, GEvent *event) {
360     GRect r;
361 
362     GDrawSetLineWidth(pixmap,0);
363     if ( sv->cv_srch.inactive )
364 	GDrawSetFont(pixmap,sv->plain);
365     else
366 	GDrawSetFont(pixmap,sv->bold);
367     GDrawDrawText8(pixmap,10,sv->mbh+5+sv->as,
368 	    _("Search Pattern:"),-1,0);
369     if ( sv->cv_rpl.inactive )
370 	GDrawSetFont(pixmap,sv->plain);
371     else
372 	GDrawSetFont(pixmap,sv->bold);
373     GDrawDrawText8(pixmap,sv->rpl_x,sv->mbh+5+sv->as,
374 	    _("Replace Pattern:"),-1,0);
375     r.x = 10-1; r.y=sv->cv_y-1;
376     r.width = sv->cv_width+1; r.height = sv->cv_height+1;
377     GDrawDrawRect(pixmap,&r,0);
378     r.x = sv->rpl_x-1;
379     GDrawDrawRect(pixmap,&r,0);
380 }
381 
SVCheck(SearchView * sv)382 static void SVCheck(SearchView *sv) {
383     int show = ( sv->sd.sc_srch.layers[ly_fore].splines!=NULL || sv->sd.sc_srch.layers[ly_fore].refs!=NULL );
384     int showrplall=show, showrpl;
385 
386     if ( sv->sd.sc_srch.changed_since_autosave && sv->showsfindnext ) {
387 	GGadgetSetTitle8(GWidgetGetControl(sv->gw,CID_Find),_("Find"));
388 	sv->showsfindnext = false;
389     }
390     if ( showrplall ) {
391 	if ( sv->sd.sc_srch.layers[ly_fore].splines!=NULL && sv->sd.sc_srch.layers[ly_fore].splines->next==NULL &&
392 		sv->sd.sc_srch.layers[ly_fore].splines->first->prev==NULL &&
393 		sv->sd.sc_rpl.layers[ly_fore].splines==NULL && sv->sd.sc_rpl.layers[ly_fore].refs==NULL )
394 	    showrplall = false;
395     }
396     showrpl = showrplall;
397     if ( !sv->showsfindnext || sv->sd.curchar==NULL || sv->sd.curchar->parent!=sv->sd.fv->sf ||
398 	    sv->sd.curchar->orig_pos<0 || sv->sd.curchar->orig_pos>=sv->sd.fv->sf->glyphcnt ||
399 	    sv->sd.curchar!=sv->sd.fv->sf->glyphs[sv->sd.curchar->orig_pos] ||
400 	    sv->sd.curchar->changed_since_search )
401 	showrpl = false;
402 
403     if ( sv->findenabled != show ) {
404 	GGadgetSetEnabled(GWidgetGetControl(sv->gw,CID_Find),show);
405 	GGadgetSetEnabled(GWidgetGetControl(sv->gw,CID_FindAll),show);
406 	sv->findenabled = show;
407     }
408     if ( sv->rplallenabled != showrplall ) {
409 	GGadgetSetEnabled(GWidgetGetControl(sv->gw,CID_ReplaceAll),showrplall);
410 	sv->rplallenabled = showrplall;
411     }
412     if ( sv->rplenabled != showrpl ) {
413 	GGadgetSetEnabled(GWidgetGetControl(sv->gw,CID_Replace),showrpl);
414 	sv->rplenabled = showrpl;
415     }
416 }
417 
SearchViewFree(SearchView * sv)418 static void SearchViewFree(SearchView *sv) {
419     SplinePointListsFree(sv->sd.sc_srch.layers[ly_fore].splines);
420     SplinePointListsFree(sv->sd.sc_rpl.layers[ly_fore].splines);
421     RefCharsFree(sv->sd.sc_srch.layers[ly_fore].refs);
422     RefCharsFree(sv->sd.sc_rpl.layers[ly_fore].refs);
423     free(sv);
424 }
425 
sv_e_h(GWindow gw,GEvent * event)426 static int sv_e_h(GWindow gw, GEvent *event) {
427     SearchView *sv = (SearchView *) ((CharViewBase *) GDrawGetUserData(gw))->container;
428 
429     switch ( event->type ) {
430       case et_expose:
431 	SVDraw(sv,gw,event);
432       break;
433       case et_resize:
434 	if ( event->u.resize.sized )
435 	    SVResize(sv,event);
436       break;
437       case et_char:
438 	SVChar(sv,event);
439       break;
440       case et_timer:
441 	SVCheck(sv);
442       break;
443       case et_close:
444 	SV_DoClose((struct cvcontainer *) sv);
445       break;
446       case et_create:
447       break;
448       case et_destroy:
449 	SearchViewFree(sv);
450       break;
451       case et_map:
452 	if ( event->u.map.is_visible )
453 	    CVPaletteActivate(sv->cv_srch.inactive?&sv->cv_rpl:&sv->cv_srch);
454 	else
455 	    CVPalettesHideIfMine(sv->cv_srch.inactive?&sv->cv_rpl:&sv->cv_srch);
456 	sv->isvisible = event->u.map.is_visible;
457       break;
458     }
459 return( true );
460 }
461 
SVSetTitle(SearchView * sv)462 static void SVSetTitle(SearchView *sv) {
463     char ubuf[150];
464     sprintf(ubuf,_("Find in %.100s"),sv->sd.fv->sf->fontname);
465     GDrawSetWindowTitles8(sv->gw,ubuf,_("Find"));
466 }
467 
SVAttachFV(FontView * fv,int ask_if_difficult)468 int SVAttachFV(FontView *fv,int ask_if_difficult) {
469     int i, doit, pos, any=0, gid;
470     RefChar *r, *rnext, *rprev;
471 
472     if ( searcher==NULL )
473 return( false );
474 
475     if ( searcher->sd.fv==(FontViewBase *) fv )
476 return( true );
477     if ( searcher->sd.fv!=NULL && searcher->sd.fv->sf==fv->b.sf ) {
478 	((FontView *) searcher->sd.fv)->sv = NULL;
479 	fv->sv = searcher;
480 	searcher->sd.fv = (FontViewBase *) fv;
481 	SVSetTitle(searcher);
482 	searcher->sd.curchar = NULL;
483 return( true );
484     }
485 
486     if ( searcher->dummy_sf.layers[ly_fore].order2 != fv->b.sf->layers[ly_fore].order2 ) {
487 	SCClearContents(&searcher->sd.sc_srch,ly_fore);
488 	SCClearContents(&searcher->sd.sc_rpl,ly_fore);
489 	for ( i=0; i<searcher->sd.sc_srch.layer_cnt; ++i )
490 	    UndoesFree(searcher->sd.sc_srch.layers[i].undoes);
491 	for ( i=0; i<searcher->sd.sc_rpl.layer_cnt; ++i )
492 	    UndoesFree(searcher->sd.sc_rpl.layers[i].undoes);
493     }
494 
495     for ( doit=!ask_if_difficult; doit<=1; ++doit ) {
496 	for ( i=0; i<2; ++i ) {
497 	    rprev = NULL;
498 	    for ( r = searcher->chars[i]->layers[ly_fore].refs; r!=NULL; r=rnext ) {
499 		rnext = r->next;
500 		pos = SFFindSlot(fv->b.sf,fv->b.map,r->sc->unicodeenc,r->sc->name);
501 		gid = -1;
502 		if ( pos!=-1 )
503 		    gid = fv->b.map->map[pos];
504 		if ( (gid==-1 || fv->b.sf->glyphs[gid]!=NULL) && !doit ) {
505 		    char *buttons[3];
506 		    buttons[0] = _("Yes");
507 		    buttons[1] = _("Cancel");
508 		    buttons[2] = NULL;
509 		    if ( ask_if_difficult==2 && !searcher->isvisible )
510 return( false );
511 		    if ( gwwv_ask(_("Bad Reference"),(const char **) buttons,1,1,
512 			    _("The %1$s in the search dialog contains a reference to %2$.20hs which does not exist in the new font.\nShould I remove the reference?"),
513 				i==0?_("Search Pattern"):_("Replace Pattern"),
514 			        r->sc->name)==1 )
515 return( false );
516 		} else if ( !doit )
517 		    /* Do Nothing */;
518 		else if ( gid==-1 || fv->b.sf->glyphs[gid]!=NULL ) {
519 		    if ( rprev==NULL )
520 			searcher->chars[i]->layers[ly_fore].refs = rnext;
521 		    else
522 			rprev->next = rnext;
523 		    RefCharFree(r);
524 		    any = true;
525 		} else {
526 		    /*SplinePointListsFree(r->layers[0].splines); r->layers[0].splines = NULL;*/
527 		    r->sc = fv->b.sf->glyphs[gid];
528 		    r->orig_pos = gid;
529 		    SCReinstanciateRefChar(searcher->chars[i],r,fv->b.active_layer);
530 		    any = true;
531 		    rprev = r;
532 		}
533 	    }
534 	}
535     }
536     fv->sv = searcher;
537     searcher->sd.fv = (FontViewBase *) fv;
538     searcher->sd.curchar = NULL;
539     if ( any ) {
540 	GDrawRequestExpose(searcher->cv_srch.v,NULL,false);
541 	GDrawRequestExpose(searcher->cv_rpl.v,NULL,false);
542     }
543     SVSetTitle(searcher);
544 return( true );
545 }
546 
SVDetachFV(FontView * fv)547 void SVDetachFV(FontView *fv) {
548     FontView *other;
549 
550     fv->sv = NULL;
551     if ( searcher==NULL || searcher->sd.fv!=(FontViewBase *) fv )
552 return;
553     SV_DoClose((struct cvcontainer *) searcher);
554     for ( other=fv_list; other!=NULL; other=(FontView *) other->b.next ) {
555 	if ( other!=fv ) {
556 	    SVAttachFV(fv,false);
557 return;
558 	}
559     }
560 }
561 
SV_Can_Navigate(struct cvcontainer * cvc,enum nav_type type)562 static int SV_Can_Navigate(struct cvcontainer *cvc, enum nav_type type) {
563 return( false );
564 }
565 
SV_Can_Open(struct cvcontainer * cvc)566 static int SV_Can_Open(struct cvcontainer *cvc) {
567 return( true );
568 }
569 
SV_Do_Navigate(struct cvcontainer * cvc,enum nav_type type)570 static void SV_Do_Navigate(struct cvcontainer *cvc, enum nav_type type) {
571 }
572 
SF_Of_SV(struct cvcontainer * cvc)573 static SplineFont *SF_Of_SV(struct cvcontainer *cvc) {
574 return( ((SearchView *) cvc)->sd.fv->sf );
575 }
576 
577 struct cvcontainer_funcs searcher_funcs = {
578     cvc_searcher,
579     (void (*) (struct cvcontainer *cvc,CharViewBase *cv)) SVMakeActive,
580     (void (*) (struct cvcontainer *cvc,void *)) SVChar,
581     SV_Can_Navigate,
582     SV_Do_Navigate,
583     SV_Can_Open,
584     SV_DoClose,
585     SF_Of_SV
586 };
587 
SVFillup(SearchView * sv,FontView * fv)588 static SearchView *SVFillup(SearchView *sv, FontView *fv) {
589 
590     SDFillup(&sv->sd,(FontViewBase *) fv);
591     sv->base.funcs = &searcher_funcs;
592 
593     sv->chars[0] = &sv->sd.sc_srch;
594     sv->chars[1] = &sv->sd.sc_rpl;
595     sv->dummy_sf.glyphs = sv->chars;
596     sv->dummy_sf.glyphcnt = sv->dummy_sf.glyphmax = 2;
597     sv->dummy_sf.pfminfo.fstype = -1;
598     sv->dummy_sf.pfminfo.stylemap = -1;
599     sv->dummy_sf.fontname = sv->dummy_sf.fullname = sv->dummy_sf.familyname = "dummy";
600     sv->dummy_sf.weight = "Medium";
601     sv->dummy_sf.origname = "dummy";
602     sv->dummy_sf.ascent = fv->b.sf->ascent;
603     sv->dummy_sf.descent = fv->b.sf->descent;
604     sv->dummy_sf.layers = sv->layerinfo;
605     sv->dummy_sf.layer_cnt = 2;
606     sv->layerinfo[ly_back].order2 = fv->b.sf->layers[ly_back].order2;
607     sv->layerinfo[ly_back].name = _("Back");
608     sv->layerinfo[ly_fore].order2 = fv->b.sf->layers[ly_fore].order2;
609     sv->layerinfo[ly_fore].name = _("Fore");
610     sv->dummy_sf.grid.order2 = fv->b.sf->grid.order2;
611     sv->dummy_sf.anchor = fv->b.sf->anchor;
612     sv->sd.sc_srch.width = sv->sd.sc_srch.vwidth = sv->sd.sc_rpl.vwidth = sv->sd.sc_rpl.width =
613 	    sv->dummy_sf.ascent + sv->dummy_sf.descent;
614     sv->sd.sc_srch.parent = sv->sd.sc_rpl.parent = &sv->dummy_sf;
615 
616     sv->dummy_sf.fv = (FontViewBase *) &sv->dummy_fv;
617     sv->dummy_fv.b.active_layer = ly_fore;
618     sv->dummy_fv.b.sf = &sv->dummy_sf;
619     sv->dummy_fv.b.selected = sv->sel;
620     sv->dummy_fv.cbw = sv->dummy_fv.cbh = default_fv_font_size+1;
621     sv->dummy_fv.magnify = 1;
622 
623     sv->cv_srch.b.container = (struct cvcontainer *) sv;
624     sv->cv_rpl.inactive = true;
625     sv->cv_rpl.b.container = (struct cvcontainer *) sv;
626 
627     sv->dummy_fv.b.map = &sv->dummy_map;
628     sv->dummy_map.map = sv->map;
629     sv->dummy_map.backmap = sv->backmap;
630     sv->dummy_map.enccount = sv->dummy_map.encmax = sv->dummy_map.backmax = 2;
631     sv->dummy_map.enc = &custom;
632 
633     sv->sd.fv = (FontViewBase *) fv;
634     if ( fv!=NULL )
635 	fv->sv = sv;
636 return( sv );
637 }
638 
SVCreate(FontView * fv)639 SearchView *SVCreate(FontView *fv) {
640     SearchView *sv;
641     GRect pos;
642     GWindow gw;
643     GWindowAttrs wattrs;
644     GGadgetCreateData gcd[14], boxes[6], *butarray[14], *allowarray[6],
645 	    *fudgearray[4], *halfarray[3], *varray[14];
646     GTextInfo label[14];
647     FontRequest rq;
648     int as, ds, ld;
649     char fudgebuf[20];
650     int k, sel_pos, efdo_pos;
651     static GFont *plainfont = NULL, *boldfont=NULL;
652 
653     if ( searcher!=NULL ) {
654 	if ( SVAttachFV(fv,true)) {
655 	    GDrawSetVisible(fv->sv->gw,true);
656 	    GDrawRaise(fv->sv->gw);
657 return( searcher );
658 	} else
659 return( NULL );
660     }
661 
662     searcher = sv = SVFillup( calloc(1,sizeof(SearchView)), fv );
663 
664     memset(&wattrs,0,sizeof(wattrs));
665     wattrs.mask = wam_events|wam_cursor|wam_isdlg/*|wam_icon*/;
666     wattrs.is_dlg = true;
667     wattrs.event_masks = -1;
668     wattrs.event_masks = -1;
669     wattrs.cursor = ct_pointer;
670     /*wattrs.icon = icon;*/
671     pos.width = 600;
672     pos.height = 400;
673     pos.x = GGadgetScale(104)+6;
674     DefaultY(&pos);
675     sv->gw = gw = GDrawCreateTopWindow(NULL,&pos,sv_e_h,&sv->cv_srch,&wattrs);
676     SVSetTitle(sv);
677 
678     if ( plainfont==NULL ) {
679 	memset(&rq,0,sizeof(rq));
680 	rq.utf8_family_name = SANS_UI_FAMILIES;
681 	rq.point_size = 12;
682 	rq.weight = 400;
683 	plainfont = GDrawInstanciateFont(NULL,&rq);
684 	plainfont = GResourceFindFont("SearchView.Font",plainfont);
685 	GDrawDecomposeFont(plainfont, &rq);
686 	rq.weight = 700;
687 	boldfont = GDrawInstanciateFont(NULL,&rq);
688 	boldfont = GResourceFindFont("SearchView.BoldFont",boldfont);
689     }
690     sv->plain = plainfont; sv->bold = boldfont;
691     GDrawWindowFontMetrics(sv->gw,sv->plain,&as,&ds,&ld);
692     sv->fh = as+ds; sv->as = as;
693 
694     SVCharViewInits(sv);
695 
696     memset(&label,0,sizeof(label));
697     memset(&gcd,0,sizeof(gcd));
698     memset(&boxes,0,sizeof(boxes));
699 
700     k=0;
701 
702     label[k].text = (unichar_t *) _("Allow:");
703     label[k].text_is_1byte = true;
704     gcd[k].gd.label = &label[k];
705     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = GDrawPixelsToPoints(NULL,sv->cv_y+sv->cv_height+8);
706     gcd[k].gd.flags = gg_enabled|gg_visible;
707     gcd[k].gd.cid = CID_Allow;
708     gcd[k].gd.popup_msg = _("Allow a match even if the search pattern has\nto be transformed by a combination of the\nfollowing transformations.");
709     gcd[k].creator = GLabelCreate;
710     allowarray[k] = &gcd[k]; ++k;
711 
712     label[k].text = (unichar_t *) _("Flipping");
713     label[k].text_is_1byte = true;
714     gcd[k].gd.label = &label[k];
715     gcd[k].gd.pos.x = 35; gcd[k].gd.pos.y = gcd[0].gd.pos.y-3;
716     gcd[k].gd.flags = gg_enabled|gg_visible|gg_cb_on;
717     gcd[k].gd.cid = CID_Flipping;
718     gcd[k].gd.popup_msg = _("Allow a match even if the search pattern has\nto be transformed by a combination of the\nfollowing transformations.");
719     gcd[k].creator = GCheckBoxCreate;
720     allowarray[k] = &gcd[k]; ++k;
721 
722     label[k].text = (unichar_t *) _("Scaling");
723     label[k].text_is_1byte = true;
724     gcd[k].gd.label = &label[k];
725     gcd[k].gd.pos.x = 100; gcd[k].gd.pos.y = gcd[1].gd.pos.y;
726     gcd[k].gd.flags = gg_enabled|gg_visible;
727     gcd[k].gd.cid = CID_Scaling;
728     gcd[k].gd.popup_msg = _("Allow a match even if the search pattern has\nto be transformed by a combination of the\nfollowing transformations.");
729     gcd[k].creator = GCheckBoxCreate;
730     allowarray[k] = &gcd[k]; ++k;
731 
732     label[k].text = (unichar_t *) _("Rotating");
733     label[k].text_is_1byte = true;
734     gcd[k].gd.label = &label[k];
735     gcd[k].gd.pos.x = 170; gcd[k].gd.pos.y = gcd[1].gd.pos.y;
736     gcd[k].gd.flags = gg_enabled|gg_visible;
737     gcd[k].gd.cid = CID_Rotating;
738     gcd[k].gd.popup_msg = _("Allow a match even if the search pattern has\nto be transformed by a combination of the\nfollowing transformations.");
739     gcd[k].creator = GCheckBoxCreate;
740     allowarray[k] = &gcd[k]; allowarray[++k] = GCD_Glue; allowarray[5] = NULL;
741 
742     label[k].text = (unichar_t *) _("_Match Fuzziness:");
743     label[k].text_is_1byte = true;
744     label[k].text_in_resource = true;
745     gcd[k].gd.label = &label[k];
746     gcd[k].gd.flags = gg_enabled|gg_visible;
747     gcd[k].creator = GLabelCreate;
748     fudgearray[0] = &gcd[k++];
749 
750     sprintf(fudgebuf,"%g",old_fudge);
751     label[k].text = (unichar_t *) fudgebuf;
752     label[k].text_is_1byte = true;
753     label[k].text_in_resource = true;
754     gcd[k].gd.label = &label[k];
755     gcd[k].gd.flags = gg_enabled|gg_visible;
756     gcd[k].gd.cid = CID_Fuzzy;
757     gcd[k].creator = GTextFieldCreate;
758     fudgearray[1] = &gcd[k++]; fudgearray[2] = GCD_Glue; fudgearray[3] = NULL;
759 
760     efdo_pos = k;
761     label[k].text = (unichar_t *) _("Endpoints specify minimum length and direction only");
762     label[k].text_is_1byte = true;
763     gcd[k].gd.label = &label[k];
764     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[1].gd.pos.y+18;
765     gcd[k].gd.flags = gg_enabled|gg_visible;
766     gcd[k].gd.cid = CID_Endpoints;
767     gcd[k].gd.popup_msg = _(
768 	"If the search pattern is a single open contour\n"
769 	"then do not match the end points. They merely\n"
770 	"specify the direction from which the curve should\n"
771 	"move toward the next point (which will be matched),\n"
772 	"and the minimum distance between the first matched\n"
773 	"point and the one before it. The endpoints of the\n"
774 	"replace contour will also only be used for positioning.\n"
775 	"\n"
776 	"This allows you to match a right angle corner\n"
777 	"without needed to specify exactly how long the edges\n"
778 	"are which form the right angle.");
779     gcd[k++].creator = GCheckBoxCreate;
780 
781     sel_pos = k;
782     label[k].text = (unichar_t *) _("Search Selected Chars Only");
783     label[k].text_is_1byte = true;
784     gcd[k].gd.label = &label[k];
785     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[1].gd.pos.y+18;
786     gcd[k].gd.flags = gg_enabled|gg_visible;
787     gcd[k].gd.cid = CID_Selected;
788     gcd[k].gd.popup_msg = _("Only search characters selected in the fontview.\nNormally we search all characters in the font.");
789     gcd[k++].creator = GCheckBoxCreate;
790 
791     label[k].text = (unichar_t *) _("Find Next");	/* Start with this to allow sufficient space */
792     label[k].text_is_1byte = true;
793     gcd[k].gd.label = &label[k];
794     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[sel_pos].gd.pos.y+24;
795     gcd[k].gd.flags = gg_visible|gg_but_default;
796     gcd[k].gd.cid = CID_Find;
797     gcd[k].gd.handle_controlevent = SV_Find;
798     gcd[k].creator = GButtonCreate;
799     butarray[0] = GCD_Glue; butarray[1] = GCD_Glue; butarray[2] = &gcd[k++];
800 
801     label[k].text = (unichar_t *) _("Find All");
802     label[k].text_is_1byte = true;
803     gcd[k].gd.label = &label[k];
804     gcd[k].gd.pos.x = 0; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
805     gcd[k].gd.flags = gg_visible;
806     gcd[k].gd.cid = CID_FindAll;
807     gcd[k].gd.handle_controlevent = SV_FindAll;
808     gcd[k].creator = GButtonCreate;
809     butarray[3] = GCD_Glue; butarray[4] = &gcd[k++];
810 
811     label[k].text = (unichar_t *) _("Replace");
812     label[k].text_is_1byte = true;
813     gcd[k].gd.label = &label[k];
814     gcd[k].gd.pos.x = 0; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y;
815     gcd[k].gd.flags = gg_visible;
816     gcd[k].gd.cid = CID_Replace;
817     gcd[k].gd.handle_controlevent = SV_RplFind;
818     gcd[k].creator = GButtonCreate;
819     butarray[5] = GCD_Glue; butarray[6] = &gcd[k++];
820 
821     label[k].text = (unichar_t *) _("Replace All");
822     label[k].text_is_1byte = true;
823     gcd[k].gd.label = &label[k];
824     gcd[k].gd.pos.x = 0; gcd[k].gd.pos.y = gcd[k-2].gd.pos.y;
825     gcd[k].gd.flags = gg_visible;
826     gcd[k].gd.cid = CID_ReplaceAll;
827     gcd[k].gd.handle_controlevent = SV_RplAll;
828     gcd[k].creator = GButtonCreate;
829     butarray[7] = GCD_Glue; butarray[8] = &gcd[k++];
830 
831     label[k].text = (unichar_t *) _("_Cancel");
832     label[k].text_is_1byte = true;
833     label[k].text_in_resource = true;
834     gcd[k].gd.label = &label[k];
835     gcd[k].gd.pos.x = 0; gcd[k].gd.pos.y = gcd[k-3].gd.pos.y;
836     gcd[k].gd.flags = gg_enabled|gg_visible|gg_but_cancel;
837     gcd[k].gd.cid = CID_Cancel;
838     gcd[k].gd.handle_controlevent = SV_Cancel;
839     gcd[k].creator = GButtonCreate;
840     butarray[9] = GCD_Glue; butarray[10] = &gcd[k++];
841     butarray[11] = GCD_Glue; butarray[12] = GCD_Glue; butarray[13] = NULL;
842 
843     boxes[2].gd.flags = gg_enabled|gg_visible;
844     boxes[2].gd.u.boxelements = allowarray;
845     boxes[2].creator = GHBoxCreate;
846 
847     boxes[3].gd.flags = gg_enabled|gg_visible;
848     boxes[3].gd.u.boxelements = fudgearray;
849     boxes[3].creator = GHBoxCreate;
850     halfarray[0] = &boxes[2]; halfarray[1] = &boxes[3]; halfarray[2] = NULL;
851 
852     boxes[4].gd.flags = gg_enabled|gg_visible;
853     boxes[4].gd.u.boxelements = halfarray;
854     boxes[4].creator = GHBoxCreate;
855 
856     boxes[5].gd.flags = gg_enabled|gg_visible;
857     boxes[5].gd.u.boxelements = butarray;
858     boxes[5].creator = GHBoxCreate;
859 
860     varray[0] = GCD_Glue;       varray[1] = NULL;
861     varray[2] = &boxes[4];      varray[3] = NULL;
862     varray[4] = &gcd[efdo_pos]; varray[5] = NULL;
863     varray[6] = GCD_Glue;       varray[7] = NULL;
864     varray[8] = &gcd[sel_pos];  varray[9] = NULL;
865     varray[10] = &boxes[5];     varray[11]= NULL;
866     varray[12] = NULL;
867 
868     boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
869     boxes[0].gd.flags = gg_enabled|gg_visible;
870     boxes[0].gd.u.boxelements = varray;
871     boxes[0].gd.cid = CID_TopBox;
872     boxes[0].creator = GHVGroupCreate;
873 
874     GGadgetsCreate(gw,boxes);
875 
876     GHVBoxSetExpandableRow(boxes[0].ret,0);
877     GHVBoxSetPadding(boxes[2].ret,6,3);
878     GHVBoxSetExpandableCol(boxes[2].ret,gb_expandglue);
879     GHVBoxSetExpandableCol(boxes[3].ret,gb_expandglue);
880     GHVBoxSetExpandableCol(boxes[5].ret,gb_expandglue);
881     GGadgetResize(boxes[0].ret,pos.width,pos.height);
882 
883     GGadgetSetTitle8(GWidgetGetControl(gw,CID_Find),_("Find"));
884     sv->showsfindnext = false;
885     GDrawRequestTimer(gw,1000,1000,NULL);
886     sv->button_height = GDrawPointsToPixels(gw,100);
887     GDrawResize(gw,650,400);		/* Force a resize event */
888 
889     GDrawSetVisible(sv->gw,true);
890 return( sv );
891 }
892 
SVDestroy(SearchView * sv)893 void SVDestroy(SearchView *sv) {
894 
895     if ( sv==NULL )
896 return;
897 
898     SDDestroy(&sv->sd);
899     free(sv);
900 }
901 
FVReplaceOutlineWithReference(FontView * fv,double fudge)902 void FVReplaceOutlineWithReference( FontView *fv, double fudge ) {
903 
904     if ( fv->v!=NULL )
905 	GDrawSetCursor(fv->v,ct_watch);
906 
907     FVBReplaceOutlineWithReference((FontViewBase *) fv, fudge);
908 
909     if ( fv->v!=NULL ) {
910 	GDrawRequestExpose(fv->v,NULL,false);
911 	GDrawSetCursor(fv->v,ct_pointer);
912     }
913 }
914