1 /* Copyright (C) 2008-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 "gkeysym.h"
33 #include "glyphcomp.h"
34 #include "splineutil.h"
35 #include "ustring.h"
36 
37 enum l2l_type { l2l_copy, l2l_compare };
38 
39 typedef struct l2ld {
40     GWindow gw;
41     SplineFont *sf;
42     CharView *cv;
43     FontView *fv;
44     enum l2l_type l2l;
45     int done;
46 } L2LDlg;
47 
48 #define CID_FromLayer	1000
49 #define CID_ToLayer	1001
50 #define CID_ClearOld	1002
51 #define CID_ErrorBound	1003
52 
SFLayerList(SplineFont * sf,int def_layer)53 static GTextInfo *SFLayerList(SplineFont *sf,int def_layer) {
54     GTextInfo *ret = calloc(sf->layer_cnt+1,sizeof(GTextInfo));
55     int l;
56 
57     for ( l=0; l<sf->layer_cnt; ++l ) {
58 	ret[l].text = (unichar_t *) copy(sf->layers[l].name);
59 	ret[l].text_is_1byte = true;
60 	ret[l].userdata = (void *) (intpt) l;
61     }
62     ret[def_layer].selected = true;
63 return( ret );
64 }
65 
_DoCVCopy(CharView * cv,int from,int to,int clear)66 static void _DoCVCopy(CharView *cv,int from,int to,int clear) {
67     SCCopyLayerToLayer(cv->b.sc,from,to,clear);
68 }
69 
RedoRefs(SplineFont * sf,SplineChar * sc,int layer)70 static void RedoRefs(SplineFont *sf,SplineChar *sc,int layer) {
71     RefChar *refs;
72 
73     sc->ticked2 = true;
74     for ( refs=sc->layers[layer].refs; refs!=NULL; refs=refs->next ) {
75 	if ( refs->sc->ticked && !refs->sc->ticked2 )
76 	    RedoRefs(sf,refs->sc,layer);
77 	SCReinstanciateRefChar(sc,refs,layer);
78     }
79 }
80 
_DoFVCopy(FontView * fv,int from,int to,int clear)81 static void _DoFVCopy(FontView *fv,int from,int to,int clear) {
82     SplineFont *sf = fv->b.sf;
83     int gid, enc;
84     SplineChar *sc;
85 
86     for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
87 	sc->ticked = sc->ticked2 = false;
88     }
89     for ( enc=0 ; enc<fv->b.map->enccount; ++enc ) {
90 	if ( fv->b.selected[enc] && (gid=fv->b.map->map[enc])!=-1 &&
91 		(sc = sf->glyphs[gid])!=NULL && !sc->ticked ) {
92 	    SCCopyLayerToLayer(sc,from,to,clear);
93 	    sc->ticked = true;
94 	}
95     }
96     for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
97 	if ( sc->ticked && !sc->ticked2 )
98 	    RedoRefs(sf,sc,to);
99     }
100 }
101 
_DoCVCompare(CharView * cv,int from,int to,double errbound)102 static void _DoCVCompare(CharView *cv,int from,int to,double errbound) {
103     if ( LayersSimilar(&cv->b.sc->layers[from],&cv->b.sc->layers[to],errbound))
104 	ff_post_notice(_("Match"),_("No significant differences found"));
105     else
106 	ff_post_notice(_("Differ"),_("The layers do not match"));
107 }
108 
_DoFVCompare(FontView * fv,int from,int to,double errbound)109 static void _DoFVCompare(FontView *fv,int from,int to,double errbound) {
110     SplineFont *sf = fv->b.sf;
111     int gid, enc;
112     SplineChar *sc;
113     int first=-1;
114 
115     memset(fv->b.selected,0,fv->b.map->enccount);
116 
117     for ( enc=0 ; enc<fv->b.map->enccount; ++enc ) {
118 	if ( /*fv->b.selected[enc] &&*/ (gid=fv->b.map->map[enc])!=-1 &&
119 		(sc = sf->glyphs[gid])!=NULL && !sc->ticked ) {
120 	    if ( !LayersSimilar(&sc->layers[from],&sc->layers[to],errbound)) {
121 		fv->b.selected[enc] = true;
122 		if ( first==-1 )
123 		    first = enc;
124 	    }
125 	}
126     }
127     GDrawRequestExpose(fv->v,NULL,true);
128     if ( first==-1 )
129 	ff_post_notice(_("Match"),_("No significant differences found"));
130     else {
131 	ff_post_notice(_("Differ"),_("The layers do not match"));
132 	FVScrollToChar(fv,first);
133 	fv->end_pos = fv->pressed_pos = first;
134 	/*FVShowInfo(fv);*/
135     }
136 }
137 
138 
L2L_OK(GGadget * g,GEvent * e)139 static int L2L_OK(GGadget *g, GEvent *e) {
140 
141     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
142 	L2LDlg *d = GDrawGetUserData(GGadgetGetWindow(g));
143 	int from, to, clear;
144 	int err=0;
145 	double errbound;
146 
147 	from = GGadgetGetFirstListSelectedItem(GWidgetGetControl(d->gw,CID_FromLayer));
148 	to   = GGadgetGetFirstListSelectedItem(GWidgetGetControl(d->gw,CID_ToLayer));
149 	if ( d->l2l==l2l_copy ) {
150 	    clear = GGadgetIsChecked(GWidgetGetControl(d->gw,CID_ClearOld));
151 	    if ( d->cv )
152 		_DoCVCopy(d->cv,from,to,clear);
153 	    else
154 		_DoFVCopy(d->fv,from,to,clear);
155 	} else {
156 	    errbound = GetReal8(d->gw,CID_ErrorBound,_("Error Bound"),&err);
157 	    if ( err )
158 return( true );
159 	    if ( d->cv )
160 		_DoCVCompare(d->cv,from,to,errbound);
161 	    else
162 		_DoFVCompare(d->fv,from,to,errbound);
163 	}
164 	d->done = true;
165     }
166 return( true );
167 }
168 
L2L_Cancel(GGadget * g,GEvent * e)169 static int L2L_Cancel(GGadget *g, GEvent *e) {
170 
171     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
172 	L2LDlg *d = GDrawGetUserData(GGadgetGetWindow(g));
173 	d->done = true;
174     }
175 return( true );
176 }
177 
l2l_e_h(GWindow gw,GEvent * event)178 static int l2l_e_h(GWindow gw, GEvent *event) {
179 
180     if ( event->type==et_close ) {
181 	L2LDlg *d = GDrawGetUserData(gw);
182 	d->done = true;
183     } else if ( event->type == et_char ) {
184 return( false );
185     }
186 return( true );
187 }
188 
Layer2Layer(CharView * cv,FontView * fv,enum l2l_type l2l,int def_layer)189 static void Layer2Layer(CharView *cv,FontView *fv,enum l2l_type l2l,int def_layer) {
190     L2LDlg d;
191     GRect pos;
192     GWindow gw;
193     GWindowAttrs wattrs;
194     GGadgetCreateData gcd[17], *hvarray[34], *harray[5], *harray2[8], *hvarray2[7], boxes[7];
195     GTextInfo label[17];
196     int k,j;
197 
198     memset(&d,0,sizeof(d));
199     d.cv = cv; d.fv = fv; d.l2l = l2l;
200     if ( cv!=NULL ) d.sf = cv->b.sc->parent;
201     else d.sf = fv->b.sf;
202 
203     memset(&wattrs,0,sizeof(wattrs));
204     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
205     wattrs.event_masks = ~(1<<et_charup);
206     wattrs.restrict_input_to_me = 1;
207     wattrs.undercursor = 1;
208     wattrs.cursor = ct_pointer;
209     wattrs.utf8_window_title = l2l==l2l_copy ? _("Copy Layers") : _("Compare Layers");
210     wattrs.is_dlg = true;
211     pos.x = pos.y = 0;
212     pos.width = GGadgetScale(GDrawPointsToPixels(NULL,170));
213     pos.height = GDrawPointsToPixels(NULL,178);
214     d.gw = gw = GDrawCreateTopWindow(NULL,&pos,l2l_e_h,&d,&wattrs);
215 
216     memset(&label,0,sizeof(label));
217     memset(&gcd,0,sizeof(gcd));
218     memset(&boxes,0,sizeof(boxes));
219 
220     k=j=0;
221     label[k].text = (unichar_t *) (l2l==l2l_copy ? _("Copy one layer to another") : _("Compare two layers"));
222     label[k].text_is_1byte = true;
223     gcd[k].gd.label = &label[k];
224     gcd[k].gd.flags = gg_enabled|gg_visible;
225     gcd[k++].creator = GLabelCreate;
226     hvarray[j++] = &gcd[k-1]; hvarray[j++] = NULL;
227 
228     label[k].text = (unichar_t *) (l2l==l2l_copy ? _("From:") : _("Base:"));
229     label[k].text_is_1byte = true;
230     gcd[k].gd.label = &label[k];
231     gcd[k].gd.flags = gg_enabled|gg_visible;
232     gcd[k++].creator = GLabelCreate;
233     hvarray2[0] = &gcd[k-1];
234 
235     gcd[k].gd.flags = gg_enabled|gg_visible;
236     gcd[k].gd.cid = CID_FromLayer;
237     gcd[k].gd.u.list = SFLayerList(d.sf,def_layer);
238     gcd[k++].creator = GListButtonCreate;
239     hvarray2[1] = &gcd[k-1]; hvarray2[2] = NULL;
240 
241     label[k].text = (unichar_t *) (l2l==l2l_copy ? _("To:") : _("Other:"));
242     label[k].text_is_1byte = true;
243     gcd[k].gd.label = &label[k];
244     gcd[k].gd.flags = gg_enabled|gg_visible;
245     gcd[k++].creator = GLabelCreate;
246     hvarray2[3] = &gcd[k-1];
247 
248     gcd[k].gd.flags = gg_enabled|gg_visible;
249     gcd[k].gd.cid = CID_ToLayer;
250     gcd[k].gd.u.list = SFLayerList(d.sf,def_layer==ly_fore ? ly_back : ly_fore );
251     gcd[k++].creator = GListButtonCreate;
252     hvarray2[4] = &gcd[k-1]; hvarray2[5] = NULL; hvarray2[6] = NULL;
253 
254     boxes[3].gd.flags = gg_enabled|gg_visible;
255     boxes[3].gd.u.boxelements = hvarray2;
256     boxes[3].creator = GHVBoxCreate;
257     hvarray[j++] = &boxes[3]; hvarray[j++] = NULL;
258 
259     if ( l2l==l2l_copy ) {
260 	label[k].text = (unichar_t *) _("Clear destination layer before copy");
261 	label[k].text_is_1byte = true;
262 	gcd[k].gd.label = &label[k];
263 	gcd[k].gd.cid = CID_ClearOld;
264 	gcd[k].gd.flags = gg_enabled|gg_visible|gg_cb_on;
265 	gcd[k++].creator = GCheckBoxCreate;
266 	hvarray[j++] = &gcd[k-1]; hvarray[j++] = NULL;
267     } else {
268 	label[k].text = (unichar_t *) _("Allow errors of:");
269 	label[k].text_is_1byte = true;
270 	gcd[k].gd.label = &label[k];
271 	gcd[k].gd.flags = gg_enabled|gg_visible;
272 	gcd[k++].creator = GLabelCreate;
273 	harray[0] = &gcd[k-1];
274 
275 	label[k].text = (unichar_t *) "1";
276 	label[k].text_is_1byte = true;
277 	gcd[k].gd.pos.width = 50;
278 	gcd[k].gd.label = &label[k];
279 	gcd[k].gd.flags = gg_enabled|gg_visible;
280 	gcd[k].gd.cid = CID_ErrorBound;
281 	gcd[k++].creator = GTextFieldCreate;
282 	harray[1] = &gcd[k-1];
283 
284 	label[k].text = (unichar_t *) _("em units");
285 	label[k].text_is_1byte = true;
286 	gcd[k].gd.label = &label[k];
287 	gcd[k].gd.flags = gg_enabled|gg_visible;
288 	gcd[k++].creator = GLabelCreate;
289 	harray[2] = &gcd[k-1]; harray[3] = GCD_Glue; harray[4] = NULL;
290 
291 	boxes[4].gd.flags = gg_enabled|gg_visible;
292 	boxes[4].gd.u.boxelements = harray;
293 	boxes[4].creator = GHBoxCreate;
294 	hvarray[j++] = &boxes[4]; hvarray[j++] = NULL;
295     }
296 
297     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
298     label[k].text = (unichar_t *) _("_OK");
299     label[k].text_is_1byte = true;
300     label[k].text_in_resource = true;
301     gcd[k].gd.label = &label[k];
302     gcd[k].gd.handle_controlevent = L2L_OK;
303     gcd[k++].creator = GButtonCreate;
304     harray2[0] = GCD_Glue; harray2[1] = &gcd[k-1]; harray2[2] = GCD_Glue; harray2[3] = GCD_Glue;
305 
306     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
307     label[k].text = (unichar_t *) _("_Cancel");
308     label[k].text_is_1byte = true;
309     label[k].text_in_resource = true;
310     gcd[k].gd.label = &label[k];
311     gcd[k].gd.handle_controlevent = L2L_Cancel;
312     gcd[k++].creator = GButtonCreate;
313     harray2[4] = GCD_Glue; harray2[5] = &gcd[k-1]; harray2[6] = GCD_Glue; harray2[7] = NULL;
314 
315     boxes[2].gd.flags = gg_enabled|gg_visible;
316     boxes[2].gd.u.boxelements = harray2;
317     boxes[2].creator = GHBoxCreate;
318     hvarray[j++] = &boxes[2]; hvarray[j++] = NULL;
319     hvarray[j++] = GCD_Glue; hvarray[j++] = NULL; hvarray[j++] = NULL;
320 
321     boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
322     boxes[0].gd.flags = gg_enabled|gg_visible;
323     boxes[0].gd.u.boxelements = hvarray;
324     boxes[0].creator = GHVGroupCreate;
325 
326     GGadgetsCreate(gw,boxes);
327     GHVBoxSetExpandableRow(boxes[0].ret,gb_expandglue);
328     GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
329     if ( l2l==l2l_compare )
330 	GHVBoxSetExpandableRow(boxes[4].ret,gb_expandglue);
331 
332     GTextInfoListFree(gcd[2].gd.u.list);
333     GTextInfoListFree(gcd[4].gd.u.list);
334 
335     GHVBoxFitWindow(boxes[0].ret);
336 
337     GDrawSetVisible(gw,true);
338     while ( !d.done )
339 	GDrawProcessOneEvent(NULL);
340 
341     GDrawDestroyWindow(gw);
342 }
343 
CVCopyLayerToLayer(CharView * cv)344 void CVCopyLayerToLayer(CharView *cv) {
345     Layer2Layer(cv,NULL,l2l_copy,CVLayer((CharViewBase *) cv));
346 }
347 
FVCopyLayerToLayer(FontView * fv)348 void FVCopyLayerToLayer(FontView *fv) {
349     Layer2Layer(NULL,fv,l2l_copy,ly_fore);
350 }
351 
CVCompareLayerToLayer(CharView * cv)352 void CVCompareLayerToLayer(CharView *cv) {
353     Layer2Layer(cv,NULL,l2l_compare,CVLayer((CharViewBase *) cv));
354 }
355 
FVCompareLayerToLayer(FontView * fv)356 void FVCompareLayerToLayer(FontView *fv) {
357     Layer2Layer(NULL,fv,l2l_compare,ly_fore);
358 }
359