1 /* -*- coding: utf-8 -*- */
2 /* Copyright (C) 2000-2012 by George Williams */
3 /*
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6 
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9 
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13 
14  * The name of the author may not be used to endorse or promote products
15  * derived from this software without specific prior written permission.
16 
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <fontforge-config.h>
30 
31 #include "autowidth2.h"
32 #include "chardata.h"
33 #include "cvundoes.h"
34 #include "fontforgeui.h"
35 #include "fvcomposite.h"
36 #include "fvfonts.h"
37 #include "gkeysym.h"
38 #include "lookups.h"
39 #include "namelist.h"
40 #include "splinefill.h"
41 #include "splineutil.h"
42 #include "tottfgpos.h"
43 #include "ttf.h"		/* For MAC_DELETED_GLYPH_NAME */
44 #include "unicodelibinfo.h"
45 #include "ustring.h"
46 #include "utype.h"
47 
48 #include <math.h>
49 
50 extern int lookup_hideunused;
51 
52 static int last_gi_aspect = 0;
53 
54 typedef struct charinfo {
55     CharView *cv;
56     EncMap *map;
57     SplineChar *sc, *cachedsc;
58     int def_layer;
59     SplineChar *oldsc;		/* oldsc->charinfo will point to us. Used to keep track of that pointer */
60     int enc;
61     GWindow gw;
62     int done, first, changed;
63     struct lookup_subtable *old_sub;
64     int r,c;
65     int lc_seen, lc_aspect, vert_aspect;
66     Color last, real_last;
67     struct splinecharlist *changes;
68     int name_change, uni_change;
69 } CharInfo;
70 
71 #define CI_Width	218
72 #define CI_Height	292
73 
74 #define CID_UName	1001
75 #define CID_UValue	1002
76 #define CID_UChar	1003
77 #define CID_Cancel	1005
78 #define CID_ComponentMsg	1006
79 #define CID_Components	1007
80 #define CID_ComponentTextField  1008
81 #define CID_ComponentChangeMsg  1009
82 #define CID_ComponentDefaultCB  1010
83 #define CID_ComponentInterpMsg  1011
84 #define CID_Comment	1012
85 #define CID_Color	1013
86 #define CID_GClass	1014
87 #define CID_Tabs	1015
88 
89 #define CID_TeX_Height	1016
90 #define CID_TeX_Depth	1017
91 #define CID_TeX_Italic	1018
92 #define CID_HorAccent	1019
93 /* Room for one more here, if we need it */
94 #define CID_TeX_HeightD	1020
95 #define CID_TeX_DepthD	1021
96 #define CID_TeX_ItalicD	1022
97 #define CID_HorAccentD	1023
98 
99 #define CID_ItalicDevTab	1024
100 #define CID_AccentDevTab	1025
101 
102 #define CID_IsExtended	1026
103 #define CID_DefLCCount	1040
104 #define CID_LCCount	1041
105 #define CID_LCCountLab	1042
106 
107 #define CID_UnlinkRmOverlap	1045
108 #define CID_AltUni	1046
109 
110 /* Offsets for repeated fields. add 100*index (index<=6) */
111 #define CID_List	1220
112 #define CID_New		1221
113 #define CID_Delete	1222
114 #define CID_Edit	1223
115 
116 #define CID_PST		1111
117 #define CID_Tag		1112
118 #define CID_Contents	1113
119 #define CID_SelectResults	1114
120 #define CID_MergeResults	1115
121 #define CID_RestrictSelection	1116
122 
123 /* Offsets for repeated fields. add 100*index (index<2) */ /* 0=>Vert, 1=>Hor */
124 #define CID_VariantList		2000
125 #define CID_ExtItalicCor	2001
126 #define CID_ExtItalicDev	2002
127 #define CID_ExtensionList	2003
128 
129 #define CID_IsTileMargin	3001
130 #define CID_TileMargin		3002
131 #define CID_IsTileBBox		3003
132 #define CID_TileBBoxMinX	3004
133 #define CID_TileBBoxMinY	3005
134 #define CID_TileBBoxMaxX	3006
135 #define CID_TileBBoxMaxY	3007
136 
137 #define SIM_DX		1
138 #define SIM_DY		3
139 #define SIM_DX_ADV	5
140 #define SIM_DY_ADV	7
141 #define PAIR_DX1	2
142 #define PAIR_DY1	4
143 #define PAIR_DX_ADV1	6
144 #define PAIR_DY_ADV1	8
145 #define PAIR_DX2	10
146 #define PAIR_DY2	12
147 #define PAIR_DX_ADV2	14
148 #define PAIR_DY_ADV2	16
149 
150 static GTextInfo glyphclasses[] = {
151     { (unichar_t *) N_("Automatic"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
152     { (unichar_t *) N_("No Class"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
153     { (unichar_t *) N_("Base Glyph"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
154     { (unichar_t *) N_("Base Lig"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
155     { (unichar_t *) N_("Mark"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
156     { (unichar_t *) N_("Component"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
157     GTEXTINFO_EMPTY
158 };
159 
160 #define CUSTOM_COLOR	9
161 #define COLOR_CHOOSE	(-10)
162 static GTextInfo std_colors[] = {
163     { (unichar_t *) N_("Color|Choose..."), NULL, 0, 0, (void *) COLOR_CHOOSE, NULL, 0, 1, 0, 0, 0, 0, 1, 0, 0, '\0' },
164     { (unichar_t *) N_("Color|Default"), &def_image, 0, 0, (void *) COLOR_DEFAULT, NULL, 0, 1, 0, 0, 0, 0, 1, 0, 0, '\0' },
165     { NULL, &white_image, 0, 0, (void *) 0xffffff, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
166     { NULL, &red_image, 0, 0, (void *) 0xff0000, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
167     { NULL, &green_image, 0, 0, (void *) 0x00ff00, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
168     { NULL, &blue_image, 0, 0, (void *) 0x0000ff, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
169     { NULL, &yellow_image, 0, 0, (void *) 0xffff00, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
170     { NULL, &cyan_image, 0, 0, (void *) 0x00ffff, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
171     { NULL, &magenta_image, 0, 0, (void *) 0xff00ff, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
172     { NULL, NULL, 0, 0, (void *) 0x000000, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
173     GTEXTINFO_EMPTY
174 };
175 
176 static char *newstrings[] = { N_("New Positioning"), N_("New Pair Position"),
177 	N_("New Substitution Variant"),
178 	N_("New Alternate List"), N_("New Multiple List"), N_("New Ligature"), NULL };
179 
CounterMaskLine(SplineChar * sc,HintMask * hm)180 static unichar_t *CounterMaskLine(SplineChar *sc, HintMask *hm) {
181     unichar_t *textmask = NULL;
182     int j,k,len;
183     StemInfo *h;
184     char buffer[100];
185 
186     for ( j=0; j<2; ++j ) {
187 	len = 0;
188 	for ( h=sc->hstem, k=0; h!=NULL && k<HntMax; h=h->next, ++k ) {
189 	    if ( (*hm)[k>>3]& (0x80>>(k&7)) ) {
190 		sprintf( buffer, "H<%g,%g>, ",
191 			rint(h->start*100)/100, rint(h->width*100)/100 );
192 		if ( textmask!=NULL )
193 		    uc_strcpy(textmask+len,buffer);
194 		len += strlen(buffer);
195 	    }
196 	}
197 	for ( h=sc->vstem; h!=NULL && k<HntMax; h=h->next, ++k ) {
198 	    if ( (*hm)[k>>3]& (0x80>>(k&7)) ) {
199 		sprintf( buffer, "V<%g,%g>, ",
200 			rint(h->start*100)/100, rint(h->width*100)/100 );
201 		if ( textmask!=NULL )
202 		    uc_strcpy(textmask+len,buffer);
203 		len += strlen(buffer);
204 	    }
205 	}
206 	if ( textmask==NULL ) {
207 	    textmask = malloc((len+1)*sizeof(unichar_t));
208 	    *textmask = '\0';
209 	}
210     }
211     if ( len>1 && textmask[len-2]==',' )
212 	textmask[len-2] = '\0';
213 return( textmask );
214 }
215 
216 #define CID_HintMask	2020
217 #define HI_Width	200
218 #define HI_Height	260
219 
220 struct hi_data {
221     int done, ok, empty;
222     GWindow gw;
223     HintMask *cur;
224     SplineChar *sc;
225 };
226 
HI_Ok(GGadget * g,GEvent * e)227 static int HI_Ok(GGadget *g, GEvent *e) {
228     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
229 	struct hi_data *hi = GDrawGetUserData(GGadgetGetWindow(g));
230 	int32 i, len;
231 	GTextInfo **ti = GGadgetGetList(GWidgetGetControl(hi->gw,CID_HintMask),&len);
232 
233 	for ( i=0; i<len; ++i )
234 	    if ( ti[i]->selected )
235 	break;
236 
237 	memset(hi->cur,0,sizeof(HintMask));
238 	if ( i==len ) {
239 	    hi->empty = true;
240 	} else {
241 	    for ( i=0; i<len; ++i )
242 		if ( ti[i]->selected )
243 		    (*hi->cur)[i>>3] |= (0x80>>(i&7));
244 	}
245 	PI_ShowHints(hi->sc,GWidgetGetControl(hi->gw,CID_HintMask),false);
246 
247 	hi->done = true;
248 	hi->ok = true;
249     }
250 return( true );
251 }
252 
HI_DoCancel(struct hi_data * hi)253 static void HI_DoCancel(struct hi_data *hi) {
254     hi->done = true;
255     PI_ShowHints(hi->sc,GWidgetGetControl(hi->gw,CID_HintMask),false);
256 }
257 
HI_HintSel(GGadget * g,GEvent * e)258 static int HI_HintSel(GGadget *g, GEvent *e) {
259     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
260 	struct hi_data *hi = GDrawGetUserData(GGadgetGetWindow(g));
261 
262 	PI_ShowHints(hi->sc,g,true);
263 	/* Do I need to check for overlap here? */
264     }
265 return( true );
266 }
267 
HI_Cancel(GGadget * g,GEvent * e)268 static int HI_Cancel(GGadget *g, GEvent *e) {
269     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
270 	HI_DoCancel( GDrawGetUserData(GGadgetGetWindow(g)));
271     }
272 return( true );
273 }
274 
hi_e_h(GWindow gw,GEvent * event)275 static int hi_e_h(GWindow gw, GEvent *event) {
276     if ( event->type==et_close ) {
277 	HI_DoCancel( GDrawGetUserData(gw));
278     } else if ( event->type==et_char ) {
279 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
280 	    help("ui/dialogs/charinfo.html", "#charinfo-counters");
281 return( true );
282 	}
283 return( false );
284     }
285 return( true );
286 }
287 
CI_AskCounters(CharInfo * ci,HintMask * old)288 static void CI_AskCounters(CharInfo *ci,HintMask *old) {
289     HintMask *cur = old != NULL ? old : chunkalloc(sizeof(HintMask));
290     struct hi_data hi;
291     GWindowAttrs wattrs;
292     GGadgetCreateData hgcd[5], *varray[11], *harray[8], boxes[3];
293     GTextInfo hlabel[5];
294     GGadget *list = GWidgetGetControl(ci->gw,CID_List+600);
295     int j,k;
296     GRect pos;
297 
298     memset(&hi,0,sizeof(hi));
299     hi.cur = cur;
300     hi.sc = ci->sc;
301 
302 	memset(&wattrs,0,sizeof(wattrs));
303 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
304 	wattrs.event_masks = ~(1<<et_charup);
305 	wattrs.restrict_input_to_me = 1;
306 	wattrs.undercursor = 1;
307 	wattrs.cursor = ct_pointer;
308 	wattrs.utf8_window_title = old==NULL?_("New Counter Mask"):_("Edit Counter Mask");
309 	wattrs.is_dlg = true;
310 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,HI_Width));
311 	pos.height = GDrawPointsToPixels(NULL,HI_Height);
312 	hi.gw = GDrawCreateTopWindow(NULL,&pos,hi_e_h,&hi,&wattrs);
313 
314 
315 	memset(hgcd,0,sizeof(hgcd));
316 	memset(boxes,0,sizeof(boxes));
317 	memset(hlabel,0,sizeof(hlabel));
318 
319 	j=k=0;
320 
321 	hgcd[j].gd.pos.x = 20-3; hgcd[j].gd.pos.y = HI_Height-31-3;
322 	hgcd[j].gd.pos.width = -1; hgcd[j].gd.pos.height = 0;
323 	hgcd[j].gd.flags = gg_visible | gg_enabled;
324 	hlabel[j].text = (unichar_t *) _("Select hints between which counters are formed");
325 	hlabel[j].text_is_1byte = true;
326 	hlabel[j].text_in_resource = true;
327 	hgcd[j].gd.label = &hlabel[j];
328 	varray[k++] = &hgcd[j]; varray[k++] = NULL;
329 	hgcd[j++].creator = GLabelCreate;
330 
331 	hgcd[j].gd.pos.x = 5; hgcd[j].gd.pos.y = 5;
332 	hgcd[j].gd.pos.width = HI_Width-10; hgcd[j].gd.pos.height = HI_Height-45;
333 	hgcd[j].gd.flags = gg_visible | gg_enabled | gg_list_multiplesel;
334 	hgcd[j].gd.cid = CID_HintMask;
335 	hgcd[j].gd.u.list = SCHintList(ci->sc,old);
336 	hgcd[j].gd.handle_controlevent = HI_HintSel;
337 	varray[k++] = &hgcd[j]; varray[k++] = NULL;
338 	varray[k++] = GCD_Glue; varray[k++] = NULL;
339 	hgcd[j++].creator = GListCreate;
340 
341 	hgcd[j].gd.pos.x = 20-3; hgcd[j].gd.pos.y = HI_Height-31-3;
342 	hgcd[j].gd.pos.width = -1; hgcd[j].gd.pos.height = 0;
343 	hgcd[j].gd.flags = gg_visible | gg_enabled | gg_but_default;
344 	hlabel[j].text = (unichar_t *) _("_OK");
345 	hlabel[j].text_is_1byte = true;
346 	hlabel[j].text_in_resource = true;
347 	hgcd[j].gd.label = &hlabel[j];
348 	hgcd[j].gd.handle_controlevent = HI_Ok;
349 	harray[0] = GCD_Glue; harray[1] = &hgcd[j]; harray[2] = GCD_Glue; harray[3] = GCD_Glue;
350 	hgcd[j++].creator = GButtonCreate;
351 
352 	hgcd[j].gd.pos.x = -20; hgcd[j].gd.pos.y = HI_Height-31;
353 	hgcd[j].gd.pos.width = -1; hgcd[j].gd.pos.height = 0;
354 	hgcd[j].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
355 	hlabel[j].text = (unichar_t *) _("_Cancel");
356 	hlabel[j].text_is_1byte = true;
357 	hlabel[j].text_in_resource = true;
358 	hgcd[j].gd.label = &hlabel[j];
359 	hgcd[j].gd.handle_controlevent = HI_Cancel;
360 	harray[4] = GCD_Glue; harray[5] = &hgcd[j]; harray[6] = GCD_Glue; harray[7] = NULL;
361 	hgcd[j++].creator = GButtonCreate;
362 
363 	boxes[2].gd.flags = gg_enabled|gg_visible;
364 	boxes[2].gd.u.boxelements = harray;
365 	boxes[2].creator = GHBoxCreate;
366 	varray[k++] = &boxes[2]; varray[k++] = NULL;
367 	varray[k++] = GCD_Glue; varray[k++] = NULL;
368 	varray[k] = NULL;
369 
370 	boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
371 	boxes[0].gd.flags = gg_enabled|gg_visible;
372 	boxes[0].gd.u.boxelements = varray;
373 	boxes[0].creator = GHVGroupCreate;
374 
375 	GGadgetsCreate(hi.gw,boxes);
376 	GHVBoxSetExpandableRow(boxes[0].ret,1);
377 	GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
378 	GHVBoxFitWindow(boxes[0].ret);
379 	GTextInfoListFree(hgcd[0].gd.u.list);
380 
381 	PI_ShowHints(hi.sc,hgcd[0].ret,true);
382 
383     GDrawSetVisible(hi.gw,true);
384     while ( !hi.done )
385 	GDrawProcessOneEvent(NULL);
386     GDrawDestroyWindow(hi.gw);
387 
388     if ( !hi.ok ) {
389 	if ( old==NULL ) chunkfree(cur,sizeof(HintMask));
390 return;		/* Cancelled */
391     } else if ( old==NULL && hi.empty ) {
392 	if ( old==NULL ) chunkfree(cur,sizeof(HintMask));
393 return;		/* Didn't add anything new */
394     } else if ( old==NULL ) {
395 	GListAddStr(list,CounterMaskLine(hi.sc,cur),cur);
396 return;
397     } else if ( !hi.empty ) {
398 	GListReplaceStr(list,GGadgetGetFirstListSelectedItem(list),
399 		CounterMaskLine(hi.sc,cur),cur);
400 return;
401     } else {
402 	GListDelSelected(list);
403 	chunkfree(cur,sizeof(HintMask));
404     }
405 }
406 
CI_NewCounter(GGadget * g,GEvent * e)407 static int CI_NewCounter(GGadget *g, GEvent *e) {
408     CharInfo *ci;
409 
410     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
411 	ci = GDrawGetUserData(GGadgetGetWindow(g));
412 	CI_AskCounters(ci,NULL);
413     }
414 return( true );
415 }
416 
CI_EditCounter(GGadget * g,GEvent * e)417 static int CI_EditCounter(GGadget *g, GEvent *e) {
418     GTextInfo *ti;
419     GGadget *list;
420     CharInfo *ci;
421 
422     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
423 	ci = GDrawGetUserData(GGadgetGetWindow(g));
424 	list = GWidgetGetControl(GGadgetGetWindow(g),CID_List+6*100);
425 	if ( (ti = GGadgetGetListItemSelected(list))==NULL )
426 return( true );
427 	CI_AskCounters(ci,ti->userdata);
428     }
429 return( true );
430 }
431 
CI_DeleteCounter(GGadget * g,GEvent * e)432 static int CI_DeleteCounter(GGadget *g, GEvent *e) {
433     int32 len; int i,j, offset;
434     GTextInfo **old, **new_;
435     GGadget *list;
436     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
437 	offset = GGadgetGetCid(g)-CID_Delete;
438 	list = GWidgetGetControl(GGadgetGetWindow(g),CID_List+offset);
439 	old = GGadgetGetList(list,&len);
440 	new_ = calloc(len+1,sizeof(GTextInfo *));
441 	for ( i=j=0; i<len; ++i )
442 	    if ( !old[i]->selected ) {
443 		new_[j] = (GTextInfo *) malloc(sizeof(GTextInfo));
444 		*new_[j] = *old[i];
445 		new_[j]->text = u_copy(new_[j]->text);
446 		++j;
447 	    }
448 	new_[j] = (GTextInfo *) calloc(1,sizeof(GTextInfo));
449 	if ( offset==600 ) {
450 	    for ( i=0; i<len; ++i ) if ( old[i]->selected )
451 		chunkfree(old[i]->userdata,sizeof(HintMask));
452 	}
453 	GGadgetSetList(list,new_,false);
454 	GGadgetSetEnabled(GWidgetGetControl(GGadgetGetWindow(g),CID_Delete+offset),false);
455 	GGadgetSetEnabled(GWidgetGetControl(GGadgetGetWindow(g),CID_Edit+offset),false);
456     }
457 return( true );
458 }
459 
CI_CounterSelChanged(GGadget * g,GEvent * e)460 static int CI_CounterSelChanged(GGadget *g, GEvent *e) {
461     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
462 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
463 	int sel = GGadgetGetFirstListSelectedItem(g);
464 	int offset = GGadgetGetCid(g)-CID_List;
465 	GGadgetSetEnabled(GWidgetGetControl(ci->gw,CID_Delete+offset),sel!=-1);
466 	GGadgetSetEnabled(GWidgetGetControl(ci->gw,CID_Edit+offset),sel!=-1);
467     } else if ( e->type==et_controlevent && e->u.control.subtype == et_listdoubleclick ) {
468 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
469 	int offset = GGadgetGetCid(g)-CID_List;
470 	e->u.control.subtype = et_buttonactivate;
471 	e->u.control.g = GWidgetGetControl(ci->gw,CID_Edit+offset);
472 	CI_EditCounter(e->u.control.g,e);
473     }
474 return( true );
475 }
476 
ParseUValue(GWindow gw,int cid,int minusoneok)477 static int ParseUValue(GWindow gw, int cid, int minusoneok) {
478     const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(gw,cid));
479     unichar_t *end;
480     int val;
481 
482     if (( *ret=='U' || *ret=='u' ) && ret[1]=='+' )
483 	val = u_strtoul(ret+2,&end,16);
484     else if ( *ret=='#' )
485 	val = u_strtoul(ret+1,&end,16);
486     else
487 	val = u_strtoul(ret,&end,16);
488     if ( val==-1 && minusoneok )
489 return( -1 );
490     if ( *end || val<0 || val>0x10ffff ) {
491 	GGadgetProtest8( _("Unicode _Value:") );
492 return( -2 );
493     }
494 return( val );
495 }
496 
SetNameFromUnicode(GWindow gw,int cid,int val)497 static void SetNameFromUnicode(GWindow gw,int cid,int val) {
498     unichar_t *temp;
499     char buf[100];
500     CharInfo *ci = GDrawGetUserData(gw);
501 
502     temp = utf82u_copy(StdGlyphName(buf,val,ci->sc->parent->uni_interp,ci->sc->parent->for_new_glyphs));
503     GGadgetSetTitle(GWidgetGetControl(gw,cid),temp);
504     free(temp);
505 }
506 
SCInsertPST(SplineChar * sc,PST * new_)507 void SCInsertPST(SplineChar *sc,PST *new_) {
508     new_->next = sc->possub;
509     sc->possub = new_;
510 }
511 
CI_NameCheck(const unichar_t * name)512 static int CI_NameCheck(const unichar_t *name) {
513     int bad, questionable;
514     extern int allow_utf8_glyphnames;
515     char *buts[3];
516     buts[0] = _("_Yes"); buts[1]=_("_No"); buts[2] = NULL;
517 
518     if ( uc_strcmp(name,".notdef")==0 )		/* This name is a special case and doesn't follow conventions */
519 return( true );
520     if ( u_strlen(name)>31 ) {
521 	ff_post_error(_("Bad Name"),_("Glyph names are limited to 31 characters"));
522 return( false );
523     } else if ( *name=='\0' ) {
524 	ff_post_error(_("Bad Name"),_("Bad Name"));
525 return( false );
526     } else if ( isdigit(*name) || *name=='.' ) {
527 	ff_post_error(_("Bad Name"),_("A glyph name may not start with a digit nor a full stop (period)"));
528 return( false );
529     }
530     bad = questionable = false;
531     while ( *name ) {
532 	if ( *name<=' ' || (!allow_utf8_glyphnames && *name>=0x7f) ||
533 		*name=='(' || *name=='[' || *name=='{' || *name=='<' ||
534 		*name==')' || *name==']' || *name=='}' || *name=='>' ||
535 		*name=='%' || *name=='/' )
536 	    bad=true;
537 	else if ( !isalnum(*name) && *name!='.' && *name!='_' )
538 	    questionable = true;
539 	++name;
540     }
541     if ( bad ) {
542 	ff_post_error(_("Bad Name"),_("A glyph name must be ASCII, without spaces and may not contain the characters \"([{<>}])/%%\", and should contain only alphanumerics, periods and underscores"));
543 return( false );
544     } else if ( questionable ) {
545 	if ( gwwv_ask(_("Bad Name"),(const char **) buts,0,1,_("A glyph name should contain only alphanumerics, periods and underscores\nDo you want to use this name in spite of that?"))==1 )
546 return(false);
547     }
548 return( true );
549 }
550 
CI_ParseCounters(CharInfo * ci)551 static void CI_ParseCounters(CharInfo *ci) {
552     int32 i,len;
553     GTextInfo **ti = GGadgetGetList(GWidgetGetControl(ci->gw,CID_List+600),&len);
554     SplineChar *sc = ci->cachedsc;
555 
556     free(sc->countermasks);
557 
558     sc->countermask_cnt = len;
559     if ( len==0 )
560 	sc->countermasks = NULL;
561     else {
562 	sc->countermasks = malloc(len*sizeof(HintMask));
563 	for ( i=0; i<len; ++i ) {
564 	    memcpy(sc->countermasks[i],ti[i]->userdata,sizeof(HintMask));
565 	    chunkfree(ti[i]->userdata,sizeof(HintMask));
566 	    ti[i]->userdata = NULL;
567 	}
568     }
569 }
570 
DeviceTableOK(char * dvstr,int * _low,int * _high)571 int DeviceTableOK(char *dvstr, int *_low, int *_high) {
572     char *pt, *end;
573     int low, high, pixel, cor;
574 
575     low = high = -1;
576     if ( dvstr!=NULL ) {
577 	while ( *dvstr==' ' ) ++dvstr;
578 	for ( pt=dvstr; *pt; ) {
579 	    pixel = strtol(pt,&end,10);
580 	    if ( pixel<=0 || pt==end)
581 	break;
582 	    pt = end;
583 	    if ( *pt==':' ) ++pt;
584 	    cor = strtol(pt,&end,10);
585 	    if ( pt==end || cor<-128 || cor>127 )
586 	break;
587 	    pt = end;
588 	    while ( *pt==' ' ) ++pt;
589 	    if ( *pt==',' ) ++pt;
590 	    while ( *pt==' ' ) ++pt;
591 	    if ( low==-1 ) low = high = pixel;
592 	    else if ( pixel<low ) low = pixel;
593 	    else if ( pixel>high ) high = pixel;
594 	}
595 	if ( *pt != '\0' )
596 return( false );
597     }
598     *_low = low; *_high = high;
599 return( true );
600 }
601 
DeviceTableParse(DeviceTable * dv,char * dvstr)602 DeviceTable *DeviceTableParse(DeviceTable *dv,char *dvstr) {
603     char *pt, *end;
604     int low, high, pixel, cor;
605 
606     DeviceTableOK(dvstr,&low,&high);
607     if ( low==-1 ) {
608 	if ( dv!=NULL ) {
609 	    free(dv->corrections);
610 	    memset(dv,0,sizeof(*dv));
611 	}
612 return( dv );
613     }
614     if ( dv==NULL )
615 	dv = chunkalloc(sizeof(DeviceTable));
616     else
617 	free(dv->corrections);
618     dv->first_pixel_size = low;
619     dv->last_pixel_size = high;
620     dv->corrections = calloc(high-low+1,1);
621 
622     for ( pt=dvstr; *pt; ) {
623 	pixel = strtol(pt,&end,10);
624 	if ( pixel<=0 || pt==end)
625     break;
626 	pt = end;
627 	if ( *pt==':' ) ++pt;
628 	cor = strtol(pt,&end,10);
629 	if ( pt==end || cor<-128 || cor>127 )
630     break;
631 	pt = end;
632 	while ( *pt==' ' ) ++pt;
633 	if ( *pt==',' ) ++pt;
634 	while ( *pt==' ' ) ++pt;
635 	dv->corrections[pixel-low] = cor;
636     }
637 return( dv );
638 }
639 
VRDevTabParse(struct vr * vr,struct matrix_data * md)640 void VRDevTabParse(struct vr *vr,struct matrix_data *md) {
641     ValDevTab temp, *adjust;
642     int any = false;
643 
644     if ( (adjust = vr->adjust)==NULL ) {
645 	adjust = &temp;
646 	memset(&temp,0,sizeof(temp));
647     }
648     any |= (DeviceTableParse(&adjust->xadjust,md[0].u.md_str)!=NULL);
649     any |= (DeviceTableParse(&adjust->yadjust,md[2].u.md_str)!=NULL);
650     any |= (DeviceTableParse(&adjust->xadv,md[4].u.md_str)!=NULL);
651     any |= (DeviceTableParse(&adjust->yadv,md[6].u.md_str)!=NULL);
652     if ( any && adjust==&temp ) {
653 	vr->adjust = chunkalloc(sizeof(ValDevTab));
654 	*vr->adjust = temp;
655     } else if ( !any && vr->adjust!=NULL ) {
656 	ValDevFree(vr->adjust);
657 	vr->adjust = NULL;
658     }
659 }
660 
DevTabToString(char ** str,DeviceTable * adjust)661 void DevTabToString(char **str,DeviceTable *adjust) {
662     char *pt;
663     int i;
664 
665     if ( adjust==NULL || adjust->corrections==NULL ) {
666 	*str = NULL;
667 return;
668     }
669     *str = pt = malloc(11*(adjust->last_pixel_size-adjust->first_pixel_size+1)+1);
670     for ( i=adjust->first_pixel_size; i<=adjust->last_pixel_size; ++i ) {
671 	if ( adjust->corrections[i-adjust->first_pixel_size]!=0 )
672 	    sprintf( pt, "%d:%d, ", i, adjust->corrections[i-adjust->first_pixel_size]);
673 	pt += strlen(pt);
674     }
675     if ( pt>*str && pt[-2] == ',' )
676 	pt[-2] = '\0';
677 }
678 
ValDevTabToStrings(struct matrix_data * mds,int first_offset,ValDevTab * adjust)679 void ValDevTabToStrings(struct matrix_data *mds,int first_offset,ValDevTab *adjust) {
680     if ( adjust==NULL )
681 return;
682     DevTabToString(&mds[first_offset].u.md_str,&adjust->xadjust);
683     DevTabToString(&mds[first_offset+2].u.md_str,&adjust->yadjust);
684     DevTabToString(&mds[first_offset+4].u.md_str,&adjust->xadv);
685     DevTabToString(&mds[first_offset+6].u.md_str,&adjust->yadv);
686 }
687 
KpMDParse(SplineChar * sc,struct lookup_subtable * sub,struct matrix_data * possub,int rows,int cols,int i)688 void KpMDParse(SplineChar *sc,struct lookup_subtable *sub,
689 	struct matrix_data *possub,int rows,int cols,int i) {
690     SplineChar *other;
691     PST *pst;
692     KernPair *kp;
693     int isv, iskpable, offset, newv;
694     char *dvstr;
695     char *pt, *start;
696     int ch;
697 
698     for ( start=possub[cols*i+1].u.md_str; ; ) {
699 	while ( *start==' ' ) ++start;
700 	if ( *start=='\0' )
701     break;
702 	for ( pt=start; *pt!='\0' && *pt!=' ' && *pt!='('; ++pt );
703 	ch = *pt; *pt = '\0';
704 	other = SFGetChar(sc->parent,-1,start);
705 	for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
706 	    if ( pst->subtable == sub &&
707 		    strcmp(start,pst->u.pair.paired)==0 )
708 	break;
709 	}
710 	kp = NULL;
711 	if ( pst==NULL && other!=NULL ) {
712 	    for ( isv=0; isv<2; ++isv ) {
713 		for ( kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kp->next )
714 		    if ( kp->subtable==(void *) possub[cols*i+0].u.md_ival &&
715 			    kp->sc == other )
716 		break;
717 		if ( kp!=NULL )
718 	    break;
719 	    }
720 	}
721 	newv = false;
722 	if ( other==NULL )
723 	    iskpable = false;
724 	else if ( sub->vertical_kerning ) {
725 	    newv = true;
726 	    iskpable = possub[cols*i+PAIR_DX1].u.md_ival==0 &&
727 			possub[cols*i+PAIR_DY1].u.md_ival==0 &&
728 			possub[cols*i+PAIR_DX_ADV1].u.md_ival==0 &&
729 			possub[cols*i+PAIR_DX2].u.md_ival==0 &&
730 			possub[cols*i+PAIR_DY2].u.md_ival==0 &&
731 			possub[cols*i+PAIR_DX_ADV2].u.md_ival==0 &&
732 			possub[cols*i+PAIR_DY_ADV2].u.md_ival==0;
733 	    offset = possub[cols*i+PAIR_DY1].u.md_ival;
734 	    iskpable &= (possub[cols*i+PAIR_DX1+1].u.md_str==NULL || *possub[cols*i+PAIR_DX1+1].u.md_str==0 ) &&
735 			(possub[cols*i+PAIR_DY1+1].u.md_str==0 || *possub[cols*i+PAIR_DY1+1].u.md_str==0 ) &&
736 			(possub[cols*i+PAIR_DX_ADV1+1].u.md_str==0 || *possub[cols*i+PAIR_DX_ADV1+1].u.md_str==0 ) &&
737 			(possub[cols*i+PAIR_DX2+1].u.md_str==0 || *possub[cols*i+PAIR_DX2+1].u.md_str==0 ) &&
738 			(possub[cols*i+PAIR_DY2+1].u.md_str==0 || *possub[cols*i+PAIR_DY2+1].u.md_str==0 ) &&
739 			(possub[cols*i+PAIR_DX_ADV2+1].u.md_str==0 || *possub[cols*i+PAIR_DX_ADV2+1].u.md_str==0 ) &&
740 			(possub[cols*i+PAIR_DY_ADV2+1].u.md_str==0 || *possub[cols*i+PAIR_DY_ADV2+1].u.md_str==0 );
741 	    dvstr = possub[cols*i+PAIR_DY1+1].u.md_str;
742 	} else if ( sub->lookup->lookup_flags & pst_r2l ) {
743 	    iskpable = possub[cols*i+PAIR_DX1].u.md_ival==0 &&
744 			possub[cols*i+PAIR_DY1].u.md_ival==0 &&
745 			possub[cols*i+PAIR_DX_ADV1].u.md_ival==0 &&
746 			possub[cols*i+PAIR_DY_ADV1].u.md_ival==0 &&
747 			possub[cols*i+PAIR_DX2].u.md_ival==0 &&
748 			possub[cols*i+PAIR_DY2].u.md_ival==0 &&
749 			possub[cols*i+PAIR_DY_ADV2].u.md_ival==0;
750 	    offset = possub[cols*i+PAIR_DX_ADV2].u.md_ival;
751 	    iskpable &= (possub[cols*i+PAIR_DX1+1].u.md_str==NULL || *possub[cols*i+PAIR_DX1+1].u.md_str==0 ) &&
752 			(possub[cols*i+PAIR_DY1+1].u.md_str==0 || *possub[cols*i+PAIR_DY1+1].u.md_str==0 ) &&
753 			(possub[cols*i+PAIR_DX_ADV1+1].u.md_str==0 || *possub[cols*i+PAIR_DX_ADV1+1].u.md_str==0 ) &&
754 			(possub[cols*i+PAIR_DY_ADV1+1].u.md_str==0 || *possub[cols*i+PAIR_DY_ADV1+1].u.md_str==0 ) &&
755 			(possub[cols*i+PAIR_DX2+1].u.md_str==0 || *possub[cols*i+PAIR_DX2+1].u.md_str==0 ) &&
756 			(possub[cols*i+PAIR_DY2+1].u.md_str==0 || *possub[cols*i+PAIR_DY2+1].u.md_str==0 ) &&
757 			(possub[cols*i+PAIR_DY_ADV2+1].u.md_str==0 || *possub[cols*i+PAIR_DY_ADV2+1].u.md_str==0 );
758 	    dvstr = possub[cols*i+PAIR_DX_ADV2+1].u.md_str;
759 	} else {
760 	    iskpable = possub[cols*i+PAIR_DX1].u.md_ival==0 &&
761 			possub[cols*i+PAIR_DY1].u.md_ival==0 &&
762 			possub[cols*i+PAIR_DY_ADV1].u.md_ival==0 &&
763 			possub[cols*i+PAIR_DX2].u.md_ival==0 &&
764 			possub[cols*i+PAIR_DY2].u.md_ival==0 &&
765 			possub[cols*i+PAIR_DX_ADV2].u.md_ival==0 &&
766 			possub[cols*i+PAIR_DY_ADV2].u.md_ival==0;
767 	    offset = possub[cols*i+PAIR_DX_ADV1].u.md_ival;
768 	    iskpable &= (possub[cols*i+PAIR_DX1+1].u.md_str==NULL || *possub[cols*i+PAIR_DX1+1].u.md_str==0 ) &&
769 			(possub[cols*i+PAIR_DY1+1].u.md_str==0 || *possub[cols*i+PAIR_DY1+1].u.md_str==0 ) &&
770 			(possub[cols*i+PAIR_DY_ADV1+1].u.md_str==0 || *possub[cols*i+PAIR_DY_ADV1+1].u.md_str==0 ) &&
771 			(possub[cols*i+PAIR_DX2+1].u.md_str==0 || *possub[cols*i+PAIR_DX2+1].u.md_str==0 ) &&
772 			(possub[cols*i+PAIR_DY2+1].u.md_str==0 || *possub[cols*i+PAIR_DY2+1].u.md_str==0 ) &&
773 			(possub[cols*i+PAIR_DX_ADV2+1].u.md_str==0 || *possub[cols*i+PAIR_DX_ADV2+1].u.md_str==0 ) &&
774 			(possub[cols*i+PAIR_DY_ADV2+1].u.md_str==0 || *possub[cols*i+PAIR_DY_ADV2+1].u.md_str==0 );
775 	    dvstr = possub[cols*i+PAIR_DX_ADV1+1].u.md_str;
776 	}
777 	if ( iskpable ) {
778 	    if ( kp==NULL ) {
779 		/* If there's a pst, ignore it, it will not get ticked and will*/
780 		/*  be freed later */
781 		kp = chunkalloc(sizeof(KernPair));
782 		kp->subtable = sub;
783 		kp->sc = other;
784 		if ( newv ) {
785 		    kp->next = sc->vkerns;
786 		    sc->vkerns = kp;
787 		} else {
788 		    kp->next = sc->kerns;
789 		    sc->kerns = kp;
790 		}
791 	    }
792 	    DeviceTableFree(kp->adjust);
793 	    kp->adjust = DeviceTableParse(NULL,dvstr);
794 	    kp->off = offset;
795 	    kp->kcid = true;
796 	} else {
797 	    if ( pst == NULL ) {
798 		/* If there's a kp, ignore it, it will not get ticked and will*/
799 		/*  be freed later */
800 		pst = chunkalloc(sizeof(PST));
801 		pst->type = pst_pair;
802 		pst->subtable = sub;
803 		pst->next = sc->possub;
804 		sc->possub = pst;
805 		pst->u.pair.vr = chunkalloc(sizeof(struct vr [2]));
806 		pst->u.pair.paired = copy(start);
807 	    }
808 	    VRDevTabParse(&pst->u.pair.vr[0],&possub[cols*i+PAIR_DX1+1]);
809 	    VRDevTabParse(&pst->u.pair.vr[1],&possub[cols*i+PAIR_DX2]+1);
810 	    pst->u.pair.vr[0].xoff = possub[cols*i+PAIR_DX1].u.md_ival;
811 	    pst->u.pair.vr[0].yoff = possub[cols*i+PAIR_DY1].u.md_ival;
812 	    pst->u.pair.vr[0].h_adv_off = possub[cols*i+PAIR_DX_ADV1].u.md_ival;
813 	    pst->u.pair.vr[0].v_adv_off = possub[cols*i+PAIR_DY_ADV1].u.md_ival;
814 	    pst->u.pair.vr[1].xoff = possub[cols*i+PAIR_DX2].u.md_ival;
815 	    pst->u.pair.vr[1].yoff = possub[cols*i+PAIR_DY2].u.md_ival;
816 	    pst->u.pair.vr[1].h_adv_off = possub[cols*i+PAIR_DX_ADV2].u.md_ival;
817 	    pst->u.pair.vr[1].v_adv_off = possub[cols*i+PAIR_DY_ADV2].u.md_ival;
818 	    pst->ticked = true;
819 	}
820 	*pt = ch;
821 	if ( ch=='(' ) {
822 	    while ( *pt!=')' && *pt!='\0' ) ++pt;
823 	    if ( *pt==')' ) ++pt;
824 	}
825 	start = pt;
826     }
827 }
828 
CI_ProcessPosSubs(CharInfo * ci)829 static int CI_ProcessPosSubs(CharInfo *ci) {
830     /* Check for duplicate entries in kerning and ligatures. If we find any */
831     /*  complain and return failure */
832     /* Check for various other errors */
833     /* Otherwise process */
834     SplineChar *sc = ci->cachedsc, *found;
835     int i,j, rows, cols, isv, pstt, ch;
836     char *pt;
837     struct matrix_data *possub;
838     char *buts[3];
839     KernPair *kp, *kpprev, *kpnext;
840     PST *pst, *pstprev, *pstnext, *lcpst=NULL;
841 
842     possub = GMatrixEditGet(GWidgetGetControl(ci->gw,CID_List+(pst_ligature-1)*100), &rows );
843     cols = GMatrixEditGetColCnt(GWidgetGetControl(ci->gw,CID_List+(pst_ligature-1)*100) );
844     for ( i=0; i<rows; ++i ) {
845 	for ( j=i+1; j<rows; ++j ) {
846 	    if ( possub[cols*i+0].u.md_ival == possub[cols*j+0].u.md_ival &&
847 		    strcmp(possub[cols*i+1].u.md_str,possub[cols*j+1].u.md_str)==0 ) {
848 		ff_post_error( _("Duplicate Ligature"),_("There are two ligature entries with the same components (%.80s) in the same lookup subtable (%.30s)"),
849 			possub[cols*j+1].u.md_str,
850 			((struct lookup_subtable *) possub[cols*i+0].u.md_ival)->subtable_name );
851 return( false );
852 	    }
853 	}
854     }
855     possub = GMatrixEditGet(GWidgetGetControl(ci->gw,CID_List+(pst_pair-1)*100), &rows );
856     cols = GMatrixEditGetColCnt(GWidgetGetControl(ci->gw,CID_List+(pst_pair-1)*100));
857     for ( i=0; i<rows; ++i ) {
858 	for ( j=i+1; j<rows; ++j ) {
859 	    if ( possub[cols*i+0].u.md_ival == possub[cols*j+0].u.md_ival &&
860 		    strcmp(possub[cols*i+1].u.md_str,possub[cols*j+1].u.md_str)==0 ) {
861 		ff_post_error( _("Duplicate Kern data"),_("There are two kerning entries for the same glyph (%.80s) in the same lookup subtable (%.30s)"),
862 			possub[cols*j+1].u.md_str,
863 			((struct lookup_subtable *) possub[cols*i+0].u.md_ival)->subtable_name );
864 return( false );
865 	    }
866 	}
867     }
868 
869     /* Check for badly specified device tables */
870     for ( pstt = pst_position; pstt<=pst_pair; ++pstt ) {
871 	int startc = pstt==pst_position ? SIM_DX+1 : PAIR_DX1+1;
872 	int low, high, c, r;
873 	possub = GMatrixEditGet(GWidgetGetControl(ci->gw,CID_List+(pstt-1)*100), &rows );
874 	cols = GMatrixEditGetColCnt(GWidgetGetControl(ci->gw,CID_List+(pstt-1)*100));
875 	for ( r=0; r<rows; ++r ) {
876 	    for ( c=startc; c<cols; c+=2 ) {
877 		if ( !DeviceTableOK(possub[r*cols+c].u.md_str,&low,&high) ) {
878 		    ff_post_error( _("Bad Device Table Adjustment"),_("A device table adjustment specified for %.80s is invalid"),
879 			    possub[cols*r+0].u.md_str );
880 return( true );
881 		}
882 	    }
883 	}
884     }
885 
886     for ( pstt = pst_pair; pstt<=pst_ligature; ++pstt ) {
887 	possub = GMatrixEditGet(GWidgetGetControl(ci->gw,CID_List+(pstt-1)*100), &rows );
888 	cols = GMatrixEditGetColCnt(GWidgetGetControl(ci->gw,CID_List+(pstt-1)*100));
889 	for ( i=0; i<rows; ++i ) {
890 	    char *start = possub[cols*i+1].u.md_str;
891 	    while ( *start== ' ' ) ++start;
892 	    if ( *start=='\0' ) {
893 		ff_post_error( _("Missing glyph name"),_("You must specify a glyph name for subtable %s"),
894 			((struct lookup_subtable *) possub[cols*i+0].u.md_ival)->subtable_name );
895 return( false );
896 	    }
897 	    while ( *start ) {
898 		for ( pt=start; *pt!='\0' && *pt!=' ' && *pt!='(' ; ++pt );
899 		ch = *pt; *pt='\0';
900 		found = SFGetChar(sc->parent,-1,start);
901 		if ( found==NULL ) {
902 		    buts[0] = _("_Yes");
903 		    buts[1] = _("_Cancel");
904 		    buts[2] = NULL;
905 		    if ( gwwv_ask(_("Missing glyph"),(const char **) buts,0,1,_("In lookup subtable %.30s you refer to a glyph named %.80s, which is not in the font yet. Was this intentional?"),
906 			    ((struct lookup_subtable *) possub[cols*i+0].u.md_ival)->subtable_name,
907 			    start)==1 ) {
908 			*pt = ch;
909 return( false );
910 		    }
911 		} else if ( found==ci->sc && pstt!=pst_pair ) {
912 		    buts[0] = _("_Yes");
913 		    buts[1] = _("_Cancel");
914 		    buts[2] = NULL;
915 		    if ( gwwv_ask(_("Substitution generates itself"),(const char **) buts,0,1,_("In lookup subtable %.30s you replace a glyph with itself. Was this intentional?"),
916 			    ((struct lookup_subtable *) possub[cols*i+0].u.md_ival)->subtable_name)==1 ) {
917 			*pt = ch;
918 return( false );
919 		    }
920 		}
921 		*pt = ch;
922 		if ( ch=='(' ) {
923 		    while ( *pt!=')' && *pt!='\0' ) ++pt;
924 		    if ( *pt==')' ) ++pt;
925 		}
926 		while ( *pt== ' ' ) ++pt;
927 		start = pt;
928 	    }
929 	}
930     }
931 
932     /* If we get this far, then we didn't find any errors */
933     for ( pst = sc->possub; pst!=NULL; pst=pst->next ) {
934 	pst->ticked = false;
935 	if ( pst->type==pst_lcaret )
936 	    lcpst = pst;
937     }
938 
939     // There is no dialog datastructure for the ligature carets, so copy them here
940     // The entry will be modified in _CI_OK when needed
941     for ( pst = ci->sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next )
942 	;
943     if ( pst!=NULL ) {
944 	if ( lcpst==NULL ) {
945 	    lcpst = chunkalloc(sizeof(PST));
946 	    lcpst->type = pst_lcaret;
947 	    lcpst->next = sc->possub;
948 	    sc->possub = lcpst;
949 	}
950 	if ( lcpst->u.lcaret.carets!=NULL )
951 	    free(lcpst->u.lcaret.carets);
952 	lcpst->u.lcaret.cnt = pst->u.lcaret.cnt;
953 	lcpst->u.lcaret.carets = malloc(pst->u.lcaret.cnt*sizeof(int16));
954 	memcpy(lcpst->u.lcaret.carets,pst->u.lcaret.carets,pst->u.lcaret.cnt*sizeof(int16));
955 	lcpst->ticked = true;
956     }
957 
958     for ( isv=0; isv<2; ++isv )
959 	for ( kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kp->next )
960 	    kp->kcid = 0;
961 
962     for ( pstt=pst_substitution; pstt<=pst_ligature; ++pstt ) {
963 	possub = GMatrixEditGet(GWidgetGetControl(ci->gw,CID_List+(pstt-1)*100), &rows );
964 	cols = GMatrixEditGetColCnt(GWidgetGetControl(ci->gw,CID_List+(pstt-1)*100) );
965 	for ( i=0; i<rows; ++i ) {
966 	    for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
967 		if ( pst->subtable == (void *) possub[cols*i+0].u.md_ival &&
968 			!pst->ticked )
969 	    break;
970 	    }
971 	    if ( pst==NULL ) {
972 		pst = chunkalloc(sizeof(PST));
973 		pst->type = pstt;
974 		pst->subtable = (void *) possub[cols*i+0].u.md_ival;
975 		pst->next = sc->possub;
976 		sc->possub = pst;
977 	    } else
978 		free( pst->u.subs.variant );
979 	    pst->ticked = true;
980 	    pst->u.subs.variant = GlyphNameListDeUnicode( possub[cols*i+1].u.md_str );
981 	    if ( pstt==pst_ligature )
982 		pst->u.lig.lig = sc;
983 	}
984     }
985 
986     possub = GMatrixEditGet(GWidgetGetControl(ci->gw,CID_List+(pst_position-1)*100), &rows );
987     cols = GMatrixEditGetColCnt(GWidgetGetControl(ci->gw,CID_List+(pst_position-1)*100));
988     for ( i=0; i<rows; ++i ) {
989 	for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
990 	    if ( pst->subtable == (void *) possub[cols*i+0].u.md_ival &&
991 		    !pst->ticked )
992 	break;
993 	}
994 	if ( pst==NULL ) {
995 	    pst = chunkalloc(sizeof(PST));
996 	    pst->type = pst_position;
997 	    pst->subtable = (void *) possub[cols*i+0].u.md_ival;
998 	    pst->next = sc->possub;
999 	    sc->possub = pst;
1000 	}
1001 	VRDevTabParse(&pst->u.pos,&possub[cols*i+SIM_DX+1]);
1002 	pst->u.pos.xoff = possub[cols*i+SIM_DX].u.md_ival;
1003 	pst->u.pos.yoff = possub[cols*i+SIM_DY].u.md_ival;
1004 	pst->u.pos.h_adv_off = possub[cols*i+SIM_DX_ADV].u.md_ival;
1005 	pst->u.pos.v_adv_off = possub[cols*i+SIM_DY_ADV].u.md_ival;
1006 	pst->ticked = true;
1007     }
1008 
1009     possub = GMatrixEditGet(GWidgetGetControl(ci->gw,CID_List+(pst_pair-1)*100), &rows );
1010     cols = GMatrixEditGetColCnt(GWidgetGetControl(ci->gw,CID_List+(pst_pair-1)*100));
1011     for ( i=0; i<rows; ++i ) {
1012 	struct lookup_subtable *sub = ((struct lookup_subtable *) possub[cols*i+0].u.md_ival);
1013 	KpMDParse(sc,sub,possub,rows,cols,i);
1014     }
1015 
1016     /* Now, free anything that did not get ticked */
1017     for ( pstprev=NULL, pst = sc->possub; pst!=NULL; pst=pstnext ) {
1018 	pstnext = pst->next;
1019 	if ( pst->ticked )
1020 	    pstprev = pst;
1021 	else {
1022 	    if ( pstprev==NULL )
1023 		sc->possub = pstnext;
1024 	    else
1025 		pstprev->next = pstnext;
1026 	    pst->next = NULL;
1027 	    PSTFree(pst);
1028 	}
1029     }
1030     for ( isv=0; isv<2; ++isv ) {
1031 	for ( kpprev=NULL, kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kpnext ) {
1032 	    kpnext = kp->next;
1033 	    if ( kp->kcid!=0 )
1034 		kpprev = kp;
1035 	    else {
1036 		if ( kpprev!=NULL )
1037 		    kpprev->next = kpnext;
1038 		else if ( isv )
1039 		    sc->vkerns = kpnext;
1040 		else
1041 		    sc->kerns = kpnext;
1042 		kp->next = NULL;
1043 		KernPairsFree(kp);
1044 	    }
1045 	}
1046     }
1047     for ( isv=0; isv<2; ++isv )
1048 	for ( kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kp->next )
1049 	    kp->kcid = 0;
1050 return( true );
1051 }
1052 
gettex(GWindow gw,int cid,char * msg,int * err)1053 static int gettex(GWindow gw,int cid,char *msg,int *err) {
1054     const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(gw,cid));
1055 
1056     if ( *ret=='\0' )
1057 return( TEX_UNDEF );
1058 return( GetInt8(gw,cid,msg,err));
1059 }
1060 
GV_ParseConstruction(struct glyphvariants * gv,struct matrix_data * stuff,int rows,int cols)1061 struct glyphvariants *GV_ParseConstruction(struct glyphvariants *gv,
1062 	struct matrix_data *stuff, int rows, int cols) {
1063     int i;
1064 
1065     if ( gv==NULL )
1066 	gv = chunkalloc(sizeof(struct glyphvariants));
1067 
1068     gv->part_cnt = rows;
1069     gv->parts = calloc(rows,sizeof(struct gv_part));
1070     for ( i=0; i<rows; ++i ) {
1071 	gv->parts[i].component = copy(stuff[i*cols+0].u.md_str);
1072 	gv->parts[i].is_extender = stuff[i*cols+1].u.md_ival;
1073 	gv->parts[i].startConnectorLength = stuff[i*cols+2].u.md_ival;
1074 	gv->parts[i].endConnectorLength = stuff[i*cols+3].u.md_ival;
1075 	gv->parts[i].fullAdvance = stuff[i*cols+4].u.md_ival;
1076     }
1077 return( gv );
1078 }
1079 
CI_ParseVariants(struct glyphvariants * gv,CharInfo * ci,int is_horiz,char * italic_correction_devtab,int italic_correction,int only_parts)1080 static struct glyphvariants *CI_ParseVariants(struct glyphvariants *gv,
1081 	CharInfo *ci, int is_horiz,
1082 	char *italic_correction_devtab, int italic_correction,
1083 	int only_parts) {
1084     char *variants = GGadgetGetTitle8(GWidgetGetControl(ci->gw,CID_VariantList+is_horiz*100));
1085     GGadget *construct = GWidgetGetControl(ci->gw,CID_ExtensionList+is_horiz*100);
1086     int rows, cols = GMatrixEditGetColCnt(construct);
1087     struct matrix_data *stuff = GMatrixEditGet(construct,&rows);
1088 
1089     if ( (variants==NULL || variants[0]=='\0' || only_parts) && rows==0 ) {
1090 	free(variants);
1091 	GlyphVariantsFree(gv);
1092 return( NULL );
1093     }
1094     if ( gv==NULL )
1095 	gv = chunkalloc(sizeof(struct glyphvariants));
1096     free(gv->variants); gv->variants = NULL;
1097     if ( only_parts ) {
1098 	free(variants); variants = NULL;
1099     } else if ( variants!=NULL && *variants!='\0' )
1100 	gv->variants = variants;
1101     else {
1102 	gv->variants = NULL;
1103 	free( variants);
1104     }
1105     if ( !only_parts ) {
1106 	gv->italic_correction = italic_correction;
1107 	gv->italic_adjusts = DeviceTableParse(gv->italic_adjusts,italic_correction_devtab);
1108     }
1109     gv = GV_ParseConstruction(gv,stuff,rows,cols);
1110 return( gv );
1111 }
1112 
CI_ValidateAltUnis(CharInfo * ci)1113 static int CI_ValidateAltUnis(CharInfo *ci) {
1114     GGadget *au = GWidgetGetControl(ci->gw,CID_AltUni);
1115     int rows, cols = GMatrixEditGetColCnt(au);
1116     struct matrix_data *stuff = GMatrixEditGet(au,&rows);
1117     int i, asked = false;
1118 
1119     for ( i=0; i<rows; ++i ) {
1120 	int uni = stuff[i*cols+0].u.md_ival, vs = stuff[i*cols+1].u.md_ival;
1121 	if ( uni<0 || uni>=unicode4_size ||
1122 		vs<-1 || vs>=unicode4_size ) {
1123 	    ff_post_error(_("Unicode out of range"), _("Bad unicode value for an alternate unicode / variation selector"));
1124 return( false );
1125 	}
1126 	if ( (vs>=0x180B && vs<=0x180D) ||	/* Mongolian VS */
1127 		 (vs>=0xfe00 && vs<=0xfe0f) ||	/* First VS block */
1128 		 (vs>=0xE0100 && vs<=0xE01EF) ) {	/* Second VS block */
1129 	    /* ok, that's a reasonable value */;
1130 	} else if ( vs==0 || vs==-1 ) {
1131 	    /* That's ok too (means no selector, just an alternate encoding) */;
1132 	} else if ( !asked ) {
1133 	    char *buts[3];
1134 	    buts[0] = _("_OK"); buts[1] = _("_Cancel"); buts[2]=NULL;
1135 	    if ( gwwv_ask(_("Unexpected Variation Selector"),(const char **) buts,0,1,
1136 		    _("Variation selectors are normally between\n"
1137 		      "   U+180B and U+180D\n"
1138 		      "   U+FE00 and U+FE0F\n"
1139 		      "   U+E0100 and U+E01EF\n"
1140 		      "did you really intend to use U+%04X?"), vs)==1 )
1141 return( false );
1142 	    asked = true;
1143 	}
1144     }
1145 return( true );
1146 }
1147 
CI_ParseAltUnis(CharInfo * ci)1148 static void CI_ParseAltUnis(CharInfo *ci) {
1149     GGadget *au = GWidgetGetControl(ci->gw,CID_AltUni);
1150     int rows, cols = GMatrixEditGetColCnt(au);
1151     struct matrix_data *stuff = GMatrixEditGet(au,&rows);
1152     int i;
1153     struct altuni *altuni, *last = NULL;
1154     SplineChar *sc = ci->cachedsc;
1155     int deenc = false;
1156     FontView *fvs;
1157     int oldcnt, newcnt;
1158 
1159     oldcnt = 0;
1160     for ( altuni=sc->altuni ; altuni!=NULL; altuni = altuni->next )
1161 	if ( altuni->vs==-1 && altuni->fid==0 )
1162 	    ++oldcnt;
1163 
1164     newcnt = 0;
1165     for ( i=0; i<rows; ++i ) {
1166 	int uni = stuff[i*cols+0].u.md_ival, vs = stuff[i*cols+1].u.md_ival;
1167 	if ( vs!=0 )
1168     continue;
1169 	++newcnt;
1170 	if ( uni==sc->unicodeenc )
1171     continue;
1172 	for ( altuni=sc->altuni ; altuni!=NULL; altuni = altuni->next )
1173 	    if ( uni==altuni->unienc && altuni->vs==-1 && altuni->fid==0 )
1174 	break;
1175 	if ( altuni==NULL ) {
1176 	    deenc = true;
1177     break;
1178 	}
1179     }
1180     if ( oldcnt!=newcnt || deenc ) {
1181 	for ( fvs=(FontView *) sc->parent->fv; fvs!=NULL; fvs=(FontView *) fvs->b.nextsame ) {
1182 	    fvs->b.map->enc = &custom;
1183 	    FVSetTitle((FontViewBase *) fvs);
1184 	}
1185     }
1186     AltUniFree(sc->altuni); sc->altuni = NULL;
1187     for ( i=0; i<rows; ++i ) {
1188 	int uni = stuff[i*cols+0].u.md_ival, vs = stuff[i*cols+1].u.md_ival;
1189 	altuni = chunkalloc(sizeof(struct altuni));
1190 	altuni->unienc = uni;
1191 	altuni->vs = vs==0 ? -1 : vs;
1192 	altuni->fid = 0;
1193 	if ( last == NULL )
1194 	    sc->altuni = altuni;
1195 	else
1196 	    last->next = altuni;
1197 	last = altuni;
1198     }
1199 }
1200 
CI_KPCopy(KernPair * kp)1201 static KernPair *CI_KPCopy(KernPair *kp) {
1202     KernPair *head=NULL, *last=NULL, *newkp;
1203 
1204     while ( kp!=NULL ) {
1205 	newkp = chunkalloc(sizeof(KernPair));
1206 	*newkp = *kp;
1207 	newkp->adjust = DeviceTableCopy(kp->adjust);
1208 	newkp->next = NULL;
1209 	if ( head==NULL )
1210 	    head = newkp;
1211 	else
1212 	    last->next = newkp;
1213 	last = newkp;
1214 	kp = kp->next;
1215     }
1216 return( head );
1217 }
1218 
CI_PSTCopy(PST * pst)1219 static PST *CI_PSTCopy(PST *pst) {
1220     PST *head=NULL, *last=NULL, *newpst;
1221 
1222     while ( pst!=NULL ) {
1223 	newpst = chunkalloc(sizeof(KernPair));
1224 	*newpst = *pst;
1225 	if ( newpst->type==pst_ligature ) {
1226 	    newpst->u.lig.components = copy(pst->u.lig.components);
1227 	} else if ( newpst->type==pst_pair ) {
1228 	    newpst->u.pair.paired = copy(pst->u.pair.paired);
1229 	    newpst->u.pair.vr = chunkalloc(sizeof( struct vr [2]));
1230 	    memcpy(newpst->u.pair.vr,pst->u.pair.vr,sizeof(struct vr [2]));
1231 	    newpst->u.pair.vr[0].adjust = ValDevTabCopy(pst->u.pair.vr[0].adjust);
1232 	    newpst->u.pair.vr[1].adjust = ValDevTabCopy(pst->u.pair.vr[1].adjust);
1233 	} else if ( newpst->type==pst_lcaret ) {
1234 	    newpst->u.lcaret.carets = malloc(pst->u.lcaret.cnt*sizeof(int16));
1235 	    memcpy(newpst->u.lcaret.carets,pst->u.lcaret.carets,pst->u.lcaret.cnt*sizeof(int16));
1236 	} else if ( newpst->type==pst_substitution || newpst->type==pst_multiple || newpst->type==pst_alternate )
1237 	    newpst->u.subs.variant = copy(pst->u.subs.variant);
1238 	newpst->next = NULL;
1239 	if ( head==NULL )
1240 	    head = newpst;
1241 	else
1242 	    last->next = newpst;
1243 	last = newpst;
1244 	pst = pst->next;
1245     }
1246 return( head );
1247 }
1248 
CI_SCDuplicate(SplineChar * sc)1249 static SplineChar *CI_SCDuplicate(SplineChar *sc) {
1250     SplineChar *newsc;		/* copy everything we care about in this dlg */
1251 
1252     newsc = chunkalloc(sizeof(SplineChar));
1253     newsc->name = copy(sc->name);
1254     newsc->parent = sc->parent;
1255     newsc->unicodeenc = sc->unicodeenc;
1256     newsc->orig_pos = sc->orig_pos;
1257     newsc->comment = copy(sc->comment);
1258     newsc->user_decomp = u_copy(sc->user_decomp);
1259     newsc->unlink_rm_ovrlp_save_undo = sc->unlink_rm_ovrlp_save_undo;
1260     newsc->glyph_class = sc->glyph_class;
1261     newsc->color = sc->color;
1262     if ( sc->countermask_cnt!=0 ) {
1263 	newsc->countermask_cnt = sc->countermask_cnt;
1264 	newsc->countermasks = malloc(sc->countermask_cnt*sizeof(HintMask));
1265 	memcpy(newsc->countermasks,sc->countermasks,sc->countermask_cnt*sizeof(HintMask));
1266     }
1267     newsc->tex_height = sc->tex_height;
1268     newsc->tex_depth = sc->tex_depth;
1269     newsc->italic_correction = sc->italic_correction;
1270     newsc->top_accent_horiz = sc->top_accent_horiz;
1271     newsc->is_extended_shape = sc->is_extended_shape;
1272     newsc->italic_adjusts = DeviceTableCopy(sc->italic_adjusts);
1273     newsc->top_accent_adjusts = DeviceTableCopy(sc->top_accent_adjusts);
1274     newsc->horiz_variants = GlyphVariantsCopy(sc->horiz_variants);
1275     newsc->vert_variants = GlyphVariantsCopy(sc->vert_variants);
1276     newsc->altuni = AltUniCopy(sc->altuni,NULL);
1277     newsc->lig_caret_cnt_fixed = sc->lig_caret_cnt_fixed;
1278     newsc->possub = CI_PSTCopy(sc->possub);
1279     newsc->kerns = CI_KPCopy(sc->kerns);
1280     newsc->vkerns = CI_KPCopy(sc->vkerns);
1281     newsc->tile_margin = sc->tile_margin;
1282     newsc->tile_bounds = sc->tile_bounds;
1283 return( newsc );
1284 }
1285 
CI_CheckMetaData(CharInfo * ci,SplineChar * oldsc,char * name,int unienc,char * comment)1286 static int CI_CheckMetaData(CharInfo *ci,SplineChar *oldsc,char *name,int unienc, char *comment) {
1287     SplineFont *sf = oldsc->parent;
1288     int i;
1289     int isnotdef, samename=false, sameuni=false;
1290     struct altuni *alt;
1291     SplineChar *newsc = ci->cachedsc;
1292     struct splinecharlist *scl, *baduniscl, *badnamescl;
1293     SplineChar *baduni, *badname;
1294 
1295     for ( alt=oldsc->altuni; alt!=NULL && (alt->unienc!=unienc || alt->vs!=-1 || alt->fid!=0); alt=alt->next );
1296     if ( unienc==oldsc->unicodeenc || alt!=NULL )
1297 	sameuni=true;
1298     samename = ( oldsc->name!=NULL && strcmp(name,oldsc->name)==0 );
1299 
1300     isnotdef = strcmp(name,".notdef")==0;
1301     if (( !sameuni && unienc!=-1) || (!samename && !isnotdef) ) {
1302 	baduniscl = badnamescl = NULL;
1303 	baduni = badname = NULL;
1304 	for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL && sf->glyphs[i]!=ci->sc ) {
1305 	    if ( unienc!=-1 && sf->glyphs[i]->unicodeenc==unienc ) {
1306 		for ( scl=ci->changes; scl!=NULL && scl->sc->orig_pos!=i; scl = scl->next );
1307 		if ( scl==NULL ) {
1308 		    baduni = sf->glyphs[i];
1309 		    baduniscl = NULL;
1310 		} else if ( scl->sc->unicodeenc==unienc ) {
1311 		    baduni = scl->sc;
1312 		    baduniscl = scl;
1313 		}
1314 	    }
1315 	    if ( !isnotdef && strcmp(name,sf->glyphs[i]->name )==0 ) {
1316 		for ( scl=ci->changes; scl!=NULL && scl->sc->orig_pos!=i; scl = scl->next );
1317 		if ( scl==NULL ) {
1318 		    badname = sf->glyphs[i];
1319 		    badnamescl = NULL;
1320 		} else if ( strcmp(scl->sc->name,name)==0 ) {
1321 		    badname = scl->sc;
1322 		    badnamescl = scl;
1323 		}
1324 	    }
1325 	}
1326 	for ( scl=ci->changes; scl!=NULL ; scl = scl->next ) if ( scl->sc!=newsc ) {
1327 	    if ( unienc!=-1 && scl->sc->unicodeenc==unienc ) {
1328 		baduni = scl->sc;
1329 		baduniscl = scl;
1330 	    }
1331 	    if ( !isnotdef && strcmp(scl->sc->name,name)==0 ) {
1332 		badname = scl->sc;
1333 		badnamescl = scl;
1334 	    }
1335 	}
1336 	if ( baduni!=NULL || badname!=NULL ) {
1337 	    char *buts[3];
1338 	    buts[0] = _("_Yes"); buts[1]=_("_Cancel"); buts[2] = NULL;
1339 	    if ( badname==baduni ) {
1340 		if ( ff_ask(_("Multiple"),(const char **) buts,0,1,_("There is already a glyph with this name and encoding,\nboth must be unique within a font,\ndo you want to swap them?"))==1 )
1341 return( false );
1342 		/* If we're going to swap, then add the swapee to the list of */
1343 		/*  things that need changing */
1344 		if ( baduniscl==NULL ) {
1345 		    baduni = CI_SCDuplicate(baduni);
1346 		    baduniscl = chunkalloc(sizeof(struct splinecharlist));
1347 		    baduniscl->sc = baduni;
1348 		    baduniscl->next = ci->changes;
1349 		    ci->changes = baduniscl;
1350 		}
1351 		baduni->unicodeenc = oldsc->unicodeenc;
1352 		free(baduni->name); baduni->name = copy(oldsc->name);
1353 	    } else {
1354 		if ( baduni!=NULL ) {
1355 		    if ( ff_ask(_("Multiple"),(const char **) buts,0,1,_("There is already a glyph with this encoding,\nwhich must be unique within a font,\ndo you want to swap the encodings of the two?"))==1 )
1356 return( false );
1357 		    if ( baduniscl==NULL ) {
1358 			baduni = CI_SCDuplicate(baduni);
1359 			baduniscl = chunkalloc(sizeof(struct splinecharlist));
1360 			baduniscl->sc = baduni;
1361 			baduniscl->next = ci->changes;
1362 			ci->changes = baduniscl;
1363 		    }
1364 		    baduni->unicodeenc = oldsc->unicodeenc;
1365 		}
1366 		if ( badname!=NULL ) {
1367 		    if ( ff_ask(_("Multiple"),(const char **) buts,0,1,_("There is already a glyph with this name,\nwhich must be unique within a font,\ndo you want to swap the names of the two?"))==1 )
1368 return( false );
1369 		    if ( badnamescl==NULL ) {
1370 			badname = CI_SCDuplicate(badname);
1371 			badnamescl = chunkalloc(sizeof(struct splinecharlist));
1372 			badnamescl->sc = badname;
1373 			badnamescl->next = ci->changes;
1374 			ci->changes = badnamescl;
1375 		    }
1376 		    free(badname->name); badname->name = copy(oldsc->name);
1377 		}
1378 	    }
1379 	}
1380     }
1381     if ( !samename )
1382 	ci->name_change = true;
1383     if ( !sameuni )
1384 	ci->uni_change = true;
1385     free( newsc->name ); free( newsc->comment );
1386     newsc->name = copy( name );
1387     newsc->unicodeenc = unienc;
1388     newsc->comment = copy( comment );
1389 return( true );
1390 }
1391 
_CI_OK(CharInfo * ci)1392 static int _CI_OK(CharInfo *ci) {
1393     int val;
1394     int ret;
1395     char *name, *comment;
1396     const unichar_t *nm;
1397     int err = false;
1398     int tex_height, tex_depth, italic, topaccent;
1399     int hic, vic;
1400     int lc_cnt=-1;
1401     char *italicdevtab=NULL, *accentdevtab=NULL, *hicdt=NULL, *vicdt=NULL;
1402     int lig_caret_cnt_fixed=0;
1403     int low,high;
1404     real tile_margin=0;
1405     DBounds tileb;
1406     SplineChar *oldsc = ci->cachedsc==NULL ? ci->sc : ci->cachedsc;
1407 
1408     if ( !CI_ValidateAltUnis(ci))
1409 return( false );
1410 
1411     val = ParseUValue(ci->gw,CID_UValue,true);
1412     if ( val==-2 )
1413 return( false );
1414     tex_height = gettex(ci->gw,CID_TeX_Height,_("Height"),&err);
1415     tex_depth  = gettex(ci->gw,CID_TeX_Depth ,_("Depth") ,&err);
1416     italic     = gettex(ci->gw,CID_TeX_Italic,_("Italic Correction"),&err);
1417     topaccent  = gettex(ci->gw,CID_HorAccent,_("Top Accent Horizontal Pos"),&err);
1418     if ( err )
1419 return( false );
1420     hic = GetInt8(ci->gw,CID_ExtItalicCor+1*100,_("Horizontal Extension Italic Correction"),&err);
1421     vic = GetInt8(ci->gw,CID_ExtItalicCor+0*100,_("Vertical Extension Italic Correction"),&err);
1422     if ( err )
1423 return( false );
1424 
1425     memset(&tileb,0,sizeof(tileb));
1426     if ( ci->sc->parent->multilayer ) {
1427 	if ( GGadgetIsChecked(GWidgetGetControl(ci->gw,CID_IsTileMargin)))
1428 	    tile_margin = GetReal8(ci->gw,CID_TileMargin,_("Tile Margin"),&err);
1429 	else {
1430 	    tileb.minx = GetReal8(ci->gw,CID_TileBBoxMinX,_("Tile Min X"),&err);
1431 	    tileb.miny = GetReal8(ci->gw,CID_TileBBoxMinY,_("Tile Min Y"),&err);
1432 	    tileb.maxx = GetReal8(ci->gw,CID_TileBBoxMaxX,_("Tile Max X"),&err);
1433 	    tileb.maxy = GetReal8(ci->gw,CID_TileBBoxMaxY,_("Tile Max Y"),&err);
1434 	}
1435 	if ( err )
1436 return( false );
1437     }
1438 
1439     lig_caret_cnt_fixed = !GGadgetIsChecked(GWidgetGetControl(ci->gw,CID_DefLCCount));
1440     if ( ci->lc_seen ) {
1441 	lc_cnt = GetInt8(ci->gw,CID_LCCount,_("Ligature Caret Count"),&err);
1442 	if ( err )
1443 return( false );
1444 	if ( lc_cnt<0 || lc_cnt>100 ) {
1445 	    ff_post_error(_("Bad Lig. Caret Count"),_("Unreasonable ligature caret count"));
1446 return( false );
1447 	}
1448     }
1449     nm = _GGadgetGetTitle(GWidgetGetControl(ci->gw,CID_UName));
1450     if ( !CI_NameCheck(nm) )
1451 return( false );
1452 
1453     italicdevtab = GGadgetGetTitle8(GWidgetGetControl(ci->gw,CID_ItalicDevTab));
1454     accentdevtab = GGadgetGetTitle8(GWidgetGetControl(ci->gw,CID_AccentDevTab));
1455     hicdt = GGadgetGetTitle8(GWidgetGetControl(ci->gw,CID_ExtItalicDev+1*100));
1456     vicdt = GGadgetGetTitle8(GWidgetGetControl(ci->gw,CID_ExtItalicDev+0*100));
1457     if ( !DeviceTableOK(italicdevtab,&low,&high) || !DeviceTableOK(accentdevtab,&low,&high) ||
1458 	    !DeviceTableOK(hicdt,&low,&high) || !DeviceTableOK(vicdt,&low,&high)) {
1459 	ff_post_error( _("Bad Device Table Adjustment"),_("A device table adjustment specified for the MATH table is invalid") );
1460 	free( accentdevtab );
1461 	free( italicdevtab );
1462 	free(hicdt); free(vicdt);
1463 return( false );
1464     }
1465     if ( ci->cachedsc==NULL ) {
1466 	struct splinecharlist *scl;
1467 	ci->cachedsc = chunkalloc(sizeof(SplineChar));
1468 	ci->cachedsc->orig_pos = ci->sc->orig_pos;
1469 	ci->cachedsc->parent = ci->sc->parent;
1470 	ci->cachedsc->user_decomp = u_copy(ci->sc->user_decomp);
1471 	scl = chunkalloc(sizeof(struct splinecharlist));
1472 	scl->sc = ci->cachedsc;
1473 	scl->next = ci->changes;
1474 	ci->changes = scl;
1475     }
1476     /* CI_ProcessPosSubs is the first thing which might change anything real */
1477     if ( !CI_ProcessPosSubs(ci)) {
1478 	free( accentdevtab );
1479 	free( italicdevtab );
1480 	free(hicdt); free(vicdt);
1481 return( false );
1482     }
1483     name = u2utf8_copy( nm );
1484     comment = GGadgetGetTitle8(GWidgetGetControl(ci->gw,CID_Comment));
1485     if ( comment!=NULL && *comment=='\0' ) {
1486 	free(comment);
1487 	comment=NULL;
1488     }
1489     ret = CI_CheckMetaData(ci,oldsc,name,val,comment);
1490     free(name); free(comment);
1491     if ( !ret ) {
1492 	free( accentdevtab );
1493 	free( italicdevtab );
1494 	free(hicdt); free(vicdt);
1495 return( false );
1496     }
1497     ci->cachedsc->unlink_rm_ovrlp_save_undo = GGadgetIsChecked(GWidgetGetControl(ci->gw,CID_UnlinkRmOverlap));
1498     ci->cachedsc->glyph_class = GGadgetGetFirstListSelectedItem(GWidgetGetControl(ci->gw,CID_GClass));
1499     val = GGadgetGetFirstListSelectedItem(GWidgetGetControl(ci->gw,CID_Color));
1500     if ( val!=-1 )
1501 	ci->cachedsc->color = (intpt) (std_colors[val].userdata);
1502     CI_ParseCounters(ci);
1503     ci->cachedsc->tex_height = tex_height;
1504     ci->cachedsc->tex_depth  = tex_depth;
1505     ci->cachedsc->italic_correction = italic;
1506     ci->cachedsc->top_accent_horiz = topaccent;
1507     ci->cachedsc->is_extended_shape = GGadgetIsChecked(GWidgetGetControl(ci->gw,CID_IsExtended));
1508     ci->cachedsc->italic_adjusts = DeviceTableParse(ci->cachedsc->italic_adjusts,italicdevtab);
1509     ci->cachedsc->top_accent_adjusts = DeviceTableParse(ci->cachedsc->top_accent_adjusts,accentdevtab);
1510     ci->cachedsc->horiz_variants = CI_ParseVariants(ci->cachedsc->horiz_variants,ci,1,hicdt,hic,false);
1511     ci->cachedsc->vert_variants  = CI_ParseVariants(ci->cachedsc->vert_variants ,ci,0,vicdt,vic,false);
1512 
1513     free( accentdevtab );
1514     free( italicdevtab );
1515     free(hicdt); free(vicdt);
1516 
1517     CI_ParseAltUnis(ci);
1518 
1519     if ( ci->lc_seen ) {
1520 	PST *pst, *prev=NULL;
1521 	int i;
1522 	ci->cachedsc->lig_caret_cnt_fixed = lig_caret_cnt_fixed;
1523 	for ( pst = ci->cachedsc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next )
1524 	    prev = pst;
1525 	if ( pst==NULL && lc_cnt==0 )
1526 	    /* Nothing to do */;
1527 	else if ( pst!=NULL && lc_cnt==0 ) {
1528 	    if ( prev==NULL )
1529 		ci->cachedsc->possub = pst->next;
1530 	    else
1531 		prev->next = pst->next;
1532 	    pst->next = NULL;
1533 	    PSTFree(pst);
1534 	} else {
1535 	    if ( pst==NULL ) {
1536 		pst = chunkalloc(sizeof(PST));
1537 		pst->type = pst_lcaret;
1538 		pst->next = ci->sc->possub;
1539 		ci->cachedsc->possub = pst;
1540 	    }
1541 	    if ( lc_cnt>pst->u.lcaret.cnt )
1542 		pst->u.lcaret.carets = realloc(pst->u.lcaret.carets,lc_cnt*sizeof(int16));
1543 	    for ( i=pst->u.lcaret.cnt; i<lc_cnt; ++i )
1544 		pst->u.lcaret.carets[i] = 0;
1545 	    pst->u.lcaret.cnt = lc_cnt;
1546 	}
1547     }
1548 
1549     ci->cachedsc->tile_margin = tile_margin;
1550     ci->cachedsc->tile_bounds = tileb;
1551 
1552 return( ret );
1553 }
1554 
CI_ApplyAll(CharInfo * ci)1555 static void CI_ApplyAll(CharInfo *ci) {
1556     int refresh_fvdi = false;
1557     struct splinecharlist *scl;
1558     SplineChar *cached, *sc;
1559     SplineFont *sf = ci->sc->parent;
1560     FontView *fvs;
1561 
1562     for ( scl = ci->changes; scl!=NULL; scl=scl->next ) {
1563 	cached = scl->sc;
1564 	sc = sf->glyphs[cached->orig_pos];
1565 	SCPreserveState(sc,2);
1566 	if ( strcmp(cached->name,sc->name)!=0 || cached->unicodeenc!=sc->unicodeenc )
1567 	    refresh_fvdi = 1;
1568 	if ( sc->name==NULL || strcmp( sc->name,cached->name )!=0 ) {
1569 	    if ( sc->name!=NULL )
1570 		SFGlyphRenameFixup(sf,sc->name,cached->name,false);
1571 	    free(sc->name); sc->name = copy(cached->name);
1572 	    sc->namechanged = true;
1573 	    GlyphHashFree(sf);
1574 	}
1575 	if ( sc->unicodeenc != cached->unicodeenc ) {
1576 	    struct splinecharlist *scl;
1577 	    int layer;
1578 	    RefChar *ref;
1579 	    struct altuni *alt;
1580 
1581 	    /* All references need the new unicode value */
1582 	    for ( scl=sc->dependents; scl!=NULL; scl=scl->next ) {
1583 		for ( layer=ly_back; layer<scl->sc->layer_cnt; ++layer )
1584 		    for ( ref = scl->sc->layers[layer].refs; ref!=NULL; ref=ref->next )
1585 			if ( ref->sc==sc )
1586 			    ref->unicode_enc = cached->unicodeenc;
1587 	    }
1588 	    /* If the current unicode enc were in the list of alt unis */
1589 	    /*  the user might have forgotten to remove it. So if s/he did */
1590 	    /*  forget, swap the altuni value with the old value */
1591 	    for ( alt=cached->altuni; alt!=NULL && (alt->unienc!=cached->unicodeenc || alt->vs!=-1 || alt->fid!=0); alt=alt->next );
1592 	    if ( alt!=NULL )	/* alt->unienc==new value */
1593 		alt->unienc = sc->unicodeenc;
1594 	    sc->unicodeenc = cached->unicodeenc;
1595 	}
1596 	free(sc->comment); sc->comment = copy(cached->comment);
1597 	sc->unlink_rm_ovrlp_save_undo = cached->unlink_rm_ovrlp_save_undo;
1598 	sc->glyph_class = cached->glyph_class;
1599 	if ( sc->color != cached->color )
1600 	    refresh_fvdi = true;
1601 	sc->color = cached->color;
1602 	free(sc->countermasks);
1603 	sc->countermask_cnt = cached->countermask_cnt;
1604 	sc->countermasks = cached->countermasks;
1605 	cached->countermasks = NULL; cached->countermask_cnt = 0;
1606 	sc->tex_height = cached->tex_height;
1607 	sc->tex_depth  = cached->tex_depth;
1608 	sc->italic_correction = cached->italic_correction;
1609 	sc->top_accent_horiz = cached->top_accent_horiz;
1610 	sc->is_extended_shape = cached->is_extended_shape;
1611 	DeviceTableFree(sc->italic_adjusts);
1612 	DeviceTableFree(sc->top_accent_adjusts);
1613 	sc->italic_adjusts = cached->italic_adjusts;
1614 	sc->top_accent_adjusts = cached->top_accent_adjusts;
1615 	cached->italic_adjusts = cached->top_accent_adjusts = NULL;
1616 	GlyphVariantsFree(sc->horiz_variants);
1617 	GlyphVariantsFree(sc->vert_variants);
1618 	sc->horiz_variants = cached->horiz_variants;
1619 	sc->vert_variants = cached->vert_variants;
1620 	cached->horiz_variants = cached->vert_variants = NULL;
1621 	AltUniFree(sc->altuni);
1622 	sc->altuni = cached->altuni;
1623 	cached->altuni = NULL;
1624 	sc->lig_caret_cnt_fixed = cached->lig_caret_cnt_fixed;
1625 	PSTFree(sc->possub);
1626 	sc->possub = cached->possub;
1627 	cached->possub = NULL;
1628 	KernPairsFree(sc->kerns); KernPairsFree(sc->vkerns);
1629 	sc->kerns = cached->kerns; sc->vkerns = cached->vkerns;
1630 	cached->kerns = cached->vkerns = NULL;
1631 	sc->tile_margin = cached->tile_margin;
1632 	sc->tile_bounds = cached->tile_bounds;
1633 	if ( !sc->changed ) {
1634 	    sc->changed = true;
1635 	    refresh_fvdi = true;
1636 	}
1637 	SCRefreshTitles(sc);
1638     }
1639     if ( ci->name_change || ci->uni_change ) {
1640 	for ( fvs=(FontView *) sf->fv; fvs!=NULL; fvs=(FontView *) fvs->b.nextsame ) {
1641 	    /* Postscript encodings are by name, others are by unicode */
1642 	    /* Hence slight differences in when we update the encoding */
1643 	    if ( (ci->name_change && fvs->b.map->enc->psnames!=NULL ) ||
1644 		    (ci->uni_change && fvs->b.map->enc->psnames==NULL )) {
1645 		fvs->b.map->enc = &custom;
1646 		FVSetTitle((FontViewBase *) fvs);
1647 		refresh_fvdi = true;
1648 	    }
1649 	}
1650     }
1651     if ( refresh_fvdi ) {
1652 	for ( fvs=(FontView *) sf->fv; fvs!=NULL; fvs=(FontView *) fvs->b.nextsame ) {
1653 	    GDrawRequestExpose(fvs->gw,NULL,false);	/* Redraw info area just in case this char is selected */
1654 	    GDrawRequestExpose(fvs->v,NULL,false);	/* Redraw character area in case this char is on screen */
1655 	}
1656     }
1657     if ( ci->changes )
1658 	sf->changed = true;
1659 }
1660 
CI_Finish(CharInfo * ci)1661 static void CI_Finish(CharInfo *ci) {
1662     struct splinecharlist *scl, *next;
1663 
1664     for ( scl=ci->changes; scl!=NULL; scl=next ) {
1665 	next = scl->next;
1666 	SplineCharFree(scl->sc);
1667 	chunkfree(scl,sizeof(*scl));
1668     }
1669     GDrawDestroyWindow(ci->gw);
1670 }
1671 
CI_OK(GGadget * g,GEvent * e)1672 static int CI_OK(GGadget *g, GEvent *e) {
1673     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1674 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
1675 	if ( _CI_OK(ci) ) {
1676 	    CI_ApplyAll(ci);
1677 	    CI_Finish(ci);
1678 	}
1679     }
1680 return( true );
1681 }
1682 
CI_BoundsToMargin(CharInfo * ci)1683 static void CI_BoundsToMargin(CharInfo *ci) {
1684     int err=false;
1685     real margin = GetCalmReal8(ci->gw,CID_TileMargin,NULL,&err);
1686     DBounds b;
1687     char buffer[40];
1688 
1689     if ( err )
1690 return;
1691     SplineCharFindBounds(ci->sc,&b);
1692     sprintf( buffer, "%g", (double)(b.minx-margin) );
1693     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TileBBoxMinX),buffer);
1694     sprintf( buffer, "%g", (double)(b.miny-margin) );
1695     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TileBBoxMinY),buffer);
1696     sprintf( buffer, "%g", (double)(b.maxx+margin) );
1697     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TileBBoxMaxX),buffer);
1698     sprintf( buffer, "%g", (double)(b.maxy+margin) );
1699     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TileBBoxMaxY),buffer);
1700 }
1701 
CI_TileMarginChange(GGadget * g,GEvent * e)1702 static int CI_TileMarginChange(GGadget *g, GEvent *e) {
1703     CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
1704 
1705     if ( e->type==et_controlevent && e->u.control.subtype == et_textfocuschanged &&
1706 	    e->u.control.u.tf_focus.gained_focus )
1707 	GGadgetSetChecked(GWidgetGetControl(ci->gw,CID_IsTileMargin),true);
1708     else if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged )
1709 	CI_BoundsToMargin(ci);
1710 return( true );
1711 }
1712 
1713 /* Generate default settings for the entries in ligature lookup
1714  * TODO: expand beyond (bmp) */
LigDefaultStr(int uni,char * name,int alt_lig)1715 static char *LigDefaultStr(int uni, char *name, int alt_lig ) {
1716     const unichar_t *alt=NULL, *pt;
1717     char *components = NULL, *tmp;
1718     int len;
1719     unichar_t hack[30], *upt;
1720     char buffer[80];
1721 
1722     /* If it's not (bmp) unicode we have no info on it */
1723     /*  Unless it looks like one of adobe's special ligature names */
1724     if ( uni==-1 || uni>=0x10000 )
1725 	/* Nope */;
1726     else if ( isdecompositionnormative(uni) &&
1727 		unicode_alternates[uni>>8]!=NULL &&
1728 		(alt = unicode_alternates[uni>>8][uni&0xff])!=NULL ) {
1729 	if ( alt[1]=='\0' ||
1730 		Ligature_alt_getC(Ligature_find_N(uni))<=1 ||
1731 		Fraction_alt_getC(Fraction_find_N(uni))<=1 )
1732 	    alt = NULL;		/* Single replacements aren't ligatures */
1733 	else if ( iscombining(alt[1]) && ( alt[2]=='\0' || iscombining(alt[2]))) {
1734 	    if ( alt_lig != -10 )	/* alt_lig = 10 => mac unicode decomp */
1735 		alt = NULL;		/* Otherwise, don't treat accented letters as ligatures */
1736 	} else if (! is_LIGATURE_or_VULGAR_FRACTION((uint32)(uni)) &&
1737 		uni!=0x152 && uni!=0x153 &&	/* oe ligature should not be standard */
1738 		uni!=0x132 && uni!=0x133 &&	/* nor ij */
1739 		(uni<0xfb2a || uni>0xfb4f) &&	/* Allow hebrew precomposed chars */
1740 		uni!=0x215f &&			/* exclude 1/ */
1741 		!((uni>=0x0958 && uni<=0x095f) || uni==0x929 || uni==0x931 || uni==0x934)) {
1742 	    alt = NULL;
1743 	} else if ( (tmp=unicode_name(65))==NULL ) { /* test for 'A' to see if library exists */
1744 	    if ( (uni>=0xbc && uni<=0xbe ) ||		/* Latin1 vulgar fractions */
1745 		    (uni>=0x2150 && uni<=0x215e ) ||	/* other vulgar fractions */
1746 		    (uni>=0x2189) ||			/* other vulgar fraction */
1747 		    (uni>=0xfb00 && uni<=0xfb06 ) ||	/* latin ligatures */
1748 		    (uni>=0xfb13 && uni<=0xfb17 ) ||	/* armenian ligatures */
1749 		    uni==0xfb1f ||			/* hebrew ligature */
1750 		    (uni>=0xfb2a && uni<=0xfb4f ) ||	/* hebrew precomposed chars */
1751 		    (uni>=0xfbea && uni<=0xfd3d ) ||	/* arabic ligatures */
1752 		    (uni>=0xfd50 && uni<=0xfdcf ) ||	/* arabic ligatures */
1753 		    (uni>=0xfdf0 && uni<=0xfdfb ) ||	/* arabic ligatures */
1754 		    (uni>=0xfef5 && uni<=0xfefc ))	/* arabic ligatures */
1755 		;	/* These are good */
1756 	    else
1757 		alt = NULL;
1758 	} else
1759 	    free(tmp); /* found 'A' means there is a library, now cleanup */
1760     }
1761     if ( alt==NULL ) {
1762 	if ( name==NULL || alt_lig )
1763 return( NULL );
1764 	else
1765 return( AdobeLigatureFormat(name));
1766     }
1767 
1768     if ( uni==0xfb03 && alt_lig==1 )
1769 	components = copy("ff i");
1770     else if ( uni==0xfb04 && alt_lig==1 )
1771 	components = copy("ff l");
1772     else if ( alt!=NULL ) {
1773 	if ( alt[1]==0x2044 && (alt[2]==0 || alt[3]==0) && alt_lig==1 ) {
1774 	    u_strcpy(hack,alt);
1775 	    hack[1] = '/';
1776 	    alt = hack;
1777 	} else if ( alt_lig>0 )
1778 return( NULL );
1779 
1780 	if ( isarabisolated(uni) || isarabinitial(uni) || isarabmedial(uni) || isarabfinal(uni) ) {
1781 	    /* If it is arabic, then convert from the unformed version to the formed */
1782 	    if ( u_strlen(alt)<sizeof(hack)/sizeof(hack[0])-1 ) {
1783 		u_strcpy(hack,alt);
1784 		for ( upt=hack ; *upt ; ++upt ) {
1785 		    /* Make everything medial */
1786 		    if ( *upt>=0x600 && *upt<=0x6ff )
1787 			*upt = ArabicForms[*upt-0x600].medial;
1788 		}
1789 		if ( isarabisolated(uni) || isarabfinal(uni) ) {
1790 		    int len = upt-hack-1;
1791 		    if ( alt[len]>=0x600 && alt[len]<=0x6ff )
1792 			hack[len] = ArabicForms[alt[len]-0x600].final;
1793 		}
1794 		if ( isarabisolated(uni) || isarabinitial(uni) ) {
1795 		    if ( alt[0]>=0x600 && alt[0]<=0x6ff )
1796 			hack[0] = ArabicForms[alt[0]-0x600].initial;
1797 		}
1798 		alt = hack;
1799 	    }
1800 	}
1801 
1802 	components=NULL;
1803 	while ( 1 ) {
1804 	    len = 0;
1805 	    for ( pt=alt; *pt; ++pt ) {
1806 		if ( components==NULL ) {
1807 		    len += strlen(StdGlyphName(buffer,*pt,ui_none,(NameList *)-1))+1;
1808 		} else {
1809 		    const char *temp = StdGlyphName(buffer,*pt,ui_none,(NameList *)-1);
1810 		    strcpy(components+len,temp);
1811 		    len += strlen( temp );
1812 		    components[len++] = ' ';
1813 		}
1814 	    }
1815 	    if ( components!=NULL )
1816 	break;
1817 	    components = malloc(len+1);
1818 	}
1819 	components[len-1] = '\0';
1820     }
1821 return( components );
1822 }
1823 
AdobeLigatureFormat(char * name)1824 char *AdobeLigatureFormat(char *name) {
1825     /* There are two formats for ligs: <glyph-name>_<glyph-name>{...} or */
1826     /*  uni<code><code>{...} (only works for BMP) */
1827     /* I'm not checking to see if all the components are valid */
1828     char *components, *pt, buffer[12];
1829     const char *next;
1830     int len = strlen(name), uni;
1831 
1832     if ( strncmp(name,"uni",3)==0 && (len-3)%4==0 && len>7 ) {
1833 	pt = name+3;
1834 	components = malloc(1); *components = '\0';
1835 	while ( *pt ) {
1836 	    if ( sscanf(pt,"%4x", (unsigned *) &uni )==0 ) {
1837 		free(components); components = NULL;
1838 	break;
1839 	    }
1840 	    next = StdGlyphName(buffer,uni,ui_none,(NameList *)-1);
1841 	    components = realloc(components,strlen(components) + strlen(next) + 2);
1842 	    if ( *components!='\0' )
1843 		strcat(components," ");
1844 	    strcat(components,next);
1845 	    pt += 4;
1846 	}
1847 	if ( components!=NULL )
1848 return( components );
1849     }
1850 
1851     if ( strchr(name,'_')==NULL )
1852 return( NULL );
1853     pt = components = copy(name);
1854     while ( (pt = strchr(pt,'_'))!=NULL )
1855 	*pt = ' ';
1856 return( components );
1857 }
1858 
1859 /* TODO: see what can be brought-in from is_Ligature_data.h tables, but this */
1860 /* also appears to run various features beyond ligatures and fractions too.  */
LigTagFromUnicode(int uni)1861 uint32 LigTagFromUnicode(int uni) {
1862     int tag = CHR('l','i','g','a');	/* standard */
1863 
1864     if ( (uni>=0xbc && uni<=0xbe) ||	/* latin1 vulgar fractions */
1865 	 (uni>=0x2150 && uni<=0x215f) ||/* other vulgar fractions */
1866 	 (uni==0x2189) )
1867 	tag = CHR('f','r','a','c');	/* Fraction */
1868     /* hebrew precomposed characters */
1869     else if ( uni>=0xfb2a && uni<=0xfb4e )
1870 	tag = CHR('c','c','m','p');
1871     else if ( uni==0xfb4f )
1872 	tag = CHR('h','l','i','g');
1873     /* armenian */
1874     else if ( uni>=0xfb13 && uni<=0xfb17 )
1875 	tag = CHR('l','i','g','a');
1876     /* devanagari ligatures */
1877     else if ( (uni>=0x0958 && uni<=0x095f) || uni==0x931 || uni==0x934 || uni==0x929 )
1878 	tag = CHR('n','u','k','t');
1879     else switch ( uni ) {
1880       case 0xfb05:		/* long-s t */
1881 	/* This should be 'liga' for long-s+t and 'hlig' for s+t */
1882 	tag = CHR('l','i','g','a');
1883       break;
1884       case 0x00c6: case 0x00e6:		/* ae, AE */
1885       case 0x0152: case 0x0153:		/* oe, OE */
1886       case 0x0132: case 0x0133:		/* ij, IJ */
1887       case 0xfb06:			/* s t */
1888 	tag = CHR('d','l','i','g');
1889       break;
1890       case 0xfefb: case 0xfefc:	/* Lam & Alef, required ligs */
1891 	tag = CHR('r','l','i','g');
1892       break;
1893     }
1894 return( tag );
1895 }
1896 
SuffixCheck(SplineChar * sc,char * suffix)1897 SplineChar *SuffixCheck(SplineChar *sc,char *suffix) {
1898     SplineChar *alt = NULL;
1899     SplineFont *sf = sc->parent;
1900     char namebuf[200];
1901 
1902     if ( *suffix=='.' ) ++suffix;
1903     if ( sf->cidmaster!=NULL ) {
1904 	sprintf( namebuf, "%.20s.%d.%.80s", sf->cidmaster->ordering, sc->orig_pos, suffix );
1905 	alt = SFGetChar(sf,-1,namebuf);
1906 	if ( alt==NULL ) {
1907 	    sprintf( namebuf, "cid-%d.%.80s", sc->orig_pos, suffix );
1908 	    alt = SFGetChar(sf,-1,namebuf);
1909 	}
1910     }
1911     if ( alt==NULL && sc->unicodeenc!=-1 ) {
1912 	sprintf( namebuf, "uni%04X.%.80s", sc->unicodeenc, suffix );
1913 	alt = SFGetChar(sf,-1,namebuf);
1914     }
1915     if ( alt==NULL ) {
1916 	sprintf( namebuf, "glyph%d.%.80s", sc->orig_pos, suffix );
1917 	alt = SFGetChar(sf,-1,namebuf);
1918     }
1919     if ( alt==NULL ) {
1920 	sprintf( namebuf, "%.80s.%.80s", sc->name, suffix );
1921 	alt = SFGetChar(sf,-1,namebuf);
1922     }
1923 return( alt );
1924 }
1925 
SuffixCheckCase(SplineChar * sc,char * suffix,int cvt2lc)1926 static SplineChar *SuffixCheckCase(SplineChar *sc,char *suffix, int cvt2lc ) {
1927     SplineChar *alt = NULL;
1928     SplineFont *sf = sc->parent;
1929     char namebuf[100];
1930 
1931     if ( *suffix=='.' ) ++suffix;
1932     if ( sf->cidmaster!=NULL )
1933 return( NULL );
1934 
1935     /* Small cap characters are sometimes named "a.sc" */
1936     /*  and sometimes "A.small" */
1937     /* So if I want a 'smcp' feature I must convert "a" to "A.small" */
1938     /* And if I want a 'c2sc' feature I must convert "A" to "a.sc" */
1939     if ( cvt2lc ) {
1940 	if ( alt==NULL && sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 &&
1941 		isupper(sc->unicodeenc)) {
1942 	    sprintf( namebuf, "uni%04X.%s", tolower(sc->unicodeenc), suffix );
1943 	    alt = SFGetChar(sf,-1,namebuf);
1944 	}
1945 	if ( alt==NULL && isupper(*sc->name)) {
1946 	    sprintf( namebuf, "%c%s.%s", tolower(*sc->name), sc->name+1, suffix );
1947 	    alt = SFGetChar(sf,-1,namebuf);
1948 	}
1949     } else {
1950 	if ( alt==NULL && sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 &&
1951 		islower(sc->unicodeenc)) {
1952 	    sprintf( namebuf, "uni%04X.%s", toupper(sc->unicodeenc), suffix );
1953 	    alt = SFGetChar(sf,-1,namebuf);
1954 	}
1955 	if ( alt==NULL && islower(*sc->name)) {
1956 	    sprintf( namebuf, "%c%s.%s", toupper(*sc->name), sc->name+1, suffix );
1957 	    alt = SFGetChar(sf,-1,namebuf);
1958 	}
1959     }
1960 return( alt );
1961 }
1962 
SCLigCaretCheck(SplineChar * sc,int clean)1963 void SCLigCaretCheck(SplineChar *sc,int clean) {
1964     PST *pst, *carets=NULL, *prev_carets=NULL, *prev;
1965     int lig_comp_max=0, lc, i;
1966     char *pt;
1967     /* Check to see if this is a ligature character, and if so, does it have */
1968     /*  a ligature caret structure. If a lig but no lig caret structure then */
1969     /*  create a lig caret struct */
1970     /* This is not entirely sufficient. If we have an old type1 font with afm */
1971     /*  file then there was no way of saying "ffi = f + f + i" instead you    */
1972     /*  said "ffi = ff + i" (only two component ligatures allowed). This means*/
1973     /*  we'd get the wrong number of lcaret positions */
1974 
1975     if ( sc->lig_caret_cnt_fixed )
1976 return;
1977 
1978     for ( pst=sc->possub, prev=NULL; pst!=NULL; prev = pst, pst=pst->next ) {
1979 	if ( pst->type == pst_lcaret ) {
1980 	    if ( carets!=NULL )
1981 		IError("Too many ligature caret structures" );
1982 	    else {
1983 		carets = pst;
1984 		prev_carets = prev;
1985 	    }
1986 	} else if ( pst->type==pst_ligature ) {
1987 	    for ( lc=0, pt=pst->u.lig.components; *pt; ++pt )
1988 		if ( *pt==' ' ) ++lc;
1989 	    if ( lc>lig_comp_max )
1990 		lig_comp_max = lc;
1991 	}
1992     }
1993     if ( lig_comp_max == 0 ) {
1994 	if ( clean && carets!=NULL ) {
1995 	    if ( prev_carets==NULL )
1996 		sc->possub = carets->next;
1997 	    else
1998 		prev_carets->next = carets->next;
1999 	    carets->next = NULL;
2000 	    PSTFree(carets);
2001 	}
2002 return;
2003     }
2004     if ( carets==NULL ) {
2005 	carets = chunkalloc(sizeof(PST));
2006 	carets->type = pst_lcaret;
2007 	carets->subtable = NULL;		/* Not relevant here */
2008 	carets->next = sc->possub;
2009 	sc->possub = carets;
2010     }
2011     if ( carets->u.lcaret.cnt>=lig_comp_max ) {
2012 	carets->u.lcaret.cnt = lig_comp_max;
2013 return;
2014     }
2015     if ( carets->u.lcaret.carets==NULL )
2016 	carets->u.lcaret.carets = (int16 *) calloc(lig_comp_max,sizeof(int16));
2017     else {
2018 	carets->u.lcaret.carets = (int16 *) realloc(carets->u.lcaret.carets,lig_comp_max*sizeof(int16));
2019 	for ( i=carets->u.lcaret.cnt; i<lig_comp_max; ++i )
2020 	    carets->u.lcaret.carets[i] = 0;
2021     }
2022     carets->u.lcaret.cnt = lig_comp_max;
2023 }
2024 
CI_SName(GGadget * g,GEvent * e)2025 static int CI_SName(GGadget *g, GEvent *e) {	/* Set From Name */
2026     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2027 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
2028 	const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(ci->gw,CID_UName));
2029 	int i;
2030 	char buf[40], *ctemp; unichar_t ubuf[2], *temp;
2031 	ctemp = u2utf8_copy(ret);
2032 	i = UniFromName(ctemp,ui_none,&custom);
2033 	free(ctemp);
2034 	if ( i==-1 ) {
2035 	    /* Adobe says names like uni00410042 represent a ligature (A&B) */
2036 	    /*  (that is "uni" followed by two (or more) 4-digit codes). */
2037 	    /* But that names outside of BMP should be uXXXX or uXXXXX or uXXXXXX */
2038 	    if ( ret[0]=='u' && ret[1]!='n' && u_strlen(ret)<=1+6 ) {
2039 		unichar_t *end;
2040 		i = u_strtol(ret+1,&end,16);
2041 		if ( *end )
2042 		    i = -1;
2043 		else		/* Make sure it is properly capitalized */
2044 		    SetNameFromUnicode(ci->gw,CID_UName,i);
2045 	    }
2046 	}
2047 
2048 	sprintf(buf,"U+%04x", i);
2049 	temp = uc_copy(i==-1?"-1":buf);
2050 	GGadgetSetTitle(GWidgetGetControl(ci->gw,CID_UValue),temp);
2051 	free(temp);
2052 
2053 	ubuf[0] = i;
2054 	if ( i==-1 || i>0xffff )
2055 	    ubuf[0] = '\0';
2056 	ubuf[1] = '\0';
2057 	GGadgetSetTitle(GWidgetGetControl(ci->gw,CID_UChar),ubuf);
2058     }
2059 return( true );
2060 }
2061 
CI_SValue(GGadget * g,GEvent * e)2062 static int CI_SValue(GGadget *g, GEvent *e) {	/* Set From Value */
2063     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2064 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
2065 	unichar_t ubuf[2];
2066 	int val;
2067 
2068 	val = ParseUValue(ci->gw,CID_UValue,false);
2069 	if ( val<0 )
2070 return( true );
2071 
2072 	SetNameFromUnicode(ci->gw,CID_UName,val);
2073 
2074 	ubuf[0] = val;
2075 	if ( val==-1 )
2076 	    ubuf[0] = '\0';
2077 	ubuf[1] = '\0';
2078 	GGadgetSetTitle(GWidgetGetControl(ci->gw,CID_UChar),ubuf);
2079     }
2080 return( true );
2081 }
2082 
TIFromName(const char * name)2083 GTextInfo *TIFromName(const char *name) {
2084     GTextInfo *ti = calloc(1,sizeof(GTextInfo));
2085     ti->text = utf82u_copy(name);
2086     ti->fg = COLOR_DEFAULT;
2087     ti->bg = COLOR_DEFAULT;
2088 return( ti );
2089 }
2090 
CI_SetNameList(CharInfo * ci,int val)2091 static void CI_SetNameList(CharInfo *ci,int val) {
2092     GGadget *g = GWidgetGetControl(ci->gw,CID_UName);
2093     int cnt;
2094 
2095     if ( GGadgetGetUserData(g)==(void *) (intpt) val )
2096 return;		/* Didn't change */
2097     {
2098 	GTextInfo **list = NULL;
2099 	char **names = AllGlyphNames(val,ci->sc->parent->for_new_glyphs,ci->sc);
2100 
2101 	for ( cnt=0; names[cnt]!=NULL; ++cnt );
2102 	list = malloc((cnt+1)*sizeof(GTextInfo*));
2103 	for ( cnt=0; names[cnt]!=NULL; ++cnt ) {
2104 	    list[cnt] = TIFromName(names[cnt]);
2105 	    free(names[cnt]);
2106 	}
2107 	free(names);
2108 	list[cnt] = TIFromName(NULL);
2109 	GGadgetSetList(g,list,true);
2110     }
2111     GGadgetSetUserData(g,(void *) (intpt) val);
2112 }
2113 
CI_UValChanged(GGadget * g,GEvent * e)2114 static int CI_UValChanged(GGadget *g, GEvent *e) {
2115     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
2116 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
2117 	const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(ci->gw,CID_UValue));
2118 	unichar_t *end;
2119 	int val;
2120 
2121 	if (( *ret=='U' || *ret=='u' ) && ret[1]=='+' )
2122 	    ret += 2;
2123 	val = u_strtol(ret,&end,16);
2124 	if ( *end=='\0' )
2125 	    CI_SetNameList(ci,val);
2126     }
2127 return( true );
2128 }
2129 
CI_CharChanged(GGadget * g,GEvent * e)2130 static int CI_CharChanged(GGadget *g, GEvent *e) {
2131     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
2132 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
2133 	const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(ci->gw,CID_UChar));
2134 	int val = *ret;
2135 	unichar_t *temp, ubuf[2]; char buf[10];
2136 
2137 	if ( ret[0]=='\0' )
2138 return( true );
2139 	else if ( ret[1]!='\0' ) {
2140 	    ff_post_notice(_("Only a single character allowed"),_("Only a single character allowed"));
2141 	    ubuf[0] = ret[0];
2142 	    ubuf[1] = '\0';
2143 	    GGadgetSetTitle(GWidgetGetControl(ci->gw,CID_UChar),ubuf);
2144 return( true );
2145 	}
2146 
2147 	SetNameFromUnicode(ci->gw,CID_UName,val);
2148 	CI_SetNameList(ci,val);
2149 
2150 	sprintf(buf,"U+%04x", val);
2151 	temp = uc_copy(buf);
2152 	GGadgetSetTitle(GWidgetGetControl(ci->gw,CID_UValue),temp);
2153 	free(temp);
2154     }
2155 return( true );
2156 }
2157 
CI_CommentChanged(GGadget * g,GEvent * e)2158 static int CI_CommentChanged(GGadget *g, GEvent *e) {
2159     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
2160 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
2161 	/* Let's give things with comments a white color. This may not be a good idea */
2162 	if ( ci->first && ci->sc->color==COLOR_DEFAULT &&
2163 		0==GGadgetGetFirstListSelectedItem(GWidgetGetControl(ci->gw,CID_Color)) )
2164 	    GGadgetSelectOneListItem(GWidgetGetControl(ci->gw,CID_Color),1);
2165 	ci->first = false;
2166     }
2167 return( true );
2168 }
2169 
2170 struct devtab_dlg {
2171     int done;
2172     GWindow gw;
2173     GGadget *gme;
2174     DeviceTable devtab;
2175 };
2176 
2177 static struct col_init devtabci[] = {
2178     { me_int, NULL, NULL, NULL, N_("Pixel Size") },
2179     { me_int, NULL, NULL, NULL, N_("Correction") },
2180     COL_INIT_EMPTY
2181 };
2182 
DevTabMatrixInit(struct matrixinit * mi,char * dvstr)2183 static void DevTabMatrixInit(struct matrixinit *mi,char *dvstr) {
2184     struct matrix_data *md;
2185     int k, p, cnt;
2186     DeviceTable devtab;
2187 
2188     memset(&devtab,0,sizeof(devtab));
2189     DeviceTableParse(&devtab,dvstr);
2190     cnt = 0;
2191     if ( devtab.corrections!=NULL ) {
2192 	for ( k=devtab.first_pixel_size; k<=devtab.last_pixel_size; ++k )
2193 	    if ( devtab.corrections[k-devtab.first_pixel_size]!=0 )
2194 		++cnt;
2195     }
2196 
2197     memset(mi,0,sizeof(*mi));
2198     mi->col_cnt = 2;
2199     mi->col_init = devtabci;
2200 
2201     md = NULL;
2202     for ( k=0; k<2; ++k ) {
2203 	cnt = 0;
2204 	if ( devtab.corrections==NULL )
2205 	    /* Do Nothing */;
2206 	else for ( p=devtab.first_pixel_size; p<=devtab.last_pixel_size; ++p ) {
2207 	    if ( devtab.corrections[p-devtab.first_pixel_size]!=0 ) {
2208 		if ( k ) {
2209 		    md[2*cnt+0].u.md_ival = p;
2210 		    md[2*cnt+1].u.md_ival = devtab.corrections[p-devtab.first_pixel_size];
2211 		}
2212 		++cnt;
2213 	    }
2214 	}
2215 	if ( md==NULL )
2216 	    md = calloc(2*(cnt+10),sizeof(struct matrix_data));
2217     }
2218     mi->matrix_data = md;
2219     mi->initial_row_cnt = cnt;
2220 
2221     mi->initrow = NULL;
2222     mi->finishedit = NULL;
2223     mi->candelete = NULL;
2224     mi->popupmenu = NULL;
2225     mi->handle_key = NULL;
2226     mi->bigedittitle = NULL;
2227 
2228     free( devtab.corrections );
2229 }
2230 
DevTabDlg_OK(GGadget * g,GEvent * e)2231 static int DevTabDlg_OK(GGadget *g, GEvent *e) {
2232 
2233     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2234 	struct devtab_dlg *dvd = GDrawGetUserData(GGadgetGetWindow(g));
2235 	int rows, i, low, high;
2236 	struct matrix_data *corrections = GMatrixEditGet(dvd->gme, &rows);
2237 
2238 	low = high = -1;
2239 	for ( i=0; i<rows; ++i ) {
2240 	    if ( corrections[2*i+1].u.md_ival<-128 || corrections[2*i+1].u.md_ival>127 ) {
2241 		ff_post_error(_("Bad correction"),_("The correction on line %d is too big.  It must be between -128 and 127"),
2242 			i+1 );
2243 return(true);
2244 	    } else if ( corrections[2*i+0].u.md_ival<0 || corrections[2*i+0].u.md_ival>32767 ) {
2245 		gwwv_post_error(_("Bad pixel size"),_("The pixel size on line %d is out of bounds."),
2246 			i+1 );
2247 return(true);
2248 	    }
2249 	    if ( corrections[2*i+1].u.md_ival!=0 ) {
2250 		if ( low==-1 )
2251 		    low = high = corrections[2*i+0].u.md_ival;
2252 		else if ( corrections[2*i+0].u.md_ival<low )
2253 		    low = corrections[2*i+0].u.md_ival;
2254 		else if ( corrections[2*i+0].u.md_ival>high )
2255 		    high = corrections[2*i+0].u.md_ival;
2256 	    }
2257 	}
2258 	memset(&dvd->devtab,0,sizeof(DeviceTable));
2259 	if ( low!=-1 ) {
2260 	    dvd->devtab.first_pixel_size = low;
2261 	    dvd->devtab.last_pixel_size = high;
2262 	    dvd->devtab.corrections = calloc(high-low+1,1);
2263 	    for ( i=0; i<rows; ++i ) {
2264 		if ( corrections[2*i+1].u.md_ival!=0 ) {
2265 		    dvd->devtab.corrections[ corrections[2*i+0].u.md_ival-low ] =
2266 			    corrections[2*i+1].u.md_ival;
2267 		}
2268 	    }
2269 	}
2270 	dvd->done = 2;
2271     }
2272 return( true );
2273 }
2274 
DevTabDlg_Cancel(GGadget * g,GEvent * e)2275 static int DevTabDlg_Cancel(GGadget *g, GEvent *e) {
2276 
2277     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2278 	struct devtab_dlg *dvd = GDrawGetUserData(GGadgetGetWindow(g));
2279 	dvd->done = true;
2280     }
2281 return( true );
2282 }
2283 
devtabdlg_e_h(GWindow gw,GEvent * event)2284 static int devtabdlg_e_h(GWindow gw, GEvent *event) {
2285 
2286     if ( event->type==et_close ) {
2287 	struct devtab_dlg *dvd = GDrawGetUserData(gw);
2288 	dvd->done = true;
2289     } else if ( event->type == et_char ) {
2290 return( false );
2291     }
2292 
2293 return( true );
2294 }
2295 
DevTab_Dlg(GGadget * g,int r,int c)2296 char *DevTab_Dlg(GGadget *g, int r, int c) {
2297     int rows, k, j, cols = GMatrixEditGetColCnt(g);
2298     struct matrix_data *strings = GMatrixEditGet(g, &rows);
2299     char *dvstr = strings[cols*r+c].u.md_str;
2300     struct devtab_dlg dvd;
2301     GRect pos;
2302     GWindow gw;
2303     GWindowAttrs wattrs;
2304     GGadgetCreateData gcd[4], boxes[3];
2305     GGadgetCreateData *varray[6], *harray3[8];
2306     GTextInfo label[4];
2307     struct matrixinit mi;
2308 
2309     memset(&dvd,0,sizeof(dvd));
2310 
2311     memset(&wattrs,0,sizeof(wattrs));
2312     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
2313     wattrs.event_masks = ~(1<<et_charup);
2314     wattrs.is_dlg = true;
2315     wattrs.restrict_input_to_me = 1;
2316     wattrs.undercursor = 1;
2317     wattrs.cursor = ct_pointer;
2318     wattrs.utf8_window_title = _("Device Table Adjustments");
2319     pos.x = pos.y = 0;
2320     pos.width =GDrawPointsToPixels(NULL,GGadgetScale(268));
2321     pos.height = GDrawPointsToPixels(NULL,375);
2322     dvd.gw = gw = GDrawCreateTopWindow(NULL,&pos,devtabdlg_e_h,&dvd,&wattrs);
2323 
2324     DevTabMatrixInit(&mi,dvstr);
2325 
2326     memset(&gcd,0,sizeof(gcd));
2327     memset(&boxes,0,sizeof(boxes));
2328     memset(&label,0,sizeof(label));
2329     k=j=0;
2330     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[1].gd.pos.y+14;
2331     gcd[k].gd.flags = gg_enabled | gg_visible;
2332     gcd[k].gd.u.matrix = &mi;
2333     gcd[k].gd.popup_msg = _(
2334 	"At small pixel sizes (screen font sizes)\n"
2335 	"the rounding errors that occur may be\n"
2336 	"extremely ugly. A device table allows\n"
2337 	"you to specify adjustments to the rounded\n"
2338 	"Every pixel size my have its own adjustment.");
2339     gcd[k].creator = GMatrixEditCreate;
2340     varray[j++] = &gcd[k++]; varray[j++] = NULL;
2341 
2342     gcd[k].gd.pos.x = 30-3;
2343     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
2344     label[k].text = (unichar_t *) _("_OK");
2345     label[k].text_is_1byte = true;
2346     label[k].text_in_resource = true;
2347     gcd[k].gd.label = &label[k];
2348     gcd[k].gd.handle_controlevent = DevTabDlg_OK;
2349     gcd[k++].creator = GButtonCreate;
2350 
2351     gcd[k].gd.pos.x = -30;
2352     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
2353     label[k].text = (unichar_t *) _("_Cancel");
2354     label[k].text_is_1byte = true;
2355     label[k].text_in_resource = true;
2356     gcd[k].gd.label = &label[k];
2357     gcd[k].gd.handle_controlevent = DevTabDlg_Cancel;
2358     gcd[k].gd.cid = CID_Cancel;
2359     gcd[k++].creator = GButtonCreate;
2360 
2361     harray3[0] = harray3[2] = harray3[3] = harray3[4] = harray3[6] = GCD_Glue;
2362     harray3[7] = NULL;
2363     harray3[1] = &gcd[k-2]; harray3[5] = &gcd[k-1];
2364 
2365     boxes[0].gd.flags = gg_enabled|gg_visible;
2366     boxes[0].gd.u.boxelements = harray3;
2367     boxes[0].creator = GHBoxCreate;
2368     varray[j++] = &boxes[0]; varray[j++] = NULL; varray[j] = NULL;
2369 
2370     boxes[1].gd.pos.x = boxes[1].gd.pos.y = 2;
2371     boxes[1].gd.flags = gg_enabled|gg_visible;
2372     boxes[1].gd.u.boxelements = varray;
2373     boxes[1].creator = GHVGroupCreate;
2374 
2375     GGadgetsCreate(gw,boxes+1);
2376 
2377     free( mi.matrix_data );
2378 
2379     dvd.gme = gcd[0].ret;
2380     GMatrixEditSetNewText(gcd[0].ret,S_("PixelSize|New"));
2381     GHVBoxSetExpandableRow(boxes[1].ret,1);
2382     GHVBoxSetExpandableCol(boxes[0].ret,gb_expandgluesame);
2383 
2384     GHVBoxFitWindow(boxes[1].ret);
2385 
2386     GDrawSetVisible(gw,true);
2387     while ( !dvd.done )
2388 	GDrawProcessOneEvent(NULL);
2389     GDrawDestroyWindow(gw);
2390     if ( dvd.done==2 ) {
2391 	char *ret;
2392 	DevTabToString(&ret,&dvd.devtab);
2393 	free(dvd.devtab.corrections);
2394 return( ret );
2395     } else
2396 return( copy(dvstr));
2397 }
2398 
2399 static void finishedit(GGadget *g, int r, int c, int wasnew);
2400 static void kernfinishedit(GGadget *g, int r, int c, int wasnew);
2401 static void kerninit(GGadget *g, int r);
2402 static void enable_enum(GGadget *g, GMenuItem *mi, int r, int c);
2403 
2404 static struct col_init simplesubsci[] = {
2405     { me_enum , NULL, NULL, enable_enum, N_("Subtable") },
2406     { me_string, NULL, NULL, NULL, N_("Replacement Glyph Name") },
2407     COL_INIT_EMPTY
2408 };
2409 static struct col_init ligatureci[] = {
2410     { me_enum , NULL, NULL, NULL, N_("Subtable") },	/* There can be multiple ligatures for a glyph */
2411     { me_string, NULL, NULL, NULL, N_("Source Glyph Names") },
2412     COL_INIT_EMPTY
2413 };
2414 static struct col_init altsubsci[] = {
2415     { me_enum , NULL, NULL, enable_enum, N_("Subtable") },
2416     { me_string, NULL, NULL, NULL, N_("Replacement Glyph Names") },
2417     COL_INIT_EMPTY
2418 };
2419 static struct col_init multsubsci[] = {
2420     { me_enum , NULL, NULL, enable_enum, N_("Subtable") },
2421     { me_string, NULL, NULL, NULL, N_("Replacement Glyph Names") },
2422     COL_INIT_EMPTY
2423 };
2424 static struct col_init simpleposci[] = {
2425     { me_enum , NULL, NULL, enable_enum, N_("Subtable") },
2426     { me_int, NULL, NULL, NULL, NU_("∆x") },	/* delta-x */
2427 /* GT: "Adjust" here means Device Table based pixel adjustments, an OpenType */
2428 /* GT: concept which allows small corrections for small pixel sizes where */
2429 /* GT: rounding errors (in kerning for example) may smush too glyphs together */
2430 /* GT: or space them too far apart. Generally not a problem for big pixelsizes*/
2431     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2432     { me_int, NULL, NULL, NULL, NU_("∆y") },		/* delta-y */
2433     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2434     { me_int, NULL, NULL, NULL, NU_("∆x_adv") },	/* delta-x-adv */
2435     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2436     { me_int, NULL, NULL, NULL, NU_("∆y_adv") },	/* delta-y-adv */
2437     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2438     COL_INIT_EMPTY
2439 };
2440 static struct col_init pairposci[] = {
2441     { me_enum , NULL, NULL, NULL, N_("Subtable") },	/* There can be multiple kern-pairs for a glyph */
2442     { me_string , DevTab_Dlg, NULL, NULL, N_("Second Glyph Name") },
2443     { me_int, NULL, NULL, NULL, NU_("∆x #1") },		/* delta-x */
2444     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2445     { me_int, NULL, NULL, NULL, NU_("∆y #1") },		/* delta-y */
2446     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2447     { me_int, NULL, NULL, NULL, NU_("∆x_adv #1") },	/* delta-x-adv */
2448     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2449     { me_int, NULL, NULL, NULL, NU_("∆y_adv #1") },	/* delta-y-adv */
2450     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2451     { me_int, NULL, NULL, NULL, NU_("∆x #2") },		/* delta-x */
2452     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2453     { me_int, NULL, NULL, NULL, NU_("∆y #2") },		/* delta-y */
2454     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2455     { me_int, NULL, NULL, NULL, NU_("∆x_adv #2") },	/* delta-x-adv */
2456     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2457     { me_int, NULL, NULL, NULL, NU_("∆y_adv #2") },	/* delta-y-adv */
2458     { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2459     COL_INIT_EMPTY
2460 };
2461 static int pst2lookuptype[] = { ot_undef, gpos_single, gpos_pair, gsub_single,
2462      gsub_alternate, gsub_multiple, gsub_ligature, 0 };
2463 struct matrixinit mi[] = {
2464     { sizeof(simpleposci)/sizeof(struct col_init)-1, simpleposci, 0, NULL, NULL, NULL, finishedit, NULL, NULL, NULL },
2465     { sizeof(pairposci)/sizeof(struct col_init)-1, pairposci, 0, NULL, kerninit, NULL, kernfinishedit, NULL, NULL, NULL },
2466     { sizeof(simplesubsci)/sizeof(struct col_init)-1, simplesubsci, 0, NULL, NULL, NULL, finishedit, NULL, NULL, NULL },
2467     { sizeof(altsubsci)/sizeof(struct col_init)-1, altsubsci, 0, NULL, NULL, NULL, finishedit, NULL, NULL, NULL },
2468     { sizeof(multsubsci)/sizeof(struct col_init)-1, multsubsci, 0, NULL, NULL, NULL, finishedit, NULL, NULL, NULL },
2469     { sizeof(ligatureci)/sizeof(struct col_init)-1, ligatureci, 0, NULL, NULL, NULL, finishedit, NULL, NULL, NULL },
2470     MATRIXINIT_EMPTY
2471 };
2472 
enable_enum(GGadget * g,GMenuItem * mi,int r,int c)2473 static void enable_enum(GGadget *g, GMenuItem *mi, int r, int c) {
2474     int i,rows,j;
2475     struct matrix_data *possub;
2476     CharInfo *ci;
2477     int sel,cols;
2478 
2479     if ( c!=0 )
2480 return;
2481     ci = GDrawGetUserData(GGadgetGetWindow(g));
2482     sel = GTabSetGetSel(GWidgetGetControl(ci->gw,CID_Tabs))-2;
2483     possub = GMatrixEditGet(g, &rows);
2484     cols = GMatrixEditGetColCnt(g);
2485 
2486     ci->old_sub = (void *) possub[r*cols+0].u.md_ival;
2487 
2488     for ( i=0; mi[i].ti.text!=NULL || mi[i].ti.line; ++i ) {
2489 	if ( mi[i].ti.line )	/* Lines, and the new entry always enabled */
2490 	    mi[i].ti.disabled = false;
2491 	else if ( mi[i].ti.userdata == NULL )
2492 	    /* One of the lookup (rather than subtable) entries. leave disabled */;
2493 	else if ( mi[i].ti.userdata == (void *) possub[r*cols+0].u.md_ival ) {
2494 	    mi[i].ti.selected = true;		/* Current thing, they can keep on using it */
2495 	    mi[i].ti.disabled = false;
2496 	} else {
2497 	    for ( j=0; j<rows; ++j )
2498 		if ( mi[i].ti.userdata == (void *) possub[j*cols+0].u.md_ival ) {
2499 		    mi[i].ti.selected = false;
2500 		    mi[i].ti.disabled = true;
2501 	    break;
2502 		}
2503 	    if ( j==rows ) {	/* This subtable hasn't been used yet */
2504 		mi[i].ti.disabled = false;
2505 	    }
2506 	}
2507     }
2508 }
2509 
SCSubtableDefaultSubsCheck(SplineChar * sc,struct lookup_subtable * sub,struct matrix_data * possub,int col_cnt,int r,int layer)2510 void SCSubtableDefaultSubsCheck(SplineChar *sc, struct lookup_subtable *sub,
2511 	struct matrix_data *possub, int col_cnt, int r, int layer) {
2512     FeatureScriptLangList *fl;
2513     int lookup_type = sub->lookup->lookup_type;
2514     SplineChar *alt;
2515     char buffer[8];
2516     int i;
2517     static uint32 form_tags[] = { CHR('i','n','i','t'), CHR('m','e','d','i'), CHR('f','i','n','a'), CHR('i','s','o','l'), 0 };
2518     real loff, roff;
2519 
2520     if ( lookup_type == gsub_single && sub->suffix != NULL ) {
2521 	alt = SuffixCheck(sc,sub->suffix);
2522 	if ( alt!=NULL ) {
2523 	    possub[r*col_cnt+1].u.md_str = copy( alt->name );
2524 return;
2525 	}
2526     }
2527 
2528     for ( fl = sub->lookup->features; fl!=NULL; fl=fl->next ) {
2529 	if ( lookup_type == gpos_single ) {
2530 	    /* These too features are designed to crop off the left and right */
2531 	    /*  side bearings respectively */
2532 	    if ( fl->featuretag == CHR('l','f','b','d') ) {
2533 		GuessOpticalOffset(sc,layer,&loff,&roff,0);
2534 		/* Adjust horixontal positioning and horizontal advance by */
2535 		/*  the left side bearing */
2536 		possub[r*col_cnt+SIM_DX].u.md_ival = -loff;
2537 		possub[r*col_cnt+SIM_DX_ADV].u.md_ival = -loff;
2538 return;
2539 	    } else if ( fl->featuretag == CHR('r','t','b','d') ) {
2540 		GuessOpticalOffset(sc,layer,&loff,&roff,0);
2541 		/* Adjust horizontal advance by right side bearing */
2542 		possub[r*col_cnt+SIM_DX_ADV].u.md_ival = -roff;
2543 return;
2544 	    }
2545 	} else if ( lookup_type == gsub_single ) {
2546 	    alt = NULL;
2547 	    if ( fl->featuretag == CHR('s','m','c','p') ) {
2548 		alt = SuffixCheck(sc,"sc");
2549 		if ( alt==NULL )
2550 		    alt = SuffixCheckCase(sc,"small",false);
2551 	    } else if ( fl->featuretag == CHR('c','2','s','c') ) {
2552 		alt = SuffixCheck(sc,"small");
2553 		if ( alt==NULL )
2554 		    alt = SuffixCheckCase(sc,"sc",true);
2555 	    } else if ( fl->featuretag == CHR('r','t','l','a') ) {
2556 		if ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 && tomirror(sc->unicodeenc)!=0 )
2557 		    alt = SFGetChar(sc->parent,tomirror(sc->unicodeenc),NULL);
2558 	    } else if ( sc->unicodeenc==0x3c3 && fl->featuretag==CHR('f','i','n','a') ) {
2559 		/* Greek final sigma */
2560 		alt = SFGetChar(sc->parent,0x3c2,NULL);
2561 	    }
2562 	    if ( alt==NULL ) {
2563 		buffer[0] = fl->featuretag>>24;
2564 		buffer[1] = fl->featuretag>>16;
2565 		buffer[2] = fl->featuretag>>8;
2566 		buffer[3] = fl->featuretag&0xff;
2567 		buffer[4] = 0;
2568 		alt = SuffixCheck(sc,buffer);
2569 	    }
2570 	    if ( alt==NULL && sc->unicodeenc>=0x600 && sc->unicodeenc<0x700 ) {
2571 		/* Arabic forms */
2572 		for ( i=0; form_tags[i]!=0; ++i ) if ( form_tags[i]==fl->featuretag ) {
2573 		    if ( (&(ArabicForms[sc->unicodeenc-0x600].initial))[i]!=0 &&
2574 			    (&(ArabicForms[sc->unicodeenc-0x600].initial))[i]!=sc->unicodeenc &&
2575 			    (alt = SFGetChar(sc->parent,(&(ArabicForms[sc->unicodeenc-0x600].initial))[i],NULL))!=NULL )
2576 		break;
2577 		}
2578 	    }
2579 	    if ( alt!=NULL ) {
2580 		possub[r*col_cnt+1].u.md_str = copy( alt->name );
2581 return;
2582 	    }
2583 	} else if ( lookup_type == gsub_ligature ) {
2584 	    if ( fl->featuretag == LigTagFromUnicode(sc->unicodeenc) ) {
2585 		int alt_index;
2586 		for ( alt_index = 0; ; ++alt_index ) {
2587 		    char *components = LigDefaultStr(sc->unicodeenc,sc->name,alt_index);
2588 		    if ( components==NULL )
2589 		break;
2590 		    for ( i=0; i<r; ++i ) {
2591 			if ( possub[i*col_cnt+0].u.md_ival == (intpt) sub &&
2592 				strcmp(possub[i*col_cnt+1].u.md_str,components)==0 )
2593 		    break;
2594 		    }
2595 		    if ( i==r ) {
2596 			possub[r*col_cnt+1].u.md_str = components;
2597 return;
2598 		    }
2599 		    free( components );
2600 		}
2601 	    }
2602 	}
2603     }
2604 }
2605 
finishedit(GGadget * g,int r,int c,int wasnew)2606 static void finishedit(GGadget *g, int r, int c, int wasnew) {
2607     int rows;
2608     struct matrix_data *possub;
2609     CharInfo *ci;
2610     int sel,cols;
2611     struct lookup_subtable *sub;
2612     struct subtable_data sd;
2613     GTextInfo *ti;
2614 
2615     if ( c!=0 )
2616 return;
2617     ci = GDrawGetUserData(GGadgetGetWindow(g));
2618     sel = GTabSetGetSel(GWidgetGetControl(ci->gw,CID_Tabs))-2;
2619     possub = GMatrixEditGet(g, &rows);
2620     cols = GMatrixEditGetColCnt(g);
2621     if ( possub[r*cols+0].u.md_ival!=0 ) {
2622 	if ( wasnew )
2623 	    SCSubtableDefaultSubsCheck(ci->sc,(struct lookup_subtable *) possub[r*cols+0].u.md_ival, possub,
2624 		    cols, r, ci->def_layer );
2625 return;
2626     }
2627     /* They asked to create a new subtable */
2628 
2629     memset(&sd,0,sizeof(sd));
2630     sd.flags = sdf_dontedit;
2631     sub = SFNewLookupSubtableOfType(ci->sc->parent,pst2lookuptype[sel+1],&sd,ci->def_layer);
2632     if ( sub!=NULL ) {
2633 	possub[r*cols+0].u.md_ival = (intpt) sub;
2634 	ti = SFSubtableListOfType(ci->sc->parent, pst2lookuptype[sel+1], false, false);
2635 	GMatrixEditSetColumnChoices(g,0,ti);
2636 	GTextInfoListFree(ti);
2637 	if ( wasnew && ci->cv!=NULL )
2638 	    SCSubtableDefaultSubsCheck(ci->sc,sub, possub, cols, r, CVLayer((CharViewBase *) (ci->cv)));
2639     } else if ( ci->old_sub!=NULL ) {
2640 	/* Restore old value */
2641 	possub[r*cols+0].u.md_ival = (intpt) ci->old_sub;
2642     } else {
2643 	GMatrixEditDeleteRow(g,r);
2644     }
2645     ci->old_sub = NULL;
2646     GGadgetRedraw(g);
2647 }
2648 
kern_AddKP(void * data,SplineChar * left,SplineChar * right,int off)2649 static void kern_AddKP(void *data,SplineChar *left, SplineChar *right, int off) {
2650     int *kp_offset = data;
2651     *kp_offset = off;
2652 }
2653 
kernfinishedit(GGadget * g,int r,int c,int wasnew)2654 static void kernfinishedit(GGadget *g, int r, int c, int wasnew) {
2655     int rows;
2656     struct matrix_data *possub;
2657     CharInfo *ci;
2658     int cols;
2659     struct lookup_subtable *sub;
2660     SplineChar *lefts[2], *rights[2];
2661     int touch, separation, kp_offset=0;
2662     SplineChar *osc;
2663 
2664     if ( c==1 ) {
2665 	ci = GDrawGetUserData(GGadgetGetWindow(g));
2666 	possub = GMatrixEditGet(g, &rows);
2667 	cols = GMatrixEditGetColCnt(g);
2668 	sub = (struct lookup_subtable *) possub[r*cols+0].u.md_ival;
2669 	if ( possub[r*cols+PAIR_DX_ADV1].u.md_ival==0 &&
2670 		possub[r*cols+1].u.md_str!=NULL &&
2671 		(osc = SFGetChar(ci->sc->parent,-1,possub[r*cols+1].u.md_str))!=NULL ) {
2672 	    lefts[1] = rights[1] = NULL;
2673 	    if ( sub->lookup->lookup_flags & pst_r2l ) {
2674 		lefts[0] = osc;
2675 		rights[0] = ci->sc;
2676 	    } else {
2677 		lefts[0] = ci->sc;
2678 		rights[0] = osc;
2679 	    }
2680 	    touch = sub->kerning_by_touch;
2681 	    separation = sub->separation;
2682 	    if ( separation==0 && !touch )
2683 		separation = 15*(osc->parent->ascent+osc->parent->descent)/100;
2684 	    AutoKern2(osc->parent,ci->def_layer,lefts,rights,sub,
2685 		    separation,0,touch,0,0,	/* Don't bother with minkern or onlyCloser, they asked for this, they get it, whatever it may be */
2686 		    kern_AddKP,&kp_offset);
2687 	    possub[r*cols+PAIR_DX_ADV1].u.md_ival=kp_offset;
2688 	}
2689     } else
2690 	finishedit(g,r,c,wasnew);
2691 }
2692 
SubHasScript(uint32 script,struct lookup_subtable * sub)2693 static int SubHasScript(uint32 script,struct lookup_subtable *sub) {
2694     FeatureScriptLangList *f;
2695     struct scriptlanglist *s;
2696 
2697     if ( sub==NULL )
2698 return(false);
2699     for ( f = sub->lookup->features; f!=NULL; f=f->next ) {
2700 	for ( s=f->scripts; s!=NULL; s=s->next ) {
2701 	    if ( s->script == script )
2702 return( true );
2703 	}
2704     }
2705 return( false );
2706 }
2707 
kerninit(GGadget * g,int r)2708 static void kerninit(GGadget *g, int r) {
2709     CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
2710     GMenuItem *mi = GMatrixEditGetColumnChoices(g,0);
2711     int i,cols,rows;
2712     struct matrix_data *possub;
2713     uint32 script;
2714 
2715     possub = GMatrixEditGet(g, &rows);
2716     cols = GMatrixEditGetColCnt(g);
2717 
2718     if ( r!=0 )
2719 	possub[r*cols+0].u.md_ival = possub[(r-1)*cols+0].u.md_ival;
2720     else {
2721 	script = SCScriptFromUnicode(ci->sc);
2722 	for ( i=0; mi[i].ti.line || mi[i].ti.text!=NULL; ++i ) {
2723 	    if ( SubHasScript(script,(struct lookup_subtable *) mi[i].ti.userdata ) )
2724 	break;
2725 	}
2726 	if ( mi[i].ti.line || mi[i].ti.text!=NULL )
2727 	    possub[r*cols+0].u.md_ival = (intpt) mi[i].ti.userdata;
2728     }
2729 }
2730 
CI_DoHideUnusedSingle(CharInfo * ci)2731 static void CI_DoHideUnusedSingle(CharInfo *ci) {
2732     GGadget *pstk = GWidgetGetControl(ci->gw,CID_List+(pst_position-1)*100);
2733     int rows, cols = GMatrixEditGetColCnt(pstk);
2734     struct matrix_data *old = GMatrixEditGet(pstk,&rows);
2735     uint8 cols_used[20];
2736     int r, col, tot;
2737 
2738     if ( lookup_hideunused ) {
2739 	memset(cols_used,0,sizeof(cols_used));
2740 	for ( r=0; r<rows; ++r ) {
2741 	    for ( col=1; col<cols; col+=2 ) {
2742 		if ( old[cols*r+col].u.md_ival!=0 )
2743 		    cols_used[col] = true;
2744 		if ( old[cols*r+col+1].u.md_str!=NULL && *old[cols*r+col+1].u.md_str!='\0' )
2745 		    cols_used[col+1] = true;
2746 	    }
2747 	}
2748 	for ( col=1, tot=0; col<cols; ++col )
2749 	    tot += cols_used[col];
2750 	/* If no columns used (no info yet, all info is to preempt a kernclass and sets to 0) */
2751 	/*  then show what we expect to be the default column for this kerning mode*/
2752 	if ( tot==0 ) {
2753 	    if ( strstr(ci->sc->name,".vert")!=NULL || strstr(ci->sc->name,".vrt2")!=NULL )
2754 		cols_used[SIM_DY] = true;
2755 	    else
2756 		cols_used[SIM_DX] = true;
2757 	}
2758 	for ( col=1; col<cols; ++col )
2759 	    GMatrixEditShowColumn(pstk,col,cols_used[col]);
2760     } else {
2761 	for ( col=1; col<cols; ++col )
2762 	    GMatrixEditShowColumn(pstk,col,true);
2763     }
2764     GWidgetToDesiredSize(ci->gw);
2765 
2766     GGadgetRedraw(pstk);
2767 }
2768 
CI_DoHideUnusedPair(CharInfo * ci)2769 static void CI_DoHideUnusedPair(CharInfo *ci) {
2770     GGadget *pstk = GWidgetGetControl(ci->gw,CID_List+(pst_pair-1)*100);
2771     int rows, cols = GMatrixEditGetColCnt(pstk);
2772     struct matrix_data *old = GMatrixEditGet(pstk,&rows);
2773     uint8 cols_used[20];
2774     int r, col, tot;
2775 
2776     if ( lookup_hideunused ) {
2777 	memset(cols_used,0,sizeof(cols_used));
2778 	for ( r=0; r<rows; ++r ) {
2779 	    for ( col=2; col<cols; col+=2 ) {
2780 		if ( old[cols*r+col].u.md_ival!=0 )
2781 		    cols_used[col] = true;
2782 		if ( old[cols*r+col+1].u.md_str!=NULL && *old[cols*r+col+1].u.md_str!='\0' )
2783 		    cols_used[col+1] = true;
2784 	    }
2785 	}
2786 	for ( col=2, tot=0; col<cols; ++col )
2787 	    tot += cols_used[col];
2788 	/* If no columns used (no info yet, all info is to preempt a kernclass and sets to 0) */
2789 	/*  then show what we expect to be the default column for this kerning mode*/
2790 	if ( tot==0 ) {
2791 	    if ( strstr(ci->sc->name,".vert")!=NULL || strstr(ci->sc->name,".vrt2")!=NULL )
2792 		cols_used[PAIR_DY_ADV1] = true;
2793 	    else if ( SCRightToLeft(ci->sc))
2794 		cols_used[PAIR_DX_ADV2] = true;
2795 	    else
2796 		cols_used[PAIR_DX_ADV1] = true;
2797 	}
2798 	for ( col=2; col<cols; ++col )
2799 	    GMatrixEditShowColumn(pstk,col,cols_used[col]);
2800     } else {
2801 	for ( col=2; col<cols; ++col )
2802 	    GMatrixEditShowColumn(pstk,col,true);
2803     }
2804     GWidgetToDesiredSize(ci->gw);
2805 
2806     GGadgetRedraw(pstk);
2807 }
2808 
CI_HideUnusedPair(GGadget * g,GEvent * e)2809 static int CI_HideUnusedPair(GGadget *g, GEvent *e) {
2810     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
2811 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
2812 	lookup_hideunused = GGadgetIsChecked(g);
2813 	CI_DoHideUnusedPair(ci);
2814 	GGadgetRedraw(GWidgetGetControl(ci->gw,CID_List+(pst_pair-1)*100));
2815     }
2816 return( true );
2817 }
2818 
CI_HideUnusedSingle(GGadget * g,GEvent * e)2819 static int CI_HideUnusedSingle(GGadget *g, GEvent *e) {
2820     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
2821 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
2822 	lookup_hideunused = GGadgetIsChecked(g);
2823 	CI_DoHideUnusedSingle(ci);
2824 	GGadgetRedraw(GWidgetGetControl(ci->gw,CID_List+(pst_position-1)*100));
2825     }
2826 return( true );
2827 }
2828 
CI_FreeKernedImage(const void * _ci,GImage * img)2829 static void CI_FreeKernedImage(const void *_ci, GImage *img) {
2830     GImageDestroy(img);
2831 }
2832 
2833 static const int kern_popup_size = 100;
2834 
Rasterize(SplineChar * sc,int def_layer)2835 static BDFChar *Rasterize(SplineChar *sc,int def_layer) {
2836     void *freetypecontext=NULL;
2837     BDFChar *ret;
2838 
2839     freetypecontext = FreeTypeFontContext(sc->parent,sc,sc->parent->fv,def_layer);
2840     if ( freetypecontext!=NULL ) {
2841 	ret = SplineCharFreeTypeRasterize(freetypecontext,sc->orig_pos,kern_popup_size,72,8);
2842 	FreeTypeFreeContext(freetypecontext);
2843     } else
2844 	ret = SplineCharAntiAlias(sc,def_layer,kern_popup_size,4);
2845 return( ret );
2846 }
2847 
CI_GetKernedImage(const void * _ci)2848 static GImage *CI_GetKernedImage(const void *_ci) {
2849     CharInfo *ci = (CharInfo *) _ci;
2850     GGadget *pstk = GWidgetGetControl(ci->gw,CID_List+(pst_pair-1)*100);
2851     int rows, cols = GMatrixEditGetColCnt(pstk);
2852     struct matrix_data *old = GMatrixEditGet(pstk,&rows);
2853     SplineChar *othersc = SFGetChar(ci->sc->parent,-1, old[cols*ci->r+1].u.md_str);
2854     BDFChar *me, *other;
2855     double scale = kern_popup_size/(double) (ci->sc->parent->ascent+ci->sc->parent->descent);
2856     int kern;
2857     int width, height, miny, maxy, minx, maxx;
2858     GImage *img;
2859     struct _GImage *base;
2860     Color fg, bg;
2861     int l,clut_scale;
2862     int x,y, xoffset, yoffset, coff1, coff2;
2863     struct lookup_subtable *sub = (struct lookup_subtable *) (old[cols*ci->r+0].u.md_ival);
2864 
2865     if ( othersc==NULL )
2866 return( NULL );
2867     me = Rasterize(ci->sc,ci->def_layer);
2868     other = Rasterize(othersc,ci->def_layer);
2869     if ( sub->vertical_kerning ) {
2870 	int vwidth = rint(ci->sc->vwidth*scale);
2871 	kern = rint( old[cols*ci->r+PAIR_DY_ADV1].u.md_ival*scale );
2872 	miny = me->ymin + rint(old[cols*ci->r+PAIR_DY1].u.md_ival*scale);
2873 	maxy = me->ymax + rint(old[cols*ci->r+PAIR_DY1].u.md_ival*scale);
2874 	if ( miny > vwidth + kern + rint(old[cols*ci->r+PAIR_DY2].u.md_ival*scale) + other->ymin )
2875 	    miny = vwidth + kern + rint(old[cols*ci->r+PAIR_DY2].u.md_ival*scale) + other->ymin;
2876 	if ( maxy < vwidth + kern + rint(old[cols*ci->r+PAIR_DY2].u.md_ival*scale) + other->ymax )
2877 	    maxy = vwidth + kern + rint(old[cols*ci->r+PAIR_DY2].u.md_ival*scale) + other->ymax;
2878 	height = maxy - miny + 2;
2879 	minx = me->xmin + rint(old[cols*ci->r+PAIR_DX1].u.md_ival*scale); maxx = me->xmax + rint(old[cols*ci->r+PAIR_DX1].u.md_ival*scale);
2880 	if ( minx>other->xmin + rint(old[cols*ci->r+PAIR_DX2].u.md_ival*scale) ) minx = other->xmin+ rint(old[cols*ci->r+PAIR_DX2].u.md_ival*scale) ;
2881 	if ( maxx<other->xmax + rint(old[cols*ci->r+PAIR_DX2].u.md_ival*scale) ) maxx = other->xmax+ rint(old[cols*ci->r+PAIR_DX2].u.md_ival*scale) ;
2882 
2883 	img = GImageCreate(it_index,maxx-minx+2,height);
2884 	base = img->u.image;
2885 	memset(base->data,'\0',base->bytes_per_line*base->height);
2886 
2887 	yoffset = 1 + maxy - vwidth - kern - rint(old[cols*ci->r+PAIR_DY1].u.md_ival*scale);
2888 	xoffset = 1 - minx + rint(old[cols*ci->r+PAIR_DX1].u.md_ival*scale);
2889 	for ( y=me->ymin; y<=me->ymax; ++y ) {
2890 	    for ( x=me->xmin; x<=me->xmax; ++x ) {
2891 		base->data[(yoffset-y)*base->bytes_per_line + (x+xoffset)] =
2892 			me->bitmap[(me->ymax-y)*me->bytes_per_line + (x-me->xmin)];
2893 	    }
2894 	}
2895 	yoffset = 1 + maxy - rint(old[cols*ci->r+PAIR_DY2].u.md_ival*scale);
2896 	xoffset = 1 - minx + rint(old[cols*ci->r+PAIR_DX2].u.md_ival*scale);
2897 	for ( y=other->ymin; y<=other->ymax; ++y ) {
2898 	    for ( x=other->xmin; x<=other->xmax; ++x ) {
2899 		int n = other->bitmap[(other->ymax-y)*other->bytes_per_line + (x-other->xmin)];
2900 		if ( n>base->data[(yoffset-y)*base->bytes_per_line + (x+xoffset)] )
2901 		    base->data[(yoffset-y)*base->bytes_per_line + (x+xoffset)] = n;
2902 	    }
2903 	}
2904     } else {
2905 	coff1 = coff2 = 0;
2906 	if ( sub->lookup->lookup_flags & pst_r2l ) {
2907 	    BDFChar *temp = me;
2908 	    me = other;
2909 	    other = temp;
2910 	    coff1 = 8; coff2 = -8;
2911 	}
2912 	kern = rint( old[cols*ci->r+PAIR_DX_ADV1+coff1].u.md_ival*scale );
2913 	minx = me->xmin + rint(old[cols*ci->r+PAIR_DX1+coff1].u.md_ival*scale);
2914 	maxx = me->xmax + rint(old[cols*ci->r+PAIR_DX1+coff1].u.md_ival*scale);
2915 	if ( minx > me->width + kern + rint(old[cols*ci->r+PAIR_DX2+coff2].u.md_ival*scale) + other->xmin )
2916 	    minx = me->width + kern + rint(old[cols*ci->r+PAIR_DX2+coff2].u.md_ival*scale) + other->xmin;
2917 	if ( maxx < me->width + kern + rint(old[cols*ci->r+PAIR_DX2+coff2].u.md_ival*scale) + other->xmax )
2918 	    maxx = me->width + kern + rint(old[cols*ci->r+PAIR_DX2+coff2].u.md_ival*scale) + other->xmax;
2919 	width = maxx - minx + 2;
2920 	miny = me->ymin + rint(old[cols*ci->r+PAIR_DY1+coff1].u.md_ival*scale); maxy = me->ymax + rint(old[cols*ci->r+PAIR_DY1+coff1].u.md_ival*scale);
2921 	if ( miny>other->ymin + rint(old[cols*ci->r+PAIR_DY2+coff2].u.md_ival*scale) ) miny = other->ymin+ rint(old[cols*ci->r+PAIR_DY2+coff2].u.md_ival*scale) ;
2922 	if ( maxy<other->ymax + rint(old[cols*ci->r+PAIR_DY2+coff2].u.md_ival*scale) ) maxy = other->ymax+ rint(old[cols*ci->r+PAIR_DY2+coff2].u.md_ival*scale) ;
2923 
2924 	img = GImageCreate(it_index,width,maxy-miny+2);
2925 	base = img->u.image;
2926 	memset(base->data,'\0',base->bytes_per_line*base->height);
2927 
2928 	xoffset = rint(old[cols*ci->r+PAIR_DX1+coff1].u.md_ival*scale) + 1 - minx;
2929 	yoffset = 1 + maxy - rint(old[cols*ci->r+PAIR_DY1+coff1].u.md_ival*scale);
2930 	for ( y=me->ymin; y<=me->ymax; ++y ) {
2931 	    for ( x=me->xmin; x<=me->xmax; ++x ) {
2932 		base->data[(yoffset-y)*base->bytes_per_line + (x+xoffset)] =
2933 			me->bitmap[(me->ymax-y)*me->bytes_per_line + (x-me->xmin)];
2934 	    }
2935 	}
2936 	xoffset = 1 - minx + me->width + kern - rint(old[cols*ci->r+PAIR_DX2+coff2].u.md_ival*scale);
2937 	yoffset = 1 + maxy - rint(old[cols*ci->r+PAIR_DY2+coff2].u.md_ival*scale);
2938 	for ( y=other->ymin; y<=other->ymax; ++y ) {
2939 	    for ( x=other->xmin; x<=other->xmax; ++x ) {
2940 		int n = other->bitmap[(other->ymax-y)*other->bytes_per_line + (x-other->xmin)];
2941 		if ( n>base->data[(yoffset-y)*base->bytes_per_line + (x+xoffset)] )
2942 		    base->data[(yoffset-y)*base->bytes_per_line + (x+xoffset)] = n;
2943 	    }
2944 	}
2945     }
2946     memset(base->clut,'\0',sizeof(*base->clut));
2947     bg = GDrawGetDefaultBackground(NULL);
2948     fg = GDrawGetDefaultForeground(NULL);
2949     clut_scale = me->depth == 8 ? 8 : 4;
2950     base->clut->clut_len = 1<<clut_scale;
2951     for ( l=0; l<(1<<clut_scale); ++l )
2952 	base->clut->clut[l] =
2953 	    COLOR_CREATE(
2954 	     COLOR_RED(bg) + (l*(COLOR_RED(fg)-COLOR_RED(bg)))/((1<<clut_scale)-1),
2955 	     COLOR_GREEN(bg) + (l*(COLOR_GREEN(fg)-COLOR_GREEN(bg)))/((1<<clut_scale)-1),
2956 	     COLOR_BLUE(bg) + (l*(COLOR_BLUE(fg)-COLOR_BLUE(bg)))/((1<<clut_scale)-1) );
2957     BDFCharFree(me);
2958     BDFCharFree(other);
2959 return( img );
2960 }
2961 
2962 /* Draws an image of a glyph with a vertical bar down the middle. */
2963 /*  used to show italic correction position (we also dot in the width line) */
2964 /*  and top accent horizontal position for the MATH table */
SC_GetLinedImage(SplineChar * sc,int def_layer,int pos,int is_italic_cor)2965 GImage *SC_GetLinedImage(SplineChar *sc, int def_layer, int pos, int is_italic_cor) {
2966     BDFChar *me;
2967     double scale = kern_popup_size/(double) (sc->parent->ascent+sc->parent->descent);
2968     int miny, maxy, minx, maxx;
2969     GImage *img;
2970     struct _GImage *base;
2971     Color fg, bg;
2972     int l,clut_scale;
2973     int x,y, xoffset, yoffset;
2974     int pixel;
2975 
2976     if ( is_italic_cor )
2977 	pos += sc->width;
2978     pos = rint( pos*scale );
2979     if ( pos<-100 || pos>100 )
2980 return( NULL );
2981     me = Rasterize(sc,def_layer);
2982     if ( pos<me->xmin-10 || pos>me->xmax+30 ) {
2983 	BDFCharFree(me);
2984 return( NULL );
2985     }
2986     if ( (minx=me->xmin)>0 ) minx = 0;
2987     if ( (maxx=me->xmax)<me->width ) maxx = me->width;
2988     if ( pos<minx ) minx = pos-2;
2989     if ( pos>maxx ) maxx = pos+2;
2990     miny = me->ymin - 4;
2991     maxy = me->ymax + 4;
2992 
2993     pixel = me->depth == 8 ? 0xff : 0xf;
2994 
2995     img = GImageCreate(it_index,maxx-minx+2,maxy-miny+2);
2996     base = img->u.image;
2997     memset(base->data,'\0',base->bytes_per_line*base->height);
2998 
2999     xoffset = 1 - minx;
3000     yoffset = 1 + maxy;
3001     for ( y=me->ymin; y<=me->ymax; ++y ) {
3002 	for ( x=me->xmin; x<=me->xmax; ++x ) {
3003 	    base->data[(yoffset-y)*base->bytes_per_line + (x+xoffset)] =
3004 		    me->bitmap[(me->ymax-y)*me->bytes_per_line + (x-me->xmin)];
3005 	}
3006     }
3007     for ( y=miny; y<=maxy; ++y ) {
3008 	base->data[(yoffset-y)*base->bytes_per_line + (pos+xoffset)] = pixel;
3009 	if ( is_italic_cor && (y&1 ))
3010 	    base->data[(yoffset-y)*base->bytes_per_line + (me->width+xoffset)] = pixel;
3011     }
3012 
3013     memset(base->clut,'\0',sizeof(*base->clut));
3014     bg = GDrawGetDefaultBackground(NULL);
3015     fg = GDrawGetDefaultForeground(NULL);
3016     clut_scale = me->depth == 8 ? 8 : 4;
3017     base->clut->clut_len = 1<<clut_scale;
3018     for ( l=0; l<(1<<clut_scale); ++l )
3019 	base->clut->clut[l] =
3020 	    COLOR_CREATE(
3021 	     COLOR_RED(bg) + (l*(COLOR_RED(fg)-COLOR_RED(bg)))/((1<<clut_scale)-1),
3022 	     COLOR_GREEN(bg) + (l*(COLOR_GREEN(fg)-COLOR_GREEN(bg)))/((1<<clut_scale)-1),
3023 	     COLOR_BLUE(bg) + (l*(COLOR_BLUE(fg)-COLOR_BLUE(bg)))/((1<<clut_scale)-1) );
3024     BDFCharFree(me);
3025 return( img );
3026 }
3027 
3028 // This defines the width of the arrow that gets drawn
3029 #define ICON_WIDTH 15
3030 
GV_GetConstructedImage(SplineChar * sc,int def_layer,struct glyphvariants * gv,int is_horiz)3031 GImage *GV_GetConstructedImage(SplineChar *sc,int def_layer,struct glyphvariants *gv, int is_horiz) {
3032     SplineFont *sf = sc->parent;
3033     BDFChar *me, **others;
3034     double scale = kern_popup_size/(double) (sf->ascent+sf->descent);
3035     GImage *img;
3036     struct _GImage *base;
3037     Color fg, bg;
3038     int l,clut_scale;
3039     int x,y;
3040     int i,j;
3041 
3042     if ( gv==NULL || gv->part_cnt==0 )
3043 return( NULL );
3044     me = Rasterize(sc,def_layer);
3045     others = malloc(gv->part_cnt*sizeof(BDFChar *));
3046     for ( i=0; i<gv->part_cnt; ++i ) {
3047 	SplineChar *othersc = SFGetChar(sf,-1,gv->parts[i].component);
3048 	if ( othersc==NULL ) {
3049 	    for ( j=0; j<i; ++j )
3050 		BDFCharFree(others[j]);
3051 	    free(others);
3052 return( NULL );
3053 	}
3054 	others[i] = Rasterize(othersc,def_layer);
3055     }
3056     if ( is_horiz ) {
3057 	int ymin, ymax;
3058 	int width, xoff;
3059 
3060 	for ( i=1; i<gv->part_cnt; ++i ) {	/* Normalize all but first. Makes construction easier */
3061 	    others[i]->xmax -= others[i]->xmin;
3062 	    others[i]->xmin = 0;
3063 	}
3064 	xoff = me->xmin<0 ? -me->xmin : 0;
3065 	width = xoff + me->width + ICON_WIDTH;
3066 	ymin = me->ymin; ymax = me->ymax;
3067 	for ( i=0; i<gv->part_cnt; ++i ) {
3068 	    int overlap;
3069 	    if ( i==gv->part_cnt-1 )
3070 		overlap=0;
3071 	    else {
3072 		overlap = gv->parts[i].endConnectorLength>gv->parts[i+1].startConnectorLength ?
3073 			gv->parts[i+1].startConnectorLength :
3074 			gv->parts[i].endConnectorLength;
3075 		overlap = rint( scale*overlap );
3076 	    }
3077 	    width += rint(gv->parts[i].fullAdvance*scale) - overlap + others[i]->xmin/* Only does anything if i==0, then it normalizes the rest to the same baseline */;
3078 	    if ( others[i]->ymin<ymin ) ymin = others[i]->ymin;
3079 	    if ( others[i]->ymax>ymax ) ymax = others[i]->ymax;
3080 	}
3081 	if ( ymax<=ICON_WIDTH ) ymax = ICON_WIDTH;
3082 	if ( ymin>0 ) ymin = 0;
3083 	img = GImageCreate(it_index,width+10,ymax-ymin+2);
3084 	base = img->u.image;
3085 	memset(base->data,'\0',base->bytes_per_line*base->height);
3086 
3087 	++xoff;		/* One pixel margin */
3088 
3089 	for ( y=me->ymin; y<=me->ymax; ++y ) {
3090 	    for ( x=me->xmin; x<=me->xmax; ++x ) {
3091 		base->data[(1+ymax-y)*base->bytes_per_line + (x+xoff)] =
3092 			me->bitmap[(me->ymax-y)*me->bytes_per_line + (x-me->xmin)];
3093 	    }
3094 	}
3095 	xoff += me->width;
3096 	{
3097 	    int pixel = me->depth == 8 ? 0xff : 0xf;
3098 	    for ( j = -1; j<2; j+=2 ) {
3099 		if ( me->ymax<-me->ymin )
3100 		    y = (me->ymax+me->ymin)/2;
3101 		else
3102 		    y = 1+me->ymax/2;
3103 		y = ymax-y + j*2;
3104 		for ( x=1; x<ICON_WIDTH-5; ++x )
3105 		    base->data[y*base->bytes_per_line + (x+xoff)] = pixel;
3106 		for ( x=ICON_WIDTH-8; x<ICON_WIDTH-1; ++x )
3107 		    base->data[(y+j*(ICON_WIDTH-4-x))*base->bytes_per_line + (x+xoff)] = pixel;
3108 	    }
3109 	    xoff += ICON_WIDTH;
3110 	}
3111 	for ( i=0; i<gv->part_cnt; ++i ) {
3112 	    int overlap;
3113 	    if ( i==gv->part_cnt-1 )
3114 		overlap=0;
3115 	    else {
3116 		overlap = gv->parts[i].endConnectorLength>gv->parts[i+1].startConnectorLength ?
3117 			gv->parts[i+1].startConnectorLength :
3118 			gv->parts[i].endConnectorLength;
3119 		overlap = rint( scale*overlap );
3120 	    }
3121 	    for ( y=others[i]->ymin; y<=others[i]->ymax; ++y ) {
3122 		for ( x=others[i]->xmin; x<=others[i]->xmax; ++x ) {
3123 		    int n = others[i]->bitmap[(others[i]->ymax-y)*others[i]->bytes_per_line + (x-others[i]->xmin)];
3124 		    if ( n>base->data[(ymax-y)*base->bytes_per_line + (x+xoff)] )
3125 			base->data[(ymax-y)*base->bytes_per_line + (x+xoff)] = n;
3126 		}
3127 	    }
3128 	    xoff += rint(gv->parts[i].fullAdvance*scale) - overlap + others[i]->xmin/* Only does anything if i==0, then it normalizes the rest to the same baseline */;
3129 	}
3130     } else {
3131 	int xmin, xmax, ymin, ymax;
3132 	int yoff, xoff, width;
3133 
3134 	for ( i=1; i<gv->part_cnt; ++i ) {	/* Normalize all but first. Makes construction easier */
3135 	    others[i]->ymax -= others[i]->ymin;
3136 	    others[i]->ymin = 0;
3137 	}
3138 
3139 	xoff = me->xmin<0 ? -me->xmin : 0;
3140 	width = xoff + me->width + ICON_WIDTH;
3141 	ymin = me->ymin; ymax = me->ymax;
3142 	xmin = xmax = 0;
3143 	yoff = 0;
3144 	for ( i=0; i<gv->part_cnt; ++i ) {
3145 	    int overlap;
3146 	    if ( i==gv->part_cnt-1 )
3147 		overlap=0;
3148 	    else {
3149 		overlap = gv->parts[i].endConnectorLength>gv->parts[i+1].startConnectorLength ?
3150 			gv->parts[i+1].startConnectorLength :
3151 			gv->parts[i].endConnectorLength;
3152 		overlap = rint( scale*overlap );
3153 	    }
3154 	    if ( ymin>others[i]->ymin+yoff ) ymin = others[i]->ymin+yoff;
3155 	    if ( ymax<others[i]->ymax+yoff ) ymax = others[i]->ymax+yoff;
3156 	    yoff += rint(gv->parts[i].fullAdvance*scale) - overlap + others[i]->ymin/* Only does anything if i==0, then it normalizes the rest to the same baseline */;
3157 	    if ( others[i]->xmin<xmin ) xmin = others[i]->xmin;
3158 	    if ( others[i]->xmax>xmax ) xmax = others[i]->xmax;
3159 	}
3160 	if ( xmin<-width ) {
3161 	    xoff = -xmin-width;
3162 	    width = -xmin;
3163 	}
3164 	if ( xmax>0 )
3165 	    width += xmax;
3166 	if ( ymax<=ICON_WIDTH ) ymax = ICON_WIDTH;
3167 	if ( ymin>0 ) ymin = 0;
3168 	img = GImageCreate(it_index,width+2,ymax-ymin+2);
3169 	base = img->u.image;
3170 	memset(base->data,'\0',base->bytes_per_line*base->height);
3171 
3172 	++xoff;		/* One pixel margin */
3173 
3174 	for ( y=me->ymin; y<=me->ymax; ++y ) {
3175 	    for ( x=me->xmin; x<=me->xmax; ++x ) {
3176 		base->data[(1+ymax-y)*base->bytes_per_line + (x+xoff)] =
3177 			me->bitmap[(me->ymax-y)*me->bytes_per_line + (x-me->xmin)];
3178 	    }
3179 	}
3180 	xoff += me->width;
3181 	{
3182 	    int pixel = me->depth == 8 ? 0xff : 0xf;
3183 	    for ( j = -1; j<2; j+=2 ) {
3184 		if ( me->ymax<-me->ymin )
3185 		    y = (me->ymax+me->ymin)/2;
3186 		else
3187 		    y = 1+me->ymax/2;
3188 		// Figure out the centerpoint of the arrow
3189 		y = ymax - y;
3190 		if (y > (ymax - ymin - ICON_WIDTH/2)) {
3191 			// Too far down; shift up to make it fit
3192 			y = ymax - ymin - ICON_WIDTH/2;
3193 		}
3194 		y += j*2;
3195 		for ( x=1; x<ICON_WIDTH-5; ++x )
3196 		    base->data[y*base->bytes_per_line + (x+xoff)] = pixel;
3197 		for ( x=ICON_WIDTH-8; x<ICON_WIDTH-1; ++x )
3198 		    base->data[(y+j*(ICON_WIDTH-4-x))*base->bytes_per_line + (x+xoff)] = pixel;
3199 	    }
3200 	    xoff += ICON_WIDTH;
3201 	}
3202 	yoff=0;
3203 	for ( i=0; i<gv->part_cnt; ++i ) {
3204 	    int overlap;
3205 	    if ( i==gv->part_cnt-1 )
3206 		overlap=0;
3207 	    else {
3208 		overlap = gv->parts[i].endConnectorLength>gv->parts[i+1].startConnectorLength ?
3209 			gv->parts[i+1].startConnectorLength :
3210 			gv->parts[i].endConnectorLength;
3211 		overlap = rint( scale*overlap );
3212 	    }
3213 	    for ( y=others[i]->ymin; y<=others[i]->ymax; ++y ) {
3214 		for ( x=others[i]->xmin; x<=others[i]->xmax; ++x ) {
3215 		    int n = others[i]->bitmap[(others[i]->ymax-y)*others[i]->bytes_per_line + (x-others[i]->xmin)];
3216 		    if ( n>base->data[(ymax-y-yoff)*base->bytes_per_line + (x+xoff)] )
3217 			base->data[(ymax-y-yoff)*base->bytes_per_line + (x+xoff)] = n;
3218 		}
3219 	    }
3220 	    yoff += rint(gv->parts[i].fullAdvance*scale) - overlap + others[i]->ymin/* Only does anything if i==0, then it normalizes the rest to the same baseline */;
3221 	}
3222     }
3223     for ( i=0; i<gv->part_cnt; ++i )
3224 	BDFCharFree(others[i]);
3225     free(others);
3226 
3227     memset(base->clut,'\0',sizeof(*base->clut));
3228     bg = GDrawGetDefaultBackground(NULL);
3229     fg = GDrawGetDefaultForeground(NULL);
3230     clut_scale = me->depth == 8 ? 8 : 4;
3231     BDFCharFree(me);
3232     base->clut->clut_len = 1<<clut_scale;
3233     for ( l=0; l<(1<<clut_scale); ++l )
3234 	base->clut->clut[l] =
3235 	    COLOR_CREATE(
3236 	     COLOR_RED(bg) + (l*(COLOR_RED(fg)-COLOR_RED(bg)))/((1<<clut_scale)-1),
3237 	     COLOR_GREEN(bg) + (l*(COLOR_GREEN(fg)-COLOR_GREEN(bg)))/((1<<clut_scale)-1),
3238 	     COLOR_BLUE(bg) + (l*(COLOR_BLUE(fg)-COLOR_BLUE(bg)))/((1<<clut_scale)-1) );
3239 return( img );
3240 }
3241 
CI_GetConstructedImage(const void * _ci)3242 static GImage *CI_GetConstructedImage(const void *_ci) {
3243     CharInfo *ci = (CharInfo *) _ci;
3244     GImage *ret;
3245     int is_horiz = GTabSetGetSel(GWidgetGetControl(ci->gw,CID_Tabs))-ci->vert_aspect;
3246     struct glyphvariants *gv;
3247 
3248     gv = CI_ParseVariants(NULL,ci,is_horiz,NULL,0,true);
3249 
3250     ret = GV_GetConstructedImage(ci->sc,ci->def_layer,gv,is_horiz);
3251     GlyphVariantsFree(gv);
3252 return( ret );
3253 }
3254 
NameList_GetImage(SplineFont * sf,SplineChar * sc,int def_layer,char * namelist,int isliga)3255 GImage *NameList_GetImage(SplineFont *sf,SplineChar *sc,int def_layer,
3256 	char *namelist, int isliga ) {
3257     BDFChar *me, **extras;
3258     int width, xmin, xmax, ymin, ymax;
3259     GImage *img;
3260     struct _GImage *base;
3261     Color fg, bg;
3262     int l,clut_scale;
3263     int x,y;
3264     SplineChar *other;
3265     int extracnt;
3266     int i,j;
3267     char *subs = namelist, *pt, *start;
3268     int ch;
3269 
3270     if ( sc==NULL || sf==NULL || namelist==NULL )
3271 return( NULL );
3272     me = Rasterize(sc,def_layer);
3273     ymin = me->ymin; ymax = me->ymax;
3274     xmin = me->xmin; xmax = me->xmax; width = me->width;
3275     extracnt = 0; extras = NULL;
3276 
3277     for ( pt=subs; *pt ; ++extracnt ) {
3278 	while ( *pt!=' ' && *pt!='\0' ) ++pt;
3279 	if ( *pt==' ' )
3280 	    while ( *pt==' ' ) ++pt;
3281     }
3282     extras = malloc(extracnt*sizeof(BDFChar *));
3283     extracnt = 0;
3284     for ( pt=subs; *pt ; ) {
3285 	start = pt;
3286 	while ( *pt!=' ' && *pt!='\0' && *pt!='(' ) ++pt;
3287 	ch = *pt; *pt = '\0';
3288 	other = SFGetChar(sf,-1, start);
3289 	*pt = ch;
3290 	if ( ch=='(' ) {
3291 	    while ( *pt!=')' && *pt!='\0' ) ++pt;
3292 	    if ( *pt==')' ) ++pt;
3293 	}
3294 	if ( other!=NULL ) {
3295 	    if ( extracnt==0 ) width += ICON_WIDTH;
3296 	    extras[extracnt] = Rasterize(other,def_layer);
3297 	    if ( width+extras[extracnt]->xmin < xmin ) xmin = width+extras[extracnt]->xmin;
3298 	    if ( width+extras[extracnt]->xmax > xmax ) xmax = width+extras[extracnt]->xmax;
3299 	    if ( extras[extracnt]->ymin < ymin ) ymin = extras[extracnt]->ymin;
3300 	    if ( extras[extracnt]->ymax > ymax ) ymax = extras[extracnt]->ymax;
3301 	    width += extras[extracnt++]->width;
3302 	}
3303 	if ( *pt==' ' )
3304 	    while ( *pt==' ' ) ++pt;
3305     }
3306 
3307     if ( ymax<=ICON_WIDTH ) ymax = ICON_WIDTH;
3308     if ( ymin>0 ) ymin = 0;
3309     if ( xmax<xmin ) {
3310         for ( i=0; i<extracnt; ++i )
3311             BDFCharFree(extras[i]);
3312         free(extras);
3313         return( NULL );
3314     }
3315 
3316     if ( xmin>0 ) xmin = 0;
3317 
3318     img = GImageCreate(it_index,xmax - xmin + 2,ymax-ymin+2);
3319     base = img->u.image;
3320     memset(base->data,'\0',base->bytes_per_line*base->height);
3321 
3322     width = -xmin;
3323     ++width;
3324 
3325     for ( y=me->ymin; y<=me->ymax; ++y ) {
3326 	for ( x=me->xmin; x<=me->xmax; ++x ) {
3327 	    base->data[(1+ymax-y)*base->bytes_per_line + (x+width)] =
3328 		    me->bitmap[(me->ymax-y)*me->bytes_per_line + (x-me->xmin)];
3329 	}
3330     }
3331     width += me->width;
3332     if ( extracnt!=0 ) {
3333 	int pixel = me->depth == 8 ? 0xff : 0xf;
3334 	if ( !isliga ) {
3335 	    for ( j = -1; j<2; j+=2 ) {
3336 		if ( me->ymax<-me->ymin )
3337 		    y = (me->ymax+me->ymin)/2;
3338 		else
3339 		    y = 1+me->ymax/2;
3340 		// Figure out the centerpoint of the arrow
3341 		y = ymax - y;
3342 		if (y > (ymax - ymin - ICON_WIDTH/2)) {
3343 			// Too far down; shift up to make it fit
3344 			y = ymax - ymin - ICON_WIDTH/2;
3345 		}
3346 		// Draw the == part of the arrow
3347 		y += j*2;
3348 		for ( x=1; x<ICON_WIDTH-5; ++x )
3349 		    base->data[y*base->bytes_per_line + (x+width)] = pixel;
3350 		// Draw the > part of the arrow
3351 		for ( x=ICON_WIDTH-8; x<ICON_WIDTH-1; ++x )
3352 		    base->data[(y+j*(ICON_WIDTH-4-x))*base->bytes_per_line + (x+width)] = pixel;
3353 	    }
3354 	} else if ( isliga>0 ) {
3355 	    for ( j = -1; j<2; j+=2 ) {
3356 		y = 1+ymax/2 + j*2;
3357 		// Draw the == part of the arrow
3358 		for ( x=5; x<ICON_WIDTH-1; ++x )
3359 		    base->data[y*base->bytes_per_line + (x+width)] = pixel;
3360 		// Draw the < part of the arrow
3361 		for ( x=8; x>1 ; --x )
3362 		    base->data[(y+j*(x-3))*base->bytes_per_line + (x+width)] = pixel;
3363 	    }
3364 	}
3365 	width += ICON_WIDTH;
3366 	for ( i=0; i<extracnt; ++i ) {
3367 	    BDFChar *other = extras[i];
3368 	    for ( y=other->ymin; y<=other->ymax; ++y ) {
3369 		for ( x=other->xmin; x<=other->xmax; ++x ) {
3370 		    if ( other->bitmap[(other->ymax-y)*other->bytes_per_line + (x-other->xmin)] != 0 )
3371 			base->data[(1+ymax-y)*base->bytes_per_line + (x+width)] =
3372 				other->bitmap[(other->ymax-y)*other->bytes_per_line + (x-other->xmin)];
3373 		}
3374 	    }
3375 	    width += other->width;
3376 	}
3377     }
3378     memset(base->clut,'\0',sizeof(*base->clut));
3379     bg = GDrawGetDefaultBackground(NULL);
3380     fg = GDrawGetDefaultForeground(NULL);
3381     clut_scale = me->depth == 8 ? 8 : 4;
3382     base->clut->clut_len = 1<<clut_scale;
3383     for ( l=0; l<(1<<clut_scale); ++l )
3384 	base->clut->clut[l] =
3385 	    COLOR_CREATE(
3386 	     COLOR_RED(bg) + (l*(COLOR_RED(fg)-COLOR_RED(bg)))/((1<<clut_scale)-1),
3387 	     COLOR_GREEN(bg) + (l*(COLOR_GREEN(fg)-COLOR_GREEN(bg)))/((1<<clut_scale)-1),
3388 	     COLOR_BLUE(bg) + (l*(COLOR_BLUE(fg)-COLOR_BLUE(bg)))/((1<<clut_scale)-1) );
3389      BDFCharFree(me);
3390      for ( i=0; i<extracnt; ++i )
3391 	 BDFCharFree(extras[i]);
3392      free(extras);
3393 return( img );
3394 }
3395 
PST_GetImage(GGadget * pstk,SplineFont * sf,int def_layer,struct lookup_subtable * sub,int popup_r,SplineChar * sc)3396 GImage *PST_GetImage(GGadget *pstk,SplineFont *sf,int def_layer,
3397 	struct lookup_subtable *sub,int popup_r, SplineChar *sc ) {
3398     int rows, cols = GMatrixEditGetColCnt(pstk);
3399     struct matrix_data *old = GMatrixEditGet(pstk,&rows);
3400 
3401     if ( sc==NULL || sub==NULL )
3402 return( NULL );
3403     if ( sub->lookup->lookup_type<gsub_single || sub->lookup->lookup_type>gsub_ligature )
3404 return( NULL );
3405 
3406 return( NameList_GetImage(sf,sc,def_layer,old[cols*popup_r+1].u.md_str,
3407 	sub->lookup->lookup_type==gsub_ligature));
3408 }
3409 
_CI_GetImage(const void * _ci)3410 static GImage *_CI_GetImage(const void *_ci) {
3411     CharInfo *ci = (CharInfo *) _ci;
3412     int offset = GTabSetGetSel(GWidgetGetControl(ci->gw,CID_Tabs))-2;
3413     GGadget *pstk = GWidgetGetControl(ci->gw,CID_List+offset*100);
3414     int rows, cols = GMatrixEditGetColCnt(pstk);
3415     struct matrix_data *old = GMatrixEditGet(pstk,&rows);
3416     struct lookup_subtable *sub = (struct lookup_subtable *) (old[cols*ci->r+0].u.md_ival);
3417 
3418     if ( ci->r>=rows )
3419 return( NULL );
3420 
3421 return( PST_GetImage(pstk,ci->sc->parent,ci->def_layer,sub,ci->r,ci->sc) );
3422 }
3423 
CI_KerningPopupPrepare(GGadget * g,int r,int c)3424 static void CI_KerningPopupPrepare(GGadget *g, int r, int c) {
3425     CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
3426     int rows, cols = GMatrixEditGetColCnt(g);
3427     struct matrix_data *old = GMatrixEditGet(g,&rows);
3428 
3429     if ( c<0 || c>=cols || r<0 || r>=rows || old[cols*r+1].u.md_str==NULL ||
3430 	SFGetChar(ci->sc->parent,-1, old[cols*r+1].u.md_str)==NULL )
3431 return;
3432     ci->r = r; ci->c = c;
3433     GGadgetPreparePopupImage(GGadgetGetWindow(g),NULL,ci,CI_GetKernedImage,CI_FreeKernedImage);
3434 }
3435 
CI_SubsPopupPrepare(GGadget * g,int r,int c)3436 static void CI_SubsPopupPrepare(GGadget *g, int r, int c) {
3437     CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
3438     int rows, cols = GMatrixEditGetColCnt(g);
3439 
3440     (void) GMatrixEditGet(g,&rows);
3441     if ( c<0 || c>=cols || r<0 || r>=rows )
3442 return;
3443     ci->r = r; ci->c = c;
3444     GGadgetPreparePopupImage(GGadgetGetWindow(g),NULL,ci,_CI_GetImage,CI_FreeKernedImage);
3445 }
3446 
CI_ConstructionPopupPrepare(GGadget * g,int r,int c)3447 static void CI_ConstructionPopupPrepare(GGadget *g, int r, int c) {
3448     CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
3449     int rows/*, cols = GMatrixEditGetColCnt(g)*/;
3450 
3451     (void) GMatrixEditGet(g,&rows);
3452     if ( rows==0 )
3453 return;
3454     GGadgetPreparePopupImage(GGadgetGetWindow(g),NULL,ci,CI_GetConstructedImage,CI_FreeKernedImage);
3455 }
3456 
CI_GlyphNameCompletion(GGadget * t,int from_tab)3457 static unichar_t **CI_GlyphNameCompletion(GGadget *t,int from_tab) {
3458     CharInfo *ci = GDrawGetUserData(GDrawGetParentWindow(GGadgetGetWindow(t)));
3459     SplineFont *sf = ci->sc->parent;
3460 
3461 return( SFGlyphNameCompletion(sf,t,from_tab,false));
3462 }
3463 
CI_GlyphListCompletion(GGadget * t,int from_tab)3464 static unichar_t **CI_GlyphListCompletion(GGadget *t,int from_tab) {
3465     CharInfo *ci = GDrawGetUserData(GDrawGetParentWindow(GGadgetGetWindow(t)));
3466     SplineFont *sf = ci->sc->parent;
3467 
3468 return( SFGlyphNameCompletion(sf,t,from_tab,true));
3469 }
3470 
3471 
extpart_finishedit(GGadget * g,int r,int c,int wasnew)3472 static void extpart_finishedit(GGadget *g, int r, int c, int wasnew) {
3473     int rows;
3474     struct matrix_data *possub;
3475     CharInfo *ci;
3476     int is_horiz,cols;
3477     DBounds b;
3478     double full_advance;
3479     SplineChar *sc;
3480 
3481     if ( c!=0 )
3482 return;
3483     if ( !wasnew )
3484 return;
3485     /* If they added a new glyph to the sequence then set some defaults for it. */
3486     /*  only the full advance has any likelyhood of being correct */
3487     ci = GDrawGetUserData(GGadgetGetWindow(g));
3488     is_horiz = GTabSetGetSel(GWidgetGetControl(ci->gw,CID_Tabs))-ci->vert_aspect;
3489     possub = GMatrixEditGet(g, &rows);
3490     cols = GMatrixEditGetColCnt(g);
3491     if ( possub[r*cols+0].u.md_str==NULL )
3492 return;
3493     sc = SFGetChar(ci->sc->parent,-1,possub[r*cols+0].u.md_str);
3494     if ( sc==NULL )
3495 return;
3496     SplineCharFindBounds(sc,&b);
3497     if ( is_horiz )
3498 	full_advance = b.maxx - b.minx;
3499     else
3500 	full_advance = b.maxy - b.miny;
3501     possub[r*cols+2].u.md_ival = possub[r*cols+3].u.md_ival = rint(full_advance/3);
3502     possub[r*cols+4].u.md_ival = rint(full_advance);
3503     GGadgetRedraw(g);
3504 }
3505 
3506 static GTextInfo truefalse[] = {
3507     { (unichar_t *) N_("false"), NULL, 0, 0, (void *) 0, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
3508     { (unichar_t *) N_("true"),  NULL, 0, 0, (void *) 1, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
3509     GTEXTINFO_EMPTY
3510 };
3511 
3512 static struct col_init extensionpart[] = {
3513     { me_string , NULL, NULL, NULL, N_("Glyph") },
3514     { me_enum, NULL, truefalse, NULL, N_("Extender") },
3515 /* GT: "Len" is an abreviation for "Length" */
3516     { me_int, NULL, NULL, NULL, N_("StartLen") },
3517     { me_int, NULL, NULL, NULL, N_("EndLen") },
3518     { me_int, NULL, NULL, NULL, N_("FullLen") },
3519     COL_INIT_EMPTY
3520 };
3521 static struct matrixinit mi_extensionpart =
3522     { sizeof(extensionpart)/sizeof(struct col_init)-1, extensionpart, 0, NULL, NULL, NULL, extpart_finishedit, NULL, NULL, NULL };
3523 
isxheight(int uni)3524 static int isxheight(int uni) {
3525     if ( uni>=0x10000 || !islower(uni))
3526 return( false );
3527 
3528     if ( uni=='a' || uni=='c' || uni=='e' || uni=='i' || uni=='j' ||
3529 	    (uni>='m' && uni<='z') ||
3530 	    uni==0x131 || uni==0x237 || /* Ignore accented letters except the dotlessi/j */
3531 	    (uni>=0x250 && uni<0x253) || uni==0x254 || uni==0x255 ||
3532 	    (uni>=0x258 && uni<0x265) || uni==0x269 || uni==0x26A ||
3533 	    (uni>=0x26f && uni<=0x277) || uni==0x279 ||
3534 	    uni==0x3b1 || uni==0x3b3 || uni==0x3b5 || uni==0x3b7 ||
3535 	    uni==0x3b9 || uni==0x3ba || uni==0x3bc || uni==0x3bd ||
3536 	    (uni>=0x3bf && uni<=0x3c7) || uni==0x3c9 ||
3537 	    (uni>=0x400 && uni<=0x45f))
3538 return( true );
3539 
3540 return( false );
3541 }
3542 
isbaseline(int uni)3543 static int isbaseline(int uni) {
3544     /* Treat rounded letters as being on the base line */
3545     /* But don't bother including guys that normally are on the baseline */
3546 
3547     if ( (uni>='a' && uni<'g') || uni=='h' || uni=='i' ||
3548 	    (uni>='k' && uni<='o') || (uni>='r' && uni<'y') || uni=='z' ||
3549 	    (uni>='A' && uni<='Z' ) ||
3550 	    uni==0x3b0 || uni==0x3b4 || uni==0x3b5 || (uni>=0x3b8 && uni<0x3bb) ||
3551 	    uni==0x3bd || uni==0x3bf || uni==0x3c0 || (uni>=0x3c2 && uni<0x3c6) ||
3552 	    uni==0x3c8 || uni==0x3c7 || uni==0x3c9 ||
3553 	    (uni>=0x391 && uni<0x3aa) ||
3554 	    (uni>=0x400 && uni<0x40f) || (uni>=0x410 && uni<=0x413) ||
3555 	    (uni>=0x415 && uni<0x425) || uni==0x427 || uni==0x428 ||
3556 	    (uni>=0x42a && uni<0x433) || (uni>=0x435 && uni<0x45e) )
3557 return( true );
3558 
3559 return( false );
3560 }
3561 
TeX_Default(GGadget * g,GEvent * e)3562 static int TeX_Default(GGadget *g, GEvent *e) {
3563     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3564 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
3565 	int cid = GGadgetGetCid(g), mcid = 0;
3566 	DBounds b;
3567 	int value;
3568 	SplineChar *basesc = NULL;
3569 	SplineFont *sf = ci->sc->parent;
3570 	int style;
3571 	char buf[12];
3572 
3573 	basesc = ci->sc;
3574 	/* Try to align the top of lowercase (xheight) letters all at the */
3575 	/*  same height. Ditto for uppercase & ascender letters */
3576 	if ( cid==CID_TeX_HeightD && ci->sc->unicodeenc<0x10000 &&
3577 		isxheight(ci->sc->unicodeenc) &&
3578 		(basesc = SFGetChar(sf,'x',NULL))!=NULL )
3579 	    /* Done */;
3580 	else if ( cid==CID_TeX_HeightD && ci->sc->unicodeenc<0x10000 &&
3581 		islower(ci->sc->unicodeenc) &&
3582 		(basesc = SFGetChar(sf,'l',NULL))!=NULL )
3583 	    /* Done */;
3584 	else if ( cid==CID_TeX_HeightD && ci->sc->unicodeenc<0x10000 &&
3585 		isupper(ci->sc->unicodeenc) &&
3586 		(basesc = SFGetChar(sf,'I',NULL))!=NULL )
3587 	    /* Done */;
3588 	else if ( cid==CID_TeX_DepthD && ci->sc->unicodeenc<0x10000 &&
3589 		isbaseline(ci->sc->unicodeenc) &&
3590 		(basesc = SFGetChar(sf,'I',NULL))!=NULL )
3591 	    /* Done */;
3592 	else
3593 	    basesc = ci->sc;
3594 
3595 	SplineCharFindBounds(basesc,&b);
3596 	style = MacStyleCode(sf,NULL);
3597 
3598 	if ( cid == CID_TeX_HeightD ) {
3599 	    mcid = CID_TeX_Height;
3600 	    if ( basesc!=ci->sc && basesc->tex_height!=TEX_UNDEF )
3601 		value = basesc->tex_height;
3602 	    else
3603 		value = rint(b.maxy);
3604 	    if ( value<0 ) value = 0;
3605 	} else if ( cid == CID_TeX_DepthD ) {
3606 	    mcid = CID_TeX_Depth;
3607 	    if ( basesc!=ci->sc && basesc->tex_depth!=TEX_UNDEF )
3608 		value = basesc->tex_depth;
3609 	    else {
3610 		value = -rint(b.miny);
3611 		if ( value<5 ) value = 0;
3612 	    }
3613 	} else if ( cid == CID_HorAccentD ) {
3614 	    mcid = CID_HorAccent;
3615 	    double italic_off = (b.maxy-b.miny)*tan(-sf->italicangle);
3616 	    if ( b.maxx-b.minx-italic_off < 0 )
3617 		value = rint(b.minx + (b.maxx-b.minx)/2);
3618 	    else
3619 		value = rint(b.minx + italic_off + (b.maxx - b.minx - italic_off)/2);
3620 	} else if ( cid == CID_TeX_ItalicD ) {
3621 	    mcid = CID_TeX_Italic;
3622 	    if ( (style&sf_italic) || sf->italicangle!=0 )
3623 		value = rint((b.maxx-ci->sc->width) +
3624 		             (sf->ascent+sf->descent)/16.0);
3625 	    else
3626 		value = 0;
3627 	}
3628 	if (mcid!=0) {
3629 	    sprintf( buf, "%d", value );
3630 	    GGadgetSetTitle8(GWidgetGetControl(ci->gw,mcid),buf);
3631 	}
3632     }
3633 return( true );
3634 }
3635 
CI_SubSuperPositionings(GGadget * g,GEvent * e)3636 static int CI_SubSuperPositionings(GGadget *g, GEvent *e) {
3637     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3638 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
3639 	MathKernDialog(ci->sc,ci->def_layer);
3640     }
3641 return( true );
3642 }
3643 
3644 static struct col_init altuniinfo[] = {
3645     { me_uhex , NULL, NULL, NULL, N_("Unicode") },
3646     { me_uhex, NULL, truefalse, NULL, N_("Variation Selector (or 0)") },
3647     COL_INIT_EMPTY
3648 };
3649 static struct matrixinit mi_altuniinfo =
3650     { sizeof(altuniinfo)/sizeof(struct col_init)-1, altuniinfo, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
3651 
CI_NoteAspect(CharInfo * ci)3652 static void CI_NoteAspect(CharInfo *ci) {
3653     int new_aspect = GTabSetGetSel(GWidgetGetControl(ci->gw,CID_Tabs));
3654     PST *pst;
3655     int cnt;
3656     char buf[20];
3657 
3658     last_gi_aspect = new_aspect;
3659     if ( new_aspect == ci->lc_aspect && (!ci->lc_seen || GGadgetIsChecked(GWidgetGetControl(ci->gw,CID_DefLCCount)))) {
3660 	ci->lc_seen = true;
3661 	for ( pst=ci->sc->possub; pst!=NULL && pst->type!=pst_lcaret; pst=pst->next );
3662 	if ( GGadgetIsChecked(GWidgetGetControl(ci->gw,CID_DefLCCount)) &&
3663 		pst==NULL ) {
3664 	    int rows, cols, i;
3665 	    struct matrix_data *possub;
3666 	    /* Normally we look for ligatures in the possub list, but here*/
3667 	    /*  we will examine the ligature pane itself to get the most */
3668 	    /*  up to date info */
3669 	    possub = GMatrixEditGet(GWidgetGetControl(ci->gw,CID_List+(pst_ligature-1)*100), &rows );
3670 	    cols = GMatrixEditGetColCnt(GWidgetGetControl(ci->gw,CID_List+(pst_ligature-1)*100) );
3671 	    cnt = 0;
3672 	    for ( i=0; i<rows; ++i ) {
3673 		char *pt = possub[cols*i+1].u.md_str;
3674 		int comp = 0;
3675 		while ( *pt!='\0' ) {
3676 		    while ( *pt==' ' ) ++pt;
3677 		    if ( *pt=='\0' )
3678 		break;
3679 		    while ( *pt!=' ' && *pt!='\0' ) ++pt;
3680 		    ++comp;
3681 		}
3682 		if ( comp>cnt ) cnt = comp;
3683 	    }
3684 	    --cnt;		/* We want one fewer caret than there are components -- carets go BETWEEN components */
3685 	} else if ( pst!=NULL )
3686 	    cnt = pst->u.lcaret.cnt;
3687 	else
3688 	    cnt = 0;
3689 	if ( cnt<0 ) cnt = 0;
3690 	sprintf( buf, "%d", cnt );
3691 	GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_LCCount),buf);
3692     }
3693 }
3694 
CI_AspectChange(GGadget * g,GEvent * e)3695 static int CI_AspectChange(GGadget *g, GEvent *e) {
3696     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
3697 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
3698 	CI_NoteAspect(ci);
3699     }
3700 return( true );
3701 }
3702 
CI_DefLCChange(GGadget * g,GEvent * e)3703 static int CI_DefLCChange(GGadget *g, GEvent *e) {
3704     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
3705 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
3706 	int show = !GGadgetIsChecked(g);
3707 	GGadgetSetEnabled(GWidgetGetControl(ci->gw,CID_LCCount),show);
3708 	GGadgetSetEnabled(GWidgetGetControl(ci->gw,CID_LCCountLab),show);
3709     }
3710 return( true );
3711 }
3712 
3713 // Turn the user's entered decomposition into the format returned by SFGetAlternate
CI_ParseUserDecomposition(char * inp)3714 unichar_t* CI_ParseUserDecomposition(char* inp) {
3715     unsigned long len = strlen(inp);
3716     char* end;
3717     unichar_t* decomp = malloc((len+1)*sizeof(unichar_t));
3718 
3719     int j = 0;
3720     for (unsigned long i = strtoul(inp, &end, 16); inp != end; i = strtoul(inp, &end, 16)) {
3721         inp = end;
3722         decomp[j] = i;
3723         j++;
3724     }
3725 
3726     decomp[j] = '\0';
3727 
3728     return decomp;
3729 }
3730 
CI_CreateInterpretedAsLabel(unichar_t * inp)3731 char* CI_CreateInterpretedAsLabel(unichar_t* inp) {
3732     char* lblprefix = _("Interpreted as: ");
3733     char* lblerror = _("Error: wrong format");
3734     char* lblbuf;
3735 
3736     // If I don't validate it, the string will become "(null)"
3737     bool valid = true;
3738     unichar_t* inp_ptr = inp;
3739     if (inp != NULL) {
3740         while (*inp_ptr != '\0') {
3741             if (*inp_ptr > 0x10FFFF) valid = false;
3742             inp_ptr++;
3743         }
3744     }
3745 
3746     if (inp != NULL && inp[0] != 0 && valid) {
3747         char* inp_l = u2utf8_copy(inp);
3748         lblbuf = malloc(strlen(lblprefix)+strlen(inp_l)+1);
3749         sprintf(lblbuf, "%s%s", lblprefix, inp_l);
3750         free(inp_l);
3751     } else {
3752         lblbuf = copy(lblerror);
3753     }
3754 
3755     return lblbuf;
3756 }
3757 
CI_CmpUseNonDefault(GGadget * g,GEvent * e)3758 static int CI_CmpUseNonDefault(GGadget *g, GEvent *e) {
3759     int show = !GGadgetIsChecked(g);
3760     CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
3761     GGadget* cotf = GWidgetGetControl(ci->gw,CID_ComponentTextField);
3762     GGadget* coim = GWidgetGetControl(ci->gw,CID_ComponentInterpMsg);
3763     GGadgetSetEnabled(cotf, show);
3764     if (ci->sc->user_decomp != NULL) free(ci->sc->user_decomp);
3765     if (!show) {
3766         ci->sc->user_decomp = NULL;
3767         GGadgetSetTitle8(coim, "");
3768     } else {
3769         unichar_t* ud = CI_ParseUserDecomposition(GGadgetGetTitle8(cotf));
3770         ci->sc->user_decomp = ud;
3771         char* lbl = CI_CreateInterpretedAsLabel(ci->sc->user_decomp);
3772         GGadgetSetTitle8(coim, lbl);
3773         free(lbl);
3774     }
3775 
3776     return true;
3777 }
3778 
GV_ToMD(GGadget * g,struct glyphvariants * gv)3779 void GV_ToMD(GGadget *g, struct glyphvariants *gv) {
3780     int cols = GMatrixEditGetColCnt(g), j;
3781     struct matrix_data *mds;
3782     if ( gv==NULL ) {
3783 	GMatrixEditSet(g, NULL,0,false);
3784 return;
3785     }
3786     mds = calloc(gv->part_cnt*cols,sizeof(struct matrix_data));
3787     for ( j=0; j<gv->part_cnt; ++j ) {
3788 	mds[j*cols+0].u.md_str = copy(gv->parts[j].component);
3789 	mds[j*cols+1].u.md_ival = gv->parts[j].is_extender;
3790 	mds[j*cols+2].u.md_ival = gv->parts[j].startConnectorLength;
3791 	mds[j*cols+3].u.md_ival = gv->parts[j].endConnectorLength;
3792 	mds[j*cols+4].u.md_ival = gv->parts[j].fullAdvance;
3793     }
3794     GMatrixEditSet(g, mds,gv->part_cnt,false);
3795 }
3796 
GA_ToMD(GGadget * g,SplineChar * sc)3797 static void GA_ToMD(GGadget *g, SplineChar *sc) {
3798     struct altuni *alt;
3799     int cols = GMatrixEditGetColCnt(g), cnt;
3800     struct matrix_data *mds;
3801     if ( sc->altuni==NULL ) {
3802 	GMatrixEditSet(g, NULL,0,false);
3803 return;
3804     }
3805     for ( cnt=0, alt=sc->altuni; alt!=NULL; ++cnt, alt=alt->next );
3806     mds = calloc(cnt*cols,sizeof(struct matrix_data));
3807     for ( cnt=0, alt=sc->altuni; alt!=NULL; ++cnt, alt=alt->next ) {
3808 	mds[cnt*cols+0].u.md_ival = alt->unienc;
3809 	mds[cnt*cols+1].u.md_ival = alt->vs==-1? 0 : alt->vs;
3810     }
3811     GMatrixEditSet(g, mds,cnt,false);
3812 }
3813 
CI_SetColorList(CharInfo * ci,Color color)3814 static void CI_SetColorList(CharInfo *ci,Color color) {
3815     int i;
3816     uint16 junk;
3817 
3818     std_colors[CUSTOM_COLOR].image = NULL;
3819     for ( i=0; std_colors[i].image!=NULL; ++i ) {
3820 	if ( std_colors[i].userdata == (void *) (intpt) color )
3821     break;
3822     }
3823     if ( std_colors[i].image==NULL ) {
3824 	std_colors[i].image = &customcolor_image;
3825 	customcolor_image.u.image->clut->clut[1] = color;
3826 	std_colors[i].userdata = (void *) (intpt) color;
3827     }
3828     GGadgetSetList(GWidgetGetControl(ci->gw,CID_Color), GTextInfoArrayFromList(std_colors,&junk), false);
3829     GGadgetSelectOneListItem(GWidgetGetControl(ci->gw,CID_Color),i);
3830     if ( color!=COLOR_DEFAULT )
3831 	ci->last = color;
3832     ci->real_last = color;
3833 }
3834 
3835 struct colcount { Color color; int cnt; };
colcountorder(const void * op1,const void * op2)3836 static int colcountorder(const void *op1, const void *op2) {
3837     const struct colcount *c1 = op1, *c2 = op2;
3838 
3839 return( c2->cnt - c1->cnt );		/* Biggest first */
3840 }
3841 
SFFontCols(SplineFont * sf,struct hslrgb fontcols[6])3842 struct hslrgb *SFFontCols(SplineFont *sf,struct hslrgb fontcols[6]) {
3843     int i, gid, cnt;
3844     struct colcount *colcount, stds[7];
3845     SplineChar *sc;
3846 
3847     memset(stds,0,sizeof(stds));
3848     stds[0].color = 0xffffff;
3849     stds[1].color = 0xff0000;
3850     stds[2].color = 0x00ff00;
3851     stds[3].color = 0x0000ff;
3852     stds[4].color = 0xffff00;
3853     stds[5].color = 0x00ffff;
3854     stds[6].color = 0xff00ff;
3855     colcount = calloc(sf->glyphcnt,sizeof(struct colcount));
3856 
3857     cnt = 0;
3858     for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc= sf->glyphs[gid])!=NULL ) {
3859 	if ( sc->color==COLOR_DEFAULT )
3860     continue;
3861 	for ( i=0; i<7 && sc->color!=stds[i].color; ++i );
3862 	if ( i<7 ) {
3863 	    ++stds[i].cnt;
3864     continue;
3865 	}
3866 	for ( i=0; i<cnt && sc->color!=colcount[i].color; ++i );
3867 	if ( i==cnt )
3868 	    colcount[cnt++].color = sc->color;
3869 	++colcount[i].cnt;
3870     }
3871 
3872     if ( cnt<6 ) {
3873 	for ( i=0; i<cnt; ++i )
3874 	    ++colcount[i].cnt;
3875 	for ( i=0; i<7; ++i ) if ( stds[i].cnt!=0 ) {
3876 	    colcount[cnt].color = stds[i].color;
3877 	    colcount[cnt++].cnt = 1;
3878 	}
3879     }
3880     qsort(colcount,cnt,sizeof(struct colcount),colcountorder);
3881 
3882     memset(fontcols,0,6*sizeof(struct hslrgb));
3883     for ( i=0; i<6 && i<cnt; ++i ) {
3884 	fontcols[i].rgb = true;
3885 	fontcols[i].r = ((colcount[i].color>>16)&0xff)/255.0;
3886 	fontcols[i].g = ((colcount[i].color>>8 )&0xff)/255.0;
3887 	fontcols[i].b = ((colcount[i].color    )&0xff)/255.0;
3888     }
3889     free(colcount);
3890     if ( cnt==0 )
3891 return( NULL );
3892 
3893 return(fontcols);
3894 }
3895 
CI_PickColor(GGadget * g,GEvent * e)3896 static int CI_PickColor(GGadget *g, GEvent *e) {
3897     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
3898 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
3899 	GTextInfo *ti = GGadgetGetListItemSelected(g);
3900 	if ( ti==NULL )
3901 	    /* Can't happen */;
3902 	else if ( ti->userdata == (void *) COLOR_CHOOSE ) {
3903 	    struct hslrgb col, font_cols[6];
3904 	    memset(&col,0,sizeof(col));
3905 	    col.rgb = true;
3906 	    col.r = ((ci->last>>16)&0xff)/255.;
3907 	    col.g = ((ci->last>>8 )&0xff)/255.;
3908 	    col.b = ((ci->last    )&0xff)/255.;
3909 	    col = GWidgetColor(_("Pick a color"),&col,SFFontCols(ci->sc->parent,font_cols));
3910 	    if ( col.rgb ) {
3911 		ci->last = (((int) rint(255.*col.r))<<16 ) |
3912 			    (((int) rint(255.*col.g))<<8 ) |
3913 			    (((int) rint(255.*col.b)) );
3914 		CI_SetColorList(ci,ci->last);
3915 	    } else /* Cancelled */
3916 		CI_SetColorList(ci,ci->real_last);
3917 	} else {
3918 	    if ( (intpt) ti->userdata!=COLOR_DEFAULT )
3919 		ci->last = (intpt) ti->userdata;
3920 	    ci->real_last = (intpt) ti->userdata;
3921 	}
3922     }
3923 return( true );
3924 }
3925 
BytesNeeded(unichar_t in)3926 static int BytesNeeded(unichar_t in) {
3927     if (in <= 0xff) {
3928         return 1;
3929     } else if (in <= 0xffff) {
3930         return 2;
3931     } else if (in <= 0xffffff) {
3932         return 3;
3933     } else {
3934         return 4;
3935     }
3936 }
3937 
CIFillup(CharInfo * ci)3938 static void CIFillup(CharInfo *ci) {
3939     SplineChar *sc = ci->cachedsc!=NULL ? ci->cachedsc : ci->sc;
3940     SplineFont *sf = sc->parent;
3941     unichar_t *temp;
3942     char buffer[400];
3943     char buf[200];
3944     const unichar_t *bits, *bits2;
3945     const unichar_t *d_ptr = sc->user_decomp;
3946     int i,j,gid, isv;
3947     struct matrix_data *mds[pst_max];
3948     int cnts[pst_max];
3949     PST *pst;
3950     KernPair *kp;
3951     unichar_t ubuf[4];
3952     GTextInfo **ti;
3953     char *devtabstr;
3954 
3955     sprintf(buf,_("Glyph Info for %.40s"),sc->name);
3956     GDrawSetWindowTitles8(ci->gw, buf, _("Glyph Info..."));
3957 
3958     if ( ci->oldsc!=NULL && ci->oldsc->charinfo==ci )
3959 	ci->oldsc->charinfo = NULL;
3960     ci->sc->charinfo = ci;
3961     ci->oldsc = ci->sc;
3962 
3963     GGadgetSetEnabled(GWidgetGetControl(ci->gw,-1), ci->enc>0 &&
3964 	    ((gid=ci->map->map[ci->enc-1])==-1 ||
3965 	     sf->glyphs[gid]==NULL || sf->glyphs[gid]->charinfo==NULL ||
3966 	     gid==sc->orig_pos));
3967     GGadgetSetEnabled(GWidgetGetControl(ci->gw,1), ci->enc<ci->map->enccount-1 &&
3968 	    ((gid=ci->map->map[ci->enc+1])==-1 ||
3969 	     sf->glyphs[gid]==NULL || sf->glyphs[gid]->charinfo==NULL ||
3970 	     gid==sc->orig_pos));
3971 
3972     temp = utf82u_copy(sc->name);
3973     GGadgetSetTitle(GWidgetGetControl(ci->gw,CID_UName),temp);
3974     free(temp);
3975     CI_SetNameList(ci,sc->unicodeenc);
3976 
3977     sprintf(buffer,"U+%04x", sc->unicodeenc);
3978     temp = utf82u_copy(sc->unicodeenc==-1?"-1":buffer);
3979     GGadgetSetTitle(GWidgetGetControl(ci->gw,CID_UValue),temp);
3980     free(temp);
3981 
3982     ubuf[0] = sc->unicodeenc;
3983     if ( sc->unicodeenc==-1 )
3984 	ubuf[0] = '\0';
3985     ubuf[1] = '\0';
3986     GGadgetSetTitle(GWidgetGetControl(ci->gw,CID_UChar),ubuf);
3987 
3988     memset(cnts,0,sizeof(cnts));
3989     for ( pst = sc->possub; pst!=NULL; pst=pst->next ) if ( pst->type!=pst_lcaret )
3990 	++cnts[pst->type];
3991     for ( isv=0; isv<2; ++isv ) {
3992 	for ( kp=isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kp->next )
3993 	    ++cnts[pst_pair];
3994     }
3995     for ( i=pst_null+1; i<pst_max && i<pst_lcaret ; ++i )
3996 	mds[i] = calloc((cnts[i]+1)*mi[i-1].col_cnt,sizeof(struct matrix_data));
3997     memset(cnts,0,sizeof(cnts));
3998     for ( pst = sc->possub; pst!=NULL; pst=pst->next ) if ( pst->type!=pst_lcaret ) {
3999 	j = (cnts[pst->type]++ * mi[pst->type-1].col_cnt);
4000 	mds[pst->type][j+0].u.md_ival = (intpt) pst->subtable;
4001 	if ( pst->type==pst_position ) {
4002 	    mds[pst->type][j+SIM_DX].u.md_ival = pst->u.pos.xoff;
4003 	    mds[pst->type][j+SIM_DY].u.md_ival = pst->u.pos.yoff;
4004 	    mds[pst->type][j+SIM_DX_ADV].u.md_ival = pst->u.pos.h_adv_off;
4005 	    mds[pst->type][j+SIM_DY_ADV].u.md_ival = pst->u.pos.v_adv_off;
4006 	    ValDevTabToStrings(mds[pst_position],j+SIM_DX+1,pst->u.pos.adjust);
4007 	} else if ( pst->type==pst_pair ) {
4008 	    mds[pst->type][j+1].u.md_str = copy(pst->u.pair.paired);
4009 	    mds[pst->type][j+PAIR_DX1].u.md_ival = pst->u.pair.vr[0].xoff;
4010 	    mds[pst->type][j+PAIR_DY1].u.md_ival = pst->u.pair.vr[0].yoff;
4011 	    mds[pst->type][j+PAIR_DX_ADV1].u.md_ival = pst->u.pair.vr[0].h_adv_off;
4012 	    mds[pst->type][j+PAIR_DY_ADV1].u.md_ival = pst->u.pair.vr[0].v_adv_off;
4013 	    mds[pst->type][j+PAIR_DX2].u.md_ival = pst->u.pair.vr[1].xoff;
4014 	    mds[pst->type][j+PAIR_DY2].u.md_ival = pst->u.pair.vr[1].yoff;
4015 	    mds[pst->type][j+PAIR_DX_ADV2].u.md_ival = pst->u.pair.vr[1].h_adv_off;
4016 	    mds[pst->type][j+PAIR_DY_ADV2].u.md_ival = pst->u.pair.vr[1].v_adv_off;
4017 	    ValDevTabToStrings(mds[pst_pair],j+PAIR_DX1+1,pst->u.pair.vr[0].adjust);
4018 	    ValDevTabToStrings(mds[pst_pair],j+PAIR_DX2+1,pst->u.pair.vr[1].adjust);
4019 	} else {
4020 	    mds[pst->type][j+1].u.md_str = SFNameList2NameUni(sf,pst->u.subs.variant);
4021 	}
4022     }
4023     for ( isv=0; isv<2; ++isv ) {
4024 	for ( kp=isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kp->next ) {
4025 	    j = (cnts[pst_pair]++ * mi[pst_pair-1].col_cnt);
4026 	    mds[pst_pair][j+0].u.md_ival = (intpt) kp->subtable;
4027 	    mds[pst_pair][j+1].u.md_str = SCNameUniStr(kp->sc);
4028 	    if ( isv ) {
4029 		mds[pst_pair][j+PAIR_DY_ADV1].u.md_ival = kp->off;
4030 		DevTabToString(&mds[pst_pair][j+PAIR_DY_ADV1+1].u.md_str,kp->adjust);
4031 	    } else if ( kp->subtable->lookup->lookup_flags&pst_r2l ) {
4032 		mds[pst_pair][j+PAIR_DX_ADV2].u.md_ival = kp->off;
4033 		DevTabToString(&mds[pst_pair][j+PAIR_DX_ADV2+1].u.md_str,kp->adjust);
4034 	    } else {
4035 		mds[pst_pair][j+PAIR_DX_ADV1].u.md_ival = kp->off;
4036 		DevTabToString(&mds[pst_pair][j+PAIR_DX_ADV1+1].u.md_str,kp->adjust);
4037 	    }
4038 	}
4039     }
4040     for ( i=pst_null+1; i<pst_lcaret /* == pst_max-1 */; ++i ) {
4041 	GMatrixEditSet(GWidgetGetControl(ci->gw,CID_List+(i-1)*100),
4042 		mds[i],cnts[i],false);
4043     }
4044     /* There's always a pane showing kerning data */
4045     CI_DoHideUnusedPair(ci);
4046     CI_DoHideUnusedSingle(ci);
4047 
4048 	GGadget *cotf = GWidgetGetControl(ci->gw,CID_ComponentTextField);
4049 	GGadget *codcb = GWidgetGetControl(ci->gw,CID_ComponentDefaultCB);
4050 	GGadget *coim = GWidgetGetControl(ci->gw,CID_ComponentInterpMsg);
4051 	GGadget *cola = GWidgetGetControl(ci->gw,CID_ComponentChangeMsg);
4052 
4053     bits = bits2 = SFGetAlternate(sc->parent,sc->unicodeenc,NULL,true);
4054     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_ComponentMsg),
4055 	bits==NULL ? _("No components") :
4056 	hascomposing(sc->parent,sc->unicodeenc,sc) ? _("Accented glyph composed of:") :
4057 	    _("Glyph composed of:"));
4058     if ( bits==NULL ) {
4059 	ubuf[0] = '\0';
4060 	GGadgetSetTitle(GWidgetGetControl(ci->gw,CID_Components),ubuf);
4061     GGadgetSetTitle(cotf,ubuf);
4062     } else {
4063 	unichar_t *temp = malloc(18*u_strlen(bits)*sizeof(unichar_t));
4064 	unichar_t *upt=temp;
4065 	while ( *bits!='\0' ) {
4066 	    sprintf(buffer, "U+%04x (", *bits );
4067 	    uc_strcpy(upt,buffer);
4068 	    upt += u_strlen(upt);
4069 	    if (iscombining(*bits)) {
4070 	        *upt = 0x25CC; // DOTTED CIRCLE “◌”
4071 	        upt += 1;
4072 	    }
4073 	    *upt = *bits;
4074 	    upt += 1;
4075 	    sprintf(buffer, ") ");
4076 	    uc_strcpy(upt,buffer);
4077 	    upt += u_strlen(upt);
4078 
4079 	    ++bits;
4080 	}
4081 	upt[-1] = '\0';
4082 	GGadgetSetTitle(GWidgetGetControl(ci->gw,CID_Components),temp);
4083 	free(temp);
4084     }
4085 
4086     if (d_ptr == NULL) d_ptr = bits2;
4087 
4088     int d_length = d_ptr ? u_strlen(d_ptr) : 0;
4089 
4090     const int MAX_UNICHAR_T_BYTES = 4;
4091     char* codepoints_as_hex = malloc(((2 * MAX_UNICHAR_T_BYTES) * d_length) + d_length + 1);
4092     codepoints_as_hex[0] = '\0';
4093 
4094     if (d_ptr == NULL) d_ptr = bits2;
4095 
4096     while (d_ptr != NULL && *d_ptr != '\0') {
4097         switch (BytesNeeded(*d_ptr)) {
4098             case 1:
4099                 sprintf(buffer, "%02x ", *d_ptr); break;
4100             case 2:
4101                 sprintf(buffer, "%04x ", *d_ptr); break;
4102             case 3:
4103                 sprintf(buffer, "%06x ", *d_ptr); break;
4104             default:
4105                 sprintf(buffer, "%08x ", *d_ptr);
4106         }
4107         codepoints_as_hex = strcat(codepoints_as_hex, buffer);
4108         ++d_ptr;
4109     }
4110 
4111     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_ComponentTextField),codepoints_as_hex);
4112     free(codepoints_as_hex);
4113 
4114     if (!GGadgetIsEnabled(codcb)) GGadgetSetEnabled(codcb, true);
4115     char* lbl = CI_CreateInterpretedAsLabel(sc->user_decomp);
4116     if (sc->user_decomp != NULL) {
4117         GGadgetSetChecked(codcb, false);
4118         GGadgetSetEnabled(cotf, true);
4119         GGadgetSetEnabled(cola, true);
4120         GGadgetSetTitle8(coim,lbl);
4121     } else {
4122         GGadgetSetChecked(codcb, true);
4123         GGadgetSetEnabled(cotf, false);
4124         GGadgetSetEnabled(cola, false);
4125         GGadgetSetTitle8(coim,"");
4126     }
4127     free(lbl);
4128 
4129     GGadgetSelectOneListItem(GWidgetGetControl(ci->gw,CID_Color),0);
4130 
4131     GGadgetSetChecked(GWidgetGetControl(ci->gw,CID_UnlinkRmOverlap),sc->unlink_rm_ovrlp_save_undo);
4132 
4133     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_Comment),
4134 	    sc->comment?sc->comment:"");
4135     GGadgetSelectOneListItem(GWidgetGetControl(ci->gw,CID_GClass),sc->glyph_class);
4136     CI_SetColorList(ci,sc->color);
4137     ci->first = sc->comment==NULL;
4138 
4139     ti = malloc((sc->countermask_cnt+1)*sizeof(GTextInfo *));
4140     ti[sc->countermask_cnt] = calloc(1,sizeof(GTextInfo));
4141     for ( i=0; i<sc->countermask_cnt; ++i ) {
4142 	ti[i] = calloc(1,sizeof(GTextInfo));
4143 	ti[i]->text = CounterMaskLine(sc,&sc->countermasks[i]);
4144 	ti[i]->fg = ti[i]->bg = COLOR_DEFAULT;
4145 	ti[i]->userdata = chunkalloc(sizeof(HintMask));
4146 	memcpy(ti[i]->userdata,sc->countermasks[i],sizeof(HintMask));
4147     }
4148     GGadgetSetList(GWidgetGetControl(ci->gw,CID_List+600),ti,false);
4149 
4150     if ( sc->tex_height!=TEX_UNDEF )
4151 	sprintf(buffer,"%d",sc->tex_height);
4152     else
4153 	buffer[0] = '\0';
4154     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TeX_Height),buffer);
4155 
4156     if ( sc->tex_depth!=TEX_UNDEF )
4157 	sprintf(buffer,"%d",sc->tex_depth);
4158     else
4159 	buffer[0] = '\0';
4160     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TeX_Depth),buffer);
4161 
4162     if ( sc->italic_correction!=TEX_UNDEF )
4163 	sprintf(buffer,"%d",sc->italic_correction);
4164     else
4165 	buffer[0] = '\0';
4166     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TeX_Italic),buffer);
4167     DevTabToString(&devtabstr,sc->italic_adjusts);
4168     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_ItalicDevTab),devtabstr==NULL?"":devtabstr);
4169     free(devtabstr);
4170 
4171     if ( sc->top_accent_horiz!=TEX_UNDEF )
4172 	sprintf(buffer,"%d",sc->top_accent_horiz);
4173     else
4174 	buffer[0] = '\0';
4175     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_HorAccent),buffer);
4176     DevTabToString(&devtabstr,sc->top_accent_adjusts);
4177     GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_AccentDevTab),devtabstr==NULL?"":devtabstr);
4178     free(devtabstr);
4179 
4180     GGadgetSetChecked(GWidgetGetControl(ci->gw,CID_IsExtended),sc->is_extended_shape);
4181 
4182     {
4183 	GGadget *g = GWidgetGetControl(ci->gw,CID_VariantList+0*100);
4184 	if ( sc->vert_variants==NULL || sc->vert_variants->variants==NULL )
4185 	    GGadgetSetTitle8(g,"");
4186 	else
4187 	    GGadgetSetTitle8(g,sc->vert_variants->variants);
4188 	sprintf(buffer,"%d",sc->vert_variants!=NULL?sc->vert_variants->italic_correction:0);
4189 	GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_ExtItalicCor+0*100),buffer);
4190 	DevTabToString(&devtabstr,sc->vert_variants!=NULL?
4191 		sc->vert_variants->italic_adjusts:
4192 		NULL);
4193 	GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_ExtItalicDev+0*100),devtabstr==NULL?"":devtabstr);
4194 	free(devtabstr);
4195 
4196 	g = GWidgetGetControl(ci->gw,CID_VariantList+1*100);
4197 	if ( sc->horiz_variants==NULL || sc->horiz_variants->variants==NULL )
4198 	    GGadgetSetTitle8(g,"");
4199 	else
4200 	    GGadgetSetTitle8(g,sc->horiz_variants->variants);
4201 	sprintf(buffer,"%d",sc->horiz_variants!=NULL?sc->horiz_variants->italic_correction:0);
4202 	GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_ExtItalicCor+1*100),buffer);
4203 	DevTabToString(&devtabstr,sc->horiz_variants!=NULL?
4204 		sc->horiz_variants->italic_adjusts:
4205 		NULL);
4206 	GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_ExtItalicDev+1*100),devtabstr==NULL?"":devtabstr);
4207 	free(devtabstr);
4208     }
4209     for ( i=0; i<2; ++i ) {
4210 	struct glyphvariants *gv = i ? sc->horiz_variants : sc->vert_variants ;
4211 	GGadget *g = GWidgetGetControl(ci->gw,CID_ExtensionList+i*100);
4212 	GV_ToMD(g, gv);
4213     }
4214     GA_ToMD(GWidgetGetControl(ci->gw,CID_AltUni), sc);
4215 
4216     if ( ci->sc->parent->multilayer ) {
4217 	int margined = sc->tile_margin!=0 || (sc->tile_bounds.minx==0 && sc->tile_bounds.maxx==0);
4218 	char buffer[40];
4219 
4220 	GGadgetSetChecked(GWidgetGetControl(ci->gw,CID_IsTileMargin),margined);
4221 	if ( margined ) {
4222 	    sprintf( buffer, "%g", (double) sc->tile_margin );
4223 	    GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TileMargin),buffer);
4224 	    CI_BoundsToMargin(ci);
4225 	} else {
4226 	    GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TileMargin),"0");
4227 	    sprintf( buffer, "%g", (double) sc->tile_bounds.minx );
4228 	    GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TileBBoxMinX),buffer);
4229 	    sprintf( buffer, "%g", (double) sc->tile_bounds.miny );
4230 	    GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TileBBoxMinY),buffer);
4231 	    sprintf( buffer, "%g", (double) sc->tile_bounds.maxx );
4232 	    GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TileBBoxMaxX),buffer);
4233 	    sprintf( buffer, "%g", (double) sc->tile_bounds.maxy );
4234 	    GGadgetSetTitle8(GWidgetGetControl(ci->gw,CID_TileBBoxMaxY),buffer);
4235 	}
4236     }
4237 
4238     GGadgetSetChecked(GWidgetGetControl(ci->gw,CID_DefLCCount), !sc->lig_caret_cnt_fixed );
4239     GGadgetSetEnabled(GWidgetGetControl(ci->gw,CID_LCCountLab), sc->lig_caret_cnt_fixed );
4240     GGadgetSetEnabled(GWidgetGetControl(ci->gw,CID_LCCount), sc->lig_caret_cnt_fixed );
4241     ci->lc_seen = false;
4242     CI_NoteAspect(ci);
4243 }
4244 
CI_NextPrev(GGadget * g,GEvent * e)4245 static int CI_NextPrev(GGadget *g, GEvent *e) {
4246     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
4247 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
4248 	int enc = ci->enc + GGadgetGetCid(g);	/* cid is 1 for next, -1 for prev */
4249 	SplineChar *new_;
4250 	struct splinecharlist *scl;
4251 
4252 	if ( enc<0 || enc>=ci->map->enccount ) {
4253 	    GGadgetSetEnabled(g,false);
4254 return( true );
4255 	}
4256 	if ( !_CI_OK(ci))
4257 return( true );
4258 	new_ = SFMakeChar(ci->sc->parent,ci->map,enc);
4259 	if ( new_->charinfo!=NULL && new_->charinfo!=ci ) {
4260 	    GGadgetSetEnabled(g,false);
4261 return( true );
4262 	}
4263 	ci->sc = new_;
4264 	ci->enc = enc;
4265 	for ( scl=ci->changes; scl!=NULL && scl->sc->orig_pos!=new_->orig_pos;
4266 		scl = scl->next );
4267 	ci->cachedsc = scl==NULL ? NULL : scl->sc;
4268 	CIFillup(ci);
4269     }
4270 return( true );
4271 }
4272 
CI_DoCancel(CharInfo * ci)4273 static void CI_DoCancel(CharInfo *ci) {
4274     int32 i,len;
4275     GTextInfo **ti = GGadgetGetList(GWidgetGetControl(ci->gw,CID_List+600),&len);
4276 
4277     for ( i=0; i<len; ++i )
4278 	chunkfree(ti[i]->userdata,sizeof(HintMask));
4279     CI_Finish(ci);
4280 }
4281 
CI_Cancel(GGadget * g,GEvent * e)4282 static int CI_Cancel(GGadget *g, GEvent *e) {
4283     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
4284 	CharInfo *ci = GDrawGetUserData(GGadgetGetWindow(g));
4285 	CI_DoCancel(ci);
4286     }
4287 return( true );
4288 }
4289 
ci_e_h(GWindow gw,GEvent * event)4290 static int ci_e_h(GWindow gw, GEvent *event) {
4291     if ( event->type==et_close ) {
4292 	CharInfo *ci = GDrawGetUserData(gw);
4293 	CI_DoCancel(ci);
4294     } else if ( event->type==et_char ) {
4295 	CharInfo *ci = GDrawGetUserData(gw);
4296 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
4297 	    help("ui/dialogs/charinfo.html", NULL);
4298 return( true );
4299 	} else if ( GMenuIsCommand(event,H_("Quit|Ctl+Q") )) {
4300 	    MenuExit(NULL,NULL,NULL);
4301 	} else if ( GMenuIsCommand(event,H_("Close|Ctl+Shft+Q") )) {
4302 	    CI_DoCancel(ci);
4303 	}
4304 return( false );
4305     } else if ( event->type == et_destroy ) {
4306 	CharInfo *ci = GDrawGetUserData(gw);
4307 	ci->sc->charinfo = NULL;
4308 	free(ci);
4309     } else if ( event->type == et_map ) {
4310 	/* Above palettes */
4311 	GDrawRaise(gw);
4312     }
4313 return( true );
4314 }
4315 
SCCharInfo(SplineChar * sc,int deflayer,EncMap * map,int enc)4316 void SCCharInfo(SplineChar *sc,int deflayer, EncMap *map,int enc) {
4317     CharInfo *ci;
4318     GRect pos;
4319     GWindowAttrs wattrs;
4320     GGadgetCreateData ugcd[14], cgcd[6], psgcd[7][7], cogcd[6], mgcd[9], tgcd[16];
4321     GGadgetCreateData lcgcd[4], vargcd[2][7];
4322     GTextInfo ulabel[14], clabel[6], pslabel[7][6], colabel[4], mlabel[9], tlabel[16];
4323     GTextInfo lclabel[4], varlabel[2][6];
4324     GGadgetCreateData mbox[4], *mvarray[7], *mharray1[7], *mharray2[8];
4325     GGadgetCreateData ubox[3], *uhvarray[29], *uharray[6];
4326     GGadgetCreateData cbox[3], *cvarray[5], *charray[4];
4327     GGadgetCreateData pstbox[7][4], *pstvarray[7][5], *pstharray1[7][8];
4328     GGadgetCreateData cobox[2], *covarray[8];
4329     GGadgetCreateData tbox[3], *thvarray[36], *tbarray[4];
4330     GGadgetCreateData lcbox[2], *lchvarray[4][4];
4331     GGadgetCreateData varbox[2][2], *varhvarray[2][5][4];
4332     GGadgetCreateData tilegcd[16], tilebox[4];
4333     GTextInfo tilelabel[16];
4334     GGadgetCreateData *tlvarray[6], *tlharray[4], *tlhvarray[4][5];
4335     int i;
4336     GTabInfo aspects[17];
4337     static GBox smallbox = { bt_raised, bs_rect, 2, 1, 0, 0, 0, 0, 0, 0, COLOR_DEFAULT, COLOR_DEFAULT, 0, 0, 0, 0, 0, 0, 0 };
4338     static int boxset=0;
4339     FontRequest rq;
4340     static GFont *font=NULL;
4341 
4342     CharInfoInit();
4343 
4344     if ( sc->charinfo!=NULL ) {
4345 	GDrawSetVisible(sc->charinfo->gw,true);
4346 	GDrawRaise(sc->charinfo->gw);
4347 return;
4348     }
4349 
4350     ci = calloc(1,sizeof(CharInfo));
4351     ci->sc = sc;
4352     ci->def_layer = deflayer;
4353     ci->done = false;
4354     ci->map = map;
4355     ci->last = 0xffffff;
4356     if ( enc==-1 )
4357 	enc = map->backmap[sc->orig_pos];
4358     ci->enc = enc;
4359 
4360     if ( !boxset ) {
4361 	extern GBox _ggadget_Default_Box;
4362 	GGadgetInit();
4363 	smallbox = _ggadget_Default_Box;
4364 	smallbox.padding = 1;
4365 	boxset = 1;
4366     }
4367 
4368 	memset(&wattrs,0,sizeof(wattrs));
4369 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
4370 	wattrs.event_masks = ~(1<<et_charup);
4371 	wattrs.restrict_input_to_me = false;
4372 	wattrs.undercursor = 1;
4373 	wattrs.cursor = ct_pointer;
4374 	wattrs.utf8_window_title =  _("Glyph Info");
4375 	wattrs.is_dlg = false;
4376 	pos.x = pos.y = 0;
4377 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,CI_Width+65));
4378 	pos.height = GDrawPointsToPixels(NULL,CI_Height);
4379 	ci->gw = GDrawCreateTopWindow(NULL,&pos,ci_e_h,ci,&wattrs);
4380 
4381 	memset(&ugcd,0,sizeof(ugcd));
4382 	memset(&ubox,0,sizeof(ubox));
4383 	memset(&ulabel,0,sizeof(ulabel));
4384 
4385 	ulabel[0].text = (unichar_t *) _("Gl_yph Name:");
4386 	ulabel[0].text_is_1byte = true;
4387 	ulabel[0].text_in_resource = true;
4388 	ugcd[0].gd.label = &ulabel[0];
4389 	ugcd[0].gd.pos.x = 5; ugcd[0].gd.pos.y = 5+4;
4390 	ugcd[0].gd.flags = gg_enabled|gg_visible;
4391 	ugcd[0].gd.mnemonic = 'N';
4392 	ugcd[0].creator = GLabelCreate;
4393 	uhvarray[0] = &ugcd[0];
4394 
4395 	ugcd[1].gd.pos.x = 85; ugcd[1].gd.pos.y = 5;
4396 	ugcd[1].gd.flags = gg_enabled|gg_visible;
4397 	ugcd[1].gd.mnemonic = 'N';
4398 	ugcd[1].gd.cid = CID_UName;
4399 	ugcd[1].creator = GListFieldCreate;
4400 	ugcd[1].data = (void *) (-2);
4401 	uhvarray[1] = &ugcd[1]; uhvarray[2] = NULL;
4402 
4403 	ulabel[2].text = (unichar_t *) _("Unicode _Value:");
4404 	ulabel[2].text_in_resource = true;
4405 	ulabel[2].text_is_1byte = true;
4406 	ugcd[2].gd.label = &ulabel[2];
4407 	ugcd[2].gd.pos.x = 5; ugcd[2].gd.pos.y = 31+4;
4408 	ugcd[2].gd.flags = gg_enabled|gg_visible;
4409 	ugcd[2].gd.mnemonic = 'V';
4410 	ugcd[2].creator = GLabelCreate;
4411 	uhvarray[3] = &ugcd[2];
4412 
4413 	ugcd[3].gd.pos.x = 85; ugcd[3].gd.pos.y = 31;
4414 	ugcd[3].gd.flags = gg_enabled|gg_visible;
4415 	ugcd[3].gd.mnemonic = 'V';
4416 	ugcd[3].gd.cid = CID_UValue;
4417 	ugcd[3].gd.handle_controlevent = CI_UValChanged;
4418 	ugcd[3].creator = GTextFieldCreate;
4419 	uhvarray[4] = &ugcd[3]; uhvarray[5] = NULL;
4420 
4421 	ulabel[4].text = (unichar_t *) _("Unicode C_har:");
4422 	ulabel[4].text_in_resource = true;
4423 	ulabel[4].text_is_1byte = true;
4424 	ugcd[4].gd.label = &ulabel[4];
4425 	ugcd[4].gd.pos.x = 5; ugcd[4].gd.pos.y = 57+4;
4426 	ugcd[4].gd.flags = gg_enabled|gg_visible;
4427 	ugcd[4].gd.mnemonic = 'h';
4428 	ugcd[4].creator = GLabelCreate;
4429 	uhvarray[6] = &ugcd[4];
4430 
4431 	ugcd[5].gd.pos.x = 85; ugcd[5].gd.pos.y = 57;
4432 	ugcd[5].gd.flags = gg_enabled|gg_visible|gg_text_xim;
4433 	ugcd[5].gd.mnemonic = 'h';
4434 	ugcd[5].gd.cid = CID_UChar;
4435 	ugcd[5].gd.handle_controlevent = CI_CharChanged;
4436 	ugcd[5].creator = GTextFieldCreate;
4437 	uhvarray[7] = &ugcd[5]; uhvarray[8] = NULL;
4438 
4439 	ugcd[6].gd.pos.x = 12; ugcd[6].gd.pos.y = 117;
4440 	ugcd[6].gd.flags = gg_visible | gg_enabled;
4441 	ulabel[6].text = (unichar_t *) _("Set From N_ame");
4442 	ulabel[6].text_is_1byte = true;
4443 	ulabel[6].text_in_resource = true;
4444 	ugcd[6].gd.mnemonic = 'a';
4445 	ugcd[6].gd.label = &ulabel[6];
4446 	ugcd[6].gd.handle_controlevent = CI_SName;
4447 	ugcd[6].creator = GButtonCreate;
4448 	uharray[0] = GCD_Glue; uharray[1] = &ugcd[6];
4449 
4450 	ugcd[7].gd.pos.x = 107; ugcd[7].gd.pos.y = 117;
4451 	ugcd[7].gd.flags = gg_visible | gg_enabled;
4452 	ulabel[7].text = (unichar_t *) _("Set From Val_ue");
4453 	ulabel[7].text_is_1byte = true;
4454 	ulabel[7].text_in_resource = true;
4455 	ugcd[7].gd.mnemonic = 'l';
4456 	ugcd[7].gd.label = &ulabel[7];
4457 	ugcd[7].gd.handle_controlevent = CI_SValue;
4458 	ugcd[7].creator = GButtonCreate;
4459 	uharray[2] = GCD_Glue; uharray[3] = &ugcd[7]; uharray[4] = GCD_Glue; uharray[5] = NULL;
4460 
4461 	ubox[2].gd.flags = gg_enabled|gg_visible;
4462 	ubox[2].gd.u.boxelements = uharray;
4463 	ubox[2].creator = GHBoxCreate;
4464 	uhvarray[9] = &ubox[2]; uhvarray[10] = GCD_ColSpan; uhvarray[11] = NULL;
4465 
4466 	ugcd[8].gd.flags = gg_visible | gg_enabled;
4467 	ulabel[8].text = (unichar_t *) _("Alternate Unicode Encodings / Variation Selectors");
4468 	ulabel[8].text_is_1byte = true;
4469 	ugcd[8].gd.label = &ulabel[8];
4470 	ugcd[8].gd.popup_msg = _(
4471 	    "Some glyphs may be used for more than one\n"
4472 	    "unicode code point -- I don't recommend\n"
4473 	    "doing this, better to use a reference --\n"
4474 	    "but it is possible.\n"
4475 	    "The latin \"A\", the greek \"Alpha\" and the\n"
4476 	    "cyrillic \"A\" look very much the same.\n\n"
4477 	    "On the other hand certain Mongolian and CJK\n"
4478 	    "characters have multiple glyphs depending\n"
4479 	    "on a unicode Variation Selector.\n\n"
4480 	    "In the first case use a variation selector\n"
4481 	    "of 0, in the second use the appropriate\n"
4482 	    "codepoint.");
4483 	ugcd[8].creator = GLabelCreate;
4484 	uhvarray[12] = &ugcd[8]; uhvarray[13] = GCD_ColSpan; uhvarray[14] = NULL;
4485 
4486 	ugcd[9].gd.flags = gg_enabled|gg_visible;
4487 	ugcd[9].gd.u.matrix = &mi_altuniinfo;
4488 	ugcd[9].gd.cid = CID_AltUni;
4489 	ugcd[9].gd.popup_msg = ugcd[8].gd.popup_msg;
4490 	ugcd[9].creator = GMatrixEditCreate;
4491 	uhvarray[15] = &ugcd[9]; uhvarray[16] = GCD_ColSpan; uhvarray[17] = NULL;
4492 
4493 	ugcd[10].gd.pos.x = 5; ugcd[10].gd.pos.y = 83+4;
4494 	ugcd[10].gd.flags = gg_visible | gg_enabled;
4495 	ulabel[10].text = (unichar_t *) _("OT _Glyph Class:");
4496 	ulabel[10].text_is_1byte = true;
4497 	ulabel[10].text_in_resource = true;
4498 	ugcd[10].gd.label = &ulabel[10];
4499 	ugcd[10].creator = GLabelCreate;
4500 	uhvarray[18] = &ugcd[10];
4501 
4502 	ugcd[11].gd.pos.x = 85; ugcd[11].gd.pos.y = 83;
4503 	ugcd[11].gd.flags = gg_visible | gg_enabled;
4504 	ugcd[11].gd.cid = CID_GClass;
4505 	ugcd[11].gd.u.list = glyphclasses;
4506 	ugcd[11].creator = GListButtonCreate;
4507 	uhvarray[19] = &ugcd[11]; uhvarray[20] = NULL;
4508 
4509 	ugcd[12].gd.flags = gg_visible | gg_enabled;
4510 	ulabel[12].text = (unichar_t *) _("Mark for Unlink, Remove Overlap before Generating");
4511 	ulabel[12].text_is_1byte = true;
4512 	ulabel[12].text_in_resource = true;
4513 	ugcd[12].gd.label = &ulabel[12];
4514 	ugcd[12].gd.cid = CID_UnlinkRmOverlap;
4515 	ugcd[12].gd.popup_msg = _("A few glyphs, like Aring, Ccedilla, Eogonek\nare composed of two overlapping references.\nOften it is desirable to retain the references\n(so that changes made to the base glyph are\nreflected in the composed glyph), but that\nmeans you are stuck with overlapping contours.\nThis flag means that just before generating\nthe font, FontForge will unlink the references\nand run remove overlap on them, while\n retaining the references in the SFD.");
4516 	ugcd[12].creator = GCheckBoxCreate;
4517 	uhvarray[21] = &ugcd[12]; uhvarray[22] = GCD_ColSpan; uhvarray[23] = NULL;
4518 	uhvarray[24] = GCD_Glue; uhvarray[25] = GCD_Glue; uhvarray[26] = NULL;
4519 	uhvarray[27] = NULL;
4520 
4521 	ubox[0].gd.flags = gg_enabled|gg_visible;
4522 	ubox[0].gd.u.boxelements = uhvarray;
4523 	ubox[0].creator = GHVBoxCreate;
4524 
4525 
4526 	memset(&cgcd,0,sizeof(cgcd));
4527 	memset(&cbox,0,sizeof(cbox));
4528 	memset(&clabel,0,sizeof(clabel));
4529 
4530 	clabel[0].text = (unichar_t *) _("Comment");
4531 	clabel[0].text_is_1byte = true;
4532 	cgcd[0].gd.label = &clabel[0];
4533 	cgcd[0].gd.pos.x = 5; cgcd[0].gd.pos.y = 5;
4534 	cgcd[0].gd.flags = gg_enabled|gg_visible;
4535 	cgcd[0].creator = GLabelCreate;
4536 	cvarray[0] = &cgcd[0];
4537 
4538 	cgcd[1].gd.pos.x = 5; cgcd[1].gd.pos.y = cgcd[0].gd.pos.y+13;
4539 	cgcd[1].gd.pos.height = 7*12+6;
4540 	cgcd[1].gd.flags = gg_enabled|gg_visible|gg_textarea_wrap|gg_text_xim;
4541 	cgcd[1].gd.cid = CID_Comment;
4542 	cgcd[1].gd.handle_controlevent = CI_CommentChanged;
4543 	cgcd[1].creator = GTextAreaCreate;
4544 	cvarray[1] = &cgcd[1]; cvarray[2] = GCD_Glue;
4545 
4546 	clabel[2].text = (unichar_t *) _("Color:");
4547 	clabel[2].text_is_1byte = true;
4548 	cgcd[2].gd.label = &clabel[2];
4549 	cgcd[2].gd.pos.x = 5; cgcd[2].gd.pos.y = cgcd[1].gd.pos.y+cgcd[1].gd.pos.height+5+6;
4550 	cgcd[2].gd.flags = gg_enabled|gg_visible;
4551 	cgcd[2].creator = GLabelCreate;
4552 	charray[0] = &cgcd[2];
4553 
4554 	cgcd[3].gd.pos.x = cgcd[3].gd.pos.x; cgcd[3].gd.pos.y = cgcd[2].gd.pos.y-6;
4555 	cgcd[3].gd.flags = gg_enabled|gg_visible;
4556 	cgcd[3].gd.cid = CID_Color;
4557 	cgcd[3].gd.handle_controlevent = CI_PickColor;
4558 	std_colors[0].image = GGadgetImageCache("colorwheel.png");
4559 	cgcd[3].gd.u.list = std_colors;
4560 	cgcd[3].creator = GListButtonCreate;
4561 	charray[1] = &cgcd[3]; charray[2] = GCD_Glue; charray[3] = NULL;
4562 
4563 	cbox[2].gd.flags = gg_enabled|gg_visible;
4564 	cbox[2].gd.u.boxelements = charray;
4565 	cbox[2].creator = GHBoxCreate;
4566 	cvarray[3] = &cbox[2]; cvarray[4] = NULL;
4567 
4568 	cbox[0].gd.flags = gg_enabled|gg_visible;
4569 	cbox[0].gd.u.boxelements = cvarray;
4570 	cbox[0].creator = GVBoxCreate;
4571 
4572 	memset(&psgcd,0,sizeof(psgcd));
4573 	memset(&pstbox,0,sizeof(pstbox));
4574 	memset(&pslabel,0,sizeof(pslabel));
4575 
4576 	for ( i=0; i<6; ++i ) {
4577 	    psgcd[i][0].gd.pos.x = 5; psgcd[i][0].gd.pos.y = 5;
4578 	    psgcd[i][0].gd.flags = gg_visible | gg_enabled;
4579 	    psgcd[i][0].gd.cid = CID_List+i*100;
4580 	    psgcd[i][0].gd.u.matrix = &mi[i];
4581 	    mi[i].col_init[0].enum_vals = SFSubtableListOfType(sc->parent, pst2lookuptype[i+1], false, false);
4582 	    psgcd[i][0].creator = GMatrixEditCreate;
4583 	}
4584 	for ( i=pst_position; i<=pst_pair; ++i ) {
4585 	    pslabel[i-1][1].text = (unichar_t *) _("_Hide Unused Columns");
4586 	    pslabel[i-1][1].text_is_1byte = true;
4587 	    pslabel[i-1][1].text_in_resource = true;
4588 	    psgcd[i-1][1].gd.label = &pslabel[i-1][1];
4589 	    psgcd[i-1][1].gd.pos.x = 5; psgcd[i-1][1].gd.pos.y = 5+4;
4590 	    psgcd[i-1][1].gd.flags = lookup_hideunused ? (gg_enabled|gg_visible|gg_cb_on) : (gg_enabled|gg_visible);
4591 	    psgcd[i-1][1].gd.popup_msg = _("Don't display columns of 0s.\nThe OpenType lookup allows for up to 8 kinds\nof data, but almost all kerning lookups will use just one.\nOmitting the others makes the behavior clearer.");
4592 	    psgcd[i-1][1].gd.handle_controlevent = i==pst_position ? CI_HideUnusedSingle : CI_HideUnusedPair;
4593 	    psgcd[i-1][1].creator = GCheckBoxCreate;
4594 	    pstvarray[i-1][0] = &psgcd[i-1][0];
4595 	    pstvarray[i-1][1] = &psgcd[i-1][1];
4596 	    pstvarray[i-1][2] = NULL;
4597 
4598 	    pstbox[i-1][0].gd.flags = gg_enabled|gg_visible;
4599 	    pstbox[i-1][0].gd.u.boxelements = pstvarray[i-1];
4600 	    pstbox[i-1][0].creator = GVBoxCreate;
4601 	}
4602 
4603 	    psgcd[6][0].gd.pos.x = 5; psgcd[6][0].gd.pos.y = 5;
4604 	    psgcd[6][0].gd.flags = gg_visible | gg_enabled;
4605 	    psgcd[6][0].gd.cid = CID_List+6*100;
4606 	    psgcd[6][0].gd.handle_controlevent = CI_CounterSelChanged;
4607 	    psgcd[6][0].gd.box = &smallbox;
4608 	    psgcd[6][0].creator = GListCreate;
4609 	    pstvarray[6][0] = &psgcd[6][0];
4610 
4611 	    psgcd[6][1].gd.pos.x = 10; psgcd[6][1].gd.pos.y = psgcd[6][0].gd.pos.y+psgcd[6][0].gd.pos.height+4;
4612 	    psgcd[6][1].gd.flags = gg_visible | gg_enabled;
4613 	    pslabel[6][1].text = (unichar_t *) S_("CounterHint|_New...");
4614 	    pslabel[6][1].text_is_1byte = true;
4615 	    pslabel[6][1].text_in_resource = true;
4616 	    psgcd[6][1].gd.label = &pslabel[6][1];
4617 	    psgcd[6][1].gd.cid = CID_New+6*100;
4618 	    psgcd[6][1].gd.handle_controlevent = CI_NewCounter;
4619 	    psgcd[6][1].gd.box = &smallbox;
4620 	    psgcd[6][1].creator = GButtonCreate;
4621 	    pstharray1[6][0] = GCD_Glue; pstharray1[6][1] = &psgcd[6][1];
4622 
4623 	    psgcd[6][2].gd.pos.x = 20+GIntGetResource(_NUM_Buttonsize)*100/GIntGetResource(_NUM_ScaleFactor); psgcd[6][2].gd.pos.y = psgcd[6][1].gd.pos.y;
4624 	    psgcd[6][2].gd.flags = gg_visible;
4625 	    pslabel[6][2].text = (unichar_t *) _("_Delete");
4626 	    pslabel[6][2].text_is_1byte = true;
4627 	    pslabel[6][2].text_in_resource = true;
4628 	    psgcd[6][2].gd.label = &pslabel[6][2];
4629 	    psgcd[6][2].gd.cid = CID_Delete+6*100;
4630 	    psgcd[6][2].gd.handle_controlevent = CI_DeleteCounter;
4631 	    psgcd[6][2].gd.box = &smallbox;
4632 	    psgcd[6][2].creator = GButtonCreate;
4633 	    pstharray1[6][2] = GCD_Glue; pstharray1[6][3] = &psgcd[6][2];
4634 
4635 	    psgcd[6][3].gd.pos.x = -10; psgcd[6][3].gd.pos.y = psgcd[6][1].gd.pos.y;
4636 	    psgcd[6][3].gd.flags = gg_visible;
4637 	    pslabel[6][3].text = (unichar_t *) _("_Edit...");
4638 	    pslabel[6][3].text_is_1byte = true;
4639 	    pslabel[6][3].text_in_resource = true;
4640 	    psgcd[6][3].gd.label = &pslabel[6][3];
4641 	    psgcd[6][3].gd.cid = CID_Edit+6*100;
4642 	    psgcd[6][3].gd.handle_controlevent = CI_EditCounter;
4643 	    psgcd[6][3].gd.box = &smallbox;
4644 	    psgcd[6][3].creator = GButtonCreate;
4645 	    pstharray1[6][4] = GCD_Glue; pstharray1[6][5] = &psgcd[6][3]; pstharray1[6][6] = GCD_Glue; pstharray1[6][7] = NULL;
4646 
4647 	    pstbox[6][2].gd.flags = gg_enabled|gg_visible;
4648 	    pstbox[6][2].gd.u.boxelements = pstharray1[6];
4649 	    pstbox[6][2].creator = GHBoxCreate;
4650 	    pstvarray[6][1] = &pstbox[6][2]; pstvarray[6][2] = NULL;
4651 
4652 	    pstbox[6][0].gd.flags = gg_enabled|gg_visible;
4653 	    pstbox[6][0].gd.u.boxelements = pstvarray[6];
4654 	    pstbox[6][0].creator = GVBoxCreate;
4655 	psgcd[6][4].gd.flags = psgcd[6][5].gd.flags = 0;	/* No copy, paste for hint masks */
4656 
4657 	memset(&cogcd,0,sizeof(cogcd));
4658 	memset(&cobox,0,sizeof(cobox));
4659 	memset(&colabel,0,sizeof(colabel));
4660 
4661 	colabel[0].text = (unichar_t *) _("Accented glyph composed of:");
4662 	colabel[0].text_is_1byte = true;
4663 	cogcd[0].gd.label = &colabel[0];
4664 	cogcd[0].gd.flags = gg_enabled|gg_visible;
4665 	cogcd[0].gd.cid = CID_ComponentMsg;
4666 	cogcd[0].creator = GLabelCreate;
4667 
4668 	cogcd[1].gd.flags = gg_enabled|gg_visible;
4669 	cogcd[1].gd.cid = CID_Components;
4670 	cogcd[1].creator = GLabelCreate;
4671 
4672 	colabel[1].text = (unichar_t *) _("\n\nIf the default decomposition is inappropriate for this font, you may choose your own.");
4673 	colabel[1].text_is_1byte = true;
4674 	cogcd[2].gd.label = &colabel[1];
4675 	cogcd[2].gd.flags = gg_enabled|gg_visible;
4676 	cogcd[2].gd.cid = CID_ComponentChangeMsg;
4677 	cogcd[2].creator = GLabelCreate;
4678 
4679 	colabel[2].text = (unichar_t *) _("Use default?");
4680 	colabel[2].text_is_1byte = true;
4681 	cogcd[3].gd.flags = gg_enabled | gg_visible | gg_cb_on;
4682 	cogcd[3].gd.cid = CID_ComponentDefaultCB;
4683 	cogcd[3].gd.handle_controlevent = CI_CmpUseNonDefault;
4684 	cogcd[3].gd.label = &colabel[2];
4685 	cogcd[3].creator = GCheckBoxCreate;
4686 
4687 	cogcd[4].gd.flags = gg_visible;
4688 	cogcd[4].gd.popup_msg = _("For example, to build this character from U+0061 (lowercase a) as the base and U+030C (combining caron), write:\n0061 030C");
4689 	cogcd[4].gd.cid = CID_ComponentTextField;
4690 	cogcd[4].gd.handle_controlevent = CI_CmpUseNonDefault;
4691 	cogcd[4].gd.pos.x = 500; cogcd[4].gd.pos.y = 20;
4692 	cogcd[4].creator = GTextFieldCreate;
4693 
4694 	cogcd[5].gd.flags = gg_enabled|gg_visible;
4695 	cogcd[5].gd.cid = CID_ComponentInterpMsg;
4696 	cogcd[5].creator = GLabelCreate;
4697 
4698 	covarray[0] = &cogcd[0]; covarray[1] = &cogcd[1]; covarray[2] = &cogcd[2]; covarray[3] = &cogcd[3]; covarray[4] = &cogcd[4]; covarray[5] = &cogcd[5]; covarray[6] = GCD_Glue; covarray[7] = NULL;
4699 	cobox[0].gd.flags = gg_enabled|gg_visible;
4700 	cobox[0].gd.u.boxelements = covarray;
4701 	cobox[0].creator = GVBoxCreate;
4702 
4703 	memset(&tgcd,0,sizeof(tgcd));
4704 	memset(&tbox,0,sizeof(tbox));
4705 	memset(&tlabel,0,sizeof(tlabel));
4706 
4707 	tlabel[0].text = (unichar_t *) _("Height:");
4708 	tlabel[0].text_is_1byte = true;
4709 	tgcd[0].gd.label = &tlabel[0];
4710 	tgcd[0].gd.pos.x = 5; tgcd[0].gd.pos.y = 5+4;
4711 	tgcd[0].gd.flags = gg_enabled|gg_visible;
4712 	tgcd[0].gd.popup_msg = _("The height and depth fields are the metrics fields used\nby TeX, they are corrected for optical distortion.\nSo 'x' and 'o' probably have the same height.");
4713 	tgcd[0].creator = GLabelCreate;
4714 	thvarray[0] = &tgcd[0];
4715 
4716 	tgcd[1].gd.pos.x = 85; tgcd[1].gd.pos.y = 5;
4717 	tgcd[1].gd.pos.width = 60;
4718 	tgcd[1].gd.flags = gg_enabled|gg_visible;
4719 	tgcd[1].gd.cid = CID_TeX_Height;
4720 	tgcd[1].creator = GTextFieldCreate;
4721 	tgcd[1].gd.popup_msg = tgcd[0].gd.popup_msg;
4722 	thvarray[1] = &tgcd[1];
4723 
4724 	tlabel[2].text = (unichar_t *) _("Guess");
4725 	tlabel[2].text_is_1byte = true;
4726 	tgcd[2].gd.label = &tlabel[2];
4727 	tgcd[2].gd.flags = gg_enabled|gg_visible;
4728 	tgcd[2].gd.cid = CID_TeX_HeightD;
4729 	tgcd[2].gd.handle_controlevent = TeX_Default;
4730 	tgcd[2].creator = GButtonCreate;
4731 	tgcd[2].gd.popup_msg = tgcd[0].gd.popup_msg;
4732 	thvarray[2] = &tgcd[2]; thvarray[3] = GCD_Glue; thvarray[4] = NULL;
4733 
4734 	tlabel[3].text = (unichar_t *) _("Depth:");
4735 	tlabel[3].text_is_1byte = true;
4736 	tgcd[3].gd.label = &tlabel[3];
4737 	tgcd[3].gd.pos.x = 5; tgcd[3].gd.pos.y = 31+4;
4738 	tgcd[3].gd.flags = gg_enabled|gg_visible;
4739 	tgcd[3].gd.popup_msg = tgcd[0].gd.popup_msg;
4740 	tgcd[3].creator = GLabelCreate;
4741 	thvarray[5] = &tgcd[3];
4742 
4743 	tgcd[4].gd.pos.x = 85; tgcd[4].gd.pos.y = 31;
4744 	tgcd[4].gd.pos.width = 60;
4745 	tgcd[4].gd.flags = gg_enabled|gg_visible;
4746 	tgcd[4].gd.cid = CID_TeX_Depth;
4747 	tgcd[4].creator = GTextFieldCreate;
4748 	tgcd[4].gd.popup_msg = tgcd[0].gd.popup_msg;
4749 	thvarray[6] = &tgcd[4];
4750 
4751 	tlabel[5].text = (unichar_t *) _("Guess");
4752 	tlabel[5].text_is_1byte = true;
4753 	tgcd[5].gd.label = &tlabel[5];
4754 	tgcd[5].gd.flags = gg_enabled|gg_visible;
4755 	tgcd[5].gd.cid = CID_TeX_DepthD;
4756 	tgcd[5].gd.handle_controlevent = TeX_Default;
4757 	tgcd[5].creator = GButtonCreate;
4758 	tgcd[5].gd.popup_msg = tgcd[0].gd.popup_msg;
4759 	thvarray[7] = &tgcd[5];  thvarray[8] = GCD_Glue; thvarray[9] = NULL;
4760 
4761 	tlabel[6].text = (unichar_t *) _("Italic Correction:");
4762 	tlabel[6].text_is_1byte = true;
4763 	tgcd[6].gd.label = &tlabel[6];
4764 	tgcd[6].gd.pos.x = 5; tgcd[6].gd.pos.y = 57+4;
4765 	tgcd[6].gd.flags = gg_enabled|gg_visible;
4766 	tgcd[6].creator = GLabelCreate;
4767 	tgcd[6].gd.popup_msg = _("The Italic correction field is used by both TeX and the MS 'MATH'\ntable. It is used when joining slanted text (italic) to upright.\nIt is the amount of extra white space needed so the slanted text\nwill not run into the upright text.");
4768 	thvarray[10] = &tgcd[6];
4769 
4770 	tgcd[7].gd.pos.x = 85; tgcd[7].gd.pos.y = 57;
4771 	tgcd[7].gd.pos.width = 60;
4772 	tgcd[7].gd.flags = gg_enabled|gg_visible;
4773 	tgcd[7].gd.cid = CID_TeX_Italic;
4774 	tgcd[7].creator = GTextFieldCreate;
4775 	tgcd[7].gd.popup_msg = tgcd[6].gd.popup_msg;
4776 	thvarray[11] = &tgcd[7];
4777 
4778 	tlabel[8].text = (unichar_t *) _("Guess");
4779 	tlabel[8].text_is_1byte = true;
4780 	tgcd[8].gd.label = &tlabel[8];
4781 	tgcd[8].gd.flags = gg_enabled|gg_visible;
4782 	tgcd[8].gd.cid = CID_TeX_ItalicD;
4783 	tgcd[8].gd.handle_controlevent = TeX_Default;
4784 	tgcd[8].creator = GButtonCreate;
4785 	tgcd[8].gd.popup_msg = tgcd[6].gd.popup_msg;
4786 	thvarray[12] = &tgcd[8];
4787 
4788 	tgcd[9].gd.flags = gg_enabled|gg_visible;
4789 	tgcd[9].gd.cid = CID_ItalicDevTab;
4790 	tgcd[9].creator = GTextFieldCreate;
4791 	tgcd[9].gd.popup_msg = _("A device table for italic correction.\nExpects a comma separated list of <pixelsize>\":\"<adjustment>\nAs \"9:-1,12:1,13:1\"");
4792 	thvarray[13] = &tgcd[9]; thvarray[14] = NULL;
4793 
4794 	tlabel[10].text = (unichar_t *) _("Top Accent Pos:");
4795 	tlabel[10].text_is_1byte = true;
4796 	tgcd[10].gd.label = &tlabel[10];
4797 	tgcd[10].gd.pos.x = 5; tgcd[10].gd.pos.y = 57+4;
4798 	tgcd[10].gd.flags = gg_enabled|gg_visible;
4799 	tgcd[10].gd.popup_msg = tgcd[9].gd.popup_msg;
4800 	tgcd[10].creator = GLabelCreate;
4801 	tgcd[10].gd.popup_msg = _("In the MS 'MATH' table this value specifies where (horizontally)\nan accent should be placed above the glyph. Vertical placement\nis handled by other means");
4802 	thvarray[15] = &tgcd[10];
4803 
4804 	tgcd[11].gd.pos.x = 85; tgcd[11].gd.pos.y = 57;
4805 	tgcd[11].gd.pos.width = 60;
4806 	tgcd[11].gd.flags = gg_enabled|gg_visible;
4807 	tgcd[11].gd.cid = CID_HorAccent;
4808 	tgcd[11].creator = GTextFieldCreate;
4809 	tgcd[11].gd.popup_msg = tgcd[10].gd.popup_msg;
4810 	thvarray[16] = &tgcd[11];
4811 
4812 	tlabel[12].text = (unichar_t *) _("Guess");
4813 	tlabel[12].text_is_1byte = true;
4814 	tgcd[12].gd.label = &tlabel[12];
4815 	tgcd[12].gd.flags = gg_enabled|gg_visible;
4816 	tgcd[12].gd.cid = CID_HorAccentD;
4817 	tgcd[12].gd.handle_controlevent = TeX_Default;
4818 	tgcd[12].creator = GButtonCreate;
4819 	tgcd[12].gd.popup_msg = tgcd[10].gd.popup_msg;
4820 	thvarray[17] = &tgcd[12];
4821 
4822 	tgcd[13].gd.flags = gg_enabled|gg_visible;
4823 	tgcd[13].gd.cid = CID_AccentDevTab;
4824 	tgcd[13].creator = GTextFieldCreate;
4825 	tgcd[13].gd.popup_msg = _("A device table for horizontal accent positioning.\nExpects a comma separated list of <pixelsize>\":\"<adjustment>\nAs \"9:-1,12:1,13:1\"");
4826 	thvarray[18] = &tgcd[13]; thvarray[19] = NULL;
4827 
4828 	tlabel[14].text = (unichar_t *) _("Is Extended Shape");
4829 	tlabel[14].text_is_1byte = true;
4830 	tgcd[14].gd.label = &tlabel[14];
4831 	tgcd[14].gd.pos.x = 5; tgcd[14].gd.pos.y = 57+4;
4832 	tgcd[14].gd.flags = gg_enabled|gg_visible;
4833 	tgcd[14].gd.cid = CID_IsExtended;
4834 	tgcd[14].creator = GCheckBoxCreate;
4835 	tgcd[14].gd.popup_msg = _("Is this an extended shape (like a tall parenthesis)?\nExtended shapes need special attention for vertical\nsuperscript placement.");
4836 	thvarray[20] = &tgcd[14];
4837 	thvarray[21] = thvarray[22] = GCD_ColSpan; thvarray[23] = GCD_Glue; thvarray[24] = NULL;
4838 
4839 	tlabel[15].text = (unichar_t *) _("Math Kerning");	/* Graphical */
4840 	tlabel[15].text_is_1byte = true;
4841 	tgcd[15].gd.label = &tlabel[15];
4842 	tgcd[15].gd.flags = gg_enabled|gg_visible;
4843 	tgcd[15].gd.handle_controlevent = CI_SubSuperPositionings;
4844 	tgcd[15].creator = GButtonCreate;
4845 	tgcd[15].gd.popup_msg = _("Brings up a dialog which gives fine control over\nhorizontal positioning of subscripts and superscripts\ndepending on their vertical positioning.");
4846 	tbarray[0] = GCD_Glue; tbarray[1] = &tgcd[15]; tbarray[2] = GCD_Glue; tbarray[3] = NULL;
4847 
4848 	tbox[2].gd.flags = gg_enabled|gg_visible;
4849 	tbox[2].gd.u.boxelements = tbarray;
4850 	tbox[2].creator = GHBoxCreate;
4851 
4852 	thvarray[25] = &tbox[2];
4853 	thvarray[26] = thvarray[27] = thvarray[28] = GCD_ColSpan; thvarray[29] = NULL;
4854 
4855 	thvarray[30] = thvarray[31] = thvarray[32] = thvarray[33] = GCD_Glue; thvarray[34] = NULL;
4856 	thvarray[35] = NULL;
4857 
4858 	tbox[0].gd.flags = gg_enabled|gg_visible;
4859 	tbox[0].gd.u.boxelements = thvarray;
4860 	tbox[0].creator = GHVBoxCreate;
4861 
4862 	memset(&lcgcd,0,sizeof(lcgcd));
4863 	memset(&lcbox,0,sizeof(lcbox));
4864 	memset(&lclabel,0,sizeof(lclabel));
4865 
4866 	lclabel[0].text = (unichar_t *) _("Default Ligature Caret Count");
4867 	lclabel[0].text_is_1byte = true;
4868 	lclabel[0].text_in_resource = true;
4869 	lcgcd[0].gd.cid = CID_DefLCCount;
4870 	lcgcd[0].gd.label = &lclabel[0];
4871 	lcgcd[0].gd.flags = gg_enabled|gg_visible;
4872 	lcgcd[0].gd.popup_msg = _("Ligature caret locations are used by a text editor\nwhen it needs to draw a text edit caret inside a\nligature. This means there should be a caret between\neach ligature component so if there are n components\nthere should be n-1 caret locations.\n  You may adjust the caret locations themselves in the\noutline glyph view (drag them from to origin to the\nappropriate place)." );
4873 	lcgcd[0].gd.handle_controlevent = CI_DefLCChange;
4874 	lcgcd[0].creator = GCheckBoxCreate;
4875 	lchvarray[0][0] = &lcgcd[0];
4876 	lchvarray[0][1] = lchvarray[0][2] = GCD_Glue; lchvarray[0][3] = NULL;
4877 
4878 	lclabel[1].text = (unichar_t *) _("Ligature Caret Count:");
4879 	lclabel[1].text_is_1byte = true;
4880 	lclabel[1].text_in_resource = true;
4881 	lcgcd[1].gd.label = &lclabel[1];
4882 	lcgcd[1].gd.flags = gg_enabled|gg_visible;
4883 	lcgcd[1].gd.cid = CID_LCCountLab;
4884 	lcgcd[1].gd.popup_msg = _("Ligature caret locations are used by a text editor\nwhen it needs to draw a text edit caret inside a\nligature. This means there should be a caret between\neach ligature component so if there are n components\nthere should be n-1 caret locations.\n  You may adjust the caret locations themselves in the\noutline glyph view (drag them from to origin to the\nappropriate place)." );
4885 	lcgcd[1].creator = GLabelCreate;
4886 	lchvarray[1][0] = &lcgcd[1];
4887 
4888 	lcgcd[2].gd.pos.width = 50;
4889 	lcgcd[2].gd.flags = gg_enabled|gg_visible;
4890 	lcgcd[2].gd.cid = CID_LCCount;
4891 	lcgcd[2].gd.popup_msg = _("Ligature caret locations are used by a text editor\nwhen it needs to draw a text edit caret inside a\nligature. This means there should be a caret between\neach ligature component so if there are n components\nthere should be n-1 caret locations.\n  You may adjust the caret locations themselves in the\noutline glyph view (drag them from to origin to the\nappropriate place)." );
4892 	lcgcd[2].creator = GNumericFieldCreate;
4893 	lchvarray[1][1] = &lcgcd[2]; lchvarray[1][2] = GCD_Glue; lchvarray[1][3] = NULL;
4894 
4895 	lchvarray[2][0] = lchvarray[2][1] = lchvarray[2][2] = GCD_Glue;
4896 	lchvarray[2][3] = lchvarray[3][0] = NULL;
4897 
4898 	lcbox[0].gd.flags = gg_enabled|gg_visible;
4899 	lcbox[0].gd.u.boxelements = lchvarray[0];
4900 	lcbox[0].creator = GHVBoxCreate;
4901 
4902 	memset(&vargcd,0,sizeof(vargcd));
4903 	memset(&varbox,0,sizeof(varbox));
4904 	memset(&varlabel,0,sizeof(varlabel));
4905 
4906 	for ( i=0; i<2; ++i ) {
4907 	    varlabel[i][0].text = (unichar_t *) _("Variant Glyphs:");
4908 	    varlabel[i][0].text_is_1byte = true;
4909 	    vargcd[i][0].gd.label = &varlabel[i][0];
4910 	    vargcd[i][0].gd.pos.x = 5; vargcd[i][0].gd.pos.y = 57+4;
4911 	    vargcd[i][0].gd.flags = gg_enabled|gg_visible;
4912 	    vargcd[i][0].creator = GLabelCreate;
4913 	    vargcd[i][0].gd.popup_msg = _("A list of the names of pre defined glyphs which represent\nbigger versions of the current glyph.");
4914 	    varhvarray[i][0][0] = &vargcd[i][0];
4915 
4916 	    vargcd[i][1].gd.flags = gg_enabled|gg_visible;
4917 	    vargcd[i][1].gd.cid = CID_VariantList+i*100;
4918 	    vargcd[i][1].creator = GTextCompletionCreate;
4919 	    vargcd[i][1].gd.popup_msg = vargcd[i][0].gd.popup_msg;
4920 	    varhvarray[i][0][1] = &vargcd[i][1]; varhvarray[i][0][2] = GCD_ColSpan; varhvarray[i][0][3] = NULL;
4921 
4922 	    varlabel[i][2].text = (unichar_t *) _("Glyph Extension Components");
4923 	    varlabel[i][2].text_is_1byte = true;
4924 	    vargcd[i][2].gd.label = &varlabel[i][2];
4925 	    vargcd[i][2].gd.flags = gg_enabled|gg_visible;
4926 	    vargcd[i][2].creator = GLabelCreate;
4927 	    vargcd[i][2].gd.popup_msg = _("A really big version of this glyph may be made up of the\nfollowing component glyphs. They will be stacked either\nhorizontally or vertically. Glyphs marked as Extenders may\nbe removed or repeated (to make shorter or longer versions).\nThe StartLength is the length of the flat section at the\nstart of the glyph which may be overlapped with the previous\nglyph, while the EndLength is the similar region at the end\nof the glyph. The FullLength is the full length of the glyph." );
4928 	    varhvarray[i][1][0] = &vargcd[i][2];
4929 	    varhvarray[i][1][1] = varhvarray[i][1][2] = GCD_ColSpan; varhvarray[i][1][3] = NULL;
4930 
4931 /* GT: "Cor" is an abbreviation for correction */
4932 	    varlabel[i][3].text = (unichar_t *) _("Italic Cor:");
4933 	    varlabel[i][3].text_is_1byte = true;
4934 	    vargcd[i][3].gd.label = &varlabel[i][3];
4935 	    vargcd[i][3].gd.flags = gg_enabled|gg_visible;
4936 	    vargcd[i][3].creator = GLabelCreate;
4937 	    vargcd[i][3].gd.popup_msg = _("The italic correction of the composed glyph. Should be independent of glyph size");
4938 	    varhvarray[i][2][0] = &vargcd[i][3];
4939 
4940 	    vargcd[i][4].gd.flags = gg_enabled|gg_visible;
4941 	    vargcd[i][4].gd.pos.width = 60;
4942 	    vargcd[i][4].gd.cid = CID_ExtItalicCor+i*100;
4943 	    vargcd[i][4].creator = GTextFieldCreate;
4944 	    vargcd[i][4].gd.popup_msg = vargcd[i][3].gd.popup_msg;
4945 	    varhvarray[i][2][1] = &vargcd[i][4];
4946 
4947 	    vargcd[i][5].gd.flags = gg_enabled|gg_visible;
4948 	    vargcd[i][5].gd.pos.width = 60;
4949 	    vargcd[i][5].gd.cid = CID_ExtItalicDev+i*100;
4950 	    vargcd[i][5].creator = GTextFieldCreate;
4951 	    vargcd[i][5].gd.popup_msg = vargcd[i][3].gd.popup_msg;
4952 	    varhvarray[i][2][2] = &vargcd[i][5];
4953 	    varhvarray[i][2][3] = NULL;
4954 
4955 	    vargcd[i][6].gd.flags = gg_enabled|gg_visible;
4956 	    vargcd[i][6].gd.u.matrix = &mi_extensionpart;
4957 	    vargcd[i][6].gd.cid = CID_ExtensionList+i*100;
4958 	    vargcd[i][6].creator = GMatrixEditCreate;
4959 	    varhvarray[i][3][0] = &vargcd[i][6];
4960 	    varhvarray[i][3][1] = varhvarray[i][3][2] = GCD_ColSpan; varhvarray[i][3][3] = NULL;
4961 
4962 	    varhvarray[i][4][0] = NULL;
4963 
4964 	    varbox[i][0].gd.flags = gg_enabled|gg_visible;
4965 	    varbox[i][0].gd.u.boxelements = varhvarray[i][0];
4966 	    varbox[i][0].creator = GHVBoxCreate;
4967 	}
4968 
4969 	memset(&tilegcd,0,sizeof(tilegcd));
4970 	memset(&tilebox,0,sizeof(tilebox));
4971 	memset(&tilelabel,0,sizeof(tilelabel));
4972 
4973 	i=0;
4974 	tilelabel[i].text = (unichar_t *) _(
4975 	    "If this glyph is used as a pattern to tile\n"
4976 	    "some other glyph then it is useful to specify\n"
4977 	    "the amount of whitespace surrounding the tile.\n"
4978 	    "Either specify a margin to extend the bounding\n"
4979 	    "box of the contents, or specify the bounds\n"
4980 	    "explicitly.");
4981 	tilelabel[i].text_is_1byte = true;
4982 	tilelabel[i].text_in_resource = true;
4983 	tilegcd[i].gd.label = &tilelabel[i];
4984 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
4985 	tilegcd[i++].creator = GLabelCreate;
4986 	tlvarray[0] = &tilegcd[i-1];
4987 
4988 	tilelabel[i].text = (unichar_t *) _("Tile Margin:");
4989 	tilelabel[i].text_is_1byte = true;
4990 	tilelabel[i].text_in_resource = true;
4991 	tilegcd[i].gd.label = &tilelabel[i];
4992 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
4993 	tilegcd[i].gd.cid = CID_IsTileMargin;
4994 	tilegcd[i++].creator = GRadioCreate;
4995 	tlharray[0] = &tilegcd[i-1];
4996 
4997 	tilegcd[i].gd.pos.width = 60;
4998 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
4999 	tilegcd[i].gd.cid = CID_TileMargin;
5000 	tilegcd[i].gd.handle_controlevent = CI_TileMarginChange;
5001 	tilegcd[i++].creator = GTextFieldCreate;
5002 	tlharray[1] = &tilegcd[i-1]; tlharray[2] = GCD_Glue; tlharray[3] = NULL;
5003 
5004 	tilebox[2].gd.flags = gg_enabled|gg_visible;
5005 	tilebox[2].gd.u.boxelements = tlharray;
5006 	tilebox[2].creator = GHBoxCreate;
5007 	tlvarray[1] = &tilebox[2];
5008 
5009 	tilelabel[i].text = (unichar_t *) _("Tile Bounding Box:");
5010 	tilelabel[i].text_is_1byte = true;
5011 	tilelabel[i].text_in_resource = true;
5012 	tilegcd[i].gd.label = &tilelabel[i];
5013 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
5014 	tilegcd[i].gd.cid = CID_IsTileBBox;
5015 	tilegcd[i++].creator = GRadioCreate;
5016 	tlvarray[2] = &tilegcd[i-1];
5017 
5018 	tlhvarray[0][0] = GCD_Glue;
5019 
5020 	tilelabel[i].text = (unichar_t *) _("  X");
5021 	tilelabel[i].text_is_1byte = true;
5022 	tilegcd[i].gd.label = &tilelabel[i];
5023 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
5024 	tilegcd[i++].creator = GLabelCreate;
5025 	tlhvarray[0][1] = &tilegcd[i-1];
5026 
5027 	tilelabel[i].text = (unichar_t *) _("  Y");
5028 	tilelabel[i].text_is_1byte = true;
5029 	tilegcd[i].gd.label = &tilelabel[i];
5030 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
5031 	tilegcd[i++].creator = GLabelCreate;
5032 	tlhvarray[0][2] = &tilegcd[i-1]; tlhvarray[0][3] = GCD_Glue; tlhvarray[0][4] = NULL;
5033 
5034 	tilelabel[i].text = (unichar_t *) _("Min");
5035 	tilelabel[i].text_is_1byte = true;
5036 	tilegcd[i].gd.label = &tilelabel[i];
5037 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
5038 	tilegcd[i++].creator = GLabelCreate;
5039 	tlhvarray[1][0] = &tilegcd[i-1];
5040 
5041 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
5042 	tilegcd[i].gd.cid = CID_TileBBoxMinX;
5043 	tilegcd[i++].creator = GTextFieldCreate;
5044 	tlhvarray[1][1] = &tilegcd[i-1];
5045 
5046 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
5047 	tilegcd[i].gd.cid = CID_TileBBoxMinY;
5048 	tilegcd[i++].creator = GTextFieldCreate;
5049 	tlhvarray[1][2] = &tilegcd[i-1]; tlhvarray[1][3] = GCD_Glue; tlhvarray[1][4] = NULL;
5050 
5051 	tilelabel[i].text = (unichar_t *) _("Max");
5052 	tilelabel[i].text_is_1byte = true;
5053 	tilegcd[i].gd.label = &tilelabel[i];
5054 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
5055 	tilegcd[i++].creator = GLabelCreate;
5056 	tlhvarray[2][0] = &tilegcd[i-1];
5057 
5058 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
5059 	tilegcd[i].gd.cid = CID_TileBBoxMaxX;
5060 	tilegcd[i++].creator = GTextFieldCreate;
5061 	tlhvarray[2][1] = &tilegcd[i-1];
5062 
5063 	tilegcd[i].gd.flags = gg_enabled|gg_visible;
5064 	tilegcd[i].gd.cid = CID_TileBBoxMaxY;
5065 	tilegcd[i++].creator = GTextFieldCreate;
5066 	tlhvarray[2][2] = &tilegcd[i-1]; tlhvarray[2][3] = GCD_Glue; tlhvarray[2][4] = NULL;
5067 	tlhvarray[3][0] = NULL;
5068 
5069 	tilebox[3].gd.flags = gg_enabled|gg_visible;
5070 	tilebox[3].gd.u.boxelements = tlhvarray[0];
5071 	tilebox[3].creator = GHVBoxCreate;
5072 	tlvarray[3] = &tilebox[3]; tlvarray[4] = GCD_Glue; tlvarray[5] = NULL;
5073 
5074 	tilebox[0].gd.flags = gg_enabled|gg_visible;
5075 	tilebox[0].gd.u.boxelements = tlvarray;
5076 	tilebox[0].creator = GVBoxCreate;
5077 
5078 	memset(&mgcd,0,sizeof(mgcd));
5079 	memset(&mbox,0,sizeof(mbox));
5080 	memset(&mlabel,0,sizeof(mlabel));
5081 	memset(&aspects,'\0',sizeof(aspects));
5082 
5083 	i = 0;
5084 	aspects[i].text = (unichar_t *) _("Unicode");
5085 	aspects[i].text_is_1byte = true;
5086 	aspects[i].selected = true;
5087 	aspects[i++].gcd = ubox;
5088 
5089 	aspects[i].text = (unichar_t *) _("Comment");
5090 	aspects[i].text_is_1byte = true;
5091 	aspects[i++].gcd = cbox;
5092 
5093 	aspects[i].text = (unichar_t *) _("Positionings");
5094 	aspects[i].text_is_1byte = true;
5095 	aspects[i++].gcd = pstbox[pst_position-1];
5096 
5097 	aspects[i].text = (unichar_t *) _("Pairwise Pos");
5098 	aspects[i].text_is_1byte = true;
5099 	aspects[i++].gcd = pstbox[pst_pair-1];
5100 
5101 	aspects[i].text = (unichar_t *) _("Substitutions");
5102 	aspects[i].text_is_1byte = true;
5103 	aspects[i++].gcd = psgcd[2];
5104 
5105 	aspects[i].text = (unichar_t *) _("Alt Subs");
5106 	aspects[i].text_is_1byte = true;
5107 	aspects[i++].gcd = psgcd[3];
5108 
5109 	aspects[i].text = (unichar_t *) _("Mult Subs");
5110 	aspects[i].text_is_1byte = true;
5111 	aspects[i++].gcd = psgcd[4];
5112 
5113 	aspects[i].text = (unichar_t *) _("Ligatures");
5114 	aspects[i].text_is_1byte = true;
5115 	aspects[i++].gcd = psgcd[5];
5116 
5117 	ci->lc_aspect = i;
5118 	aspects[i].text = (unichar_t *) _("Lig. Carets");
5119 	aspects[i].text_is_1byte = true;
5120 	aspects[i].nesting = 1;
5121 	aspects[i++].gcd = lcbox;
5122 
5123 	aspects[i].text = (unichar_t *) _("Components");
5124 	aspects[i].text_is_1byte = true;
5125 	aspects[i++].gcd = cobox;
5126 
5127 	aspects[i].text = (unichar_t *) _("Counters");
5128 	aspects[i].text_is_1byte = true;
5129 	aspects[i++].gcd = pstbox[6];
5130 
5131 	aspects[i].text = (unichar_t *) U_("ΤεΧ & Math");	/* TeX */
5132 	aspects[i].text_is_1byte = true;
5133 	aspects[i++].gcd = tbox;
5134 
5135 	ci->vert_aspect = i;
5136 /* GT: "Vert." is an abbreviation for Vertical */
5137 	aspects[i].text = (unichar_t *) U_("Vert. Variants");
5138 	aspects[i].text_is_1byte = true;
5139 	aspects[i].nesting = 1;
5140 	aspects[i++].gcd = varbox[0];
5141 
5142 /* GT: "Horiz." is an abbreviation for Horizontal */
5143 	aspects[i].text = (unichar_t *) U_("Horiz. Variants");
5144 	aspects[i].text_is_1byte = true;
5145 	aspects[i].nesting = 1;
5146 	aspects[i++].gcd = varbox[1];
5147 
5148 	if ( sc->parent->multilayer ) {
5149 	    aspects[i].text = (unichar_t *) U_("Tile Size");
5150 	    aspects[i].text_is_1byte = true;
5151 	    aspects[i++].gcd = tilebox;
5152 	}
5153 
5154 	if ( last_gi_aspect<i )
5155 	    aspects[last_gi_aspect].selected = true;
5156 
5157 	mgcd[0].gd.pos.x = 4; mgcd[0].gd.pos.y = 6;
5158 	mgcd[0].gd.u.tabs = aspects;
5159 	mgcd[0].gd.flags = gg_visible | gg_enabled | gg_tabset_vert;
5160 	mgcd[0].gd.cid = CID_Tabs;
5161 	mgcd[0].gd.handle_controlevent = CI_AspectChange;
5162 	mgcd[0].creator = GTabSetCreate;
5163 	mvarray[0] = &mgcd[0]; mvarray[1] = NULL;
5164 
5165 	mgcd[1].gd.pos.x = 40; mgcd[1].gd.pos.y = mgcd[0].gd.pos.y+mgcd[0].gd.pos.height+3;
5166 	mgcd[1].gd.flags = gg_visible | gg_enabled ;
5167 	mlabel[1].text = (unichar_t *) _("< _Prev");
5168 	mlabel[1].text_is_1byte = true;
5169 	mlabel[1].text_in_resource = true;
5170 	mgcd[1].gd.mnemonic = 'P';
5171 	mgcd[1].gd.label = &mlabel[1];
5172 	mgcd[1].gd.handle_controlevent = CI_NextPrev;
5173 	mgcd[1].gd.cid = -1;
5174 	mharray1[0] = GCD_Glue; mharray1[1] = &mgcd[1]; mharray1[2] = GCD_Glue;
5175 	mgcd[1].creator = GButtonCreate;
5176 
5177 	mgcd[2].gd.pos.x = -40; mgcd[2].gd.pos.y = mgcd[1].gd.pos.y;
5178 	mgcd[2].gd.flags = gg_visible | gg_enabled ;
5179 	mlabel[2].text = (unichar_t *) _("_Next >");
5180 	mlabel[2].text_is_1byte = true;
5181 	mlabel[2].text_in_resource = true;
5182 	mgcd[2].gd.label = &mlabel[2];
5183 	mgcd[2].gd.mnemonic = 'N';
5184 	mgcd[2].gd.handle_controlevent = CI_NextPrev;
5185 	mgcd[2].gd.cid = 1;
5186 	mharray1[3] = GCD_Glue; mharray1[4] = &mgcd[2]; mharray1[5] = GCD_Glue; mharray1[6] = NULL;
5187 	mgcd[2].creator = GButtonCreate;
5188 
5189 	mbox[2].gd.flags = gg_enabled|gg_visible;
5190 	mbox[2].gd.u.boxelements = mharray1;
5191 	mbox[2].creator = GHBoxCreate;
5192 	mvarray[2] = &mbox[2]; mvarray[3] = NULL;
5193 
5194 	mgcd[3].gd.pos.x = 25-3; mgcd[3].gd.pos.y = CI_Height-31-3;
5195 	mgcd[3].gd.flags = gg_visible | gg_enabled | gg_but_default;
5196 	mlabel[3].text = (unichar_t *) _("_OK");
5197 	mlabel[3].text_is_1byte = true;
5198 	mlabel[3].text_in_resource = true;
5199 	mgcd[3].gd.mnemonic = 'O';
5200 	mgcd[3].gd.label = &mlabel[3];
5201 	mgcd[3].gd.handle_controlevent = CI_OK;
5202 	mharray2[0] = GCD_Glue; mharray2[1] = &mgcd[3]; mharray2[2] = GCD_Glue; mharray2[3] = GCD_Glue;
5203 	mgcd[3].creator = GButtonCreate;
5204 
5205 	mgcd[4].gd.pos.x = -25; mgcd[4].gd.pos.y = mgcd[3].gd.pos.y+3;
5206 	mgcd[4].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
5207 	mlabel[4].text = (unichar_t *) _("_Cancel");
5208 	mlabel[4].text_is_1byte = true;
5209 	mlabel[4].text_in_resource = true;
5210 	mgcd[4].gd.label = &mlabel[4];
5211 	mgcd[4].gd.handle_controlevent = CI_Cancel;
5212 	mgcd[4].gd.cid = CID_Cancel;
5213 	mharray2[4] = GCD_Glue; mharray2[5] = &mgcd[4]; mharray2[6] = GCD_Glue; mharray2[7] = NULL;
5214 	mgcd[4].creator = GButtonCreate;
5215 
5216 	mbox[3].gd.flags = gg_enabled|gg_visible;
5217 	mbox[3].gd.u.boxelements = mharray2;
5218 	mbox[3].creator = GHBoxCreate;
5219 	mvarray[4] = &mbox[3]; mvarray[5] = NULL;
5220 	mvarray[6] = NULL;
5221 
5222 	mbox[0].gd.pos.x = mbox[0].gd.pos.y = 2;
5223 	mbox[0].gd.flags = gg_enabled|gg_visible;
5224 	mbox[0].gd.u.boxelements = mvarray;
5225 	mbox[0].creator = GHVGroupCreate;
5226 
5227 	GGadgetsCreate(ci->gw,mbox);
5228 
5229 	GHVBoxSetExpandableRow(mbox[0].ret,0);
5230 	GHVBoxSetExpandableCol(mbox[2].ret,gb_expandgluesame);
5231 	GHVBoxSetExpandableCol(mbox[3].ret,gb_expandgluesame);
5232 
5233 	GHVBoxSetExpandableRow(ubox[0].ret,gb_expandglue);
5234 	GHVBoxSetExpandableCol(ubox[0].ret,1);
5235 	GHVBoxSetExpandableCol(ubox[2].ret,gb_expandgluesame);
5236 
5237 	GHVBoxSetExpandableRow(cbox[0].ret,1);
5238 	GHVBoxSetExpandableCol(cbox[2].ret,gb_expandglue);
5239 
5240 	for ( i=0; i<6; ++i ) {
5241 	    GGadget *g = GWidgetGetControl(ci->gw,CID_List+i*100);
5242 	    GMatrixEditSetNewText(g, newstrings[i]);
5243 	    if ( i==pst_substitution-1 || i==pst_pair-1 )
5244 		GMatrixEditSetColumnCompletion(g,1,CI_GlyphNameCompletion);
5245 	    else if ( i==pst_alternate-1 || i==pst_multiple-1 ||
5246 		    i==pst_ligature-1)
5247 		GMatrixEditSetColumnCompletion(g,1,CI_GlyphListCompletion);
5248 	}
5249 	GHVBoxSetExpandableRow(pstbox[pst_pair-1][0].ret,0);
5250 	for ( i=0; i<6; ++i )
5251 	    GMatrixEditSetMouseMoveReporter(psgcd[i][0].ret,CI_SubsPopupPrepare);
5252 	GMatrixEditSetMouseMoveReporter(psgcd[pst_pair-1][0].ret,CI_KerningPopupPrepare);
5253 	for ( i=6; i<7; ++i ) {
5254 	    GHVBoxSetExpandableRow(pstbox[i][0].ret,0);
5255 	    GHVBoxSetExpandableCol(pstbox[i][2].ret,gb_expandgluesame);
5256 	}
5257 
5258 	GHVBoxSetExpandableRow(cobox[0].ret,gb_expandglue);
5259 	GHVBoxSetExpandableRow(tbox[0].ret,gb_expandglue);
5260 	GHVBoxSetExpandableCol(tbox[0].ret,gb_expandglue);
5261 	GHVBoxSetPadding(tbox[0].ret,6,4);
5262 	GHVBoxSetExpandableCol(tbox[2].ret,gb_expandglue);
5263 
5264 	GHVBoxSetExpandableRow(lcbox[0].ret,gb_expandglue);
5265 	GHVBoxSetExpandableCol(lcbox[0].ret,gb_expandglue);
5266 
5267 	GHVBoxSetExpandableRow(varbox[0][0].ret,3);
5268 	GHVBoxSetExpandableRow(varbox[1][0].ret,3);
5269 
5270 	if ( sc->parent->multilayer ) {
5271 	    GHVBoxSetExpandableRow(tilebox[0].ret,gb_expandglue);
5272 	    GHVBoxSetExpandableCol(tilebox[2].ret,gb_expandglue);
5273 	    GHVBoxSetExpandableCol(tilebox[3].ret,gb_expandglue);
5274 	}
5275 
5276 	GHVBoxFitWindow(mbox[0].ret);
5277 
5278 	if ( font==NULL ) {
5279 	    memset(&rq,0,sizeof(rq));
5280 	    rq.utf8_family_name = MONO_UI_FAMILIES;
5281 	    rq.point_size = 12;
5282 	    rq.weight = 400;
5283 	    font = GDrawInstanciateFont(ci->gw,&rq);
5284 	    font = GResourceFindFont("GlyphInfo.Font",font);
5285 	}
5286 	for ( i=0; i<5; ++i )
5287 	    GGadgetSetFont(psgcd[i][0].ret,font);
5288 	for ( i=0; i<2; ++i ) {
5289 	    GCompletionFieldSetCompletion(vargcd[i][1].ret,CI_GlyphNameCompletion);
5290 	    GCompletionFieldSetCompletionMode(vargcd[i][1].ret,true);
5291 	    GMatrixEditSetColumnCompletion(vargcd[i][6].ret,0,CI_GlyphNameCompletion);
5292 	    GMatrixEditSetMouseMoveReporter(vargcd[i][6].ret,CI_ConstructionPopupPrepare);
5293 	}
5294 
5295     CIFillup(ci);
5296 
5297     GWidgetHidePalettes();
5298     GDrawSetVisible(ci->gw,true);
5299 }
5300 
CharInfoDestroy(CharInfo * ci)5301 void CharInfoDestroy(CharInfo *ci) {
5302     GDrawDestroyWindow(ci->gw);
5303 }
5304 
5305 struct sel_dlg {
5306     int done;
5307     int ok;
5308     FontView *fv;
5309 };
5310 
FVParseSelectByPST(FontView * fv,struct lookup_subtable * sub,int search_type)5311 int FVParseSelectByPST(FontView *fv,struct lookup_subtable *sub,
5312 	int search_type) {
5313     int first;
5314 
5315     first = FVBParseSelectByPST((FontViewBase *) fv,sub,search_type);
5316 
5317     if ( first!=-1 )
5318 	FVScrollToChar(fv,first);
5319     else if ( !no_windowing_ui )
5320 	ff_post_notice(_("Select By ATT..."),_("No glyphs matched"));
5321     if (  !no_windowing_ui )
5322 	GDrawRequestExpose(fv->v,NULL,false);
5323 return( true );
5324 }
5325 
SelectStuff(struct sel_dlg * sld,GWindow gw)5326 static int SelectStuff(struct sel_dlg *sld,GWindow gw) {
5327     struct lookup_subtable *sub = GGadgetGetListItemSelected(GWidgetGetControl(gw,CID_PST))->userdata;
5328     int search_type = GGadgetIsChecked(GWidgetGetControl(gw,CID_SelectResults)) ? 1 :
5329 	    GGadgetIsChecked(GWidgetGetControl(gw,CID_MergeResults)) ? 2 :
5330 		3;
5331 return( FVParseSelectByPST(sld->fv, sub, search_type));
5332 }
5333 
selpst_e_h(GWindow gw,GEvent * event)5334 static int selpst_e_h(GWindow gw, GEvent *event) {
5335     struct sel_dlg *sld = GDrawGetUserData(gw);
5336 
5337     if ( event->type==et_close ) {
5338 	sld->done = true;
5339 	sld->ok = false;
5340     } else if ( event->type==et_char ) {
5341 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
5342 	    help("ui/dialogs/selectbyatt.html", NULL);
5343 return( true );
5344 	}
5345 return( false );
5346     } else if ( event->type==et_controlevent && event->u.control.subtype == et_buttonactivate ) {
5347 	sld->ok = GGadgetGetCid(event->u.control.g);
5348 	if ( !sld->ok || SelectStuff(sld,gw))
5349 	    sld->done = true;
5350     }
5351 return( true );
5352 }
5353 
FVSelectByPST(FontView * fv)5354 void FVSelectByPST(FontView *fv) {
5355     struct sel_dlg sld;
5356     GWindow gw;
5357     GRect pos;
5358     GWindowAttrs wattrs;
5359     GGadgetCreateData gcd[14];
5360     GTextInfo label[14];
5361     GGadgetCreateData *varray[20], *harray[8];
5362     int i,j,isgpos, cnt;
5363     OTLookup *otl;
5364     struct lookup_subtable *sub;
5365     GTextInfo *ti;
5366     SplineFont *sf = fv->b.sf;
5367 
5368     if ( sf->cidmaster ) sf = sf->cidmaster;
5369     ti = NULL;
5370     for ( j=0; j<2; ++j ) {
5371 	cnt = 0;
5372 	for ( isgpos=0; isgpos<2; ++isgpos ) {
5373 	    for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
5374 		if ( otl->lookup_type== gsub_single ||
5375 			otl->lookup_type== gsub_multiple ||
5376 			otl->lookup_type== gsub_alternate ||
5377 			otl->lookup_type== gsub_ligature ||
5378 			otl->lookup_type== gpos_single ||
5379 			otl->lookup_type== gpos_pair ||
5380 			otl->lookup_type== gpos_cursive ||
5381 			otl->lookup_type== gpos_mark2base ||
5382 			otl->lookup_type== gpos_mark2ligature ||
5383 			otl->lookup_type== gpos_mark2mark )
5384 		    for ( sub=otl->subtables; sub!=NULL; sub=sub->next )
5385 			if ( sub->kc==NULL ) {
5386 			    if ( ti!=NULL ) {
5387 				ti[cnt].text = (unichar_t *) copy(sub->subtable_name);
5388 			        ti[cnt].text_is_1byte = true;
5389 			        ti[cnt].userdata = sub;
5390 			        ti[cnt].selected = cnt==0;
5391 			    }
5392 			    ++cnt;
5393 			}
5394 	    }
5395 	}
5396 	if ( cnt==0 ) {
5397 	    ff_post_notice(_("No Lookups"), _("No applicable lookup subtables"));
5398 return;
5399 	}
5400 	if ( ti==NULL )
5401 	    ti = calloc(cnt+1,sizeof(GTextInfo));
5402     }
5403 
5404     CharInfoInit();
5405 
5406     memset(&sld,0,sizeof(sld));
5407     sld.fv = fv;
5408 	memset(&wattrs,0,sizeof(wattrs));
5409 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
5410 	wattrs.event_masks = ~(1<<et_charup);
5411 	wattrs.restrict_input_to_me = 1;
5412 	wattrs.undercursor = 1;
5413 	wattrs.cursor = ct_pointer;
5414 	wattrs.utf8_window_title =  _("Select By Lookup Subtable");
5415 	wattrs.is_dlg = true;
5416 	pos.x = pos.y = 0;
5417 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,160));
5418 	pos.height = GDrawPointsToPixels(NULL,204);
5419 	gw = GDrawCreateTopWindow(NULL,&pos,selpst_e_h,&sld,&wattrs);
5420 
5421 	memset(&gcd,0,sizeof(gcd));
5422 	memset(&label,0,sizeof(label));
5423 
5424 	i=j=0;
5425 
5426 	label[i].text = (unichar_t *) _("Select Glyphs in lookup subtable");
5427 	label[i].text_is_1byte = true;
5428 	gcd[i].gd.label = &label[i];
5429 	gcd[i].gd.flags = gg_enabled|gg_visible;
5430 	gcd[i++].creator = GLabelCreate;
5431 	varray[j++] = &gcd[i-1]; varray[j++] = NULL;
5432 
5433 	gcd[i].gd.label = &ti[0];
5434 	gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = 5+4;
5435 	gcd[i].gd.flags = gg_enabled|gg_visible/*|gg_list_exactlyone*/;
5436 	gcd[i].gd.u.list = ti;
5437 	gcd[i].gd.cid = CID_PST;
5438 	gcd[i++].creator = GListButtonCreate;
5439 	varray[j++] = &gcd[i-1]; varray[j++] = NULL;
5440 	varray[j++] = GCD_Glue; varray[j++] = NULL;
5441 
5442 	label[i].text = (unichar_t *) _("Select Results");
5443 	label[i].text_is_1byte = true;
5444 	gcd[i].gd.label = &label[i];
5445 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+26;
5446 	gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
5447 	gcd[i].gd.popup_msg = _("Set the selection of the font view to the glyphs\nfound by this search");
5448 	gcd[i].gd.cid = CID_SelectResults;
5449 	gcd[i++].creator = GRadioCreate;
5450 	varray[j++] = &gcd[i-1]; varray[j++] = NULL;
5451 
5452 	label[i].text = (unichar_t *) _("Merge Results");
5453 	label[i].text_is_1byte = true;
5454 	gcd[i].gd.label = &label[i];
5455 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+15;
5456 	gcd[i].gd.flags = gg_enabled|gg_visible;
5457 	gcd[i].gd.popup_msg = _("Expand the selection of the font view to include\nall the glyphs found by this search");
5458 	gcd[i].gd.cid = CID_MergeResults;
5459 	gcd[i++].creator = GRadioCreate;
5460 	varray[j++] = &gcd[i-1]; varray[j++] = NULL;
5461 
5462 	label[i].text = (unichar_t *) _("Restrict Selection");
5463 	label[i].text_is_1byte = true;
5464 	gcd[i].gd.label = &label[i];
5465 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+15;
5466 	gcd[i].gd.flags = gg_enabled|gg_visible;
5467 	gcd[i].gd.popup_msg = _("Only search the selected glyphs, and unselect\nany characters which do not match this search");
5468 	gcd[i].gd.cid = CID_RestrictSelection;
5469 	gcd[i++].creator = GRadioCreate;
5470 	varray[j++] = &gcd[i-1]; varray[j++] = NULL;
5471 	varray[j++] = GCD_Glue; varray[j++] = NULL;
5472 
5473 	gcd[i].gd.pos.x = 15-3; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+22;
5474 	gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
5475 	label[i].text = (unichar_t *) _("_OK");
5476 	label[i].text_is_1byte = true;
5477 	label[i].text_in_resource = true;
5478 	gcd[i].gd.mnemonic = 'O';
5479 	gcd[i].gd.label = &label[i];
5480 	gcd[i].gd.cid = true;
5481 	gcd[i++].creator = GButtonCreate;
5482 	harray[0] = GCD_Glue; harray[1] = &gcd[i-1]; harray[2] = GCD_Glue;
5483 
5484 	gcd[i].gd.pos.x = -15; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+3;
5485 	gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
5486 	label[i].text = (unichar_t *) _("_Cancel");
5487 	label[i].text_is_1byte = true;
5488 	label[i].text_in_resource = true;
5489 	gcd[i].gd.label = &label[i];
5490 	gcd[i].gd.mnemonic = 'C';
5491 	gcd[i].gd.cid = false;
5492 	gcd[i++].creator = GButtonCreate;
5493 	harray[3] = GCD_Glue; harray[4] = &gcd[i-1]; harray[5] = GCD_Glue;
5494 	harray[6] = NULL;
5495 
5496 	gcd[i].gd.flags = gg_enabled|gg_visible;
5497 	gcd[i].gd.u.boxelements = harray;
5498 	gcd[i].creator = GHBoxCreate;
5499 	varray[j++] = &gcd[i++]; varray[j++] = NULL; varray[j++] = NULL;
5500 
5501 	gcd[i].gd.pos.x = gcd[i].gd.pos.y = 2;
5502 	gcd[i].gd.flags = gg_enabled|gg_visible;
5503 	gcd[i].gd.u.boxelements = varray;
5504 	gcd[i].creator = GHVGroupCreate;
5505 
5506 	GGadgetsCreate(gw,gcd+i);
5507 	GTextInfoListFree(ti);
5508 	GHVBoxSetExpandableRow(gcd[i].ret,gb_expandglue);
5509 	GHVBoxSetExpandableCol(gcd[i-1].ret,gb_expandgluesame);
5510 	GHVBoxFitWindow(gcd[i].ret);
5511 
5512     GDrawSetVisible(gw,true);
5513     while ( !sld.done )
5514 	GDrawProcessOneEvent(NULL);
5515     if ( sld.ok ) {
5516     }
5517     GDrawDestroyWindow(gw);
5518 }
5519 
CharInfoInit(void)5520 void CharInfoInit(void) {
5521     static GTextInfo *lists[] = { glyphclasses, std_colors, truefalse, NULL };
5522     static int done = 0;
5523     int i, j;
5524     static char **cnames[] = { newstrings, NULL };
5525     static struct col_init *col_inits[] = { extensionpart, altuniinfo,
5526 	devtabci,
5527 	simplesubsci, ligatureci, altsubsci, multsubsci, simpleposci,
5528 	pairposci, NULL };
5529 
5530     if ( done )
5531 return;
5532     done = true;
5533     for ( i=0; lists[i]!=NULL; ++i )
5534 	for ( j=0; lists[i][j].text!=NULL; ++j )
5535 	    lists[i][j].text = (unichar_t *) S_((char *) lists[i][j].text);
5536     for ( i=0; cnames[i]!=NULL; ++i )
5537 	for ( j=0; cnames[i][j]!=NULL; ++j )
5538 	    cnames[i][j] = _(cnames[i][j]);
5539     for ( i=0; col_inits[i]!=NULL; ++i )
5540 	for ( j=0; col_inits[i][j].title!=NULL; ++j )
5541 	    col_inits[i][j].title=_(col_inits[i][j].title);
5542 }
5543