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