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 "autohint.h"
31 #include "cvundoes.h"
32 #include "fontforgeui.h"
33 #include "ustring.h"
34 
35 #include <math.h>
36 
37 #define CID_Base	1001
38 #define CID_Width	1002
39 #define CID_Label	1003
40 #define CID_HStem	1004
41 #define CID_VStem	1005
42 #define CID_Next	1006
43 #define CID_Prev	1007
44 #define CID_Remove	1008
45 #define CID_Add		1009
46 #define CID_Overlap	1010
47 #define CID_Count	1011
48 #define CID_MovePoints	1012
49 #define CID_RegenHM	1013
50 #define CID_TopBox	1014
51 
52 typedef struct reviewhintdata {
53     unsigned int done: 1;
54     unsigned int ishstem: 1;
55     unsigned int oldmanual: 1;
56     unsigned int undocreated: 1;
57     unsigned int changed: 1;
58     CharView *cv;
59     GWindow gw;
60     StemInfo *active;
61     StemInfo *lastactive;
62     StemInfo *oldh, *oldv;
63 } ReviewHintData;
64 
RH_SetNextPrev(ReviewHintData * hd)65 static void RH_SetNextPrev(ReviewHintData *hd) {
66     if ( hd->active==NULL ) {
67 	GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Next),false);
68 	GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Prev),false);
69 	GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Remove),false);
70 	GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Base),false);
71 	GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Width),false);
72     } else {
73 	GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Remove),true);
74 	GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Base),true);
75 	GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Width),true);
76 	GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Next),hd->active->next!=NULL);
77 	if ( hd->ishstem )
78 	    GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Prev),hd->active!=hd->cv->b.sc->hstem);
79 	else
80 	    GGadgetSetEnabled(GWidgetGetControl(hd->gw,CID_Prev),hd->active!=hd->cv->b.sc->vstem);
81     }
82     GDrawRequestExpose(hd->gw,NULL,false);
83 }
84 
RH_SetupHint(ReviewHintData * hd)85 static void RH_SetupHint(ReviewHintData *hd) {
86     char buffer[20]; unichar_t ubuf[20];
87     static unichar_t nullstr[] = {'\0'};
88     StemInfo *h;
89     int pos,cnt;
90 
91     if ( hd->lastactive!=NULL )
92 	hd->lastactive->active = false;
93 
94     pos = cnt = 0;
95     for ( h=hd->ishstem ? hd->cv->b.sc->hstem : hd->cv->b.sc->vstem; h!=NULL; h=h->next ) {
96 	++cnt;
97 	if ( h==hd->active ) pos=cnt;
98     }
99     sprintf( buffer,"%d/%d", pos, cnt );
100     if ( cnt==3 ) {
101 	StemInfo *h2, *h3;
102 	h = hd->ishstem ? hd->cv->b.sc->hstem : hd->cv->b.sc->vstem;
103 	h2 = h->next; h3 = h2->next;
104 	if ( h->width == h2->width && h2->width==h3->width &&
105 		h2->start-h->start == h3->start-h2->start )
106 	    strcat( buffer, hd->ishstem ? " hstem3" : " vstem3" );
107     }
108     uc_strcpy(ubuf,buffer);
109     GGadgetSetTitle(GWidgetGetControl(hd->gw,CID_Count),ubuf);
110 
111     if ( hd->active==NULL ) {
112 	GGadgetSetTitle(GWidgetGetControl(hd->gw,CID_Base),nullstr);
113 	GGadgetSetTitle(GWidgetGetControl(hd->gw,CID_Width),nullstr);
114 	GGadgetSetVisible(GWidgetGetControl(hd->gw,CID_Overlap),false);
115     } else {
116 	hd->active->active = true;
117 	sprintf( buffer,"%g", (double) (!hd->active->ghost ? hd->active->start : hd->active->start+hd->active->width) );
118 	uc_strcpy(ubuf,buffer);
119 	GGadgetSetTitle(GWidgetGetControl(hd->gw,CID_Base),ubuf);
120 	GTextFieldShow(GWidgetGetControl(hd->gw,CID_Base),0);
121 	sprintf( buffer,"%g", (double) (!hd->active->ghost ? hd->active->width : -hd->active->width) );
122 	uc_strcpy(ubuf,buffer);
123 	GGadgetSetTitle(GWidgetGetControl(hd->gw,CID_Width),ubuf);
124 	GTextFieldShow(GWidgetGetControl(hd->gw,CID_Width),0);
125 	GGadgetSetVisible(GWidgetGetControl(hd->gw,CID_Overlap),hd->active->hasconflicts);
126     }
127     if ( hd->lastactive!=hd->active ) {
128 	hd->lastactive = hd->active;
129 	SCOutOfDateBackground(hd->cv->b.sc);
130 	SCUpdateAll(hd->cv->b.sc);	/* Changing the active Hint means we should redraw everything */
131     }
132     RH_SetNextPrev(hd);
133 }
134 
OnHint(StemInfo * hint,real major,real minor)135 static int OnHint(StemInfo *hint,real major,real minor) {
136     HintInstance *hi;
137 
138     if ( true /*hint->hasconflicts*/ ) {
139 	for ( hi=hint->where; hi!=NULL; hi=hi->next ) {
140 	    if ( minor>=hi->begin && minor<=hi->end )
141 	break;
142 	}
143 	if ( hi==NULL )
144 return( 0 );
145 	/* Ok, it's active */
146     }
147     if ( major==hint->start )
148 return( 1 );
149     else if ( major==hint->start+hint->width )
150 return( 2 );
151 
152 return( 0 );
153 }
154 
RH_MovePoints(ReviewHintData * hd,StemInfo * active,int start,int width)155 static void RH_MovePoints(ReviewHintData *hd,StemInfo *active,int start,int width) {
156     SplineChar *sc = hd->cv->b.sc;
157     SplineSet *spl;
158     SplinePoint *sp;
159     int which;
160     int layer = CVLayer( (CharViewBase *) (hd->cv));
161 
162     if ( !hd->undocreated ) {
163 	SCPreserveLayer(sc,layer,true);
164 	hd->undocreated = true;
165     }
166 
167     for ( spl=sc->layers[layer].splines; spl!=NULL; spl=spl->next ) {
168 	for ( sp = spl->first ; ; ) {
169 	    if ( hd->ishstem ) {
170 		switch ((which = OnHint(active,sp->me.y,sp->me.x)) ) {
171 		  case 1:
172 		    sp->nextcp.y += start-sp->me.y;
173 		    sp->prevcp.y += start-sp->me.y;
174 		    sp->me.y = start;
175 		  break;
176 		  case 2:
177 		    sp->nextcp.y += start+width-sp->me.y;
178 		    sp->prevcp.y += start+width-sp->me.y;
179 		    sp->me.y = start+width;
180 		  break;
181 		  case 0:
182 		    /* Not on hint */;
183 		  break;
184 		}
185 	    } else {
186 		switch ( (which = OnHint(active,sp->me.x,sp->me.y)) ) {
187 		  case 1:
188 		    sp->nextcp.x += start-sp->me.x;
189 		    sp->prevcp.x += start-sp->me.x;
190 		    sp->me.x = start;
191 		  break;
192 		  case 2:
193 		    sp->nextcp.x += start+width-sp->me.x;
194 		    sp->prevcp.x += start+width-sp->me.x;
195 		    sp->me.x = start+width;
196 		  break;
197 		  case 0:
198 		    /* Not on hint */;
199 		  break;
200 		}
201 	    }
202 	    if ( which ) {
203 		if ( sp->prev )
204 		    SplineRefigure(sp->prev);
205 		if ( sp->next )
206 		    SplineRefigure(sp->next);
207 	    }
208 	    if ( sp->next==NULL )
209 	break;
210 	    sp = sp->next->to;
211 	    if ( sp== spl->first )
212 	break;
213 	}
214     }
215 }
216 
RH_TextChanged(GGadget * g,GEvent * e)217 static int RH_TextChanged(GGadget *g, GEvent *e) {
218     int wasconflict;
219     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
220 	ReviewHintData *hd = GDrawGetUserData(GGadgetGetWindow(g));
221 	if ( hd->active!=NULL ) {
222 	    int cid = GGadgetGetCid(g);
223 	    int err=0;
224 	    real start = GetCalmReal8(hd->gw,CID_Base,_("Base:"),&err);
225 	    real width = GetCalmReal8(hd->gw,CID_Width,_("Size:"),&err);
226 	    if ( err )
227 return( true );
228 	    if ( GGadgetIsChecked(GWidgetGetControl(GGadgetGetWindow(g),CID_MovePoints)) ) {
229 		if ( width<0 )
230 		    RH_MovePoints(hd,hd->active,start+width,-width);
231 		else
232 		    RH_MovePoints(hd,hd->active,start,width);
233 	    }
234 	    if ( cid==CID_Base )
235 		hd->active->start = start;
236 	    else
237 		hd->active->width = width;
238 	    if ( width<0 ) {
239 		hd->active->ghost = true;
240 		hd->active->width = -width;
241 		hd->active->start = start+width;
242 	    } else
243 		hd->active->ghost = false;
244 	    wasconflict = hd->active->hasconflicts;
245 	    if ( hd->ishstem )
246 		hd->cv->b.sc->hconflicts = StemListAnyConflicts(hd->cv->b.sc->hstem);
247 	    else
248 		hd->cv->b.sc->vconflicts = StemListAnyConflicts(hd->cv->b.sc->vstem);
249 	    hd->cv->b.sc->manualhints = true;
250 	    hd->changed = true;
251 	    if ( wasconflict!=hd->active->hasconflicts ) {
252 		GGadgetSetVisible(GWidgetGetControl(hd->gw,CID_Overlap),hd->active->hasconflicts);
253 		if ( hd->active->hasconflicts )
254 		    GHVBoxFitWindow(GWidgetGetControl(hd->gw,CID_TopBox));
255 	    }
256 	    SCOutOfDateBackground(hd->cv->b.sc);
257 	    SCUpdateAll(hd->cv->b.sc);
258 	}
259     }
260 return( true );
261 }
262 
Do_OKRegen(ReviewHintData * hd)263 static void Do_OKRegen(ReviewHintData *hd) {
264     SplineChar *sc = hd->cv->b.sc;
265     StemInfo *curh = sc->hstem, *curv = sc->vstem;
266     int do_regen = GGadgetIsChecked(GWidgetGetControl(hd->gw,CID_RegenHM));
267 
268     /* We go backwards here, but not for long. The point is to go back to */
269     /*  the original hint state so we can preserve it, now that we know we*/
270     /*  are going to modify it */
271     sc->hstem = hd->oldh; sc->vstem = hd->oldv;
272     SCPreserveHints(sc,CVLayer((CharViewBase *) hd->cv));
273     sc->hstem = curh; sc->vstem = curv;
274 
275     StemInfosFree(hd->oldh);
276     StemInfosFree(hd->oldv);
277     if ( hd->lastactive!=NULL )
278 	hd->lastactive->active = false;
279     if ( hd->changed ) {
280 	SCClearHintMasks(hd->cv->b.sc,CVLayer((CharViewBase *) (hd->cv)),true);
281 	if ( do_regen )
282 	    SCFigureHintMasks(hd->cv->b.sc,CVLayer((CharViewBase *) (hd->cv)));
283     }
284     /* Everything else got done as we went along... */
285     SCOutOfDateBackground(hd->cv->b.sc);
286     SCUpdateAll(hd->cv->b.sc);
287     SCHintsChanged(hd->cv->b.sc);
288     hd->done = true;
289 }
290 
RH_OK(GGadget * g,GEvent * e)291 static int RH_OK(GGadget *g, GEvent *e) {
292     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
293 	ReviewHintData *hd = GDrawGetUserData(GGadgetGetWindow(g));
294 	Do_OKRegen(hd);
295     }
296 return( true );
297 }
298 
DoCancel(ReviewHintData * hd)299 static void DoCancel(ReviewHintData *hd) {
300     StemInfosFree(hd->cv->b.sc->hstem);
301     StemInfosFree(hd->cv->b.sc->vstem);
302     hd->cv->b.sc->hstem = hd->oldh;
303     hd->cv->b.sc->vstem = hd->oldv;
304     hd->cv->b.sc->hconflicts = StemListAnyConflicts(hd->cv->b.sc->hstem);
305     hd->cv->b.sc->vconflicts = StemListAnyConflicts(hd->cv->b.sc->vstem);
306     hd->cv->b.sc->manualhints = hd->oldmanual;
307     if ( hd->undocreated )
308 	SCDoUndo(hd->cv->b.sc,ly_fore);
309     SCOutOfDateBackground(hd->cv->b.sc);
310     SCUpdateAll(hd->cv->b.sc);
311     hd->done = true;
312 }
313 
RH_Cancel(GGadget * g,GEvent * e)314 static int RH_Cancel(GGadget *g, GEvent *e) {
315     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
316 	DoCancel( GDrawGetUserData(GGadgetGetWindow(g)));
317     }
318 return( true );
319 }
320 
RH_Remove(GGadget * g,GEvent * e)321 static int RH_Remove(GGadget *g, GEvent *e) {
322     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
323 	ReviewHintData *hd = GDrawGetUserData(GGadgetGetWindow(g));
324 	StemInfo *prev;
325 	if ( hd->active==NULL )
326 return( true );			/* Eh? */
327 	if ( hd->active==hd->cv->b.sc->hstem ) {
328 	    hd->cv->b.sc->hstem = hd->active->next;
329 	    prev = hd->cv->b.sc->hstem;
330 	} else if ( hd->active==hd->cv->b.sc->vstem ) {
331 	    hd->cv->b.sc->vstem = hd->active->next;
332 	    prev = hd->cv->b.sc->vstem;
333 	} else {
334 	    prev = hd->ishstem ? hd->cv->b.sc->hstem : hd->cv->b.sc->vstem;
335 	    for ( ; prev->next!=hd->active && prev->next!=NULL; prev = prev->next );
336 	    prev->next = hd->active->next;
337 	}
338 	if ( hd->ishstem )
339 	    hd->cv->b.sc->hconflicts = StemListAnyConflicts(hd->cv->b.sc->hstem);
340 	else
341 	    hd->cv->b.sc->vconflicts = StemListAnyConflicts(hd->cv->b.sc->vstem);
342 	hd->cv->b.sc->manualhints = true;
343 	hd->changed = true;
344 	StemInfoFree( hd->active );
345 	hd->active = prev;
346 	SCOutOfDateBackground(hd->cv->b.sc);
347 	RH_SetupHint(hd);
348 	/*SCUpdateAll(hd->cv->b.sc);*/	/* Done in RH_SetupHint now */
349     }
350 return( true );
351 }
352 
RH_Add(GGadget * g,GEvent * e)353 static int RH_Add(GGadget *g, GEvent *e) {
354     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
355 	ReviewHintData *hd = GDrawGetUserData(GGadgetGetWindow(g));
356 	CVCreateHint(hd->cv,hd->ishstem,false);
357 	hd->active = hd->ishstem ? hd->cv->b.sc->hstem : hd->cv->b.sc->vstem;
358 	RH_SetupHint(hd);
359     }
360 return( true );
361 }
362 
RH_NextPrev(GGadget * g,GEvent * e)363 static int RH_NextPrev(GGadget *g, GEvent *e) {
364     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
365 	ReviewHintData *hd = GDrawGetUserData(GGadgetGetWindow(g));
366 	StemInfo *prev;
367 	if ( GGadgetGetCid(g)==CID_Next ) {
368 	    if ( hd->active->next !=NULL )
369 		hd->active = hd->active->next;
370 	} else {
371 	    prev = hd->ishstem ? hd->cv->b.sc->hstem : hd->cv->b.sc->vstem;
372 	    for ( ; prev->next!=hd->active && prev->next!=NULL; prev = prev->next );
373 	    hd->active = prev;
374 	}
375 	RH_SetupHint(hd);
376     }
377 return( true );
378 }
379 
RH_HVStem(GGadget * g,GEvent * e)380 static int RH_HVStem(GGadget *g, GEvent *e) {
381     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
382 	ReviewHintData *hd = GDrawGetUserData(GGadgetGetWindow(g));
383 	hd->ishstem = GGadgetIsChecked(GWidgetGetControl(GGadgetGetWindow(g),CID_HStem));
384 	hd->active =  hd->ishstem ? hd->cv->b.sc->hstem : hd->cv->b.sc->vstem;
385 	RH_SetupHint(hd);
386     }
387 return( true );
388 }
389 
rh_e_h(GWindow gw,GEvent * event)390 static int rh_e_h(GWindow gw, GEvent *event) {
391     if ( event->type==et_close ) {
392 	DoCancel( GDrawGetUserData(gw));
393     } else if ( event->type == et_char ) {
394 return( false );
395     } else if ( event->type == et_map ) {
396 	/* Above palettes */
397 	GDrawRaise(gw);
398     }
399 return( true );
400 }
401 
CVReviewHints(CharView * cv)402 void CVReviewHints(CharView *cv) {
403     GRect pos;
404     GWindow gw;
405     GWindowAttrs wattrs;
406     GGadgetCreateData gcd[18], *harray1[6], *harray2[6], *harray3[6], *harray4[6],
407 	*varray[7][2], boxes[8], *barray[6][6];
408     GTextInfo label[18];
409     static ReviewHintData hd;
410     int k;
411 
412     hd.done = false;
413     hd.cv = cv;
414 
415     if ( hd.gw==NULL ) {
416 	memset(&wattrs,0,sizeof(wattrs));
417 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
418 	wattrs.event_masks = ~(1<<et_charup);
419 	wattrs.restrict_input_to_me = 1;
420 	wattrs.undercursor = 1;
421 	wattrs.cursor = ct_pointer;
422 	wattrs.utf8_window_title = _("Review Hints");
423 	wattrs.is_dlg = true;
424 	pos.x = pos.y = 0;
425 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,170));
426 	pos.height = GDrawPointsToPixels(NULL,178);
427 	hd.gw = gw = GDrawCreateTopWindow(NULL,&pos,rh_e_h,&hd,&wattrs);
428 
429 	memset(&label,0,sizeof(label));
430 	memset(&gcd,0,sizeof(gcd));
431 	memset(&boxes,0,sizeof(boxes));
432 
433 	k=0;
434 
435 	label[k].text = (unichar_t *) _("_HStem");
436 	label[k].text_is_1byte = true;
437 	label[k].text_in_resource = true;
438 	gcd[k].gd.label = &label[k];
439 	gcd[k].gd.flags = gg_enabled|gg_visible|gg_cb_on;
440 	gcd[k].gd.cid = CID_HStem;
441 	gcd[k].gd.handle_controlevent = RH_HVStem;
442 	gcd[k].creator = GRadioCreate;
443 	harray2[0] = &gcd[k++];
444 
445 	label[k].text = (unichar_t *) _("_VStem");
446 	label[k].text_is_1byte = true;
447 	label[k].text_in_resource = true;
448 	gcd[k].gd.label = &label[k];
449 	gcd[k].gd.flags = gg_enabled|gg_visible;
450 	gcd[k].gd.cid = CID_VStem;
451 	gcd[k].gd.handle_controlevent = RH_HVStem;
452 	gcd[k].creator = GRadioCreate;
453 	harray2[1] = &gcd[k++];
454 
455 	label[k].text = (unichar_t *) "999/999 hstem3";
456 	label[k].text_is_1byte = true;
457 	gcd[k].gd.label = &label[k];
458 	gcd[k].gd.flags = gg_enabled|gg_visible;
459 	gcd[k].gd.cid = CID_Count;
460 	gcd[k].creator = GLabelCreate;
461 	harray2[2] = GCD_HPad10; harray2[3] = &gcd[k++]; harray2[4] = GCD_Glue; harray2[5] = NULL;
462 
463 	label[k].text = (unichar_t *) _("_Move Points");
464 	label[k].text_is_1byte = true;
465 	label[k].text_in_resource = true;
466 	gcd[k].gd.label = &label[k];
467 	gcd[k].gd.flags = gg_enabled|gg_visible;
468 	gcd[k].gd.cid = CID_MovePoints;
469 	gcd[k].gd.popup_msg = _("When the hint's position is changed\nadjust the postion of any points\nwhich lie on that hint");
470 	gcd[k].creator = GCheckBoxCreate;
471 	harray3[0] = &gcd[k++]; harray3[1] = GCD_Glue; harray3[2] = NULL;
472 
473 	label[k].text = (unichar_t *) _("_Base:");
474 	label[k].text_is_1byte = true;
475 	label[k].text_in_resource = true;
476 	gcd[k].gd.label = &label[k];
477 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = 14+17+5+3;
478 	gcd[k].gd.flags = gg_enabled|gg_visible;
479 	gcd[k].creator = GLabelCreate;
480 	harray1[0] = &gcd[k++];
481 
482 	gcd[k].gd.pos.width = 40;
483 	gcd[k].gd.flags = gg_enabled|gg_visible;
484 	gcd[k].gd.cid = CID_Base;
485 	gcd[k].gd.handle_controlevent = RH_TextChanged;
486 	gcd[k].creator = GTextFieldCreate;
487 	harray1[1] = &gcd[k++];
488 
489 	label[k].text = (unichar_t *) _("_Size:");
490 	label[k].text_is_1byte = true;
491 	label[k].text_in_resource = true;
492 	gcd[k].gd.label = &label[k];
493 	gcd[k].gd.flags = gg_enabled|gg_visible;
494 	gcd[k].creator = GLabelCreate;
495 	harray1[2] = &gcd[k++];
496 
497 	gcd[k].gd.pos.width = 40;
498 	gcd[k].gd.flags = gg_enabled|gg_visible;
499 	gcd[k].gd.cid = CID_Width;
500 	gcd[k].gd.handle_controlevent = RH_TextChanged;
501 	gcd[k].creator = GTextFieldCreate;
502 	harray1[3] = &gcd[k++]; harray1[4] = GCD_Glue; harray1[5] = NULL;
503 
504 	gcd[k].gd.flags = gg_visible | gg_enabled;
505 	label[k].text = (unichar_t *) "Overlaps another hint";
506 	label[k].text_is_1byte = true;
507 	label[k].fg = 0xff0000; label[k].bg = COLOR_DEFAULT;	/* Doesn't work, needs to be in box */
508 	gcd[k].gd.label = &label[k];
509 	gcd[k].gd.cid = CID_Overlap;
510 	gcd[k].creator = GLabelCreate;
511 	harray4[0] = GCD_Glue; harray4[1] = &gcd[k++]; harray4[2] = GCD_Glue; harray4[3] = NULL;
512 
513 
514 	gcd[k].gd.flags = gg_visible | gg_enabled;
515 	label[k].text = (unichar_t *) _("Cr_eate");
516 	label[k].text_is_1byte = true;
517 	label[k].text_in_resource = true;
518 	gcd[k].gd.label = &label[k];
519 	gcd[k].gd.cid = CID_Add;
520 	gcd[k].gd.handle_controlevent = RH_Add;
521 	gcd[k].creator = GButtonCreate;
522 	barray[0][0] = GCD_Glue; barray[0][1] = &gcd[k++]; barray[0][2] = GCD_Glue;
523 
524 	gcd[k].gd.flags = gg_visible | gg_enabled;
525 	label[k].text = (unichar_t *) _("Re_move");
526 	label[k].text_is_1byte = true;
527 	label[k].text_in_resource = true;
528 	gcd[k].gd.label = &label[k];
529 	gcd[k].gd.cid = CID_Remove;
530 	gcd[k].gd.handle_controlevent = RH_Remove;
531 	gcd[k].creator = GButtonCreate;
532 	barray[0][3] = &gcd[k++]; barray[0][4] = GCD_Glue; barray[0][5] = NULL;
533 
534 	gcd[k].gd.pos.width = 170-10;
535 	gcd[k].gd.flags = gg_enabled|gg_visible;
536 	gcd[k].creator = GLineCreate;
537 	barray[1][0] = GCD_Glue; barray[1][1] = &gcd[k++]; barray[1][2] = barray[1][3] = GCD_ColSpan; barray[1][4] = GCD_Glue; barray[1][5] = NULL;
538 
539 	gcd[k].gd.flags = gg_visible | gg_enabled;
540 	label[k].text = (unichar_t *) _("< _Prev");
541 	label[k].text_is_1byte = true;
542 	label[k].text_in_resource = true;
543 	gcd[k].gd.label = &label[k];
544 	gcd[k].gd.cid = CID_Prev;
545 	gcd[k].gd.popup_msg = _("Previous Hint.");
546 	gcd[k].gd.handle_controlevent = RH_NextPrev;
547 	gcd[k].creator = GButtonCreate;
548 	barray[2][0] = GCD_Glue; barray[2][1] = &gcd[k++]; barray[2][2] = GCD_Glue;
549 
550 	gcd[k].gd.flags = gg_visible | gg_enabled;
551 	label[k].text = (unichar_t *) _("_Next >");
552 	label[k].text_is_1byte = true;
553 	label[k].text_in_resource = true;
554 	gcd[k].gd.label = &label[k];
555 	gcd[k].gd.cid = CID_Next;
556 	gcd[k].gd.popup_msg = _("Next Hint.");
557 	gcd[k].gd.handle_controlevent = RH_NextPrev;
558 	gcd[k].creator = GButtonCreate;
559 	barray[2][3] = &gcd[k++]; barray[2][4] = GCD_Glue; barray[2][5] = NULL;
560 
561 	label[k].text = (unichar_t *) _("Regenerate Hint Substitution Points");
562 	label[k].text_is_1byte = true;
563 	label[k].text_in_resource = true;
564 	gcd[k].gd.label = &label[k];
565 	gcd[k].gd.flags = gg_enabled|gg_visible|gg_cb_on;
566 	gcd[k].gd.cid = CID_RegenHM;
567 	gcd[k].gd.popup_msg = _("If you have made any changes to the hints,\nthen in addition to changing the glyph's hints\nrefigure it's hint masks and substitution points.");
568 	gcd[k].creator = GCheckBoxCreate;
569 	barray[3][0] = &gcd[k++]; barray[3][1] = barray[3][2] = barray[3][3] = barray[3][4] = GCD_ColSpan; barray[3][5] = NULL;
570 
571 	gcd[k].gd.flags = gg_visible | gg_enabled;
572 	label[k].text = (unichar_t *) _("_OK");
573 	label[k].text_is_1byte = true;
574 	label[k].text_in_resource = true;
575 	gcd[k].gd.label = &label[k];
576 	gcd[k].gd.handle_controlevent = RH_OK;
577 	gcd[k].creator = GButtonCreate;
578 	barray[4][0] = GCD_Glue; barray[4][1] = &gcd[k++]; barray[4][2] = GCD_Glue;
579 
580 	gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
581 	label[k].text = (unichar_t *) _("_Cancel");
582 	label[k].text_is_1byte = true;
583 	label[k].text_in_resource = true;
584 	gcd[k].gd.label = &label[k];
585 	gcd[k].gd.handle_controlevent = RH_Cancel;
586 	gcd[k].creator = GButtonCreate;
587 	barray[4][3] = &gcd[k++]; barray[4][4] = GCD_Glue; barray[4][5] = NULL;
588 	barray[5][0] = NULL;
589 
590 	boxes[2].gd.flags = gg_enabled|gg_visible;
591 	boxes[2].gd.u.boxelements = harray2;
592 	boxes[2].creator = GHBoxCreate;
593 
594 	boxes[3].gd.flags = gg_enabled|gg_visible;
595 	boxes[3].gd.u.boxelements = harray3;
596 	boxes[3].creator = GHBoxCreate;
597 
598 	boxes[4].gd.flags = gg_enabled|gg_visible;
599 	boxes[4].gd.u.boxelements = harray1;
600 	boxes[4].creator = GHBoxCreate;
601 
602 	boxes[5].gd.flags = gg_enabled|gg_visible;
603 	boxes[5].gd.u.boxelements = harray4;
604 	boxes[5].creator = GHBoxCreate;
605 
606 	boxes[6].gd.flags = gg_enabled|gg_visible;
607 	boxes[6].gd.u.boxelements = barray[0];
608 	boxes[6].creator = GHVBoxCreate;
609 
610 	varray[0][0] = &boxes[2]; varray[0][1] = NULL;
611 	varray[1][0] = &boxes[3]; varray[1][1] = NULL;
612 	varray[2][0] = &boxes[4]; varray[2][1] = NULL;
613 	varray[3][0] = &boxes[5]; varray[3][1] = NULL;
614 	varray[4][0] = &boxes[6]; varray[4][1] = NULL;
615 	varray[5][0] = NULL;
616 
617 	boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
618 	boxes[0].gd.flags = gg_enabled|gg_visible;
619 	boxes[0].gd.u.boxelements = varray[0];
620 	boxes[0].gd.cid = CID_TopBox;
621 	boxes[0].creator = GHVGroupCreate;
622 
623 
624 	GGadgetsCreate(gw,boxes);
625 
626 	GHVBoxSetExpandableRow(boxes[0].ret,3);
627 	GHVBoxSetExpandableCol(boxes[2].ret,gb_expandglue);
628 	GHVBoxSetExpandableCol(boxes[3].ret,gb_expandglue);
629 	GHVBoxSetExpandableCol(boxes[4].ret,gb_expandglue);
630 	GHVBoxSetExpandableCol(boxes[5].ret,gb_expandglue);
631 	GHVBoxSetExpandableCol(boxes[6].ret,gb_expandglue);
632 	GHVBoxFitWindow(boxes[0].ret);
633     } else {
634 	gw = hd.gw;
635 	GDrawSetTransientFor(gw,(GWindow) -1);
636     }
637     if ( cv->b.sc->hstem==NULL && cv->b.sc->vstem==NULL )
638 	hd.active = NULL;
639     else if ( cv->b.sc->hstem!=NULL && cv->b.sc->vstem!=NULL ) {
640 	if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_HStem)))
641 	    hd.active = cv->b.sc->hstem;
642 	else
643 	    hd.active = cv->b.sc->vstem;
644     } else if ( cv->b.sc->hstem!=NULL ) {
645 	GGadgetSetChecked(GWidgetGetControl(gw,CID_HStem),true);
646 	hd.active = cv->b.sc->hstem;
647     } else {
648 	GGadgetSetChecked(GWidgetGetControl(gw,CID_VStem),true);
649 	hd.active = cv->b.sc->vstem;
650     }
651     hd.ishstem = (hd.active==cv->b.sc->hstem);
652     hd.oldh = StemInfoCopy(cv->b.sc->hstem);
653     hd.oldv = StemInfoCopy(cv->b.sc->vstem);
654     hd.oldmanual = cv->b.sc->manualhints;
655     hd.changed = false;
656     RH_SetupHint(&hd);
657     if ( hd.active!=NULL ) {
658 	GWidgetIndicateFocusGadget(GWidgetGetControl(gw,CID_Base));
659 	GTextFieldSelect(GWidgetGetControl(gw,CID_Base),0,-1);
660     }
661 
662     GWidgetHidePalettes();
663     GDrawSetVisible(gw,true);
664     while ( !hd.done )
665 	GDrawProcessOneEvent(NULL);
666     GDrawSetVisible(gw,false);
667 }
668 
669 typedef struct createhintdata {
670     unsigned int done: 1;
671     unsigned int ishstem: 1;
672     unsigned int preservehints: 1;
673     CharView *cv;
674     GWindow gw;
675 } CreateHintData;
676 
CH_OK(GGadget * g,GEvent * e)677 static int CH_OK(GGadget *g, GEvent *e) {
678     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
679 	CreateHintData *hd = GDrawGetUserData(GGadgetGetWindow(g));
680 	real base, width;
681 	int err = 0;
682 	StemInfo *h;
683 	int layer = CVLayer((CharViewBase *) hd->cv);
684 
685 	base = GetReal8(hd->gw,CID_Base,_("Base:"),&err);
686 	width = GetReal8(hd->gw,CID_Width,_("Size:"),&err);
687 	if ( err )
688 return(true);
689 	if ( hd->preservehints ) {
690 	    SCPreserveHints(hd->cv->b.sc,layer);
691 	    SCHintsChanged(hd->cv->b.sc);
692 	}
693 	h = chunkalloc(sizeof(StemInfo));
694 	if ( width==-21 || width==-20 ) {
695 	    base += width;
696 	    width = -width;
697 	    h->ghost = true;
698 	}
699 	h->start = base;
700 	h->width = width;
701 	if ( hd->ishstem ) {
702 	    SCGuessHHintInstancesAndAdd(hd->cv->b.sc,layer,h,0x80000000,0x80000000);
703 	    hd->cv->b.sc->hconflicts = StemListAnyConflicts(hd->cv->b.sc->hstem);
704 	} else {
705 	    SCGuessVHintInstancesAndAdd(hd->cv->b.sc,layer,h,0x80000000,0x80000000);
706 	    hd->cv->b.sc->vconflicts = StemListAnyConflicts(hd->cv->b.sc->vstem);
707 	}
708 	hd->cv->b.sc->manualhints = true;
709 	if ( h!=NULL && hd->cv->b.sc->parent->mm==NULL )
710 	    SCModifyHintMasksAdd(hd->cv->b.sc,layer,h);
711 	else
712 	    SCClearHintMasks(hd->cv->b.sc,layer,true);
713 	SCOutOfDateBackground(hd->cv->b.sc);
714 	SCUpdateAll(hd->cv->b.sc);
715 	hd->done = true;
716     }
717 return( true );
718 }
719 
CH_Cancel(GGadget * g,GEvent * e)720 static int CH_Cancel(GGadget *g, GEvent *e) {
721     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
722 	CreateHintData *hd = GDrawGetUserData(GGadgetGetWindow(g));
723 	hd->done = true;
724     }
725 return( true );
726 }
727 
chd_e_h(GWindow gw,GEvent * event)728 static int chd_e_h(GWindow gw, GEvent *event) {
729     if ( event->type==et_close ) {
730 	CreateHintData *hd = GDrawGetUserData(gw);
731 	hd->done = true;
732     } else if ( event->type == et_char ) {
733 return( false );
734     } else if ( event->type == et_map ) {
735 	/* Above palettes */
736 	GDrawRaise(gw);
737     }
738 return( true );
739 }
740 
CVCreateHint(CharView * cv,int ishstem,int preservehints)741 void CVCreateHint(CharView *cv,int ishstem,int preservehints) {
742     GRect pos;
743     GWindow gw;
744     GWindowAttrs wattrs;
745     GGadgetCreateData gcd[9], *harray1[4], *harray2[9], *barray[7], *varray[5][2], boxes[5];
746     GTextInfo label[9];
747     static CreateHintData chd;
748     char buffer[20]; unichar_t ubuf[20];
749 
750     chd.done = false;
751     chd.ishstem = ishstem;
752     chd.preservehints = preservehints;
753     chd.cv = cv;
754 
755     if ( chd.gw==NULL ) {
756 	memset(&wattrs,0,sizeof(wattrs));
757 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
758 	wattrs.event_masks = ~(1<<et_charup);
759 	wattrs.restrict_input_to_me = 1;
760 	wattrs.undercursor = 1;
761 	wattrs.cursor = ct_pointer;
762 	wattrs.utf8_window_title = _("Create Hint");
763 	wattrs.is_dlg = true;
764 	pos.x = pos.y = 0;
765 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,170));
766 	pos.height = GDrawPointsToPixels(NULL,90);
767 	chd.gw = gw = GDrawCreateTopWindow(NULL,&pos,chd_e_h,&chd,&wattrs);
768 
769 	memset(&label,0,sizeof(label));
770 	memset(&gcd,0,sizeof(gcd));
771 	memset(&boxes,0,sizeof(boxes));
772 
773 	label[0].text = (unichar_t *) _("_Base:");
774 	label[0].text_is_1byte = true;
775 	label[0].text_in_resource = true;
776 	gcd[0].gd.label = &label[0];
777 	gcd[0].gd.pos.x = 5; gcd[0].gd.pos.y = 17+5+6;
778 	gcd[0].gd.flags = gg_enabled|gg_visible;
779 	gcd[0].creator = GLabelCreate;
780 	harray2[0] = GCD_Glue; harray2[1] = &gcd[0];
781 
782 	sprintf( buffer, "%g", (double) (ishstem ? cv->p.cy : cv->p.cx) );
783 	label[1].text = (unichar_t *) buffer;
784 	label[1].text_is_1byte = true;
785 	gcd[1].gd.label = &label[1];
786 	gcd[1].gd.pos.x = 37; gcd[1].gd.pos.y = 17+5;  gcd[1].gd.pos.width = 40;
787 	gcd[1].gd.flags = gg_enabled|gg_visible;
788 	gcd[1].gd.cid = CID_Base;
789 	gcd[1].creator = GTextFieldCreate;
790 	harray2[2] = &gcd[1];
791 
792 	label[2].text = (unichar_t *) _("_Size:");
793 	label[2].text_is_1byte = true;
794 	label[2].text_in_resource = true;
795 	gcd[2].gd.label = &label[2];
796 	gcd[2].gd.pos.x = 90; gcd[2].gd.pos.y = 17+5+6;
797 	gcd[2].gd.flags = gg_enabled|gg_visible;
798 	gcd[2].creator = GLabelCreate;
799 	harray2[3] = GCD_Glue; harray2[4] = &gcd[2];
800 
801 	label[3].text = (unichar_t *) "60";
802 	label[3].text_is_1byte = true;
803 	gcd[3].gd.label = &label[3];
804 	gcd[3].gd.pos.x = 120; gcd[3].gd.pos.y = 17+5;  gcd[3].gd.pos.width = 40;
805 	gcd[3].gd.flags = gg_enabled|gg_visible;
806 	gcd[3].gd.cid = CID_Width;
807 	gcd[3].creator = GTextFieldCreate;
808 	harray2[5] = &gcd[3]; harray2[6] = GCD_Glue; harray2[7] = NULL;
809 
810 	gcd[4].gd.pos.x = 20-3; gcd[4].gd.pos.y = 17+37;
811 	gcd[4].gd.pos.width = -1; gcd[4].gd.pos.height = 0;
812 	gcd[4].gd.flags = gg_visible | gg_enabled | gg_but_default;
813 	label[4].text = (unichar_t *) _("_OK");
814 	label[4].text_is_1byte = true;
815 	label[4].text_in_resource = true;
816 	gcd[4].gd.mnemonic = 'O';
817 	gcd[4].gd.label = &label[4];
818 	gcd[4].gd.handle_controlevent = CH_OK;
819 	gcd[4].creator = GButtonCreate;
820 	barray[0] = GCD_Glue; barray[1] = &gcd[4]; barray[2] = GCD_Glue;
821 
822 	gcd[5].gd.pos.x = -20; gcd[5].gd.pos.y = 17+37+3;
823 	gcd[5].gd.pos.width = -1; gcd[5].gd.pos.height = 0;
824 	gcd[5].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
825 	label[5].text = (unichar_t *) _("_Cancel");
826 	label[5].text_is_1byte = true;
827 	label[5].text_in_resource = true;
828 	gcd[5].gd.label = &label[5];
829 	gcd[5].gd.mnemonic = 'C';
830 	gcd[5].gd.handle_controlevent = CH_Cancel;
831 	gcd[5].creator = GButtonCreate;
832 	barray[3] = GCD_Glue; barray[4] = &gcd[5]; barray[5] = GCD_Glue; barray[6] = NULL;
833 
834 	label[6].text = (unichar_t *) _("Create Horizontal Stem Hint");	/* Initialize to bigger size */
835 	label[6].text_is_1byte = true;
836 	gcd[6].gd.label = &label[6];
837 	gcd[6].gd.pos.x = 17; gcd[6].gd.pos.y = 5;
838 	gcd[6].gd.flags = gg_enabled|gg_visible;
839 	gcd[6].gd.cid = CID_Label;
840 	gcd[6].creator = GLabelCreate;
841 	harray1[0] = GCD_Glue; harray1[1] = &gcd[6]; harray1[2] = GCD_Glue; harray1[3] = NULL;
842 
843 	gcd[7].gd.pos.x = 5; gcd[7].gd.pos.y = 17+31;
844 	gcd[7].gd.pos.width = 170-10;
845 	gcd[7].gd.flags = gg_enabled|gg_visible;
846 	gcd[7].creator = GLineCreate;
847 
848 	boxes[2].gd.flags = gg_enabled|gg_visible;
849 	boxes[2].gd.u.boxelements = harray1;
850 	boxes[2].creator = GHBoxCreate;
851 
852 	boxes[3].gd.flags = gg_enabled|gg_visible;
853 	boxes[3].gd.u.boxelements = harray2;
854 	boxes[3].creator = GHBoxCreate;
855 
856 	boxes[4].gd.flags = gg_enabled|gg_visible;
857 	boxes[4].gd.u.boxelements = barray;
858 	boxes[4].creator = GHBoxCreate;
859 
860 	varray[0][0] = &boxes[2]; varray[0][1] = NULL;
861 	varray[1][0] = &boxes[3]; varray[1][1] = NULL;
862 	varray[2][0] = &gcd[7];   varray[2][1] = NULL;
863 	varray[3][0] = &boxes[4]; varray[3][1] = NULL;
864 	varray[4][0] = NULL;
865 
866 	boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
867 	boxes[0].gd.flags = gg_enabled|gg_visible;
868 	boxes[0].gd.u.boxelements = varray[0];
869 	boxes[0].creator = GHVGroupCreate;
870 
871 
872 	GGadgetsCreate(gw,boxes);
873 	GHVBoxSetExpandableCol(boxes[2].ret,gb_expandglue);
874 	GHVBoxSetExpandableCol(boxes[3].ret,gb_expandglue);
875 	GHVBoxSetExpandableCol(boxes[4].ret,gb_expandgluesame);
876 	GHVBoxFitWindow(boxes[0].ret);
877     } else {
878 	gw = chd.gw;
879 	sprintf( buffer, "%g", (double) (ishstem ? cv->p.cy : cv->p.cx) );
880 	uc_strcpy(ubuf,buffer);
881 	GGadgetSetTitle(GWidgetGetControl(gw,CID_Base),ubuf);
882 	GDrawSetTransientFor(gw,(GWindow) -1);
883     }
884     GGadgetSetTitle8(GWidgetGetControl(gw,CID_Label),
885 	    ishstem ? _("Create Horizontal Stem Hint") :
886 		    _("Create Vertical Stem Hint"));
887     GWidgetIndicateFocusGadget(GWidgetGetControl(gw,CID_Base));
888     GTextFieldSelect(GWidgetGetControl(gw,CID_Base),0,-1);
889 
890     GWidgetHidePalettes();
891     GDrawSetVisible(gw,true);
892     while ( !chd.done )
893 	GDrawProcessOneEvent(NULL);
894     GDrawSetVisible(gw,false);
895 }
896 
SCRemoveSelectedMinimumDistances(SplineChar * sc,int inx)897 void SCRemoveSelectedMinimumDistances(SplineChar *sc,int inx) {
898     /* Remove any minimum distance where at least one of the two points is */
899     /*  selected */
900     MinimumDistance *md, *prev, *next;
901     SplineSet *ss;
902     SplinePoint *sp;
903 
904     prev = NULL;
905     for ( md = sc->md; md!=NULL; md = next ) {
906 	next = md->next;
907 	if ( (inx==2 || inx==md->x) &&
908 		((md->sp1!=NULL && md->sp1->selected) ||
909 		 (md->sp2!=NULL && md->sp2->selected))) {
910 	    if ( prev==NULL )
911 		sc->md = next;
912 	    else
913 		prev->next = next;
914 	    chunkfree(md,sizeof(MinimumDistance));
915 	} else
916 	    prev = md;
917     }
918 
919     for ( ss=sc->layers[ly_fore].splines; ss!=NULL; ss=ss->next ) {
920 	for ( sp=ss->first; ; ) {
921 	    if ( sp->selected ) {
922 		if ( inx==2 ) sp->roundx = sp->roundy = false;
923 		else if ( inx==1 ) sp->roundx = false;
924 		else sp->roundy = false;
925 	    }
926 	    if ( sp->next==NULL )
927 	break;
928 	    sp = sp->next->to;
929 	    if ( sp==ss->first )
930 	break;
931 	}
932     }
933 }
934