1 /* -*- coding: utf-8 -*- */
2 /* Copyright (C) 2007-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 "fontforgeui.h"
34 #include "fvfonts.h"
35 #include "gfile.h"
36 #include "gkeysym.h"
37 #include "lookups.h"
38 #include "sfd.h"
39 #include "sfundo.h"
40 #include "splinefill.h"
41 #include "splinesaveafm.h"
42 #include "splineutil.h"
43 #include "tottfgpos.h"
44 #include "ttf.h"
45 #include "ustring.h"
46 #include "utype.h"
47 
48 #include <locale.h>
49 #include <math.h>
50 #include <stdlib.h>
51 
52 int add_char_to_name_list = true;
53 int default_autokern_dlg = true;
54 int fontlevel_undo_limit = 10;
55 
56 void SFUntickAllPSTandKern(SplineFont *sf);
57 int kernsLength( KernPair *kp );
58 
59 static int DEBUG = 1;
60 
61 /* ************************************************************************** */
62 /* ******************************* UI routines ****************************** */
63 /* ************************************************************************** */
64 
SFLookupListFromType(SplineFont * sf,int lookup_type)65 GTextInfo **SFLookupListFromType(SplineFont *sf, int lookup_type ) {
66     int isgpos = (lookup_type>=gpos_start);
67     int k, cnt;
68     OTLookup *otl;
69     GTextInfo **ti;
70 
71     for ( k=0; k<2; ++k ) {
72 	cnt = 0;
73 	for ( otl= isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
74 	    if ( lookup_type==gsub_start || lookup_type==gpos_start ||
75 		    otl->lookup_type == lookup_type ) {
76 		if ( k ) {
77 		    ti[cnt] = calloc(1,sizeof(GTextInfo));
78 		    ti[cnt]->userdata = (void *) otl;
79 		    ti[cnt]->fg = ti[cnt]->bg = COLOR_DEFAULT;
80 		    ti[cnt]->text = utf82u_copy(otl->lookup_name);
81 		}
82 		++cnt;
83 	    }
84 	}
85 	if ( !k )
86 	    ti = calloc(cnt+2,sizeof(GTextInfo *));
87 	else
88 	    ti[cnt] = calloc(1,sizeof(GTextInfo));
89     }
90 return( ti );
91 }
92 
SFLookupArrayFromType(SplineFont * sf,int lookup_type)93 GTextInfo *SFLookupArrayFromType(SplineFont *sf, int lookup_type ) {
94     int isgpos = (lookup_type>=gpos_start);
95     int k, cnt;
96     OTLookup *otl;
97     GTextInfo *ti;
98 
99     for ( k=0; k<2; ++k ) {
100 	cnt = 0;
101 	for ( otl= isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
102 	    if ( lookup_type==gsub_start || lookup_type==gpos_start ||
103 		    otl->lookup_type == lookup_type ) {
104 		if ( k ) {
105 		    ti[cnt].userdata = (void *) otl;
106 		    ti[cnt].fg = ti[cnt].bg = COLOR_DEFAULT;
107 		    ti[cnt].text = utf82u_copy(otl->lookup_name);
108 		}
109 		++cnt;
110 	    }
111 	}
112 	if ( !k )
113 	    ti = calloc(cnt+2,sizeof(GTextInfo));
114     }
115 return( ti );
116 }
117 
SFLookupArrayFromMask(SplineFont * sf,int mask)118 GTextInfo *SFLookupArrayFromMask(SplineFont *sf, int mask ) {
119     int k, cnt;
120     OTLookup *otl;
121     GTextInfo *ti;
122     int isgpos;
123 
124     for ( k=0; k<2; ++k ) {
125 	cnt = 0;
126 	for ( isgpos = 0; isgpos<2; ++isgpos ) {
127 	    for ( otl= isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
128 		int lmask = isgpos ? (gpos_single_mask<<(otl->lookup_type-gpos_single))
129 				   : (gsub_single_mask<<(otl->lookup_type-gsub_single));
130 		if ( mask==0 || (mask&lmask)) {
131 		    if ( k ) {
132 			ti[cnt].userdata = (void *) otl;
133 			ti[cnt].fg = ti[cnt].bg = COLOR_DEFAULT;
134 			ti[cnt].text_is_1byte = true;
135 			ti[cnt].text = (unichar_t *) copy(otl->lookup_name);
136 		    }
137 		    ++cnt;
138 		}
139 	    }
140 	}
141 	if ( !k )
142 	    ti = calloc(cnt+2,sizeof(GTextInfo ));
143     }
144 return( ti );
145 }
146 
147 /* ************************************************************************** */
148 /* ********************** Lookup dialog and subdialogs ********************** */
149 /* ************************************************************************** */
150 
151 static GTextInfo gsub_lookuptypes[] = {
152     { (unichar_t *) N_("Lookup Type|Unspecified"), NULL, 0, 0, (void *) ot_undef, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
153     { (unichar_t *) N_("Single Substitution"), NULL, 0, 0, (void *) gsub_single, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
154     { (unichar_t *) N_("Multiple Substitution"), NULL, 0, 0, (void *) gsub_multiple, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
155     { (unichar_t *) N_("Alternate Substitution"), NULL, 0, 0, (void *) gsub_alternate, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
156     { (unichar_t *) N_("Ligature Substitution"), NULL, 0, 0, (void *) gsub_ligature, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
157     { (unichar_t *) N_("Contextual Substitution"), NULL, 0, 0, (void *) gsub_context, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
158     { (unichar_t *) N_("Contextual Chaining Substitution"), NULL, 0, 0, (void *) gsub_contextchain, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
159     { (unichar_t *) N_("Reverse Chaining Substitution"), NULL, 0, 0, (void *) gsub_reversecchain, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
160     { NULL, NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0'},	/* Line */
161     { (unichar_t *) N_("Mac Indic State Machine"), NULL, 0, 0, (void *) morx_indic, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
162     { (unichar_t *) N_("Mac Contextual State Machine"), NULL, 0, 0, (void *) morx_context, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
163     { (unichar_t *) N_("Mac Insertion State Machine"), NULL, 0, 0, (void *) morx_insert, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
164     GTEXTINFO_EMPTY
165 };
166 static GTextInfo gpos_lookuptypes[] = {
167     { (unichar_t *) N_("Lookup Type|Unspecified"), NULL, 0, 0, (void *) ot_undef, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
168     { (unichar_t *) N_("Single Position"), NULL, 0, 0, (void *) gpos_single, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
169     { (unichar_t *) N_("Pair Position (kerning)"), NULL, 0, 0, (void *) gpos_pair, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
170     { (unichar_t *) N_("Cursive Position"), NULL, 0, 0, (void *) gpos_cursive, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
171     { (unichar_t *) N_("Mark to Base Position"), NULL, 0, 0, (void *) gpos_mark2base, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
172     { (unichar_t *) N_("Mark to Ligature Position"), NULL, 0, 0, (void *) gpos_mark2ligature, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
173     { (unichar_t *) N_("Mark to Mark Position"), NULL, 0, 0, (void *) gpos_mark2mark, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
174     { (unichar_t *) N_("Contextual Position"), NULL, 0, 0, (void *) gpos_context, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
175     { (unichar_t *) N_("Contextual Chaining Position"), NULL, 0, 0, (void *) gpos_contextchain, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
176     { NULL, NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0'},	/* Line */
177     { (unichar_t *) N_("Mac Kerning State Machine"), NULL, 0, 0, (void *) kern_statemachine, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
178     GTEXTINFO_EMPTY
179 };
180 static GTextInfo *lookuptypes[2] = { gsub_lookuptypes, gpos_lookuptypes };
181 
182     /* see also list in tottfgpos.c mapping code points to scripts */
183     /* see also list in lookups.c for non-ui access to these data */
184 GTextInfo scripts[] = {
185     { (unichar_t *) N_("Adlam"), NULL, 0, 0, (void *) CHR('a','d','l','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
186     { (unichar_t *) N_("Ahom"), NULL, 0, 0, (void *) CHR('a','h','o','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
187     { (unichar_t *) N_("Anatolian Hieroglyphs"), NULL, 0, 0, (void *) CHR('h','l','u','w'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
188 /* GT: See the long comment at "Property|New" */
189 /* GT: The msgstr should contain a translation of "Arabic", ignore "Script|" */
190     { (unichar_t *) N_("Script|Arabic"), NULL, 0, 0, (void *) CHR('a','r','a','b'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
191     { (unichar_t *) N_("Script|Aramaic"), NULL, 0, 0, (void *) CHR('s','a','m','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
192     { (unichar_t *) N_("Script|Armenian"), NULL, 0, 0, (void *) CHR('a','r','m','n'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
193     { (unichar_t *) N_("Script|Avestan"), NULL, 0, 0, (void *) CHR('a','v','s','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
194     { (unichar_t *) N_("Script|Balinese"), NULL, 0, 0, (void *) CHR('b','a','l','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
195     { (unichar_t *) N_("Bamum"), NULL, 0, 0, (void *) CHR('b','a','m','u'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
196     { (unichar_t *) N_("Bassa Vah"), NULL, 0, 0, (void *) CHR('b','a','s','s'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
197     { (unichar_t *) N_("Script|Batak"), NULL, 0, 0, (void *) CHR('b','a','t','k'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
198     { (unichar_t *) N_("Script|Bengali"), NULL, 0, 0, (void *) CHR('b','e','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
199     { (unichar_t *) N_("Script|Bengali2"), NULL, 0, 0, (void *) CHR('b','n','g','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
200     { (unichar_t *) N_("Bhaiksuki"), NULL, 0, 0, (void *) CHR('b','h','k','s'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
201     { (unichar_t *) N_("Bopomofo"), NULL, 0, 0, (void *) CHR('b','o','p','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
202     { (unichar_t *) NU_("Brāhmī"), NULL, 0, 0, (void *) CHR('b','r','a','h'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
203     { (unichar_t *) N_("Braille"), NULL, 0, 0, (void *) CHR('b','r','a','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
204     { (unichar_t *) N_("Script|Buginese"), NULL, 0, 0, (void *) CHR('b','u','g','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
205     { (unichar_t *) N_("Script|Buhid"), NULL, 0, 0, (void *) CHR('b','u','h','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
206     { (unichar_t *) N_("Byzantine Music"), NULL, 0, 0, (void *) CHR('b','y','z','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
207     { (unichar_t *) N_("Canadian Syllabics"), NULL, 0, 0, (void *) CHR('c','a','n','s'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
208     { (unichar_t *) N_("Carian"), NULL, 0, 0, (void *) CHR('c','a','r','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
209     { (unichar_t *) N_("Caucasian Albanian"), NULL, 0, 0, (void *) CHR('a','g','h','b'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
210     { (unichar_t *) N_("Chakma"), NULL, 0, 0, (void *) CHR('c','a','k','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
211     { (unichar_t *) N_("Script|Cham"), NULL, 0, 0, (void *) CHR('c','h','a','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
212     { (unichar_t *) N_("Script|Cherokee"), NULL, 0, 0, (void *) CHR('c','h','e','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
213     { (unichar_t *) N_("CJK Ideographic"), NULL, 0, 0, (void *) CHR('h','a','n','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
214     { (unichar_t *) N_("Script|Coptic"), NULL, 0, 0, (void *) CHR('c','o','p','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
215     { (unichar_t *) N_("Cypriot syllabary"), NULL, 0, 0, (void *) CHR('c','p','r','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
216     { (unichar_t *) N_("Cyrillic"), NULL, 0, 0, (void *) CHR('c','y','r','l'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
217     { (unichar_t *) N_("Script|Default"), NULL, 0, 0, (void *) CHR('D','F','L','T'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
218     { (unichar_t *) N_("Deseret (Mormon)"), NULL, 0, 0, (void *) CHR('d','s','r','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
219     { (unichar_t *) N_("Devanagari"), NULL, 0, 0, (void *) CHR('d','e','v','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
220     { (unichar_t *) N_("Devanagari2"), NULL, 0, 0, (void *) CHR('d','e','v','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
221     { (unichar_t *) N_("Dogra"), NULL, 0, 0, (void *) CHR('d','o','g','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
222     { (unichar_t *) N_("Duployan"), NULL, 0, 0, (void *) CHR('d','u','p','l'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
223     { (unichar_t *) N_("Egyptian Hieroglyphs"), NULL, 0, 0, (void *) CHR('e','g','y','p'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
224     { (unichar_t *) N_("Elbasan"), NULL, 0, 0, (void *) CHR('e','l','b','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
225     { (unichar_t *) N_("Script|Ethiopic"), NULL, 0, 0, (void *) CHR('e','t','h','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
226     { (unichar_t *) N_("Script|Georgian"), NULL, 0, 0, (void *) CHR('g','e','o','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
227     { (unichar_t *) N_("Glagolitic"), NULL, 0, 0, (void *) CHR('g','l','a','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
228     { (unichar_t *) N_("Gothic"), NULL, 0, 0, (void *) CHR('g','o','t','h'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
229     { (unichar_t *) N_("Grantha"), NULL, 0, 0, (void *) CHR('g','r','a','n'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
230     { (unichar_t *) N_("Script|Greek"), NULL, 0, 0, (void *) CHR('g','r','e','k'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
231     { (unichar_t *) N_("Script|Gujarati"), NULL, 0, 0, (void *) CHR('g','u','j','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
232     { (unichar_t *) N_("Script|Gujarati2"), NULL, 0, 0, (void *) CHR('g','j','r','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
233     { (unichar_t *) N_("Gunjala Gondi"), NULL, 0, 0, (void *) CHR('g','o','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
234     { (unichar_t *) N_("Gurmukhi"), NULL, 0, 0, (void *) CHR('g','u','r','u'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
235     { (unichar_t *) N_("Gurmukhi2"), NULL, 0, 0, (void *) CHR('g','u','r','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
236     { (unichar_t *) N_("Hangul Jamo"), NULL, 0, 0, (void *) CHR('j','a','m','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
237     { (unichar_t *) N_("Hangul"), NULL, 0, 0, (void *) CHR('h','a','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
238     { (unichar_t *) N_("Hanifi Rohingya"), NULL, 0, 0, (void *) CHR('r','o','h','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
239     { (unichar_t *) NU_("Script|Hanunóo"), NULL, 0, 0, (void *) CHR('h','a','n','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
240     { (unichar_t *) N_("Hatran"), NULL, 0, 0, (void *) CHR('h','a','t','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
241     { (unichar_t *) N_("Script|Hebrew"), NULL, 0, 0, (void *) CHR('h','e','b','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
242     { (unichar_t *) N_("Hiragana & Katakana"), NULL, 0, 0, (void *) CHR('k','a','n','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
243     { (unichar_t *) N_("Imperial Aramaic"), NULL, 0, 0, (void *) CHR('a','r','m','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
244     { (unichar_t *) N_("Inscriptional Pahlavi"), NULL, 0, 0, (void *) CHR('p','h','l','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
245     { (unichar_t *) N_("Inscriptional Parthian"), NULL, 0, 0, (void *) CHR('p','r','t','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
246     { (unichar_t *) N_("Script|Javanese"), NULL, 0, 0, (void *) CHR('j','a','v','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
247     { (unichar_t *) N_("Kaithi"), NULL, 0, 0, (void *) CHR('k','t','h','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
248     { (unichar_t *) N_("Script|Kannada"), NULL, 0, 0, (void *) CHR('k','n','d','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
249     { (unichar_t *) N_("Script|Kannada2"), NULL, 0, 0, (void *) CHR('k','n','d','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
250     { (unichar_t *) N_("Kayah Li"), NULL, 0, 0, (void *) CHR('k','a','l','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
251     { (unichar_t *) N_("Script|Kharosthi"), NULL, 0, 0, (void *) CHR('k','h','a','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
252     { (unichar_t *) N_("Script|Khmer"), NULL, 0, 0, (void *) CHR('k','h','m','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
253     { (unichar_t *) N_("Khojki"), NULL, 0, 0, (void *) CHR('k','h','o','j'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
254     { (unichar_t *) N_("Khudawadi"), NULL, 0, 0, (void *) CHR('s','i','n','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
255     { (unichar_t *) N_("Script|Lao"), NULL, 0, 0, (void *) CHR('l','a','o',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
256     { (unichar_t *) N_("Script|Latin"), NULL, 0, 0, (void *) CHR('l','a','t','n'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
257     { (unichar_t *) NU_("Lepcha (Róng)"), NULL, 0, 0, (void *) CHR('l','e','p','c'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
258     { (unichar_t *) N_("Script|Limbu"), NULL, 0, 0, (void *) CHR('l','i','m','b'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
259     { (unichar_t *) N_("Linear A"), NULL, 0, 0, (void *) CHR('l','i','n','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
260     { (unichar_t *) N_("Linear B"), NULL, 0, 0, (void *) CHR('l','i','n','b'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
261     { (unichar_t *) N_("Lisu"), NULL, 0, 0, (void *) CHR('l','i','s','u'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
262     { (unichar_t *) N_("Lycian"), NULL, 0, 0, (void *) CHR('l','y','c','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
263     { (unichar_t *) N_("Lydian"), NULL, 0, 0, (void *) CHR('l','y','d','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
264     { (unichar_t *) N_("Mahajani"), NULL, 0, 0, (void *) CHR('m','a','h','j'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
265     { (unichar_t *) N_("Makasar"), NULL, 0, 0, (void *) CHR('m','a','k','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
266     { (unichar_t *) NU_("Script|Malayālam"), NULL, 0, 0, (void *) CHR('m','l','y','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
267     { (unichar_t *) NU_("Script|Malayālam2"), NULL, 0, 0, (void *) CHR('m','l','m','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
268     { (unichar_t *) N_("Script|Mandaean"), NULL, 0, 0, (void *) CHR('m','a','n','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
269     { (unichar_t *) N_("Manichaean"), NULL, 0, 0, (void *) CHR('m','a','n','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
270     { (unichar_t *) N_("Marchen"), NULL, 0, 0, (void *) CHR('m','a','r','c'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
271     { (unichar_t *) N_("Masaram Gondi"), NULL, 0, 0, (void *) CHR('g','o','n','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
272     { (unichar_t *) N_("Mathematical Alphanumeric Symbols"), NULL, 0, 0, (void *) CHR('m','a','t','h'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
273     { (unichar_t *) N_("Medefaidrin"), NULL, 0, 0, (void *) CHR('m','e','d','f'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
274     { (unichar_t *) N_("Meetei Mayek"), NULL, 0, 0, (void *) CHR('m','t','e','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
275     { (unichar_t *) N_("Mende Kikakui"), NULL, 0, 0, (void *) CHR('m','e','n','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
276     { (unichar_t *) N_("Meroitic Cursive"), NULL, 0, 0, (void *) CHR('m','e','r','c'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
277     { (unichar_t *) N_("Meroitic Hieroglyphs"), NULL, 0, 0, (void *) CHR('m','e','r','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
278     { (unichar_t *) N_("Miao"), NULL, 0, 0, (void *) CHR('p','l','r','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
279     { (unichar_t *) N_("Modi"), NULL, 0, 0, (void *) CHR('m','o','d','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
280     { (unichar_t *) N_("Script|Mongolian"), NULL, 0, 0, (void *) CHR('m','o','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
281     { (unichar_t *) N_("Mro"), NULL, 0, 0, (void *) CHR('m','r','o','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
282     { (unichar_t *) N_("Multani"), NULL, 0, 0, (void *) CHR('m','u','l','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
283     { (unichar_t *) N_("Musical"), NULL, 0, 0, (void *) CHR('m','u','s','c'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
284     { (unichar_t *) N_("Script|Myanmar"), NULL, 0, 0, (void *) CHR('m','y','m','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
285     { (unichar_t *) N_("N'Ko"), NULL, 0, 0, (void *) CHR('n','k','o',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
286     { (unichar_t *) N_("Nabataean"), NULL, 0, 0, (void *) CHR('n','b','a','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
287     { (unichar_t *) N_("New Tai Lue"), NULL, 0, 0, (void *) CHR('t','a','l','u'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
288     { (unichar_t *) N_("Newa"), NULL, 0, 0, (void *) CHR('n','e','w','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
289     { (unichar_t *) N_("Nushu"), NULL, 0, 0, (void *) CHR('n','s','h','u'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
290     { (unichar_t *) N_("Ogham"), NULL, 0, 0, (void *) CHR('o','g','a','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
291     { (unichar_t *) N_("Ol Chiki"), NULL, 0, 0, (void *) CHR('o','l','c','k'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
292     { (unichar_t *) N_("Old Hungarian"), NULL, 0, 0, (void *) CHR('h','u','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
293     { (unichar_t *) N_("Old Italic (Etruscan, Oscan, etc.)"), NULL, 0, 0, (void *) CHR('i','t','a','l'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
294     { (unichar_t *) N_("Old North Arabian"), NULL, 0, 0, (void *) CHR('n','a','r','b'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
295     { (unichar_t *) N_("Script|Old Permic"), NULL, 0, 0, (void *) CHR('p','e','r','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
296     { (unichar_t *) N_("Old Persian cuneiform"), NULL, 0, 0, (void *) CHR('x','p','e','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
297     { (unichar_t *) N_("Old Sogdian"), NULL, 0, 0, (void *) CHR('s','o','g','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
298     { (unichar_t *) N_("Old South Arabian"), NULL, 0, 0, (void *) CHR('s','a','r','b'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
299     { (unichar_t *) N_("Old Turkic"), NULL, 0, 0, (void *) CHR('o','r','k','h'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
300     { (unichar_t *) N_("Script|Oriya"), NULL, 0, 0, (void *) CHR('o','r','y','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
301     { (unichar_t *) N_("Script|Oriya2"), NULL, 0, 0, (void *) CHR('o','r','y','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
302     { (unichar_t *) N_("Osage"), NULL, 0, 0, (void *) CHR('o','s','g','e'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
303     { (unichar_t *) N_("Osmanya"), NULL, 0, 0, (void *) CHR('o','s','m','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
304     { (unichar_t *) N_("Pahawh Hmong"), NULL, 0, 0, (void *) CHR('h','m','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
305     { (unichar_t *) N_("Palmyrene"), NULL, 0, 0, (void *) CHR('p','a','l','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
306     { (unichar_t *) N_("Pau Cin Hau"), NULL, 0, 0, (void *) CHR('p','a','u','c'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
307     { (unichar_t *) N_("Script|Phags-pa"), NULL, 0, 0, (void *) CHR('p','h','a','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
308     { (unichar_t *) N_("Script|Phoenician"), NULL, 0, 0, (void *) CHR('p','h','n','x'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
309     { (unichar_t *) N_("Pollard Phonetic"), NULL, 0, 0, (void *) CHR('p','l','r','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
310     { (unichar_t *) N_("Psalter Pahlavi"), NULL, 0, 0, (void *) CHR('p','h','l','p'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
311     { (unichar_t *) N_("Rejang"), NULL, 0, 0, (void *) CHR('r','j','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
312     { (unichar_t *) N_("Runic"), NULL, 0, 0, (void *) CHR('r','u','n','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
313     { (unichar_t *) N_("Saurashtra"), NULL, 0, 0, (void *) CHR('s','a','u','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
314     { (unichar_t *) N_("Sharada"), NULL, 0, 0, (void *) CHR('s','h','r','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
315     { (unichar_t *) N_("Shavian"), NULL, 0, 0, (void *) CHR('s','h','a','w'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
316     { (unichar_t *) N_("Siddham"), NULL, 0, 0, (void *) CHR('s','i','d','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
317     { (unichar_t *) N_("Sutton SignWriting"), NULL, 0, 0, (void *) CHR('s','g','n','w'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
318     { (unichar_t *) N_("Script|Sinhala"), NULL, 0, 0, (void *) CHR('s','i','n','h'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
319     { (unichar_t *) N_("Sogdian"), NULL, 0, 0, (void *) CHR('s','o','g','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
320     { (unichar_t *) N_("Sora Sompeng"), NULL, 0, 0, (void *) CHR('s','o','r','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
321     { (unichar_t *) N_("Soyombo"), NULL, 0, 0, (void *) CHR('s','o','y','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
322     { (unichar_t *) N_("Script|Sumero-Akkadian Cuneiform"), NULL, 0, 0, (void *) CHR('x','s','u','x'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
323     { (unichar_t *) N_("Script|Sundanese"), NULL, 0, 0, (void *) CHR('s','u','n','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
324     { (unichar_t *) N_("Script|Syloti Nagri"), NULL, 0, 0, (void *) CHR('s','y','l','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
325     { (unichar_t *) N_("Script|Syriac"), NULL, 0, 0, (void *) CHR('s','y','r','c'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
326     { (unichar_t *) N_("Script|Tagalog"), NULL, 0, 0, (void *) CHR('t','g','l','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
327     { (unichar_t *) N_("Script|Tagbanwa"), NULL, 0, 0, (void *) CHR('t','a','g','b'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
328     { (unichar_t *) N_("Tai Le"), NULL, 0, 0, (void *) CHR('t','a','l','e'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
329     { (unichar_t *) N_("Tai Tham"), NULL, 0, 0, (void *) CHR('l','a','n','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
330     { (unichar_t *) N_("Tai Viet"), NULL, 0, 0, (void *) CHR('t','a','v','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
331     { (unichar_t *) N_("Takri"), NULL, 0, 0, (void *) CHR('t','a','k','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
332     { (unichar_t *) N_("Script|Tamil"), NULL, 0, 0, (void *) CHR('t','a','m','l'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
333     { (unichar_t *) N_("Script|Tamil2"), NULL, 0, 0, (void *) CHR('t','m','l','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
334     { (unichar_t *) N_("Tangut"), NULL, 0, 0, (void *) CHR('t','a','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
335     { (unichar_t *) N_("Script|Telugu"), NULL, 0, 0, (void *) CHR('t','e','l','u'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
336     { (unichar_t *) N_("Script|Telugu2"), NULL, 0, 0, (void *) CHR('t','e','l','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
337     { (unichar_t *) N_("Thaana"), NULL, 0, 0, (void *) CHR('t','h','a','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
338     { (unichar_t *) N_("Script|Thai"), NULL, 0, 0, (void *) CHR('t','h','a','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
339     { (unichar_t *) N_("Script|Tibetan"), NULL, 0, 0, (void *) CHR('t','i','b','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
340     { (unichar_t *) N_("Tifinagh (Berber)"), NULL, 0, 0, (void *) CHR('t','f','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
341     { (unichar_t *) N_("Tirhuta"), NULL, 0, 0, (void *) CHR('t','i','r','h'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
342     { (unichar_t *) N_("Script|Ugaritic"), NULL, 0, 0, (void *) CHR('u','g','a','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
343     { (unichar_t *) N_("Script|Vai"), NULL, 0, 0, (void *) CHR('v','a','i',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
344     { (unichar_t *) N_("Warang Citi"), NULL, 0, 0, (void *) CHR('w','a','r','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
345     { (unichar_t *) N_("Script|Yi"), NULL, 0, 0, (void *) CHR('y','i',' ',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
346     { (unichar_t *) N_("Zanabazar Square"), NULL, 0, 0, (void *) CHR('z','a','n','b'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
347     GTEXTINFO_EMPTY
348 };
349 
350 GTextInfo languages[] = {
351     { (unichar_t *) N_("Abaza"), NULL, 0, 0, (void *) CHR('A','B','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
352     { (unichar_t *) N_("Abkhazian"), NULL, 0, 0, (void *) CHR('A','B','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
353     { (unichar_t *) N_("Acholi"), NULL, 0, 0, (void *) CHR('A','C','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
354     { (unichar_t *) N_("Achi"), NULL, 0, 0, (void *) CHR('A','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
355     { (unichar_t *) N_("Adyghe"), NULL, 0, 0, (void *) CHR('A','D','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
356     { (unichar_t *) N_("Afrikaans"), NULL, 0, 0, (void *) CHR('A','F','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
357     { (unichar_t *) N_("Afar"), NULL, 0, 0, (void *) CHR('A','F','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
358     { (unichar_t *) N_("Agaw"), NULL, 0, 0, (void *) CHR('A','G','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
359     { (unichar_t *) N_("Aiton"), NULL, 0, 0, (void *) CHR('A','I','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
360     { (unichar_t *) N_("Akan"), NULL, 0, 0, (void *) CHR('A','K','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
361     { (unichar_t *) N_("Alsatian"), NULL, 0, 0, (void *) CHR('A','L','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
362     { (unichar_t *) N_("Altai"), NULL, 0, 0, (void *) CHR('A','L','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
363 /* GT: See the long comment at "Property|New" */
364 /* GT: The msgstr should contain a translation of "Amharic", ignore "Lang|" */
365     { (unichar_t *) N_("Lang|Amharic"), NULL, 0, 0, (void *) CHR('A','M','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
366     { (unichar_t *) N_("Anglo-Saxon"), NULL, 0, 0, (void *) CHR('A','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
367     { (unichar_t *) N_("Americanist IPA"), NULL, 0, 0, (void *) CHR('A','P','P','H'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
368     { (unichar_t *) N_("Lang|Arabic"), NULL, 0, 0, (void *) CHR('A','R','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
369     { (unichar_t *) N_("Aragonese"), NULL, 0, 0, (void *) CHR('A','R','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
370     { (unichar_t *) N_("Aari"), NULL, 0, 0, (void *) CHR('A','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
371     { (unichar_t *) N_("Arakanese"), NULL, 0, 0, (void *) CHR('A','R','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
372     { (unichar_t *) N_("Assamese"), NULL, 0, 0, (void *) CHR('A','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
373     { (unichar_t *) N_("Asturian"), NULL, 0, 0, (void *) CHR('A','S','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
374     { (unichar_t *) N_("Athapaskan"), NULL, 0, 0, (void *) CHR('A','T','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
375     { (unichar_t *) N_("Lang|Avar"), NULL, 0, 0, (void *) CHR('A','V','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
376     { (unichar_t *) N_("Awadhi"), NULL, 0, 0, (void *) CHR('A','W','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
377     { (unichar_t *) N_("Aymara"), NULL, 0, 0, (void *) CHR('A','Y','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
378     { (unichar_t *) N_("Torki"), NULL, 0, 0, (void *) CHR('A','Z','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
379     { (unichar_t *) N_("Azeri"), NULL, 0, 0, (void *) CHR('A','Z','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
380     { (unichar_t *) N_("Badaga"), NULL, 0, 0, (void *) CHR('B','A','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
381     { (unichar_t *) N_("Banda"), NULL, 0, 0, (void *) CHR('B','A','D','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
382     { (unichar_t *) N_("Baghelkhandi"), NULL, 0, 0, (void *) CHR('B','A','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
383     { (unichar_t *) N_("Balkar"), NULL, 0, 0, (void *) CHR('B','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
384     { (unichar_t *) N_("Lang|Balinese"), NULL, 0, 0, (void *) CHR('B','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
385     { (unichar_t *) N_("Bavarian"), NULL, 0, 0, (void *) CHR('B','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
386     { (unichar_t *) N_("Baule"), NULL, 0, 0, (void *) CHR('B','A','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
387     { (unichar_t *) N_("Batak Toba"), NULL, 0, 0, (void *) CHR('B','B','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
388     { (unichar_t *) N_("Lang|Berber"), NULL, 0, 0, (void *) CHR('B','B','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
389     { (unichar_t *) N_("Bench"), NULL, 0, 0, (void *) CHR('B','C','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
390     { (unichar_t *) N_("Bible Cree"), NULL, 0, 0, (void *) CHR('B','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
391     { (unichar_t *) N_("Bandjalang"), NULL, 0, 0, (void *) CHR('B','D','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
392     { (unichar_t *) N_("Belarussian"), NULL, 0, 0, (void *) CHR('B','E','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
393     { (unichar_t *) N_("Bemba"), NULL, 0, 0, (void *) CHR('B','E','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
394     { (unichar_t *) N_("Lang|Bengali"), NULL, 0, 0, (void *) CHR('B','E','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
395     { (unichar_t *) N_("Haryanvi"), NULL, 0, 0, (void *) CHR('B','G','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
396     { (unichar_t *) N_("Bagri"), NULL, 0, 0, (void *) CHR('B','G','Q',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
397     { (unichar_t *) N_("Bulgarian"), NULL, 0, 0, (void *) CHR('B','G','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
398     { (unichar_t *) N_("Bhili"), NULL, 0, 0, (void *) CHR('B','H','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
399     { (unichar_t *) N_("Bhojpuri"), NULL, 0, 0, (void *) CHR('B','H','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
400     { (unichar_t *) N_("Bikol"), NULL, 0, 0, (void *) CHR('B','I','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
401     { (unichar_t *) N_("Bilen"), NULL, 0, 0, (void *) CHR('B','I','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
402     { (unichar_t *) N_("Bislama"), NULL, 0, 0, (void *) CHR('B','I','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
403     { (unichar_t *) N_("Kanauji"), NULL, 0, 0, (void *) CHR('B','J','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
404     { (unichar_t *) N_("Blackfoot"), NULL, 0, 0, (void *) CHR('B','K','F',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
405     { (unichar_t *) N_("Balochi"), NULL, 0, 0, (void *) CHR('B','L','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
406     { (unichar_t *) N_("Pa'o Karen"), NULL, 0, 0, (void *) CHR('B','L','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
407     { (unichar_t *) N_("Balante"), NULL, 0, 0, (void *) CHR('B','L','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
408     { (unichar_t *) N_("Balti"), NULL, 0, 0, (void *) CHR('B','L','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
409     { (unichar_t *) N_("Bambara"), NULL, 0, 0, (void *) CHR('B','M','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
410     { (unichar_t *) N_("Bamileke"), NULL, 0, 0, (void *) CHR('B','M','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
411     { (unichar_t *) N_("Bosnian"), NULL, 0, 0, (void *) CHR('B','O','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
412     { (unichar_t *) N_("Bishnupriya Manipuri"), NULL, 0, 0, (void *) CHR('B','P','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
413     { (unichar_t *) N_("Breton"), NULL, 0, 0, (void *) CHR('B','R','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
414     { (unichar_t *) N_("Brahui"), NULL, 0, 0, (void *) CHR('B','R','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
415     { (unichar_t *) N_("Braj Bhasha"), NULL, 0, 0, (void *) CHR('B','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
416     { (unichar_t *) N_("Burmese"), NULL, 0, 0, (void *) CHR('B','R','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
417     { (unichar_t *) N_("Bodo"), NULL, 0, 0, (void *) CHR('B','R','X',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
418     { (unichar_t *) N_("Bashkir"), NULL, 0, 0, (void *) CHR('B','S','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
419     { (unichar_t *) N_("Burushaski"), NULL, 0, 0, (void *) CHR('B','S','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
420     { (unichar_t *) N_("Beti"), NULL, 0, 0, (void *) CHR('B','T','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
421     { (unichar_t *) N_("Batak Simalungun"), NULL, 0, 0, (void *) CHR('B','T','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
422     { (unichar_t *) N_("Lang|Buginese"), NULL, 0, 0, (void *) CHR('B','U','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
423     { (unichar_t *) N_("Medumba"), NULL, 0, 0, (void *) CHR('B','Y','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
424     { (unichar_t *) N_("Kaqchikel"), NULL, 0, 0, (void *) CHR('C','A','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
425     { (unichar_t *) N_("Catalan"), NULL, 0, 0, (void *) CHR('C','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
426     { (unichar_t *) N_("Zamboanga Chavacano"), NULL, 0, 0, (void *) CHR('C','B','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
427     { (unichar_t *) N_("Chinantec"), NULL, 0, 0, (void *) CHR('C','C','H','N'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
428     { (unichar_t *) N_("Cebuano"), NULL, 0, 0, (void *) CHR('C','E','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
429     { (unichar_t *) N_("Chiga"), NULL, 0, 0, (void *) CHR('C','G','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
430     { (unichar_t *) N_("Chamorro"), NULL, 0, 0, (void *) CHR('C','H','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
431     { (unichar_t *) N_("Chechen"), NULL, 0, 0, (void *) CHR('C','H','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
432     { (unichar_t *) N_("Chaha Gurage"), NULL, 0, 0, (void *) CHR('C','H','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
433     { (unichar_t *) N_("Chattisgarhi"), NULL, 0, 0, (void *) CHR('C','H','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
434     { (unichar_t *) N_("Chichewa"), NULL, 0, 0, (void *) CHR('C','H','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
435     { (unichar_t *) N_("Chukchi"), NULL, 0, 0, (void *) CHR('C','H','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
436     { (unichar_t *) N_("Chuukese"), NULL, 0, 0, (void *) CHR('C','H','K','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
437     { (unichar_t *) N_("Choctaw"), NULL, 0, 0, (void *) CHR('C','H','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
438     { (unichar_t *) N_("Chipewyan"), NULL, 0, 0, (void *) CHR('C','H','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
439     { (unichar_t *) N_("Lang|Cherokee"), NULL, 0, 0, (void *) CHR('C','H','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
440     { (unichar_t *) N_("Chuvash"), NULL, 0, 0, (void *) CHR('C','H','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
441     { (unichar_t *) N_("Cheyenne"), NULL, 0, 0, (void *) CHR('C','H','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
442     { (unichar_t *) N_("Lang|Western Cham"), NULL, 0, 0, (void *) CHR('C','J','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
443     { (unichar_t *) N_("Eastern Cham"), NULL, 0, 0, (void *) CHR('C','J','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
444     { (unichar_t *) N_("Comorian"), NULL, 0, 0, (void *) CHR('C','M','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
445     { (unichar_t *) N_("Lang|Coptic"), NULL, 0, 0, (void *) CHR('C','O','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
446     { (unichar_t *) N_("Cornish"), NULL, 0, 0, (void *) CHR('C','O','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
447     { (unichar_t *) N_("Corsican"), NULL, 0, 0, (void *) CHR('C','O','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
448     { (unichar_t *) N_("Creoles"), NULL, 0, 0, (void *) CHR('C','P','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
449     { (unichar_t *) N_("Cree"), NULL, 0, 0, (void *) CHR('C','R','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
450     { (unichar_t *) N_("Carrier"), NULL, 0, 0, (void *) CHR('C','R','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
451     { (unichar_t *) N_("Crimean Tatar"), NULL, 0, 0, (void *) CHR('C','R','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
452     { (unichar_t *) N_("Kashubian"), NULL, 0, 0, (void *) CHR('C','S','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
453     { (unichar_t *) N_("Church Slavonic"), NULL, 0, 0, (void *) CHR('C','S','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
454     { (unichar_t *) N_("Czech"), NULL, 0, 0, (void *) CHR('C','S','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
455     { (unichar_t *) N_("Chittagonian"), NULL, 0, 0, (void *) CHR('C','T','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
456     { (unichar_t *) N_("San Blas Kuna"), NULL, 0, 0, (void *) CHR('C','U','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
457     { (unichar_t *) N_("Danish"), NULL, 0, 0, (void *) CHR('D','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
458     { (unichar_t *) N_("Dargwa"), NULL, 0, 0, (void *) CHR('D','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
459     { (unichar_t *) N_("Dayi"), NULL, 0, 0, (void *) CHR('D','A','X',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
460     { (unichar_t *) N_("Woods Cree"), NULL, 0, 0, (void *) CHR('D','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
461     { (unichar_t *) N_("German"), NULL, 0, 0, (void *) CHR('D','E','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
462     { (unichar_t *) N_("Lang|Default"), NULL, 0, 0, (void *) DEFAULT_LANG, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
463     { (unichar_t *) N_("Dogri (individual language)"), NULL, 0, 0, (void *) CHR('D','G','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
464     { (unichar_t *) N_("Dogri"), NULL, 0, 0, (void *) CHR('D','G','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
465     { (unichar_t *) N_("Dhangu"), NULL, 0, 0, (void *) CHR('D','H','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
466     { (unichar_t *) N_("Dhivehi (Obsolete)"), NULL, 0, 0, (void *) CHR('D','H','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
467     { (unichar_t *) N_("Dimli"), NULL, 0, 0, (void *) CHR('D','I','Q',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
468     { (unichar_t *) N_("Dhivehi"), NULL, 0, 0, (void *) CHR('D','I','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
469     { (unichar_t *) N_("Djerma"), NULL, 0, 0, (void *) CHR('D','J','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
470     { (unichar_t *) N_("Djambarrpuyngu"), NULL, 0, 0, (void *) CHR('D','J','R','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
471     { (unichar_t *) N_("Dangme"), NULL, 0, 0, (void *) CHR('D','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
472     { (unichar_t *) N_("Dan"), NULL, 0, 0, (void *) CHR('D','N','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
473     { (unichar_t *) N_("Dinka"), NULL, 0, 0, (void *) CHR('D','N','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
474     { (unichar_t *) N_("Dari"), NULL, 0, 0, (void *) CHR('D','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
475     { (unichar_t *) N_("Dhuwal"), NULL, 0, 0, (void *) CHR('D','U','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
476     { (unichar_t *) N_("Dungan"), NULL, 0, 0, (void *) CHR('D','U','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
477     { (unichar_t *) N_("Dzongkha"), NULL, 0, 0, (void *) CHR('D','Z','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
478     { (unichar_t *) N_("Ebira"), NULL, 0, 0, (void *) CHR('E','B','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
479     { (unichar_t *) N_("Eastern Cree"), NULL, 0, 0, (void *) CHR('E','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
480     { (unichar_t *) N_("Edo"), NULL, 0, 0, (void *) CHR('E','D','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
481     { (unichar_t *) N_("Efik"), NULL, 0, 0, (void *) CHR('E','F','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
482     { (unichar_t *) N_("Lang|Greek"), NULL, 0, 0, (void *) CHR('E','L','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
483     { (unichar_t *) N_("Eastern Maninkakan"), NULL, 0, 0, (void *) CHR('E','M','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
484     { (unichar_t *) N_("English"), NULL, 0, 0, (void *) CHR('E','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
485     { (unichar_t *) N_("Erzya"), NULL, 0, 0, (void *) CHR('E','R','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
486     { (unichar_t *) N_("Spanish"), NULL, 0, 0, (void *) CHR('E','S','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
487     { (unichar_t *) N_("Central Yupik"), NULL, 0, 0, (void *) CHR('E','S','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
488     { (unichar_t *) N_("Estonian"), NULL, 0, 0, (void *) CHR('E','T','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
489     { (unichar_t *) N_("Basque"), NULL, 0, 0, (void *) CHR('E','U','Q',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
490     { (unichar_t *) N_("Evenki"), NULL, 0, 0, (void *) CHR('E','V','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
491     { (unichar_t *) N_("Even"), NULL, 0, 0, (void *) CHR('E','V','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
492     { (unichar_t *) N_("Ewe"), NULL, 0, 0, (void *) CHR('E','W','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
493     { (unichar_t *) N_("French Antillean"), NULL, 0, 0, (void *) CHR('F','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
494     { (unichar_t *) N_("Fang"), NULL, 0, 0, (void *) CHR('F','A','N','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
495     { (unichar_t *) N_("Lang|Farsi"), NULL, 0, 0, (void *) CHR('F','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
496     { (unichar_t *) N_("Fanti"), NULL, 0, 0, (void *) CHR('F','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
497     { (unichar_t *) N_("Finnish"), NULL, 0, 0, (void *) CHR('F','I','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
498     { (unichar_t *) N_("Fijian"), NULL, 0, 0, (void *) CHR('F','J','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
499     { (unichar_t *) N_("Flemish"), NULL, 0, 0, (void *) CHR('F','L','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
500     { (unichar_t *) N_("Fe'fe'"), NULL, 0, 0, (void *) CHR('F','M','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
501     { (unichar_t *) N_("Forest Nenets"), NULL, 0, 0, (void *) CHR('F','N','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
502     { (unichar_t *) N_("Fon"), NULL, 0, 0, (void *) CHR('F','O','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
503     { (unichar_t *) N_("Faroese"), NULL, 0, 0, (void *) CHR('F','O','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
504     { (unichar_t *) N_("French"), NULL, 0, 0, (void *) CHR('F','R','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
505     { (unichar_t *) N_("Cajun French"), NULL, 0, 0, (void *) CHR('F','R','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
506     { (unichar_t *) N_("Frisian"), NULL, 0, 0, (void *) CHR('F','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
507     { (unichar_t *) N_("Friulian"), NULL, 0, 0, (void *) CHR('F','R','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
508     { (unichar_t *) N_("Arpitan"), NULL, 0, 0, (void *) CHR('F','R','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
509     { (unichar_t *) N_("Futa"), NULL, 0, 0, (void *) CHR('F','T','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
510     { (unichar_t *) N_("Fulani"), NULL, 0, 0, (void *) CHR('F','U','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
511     { (unichar_t *) N_("Nigerian Fulfulde"), NULL, 0, 0, (void *) CHR('F','U','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
512     { (unichar_t *) N_("Ga"), NULL, 0, 0, (void *) CHR('G','A','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
513     { (unichar_t *) N_("Scottish Gaelic"), NULL, 0, 0, (void *) CHR('G','A','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
514     { (unichar_t *) N_("Gagauz"), NULL, 0, 0, (void *) CHR('G','A','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
515     { (unichar_t *) N_("Galician"), NULL, 0, 0, (void *) CHR('G','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
516     { (unichar_t *) N_("Garshuni"), NULL, 0, 0, (void *) CHR('G','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
517     { (unichar_t *) N_("Garhwali"), NULL, 0, 0, (void *) CHR('G','A','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
518     { (unichar_t *) N_("Lang|Ge'ez"), NULL, 0, 0, (void *) CHR('G','E','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
519     { (unichar_t *) N_("Githabul"), NULL, 0, 0, (void *) CHR('G','I','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
520     { (unichar_t *) N_("Gilyak"), NULL, 0, 0, (void *) CHR('G','I','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
521     { (unichar_t *) N_("Kiribati"), NULL, 0, 0, (void *) CHR('G','I','L','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
522     { (unichar_t *) N_("Kpelle (Guinea)"), NULL, 0, 0, (void *) CHR('G','K','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
523     { (unichar_t *) N_("Gilaki"), NULL, 0, 0, (void *) CHR('G','L','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
524     { (unichar_t *) N_("Gumuz"), NULL, 0, 0, (void *) CHR('G','M','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
525     { (unichar_t *) N_("Gumatj"), NULL, 0, 0, (void *) CHR('G','N','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
526     { (unichar_t *) N_("Gogo"), NULL, 0, 0, (void *) CHR('G','O','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
527     { (unichar_t *) N_("Gondi"), NULL, 0, 0, (void *) CHR('G','O','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
528     { (unichar_t *) N_("Greenlandic"), NULL, 0, 0, (void *) CHR('G','R','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
529     { (unichar_t *) N_("Garo"), NULL, 0, 0, (void *) CHR('G','R','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
530     { (unichar_t *) N_("Guarani"), NULL, 0, 0, (void *) CHR('G','U','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
531     { (unichar_t *) N_("Wayuu"), NULL, 0, 0, (void *) CHR('G','U','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
532     { (unichar_t *) N_("Gupapuyngu"), NULL, 0, 0, (void *) CHR('G','U','F',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
533     { (unichar_t *) N_("Lang|Gujarati"), NULL, 0, 0, (void *) CHR('G','U','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
534     { (unichar_t *) N_("Gusii"), NULL, 0, 0, (void *) CHR('G','U','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
535     { (unichar_t *) N_("Haitian"), NULL, 0, 0, (void *) CHR('H','A','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
536     { (unichar_t *) N_("Halam"), NULL, 0, 0, (void *) CHR('H','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
537     { (unichar_t *) N_("Harauti"), NULL, 0, 0, (void *) CHR('H','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
538     { (unichar_t *) N_("Hausa"), NULL, 0, 0, (void *) CHR('H','A','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
539     { (unichar_t *) N_("Hawaiian"), NULL, 0, 0, (void *) CHR('H','A','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
540     { (unichar_t *) N_("Haya"), NULL, 0, 0, (void *) CHR('H','A','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
541     { (unichar_t *) N_("Hazaragi"), NULL, 0, 0, (void *) CHR('H','A','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
542     { (unichar_t *) N_("Hammer-Banna"), NULL, 0, 0, (void *) CHR('H','B','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
543     { (unichar_t *) N_("Herero"), NULL, 0, 0, (void *) CHR('H','E','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
544     { (unichar_t *) N_("Hiligaynon"), NULL, 0, 0, (void *) CHR('H','I','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
545     { (unichar_t *) N_("Hindi"), NULL, 0, 0, (void *) CHR('H','I','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
546     { (unichar_t *) N_("High Mari"), NULL, 0, 0, (void *) CHR('H','M','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
547     { (unichar_t *) N_("Hmong"), NULL, 0, 0, (void *) CHR('H','M','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
548     { (unichar_t *) N_("Hiri Motu"), NULL, 0, 0, (void *) CHR('H','M','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
549     { (unichar_t *) N_("Hindko"), NULL, 0, 0, (void *) CHR('H','N','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
550     { (unichar_t *) N_("Ho"), NULL, 0, 0, (void *) CHR('H','O',' ',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
551     { (unichar_t *) N_("Harari"), NULL, 0, 0, (void *) CHR('H','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
552     { (unichar_t *) N_("Croatian"), NULL, 0, 0, (void *) CHR('H','R','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
553     { (unichar_t *) N_("Hungarian"), NULL, 0, 0, (void *) CHR('H','U','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
554     { (unichar_t *) N_("Lang|Armenian"), NULL, 0, 0, (void *) CHR('H','Y','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
555     { (unichar_t *) N_("Eastern Armenian"), NULL, 0, 0, (void *) CHR('H','Y','E','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
556     { (unichar_t *) N_("Iban"), NULL, 0, 0, (void *) CHR('I','B','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
557     { (unichar_t *) N_("Ibibio"), NULL, 0, 0, (void *) CHR('I','B','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
558     { (unichar_t *) N_("Igbo"), NULL, 0, 0, (void *) CHR('I','B','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
559     { (unichar_t *) N_("Ido"), NULL, 0, 0, (void *) CHR('I','D','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
560     { (unichar_t *) N_("Ijo"), NULL, 0, 0, (void *) CHR('I','J','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
561     { (unichar_t *) N_("Interlingue"), NULL, 0, 0, (void *) CHR('I','L','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
562     { (unichar_t *) N_("Ilokano"), NULL, 0, 0, (void *) CHR('I','L','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
563     { (unichar_t *) N_("Interlingua"), NULL, 0, 0, (void *) CHR('I','N','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
564     { (unichar_t *) N_("Indonesian"), NULL, 0, 0, (void *) CHR('I','N','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
565     { (unichar_t *) N_("Ingush"), NULL, 0, 0, (void *) CHR('I','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
566     { (unichar_t *) N_("Inuktitut"), NULL, 0, 0, (void *) CHR('I','N','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
567     { (unichar_t *) N_("Inupiat"), NULL, 0, 0, (void *) CHR('I','P','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
568     { (unichar_t *) N_("IPA usage"), NULL, 0, 0, (void *) CHR('I','P','P','H'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
569     { (unichar_t *) N_("Irish"), NULL, 0, 0, (void *) CHR('I','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
570     { (unichar_t *) N_("Irish Traditional"), NULL, 0, 0, (void *) CHR('I','R','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
571     { (unichar_t *) N_("Icelandic"), NULL, 0, 0, (void *) CHR('I','S','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
572     { (unichar_t *) N_("Inari Sami"), NULL, 0, 0, (void *) CHR('I','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
573     { (unichar_t *) N_("Italian"), NULL, 0, 0, (void *) CHR('I','T','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
574     { (unichar_t *) N_("Lang|Hebrew"), NULL, 0, 0, (void *) CHR('I','W','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
575     { (unichar_t *) N_("Jamaican Creole"), NULL, 0, 0, (void *) CHR('J','A','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
576     { (unichar_t *) N_("Japanese"), NULL, 0, 0, (void *) CHR('J','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
577     { (unichar_t *) N_("Lang|Javanese"), NULL, 0, 0, (void *) CHR('J','A','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
578     { (unichar_t *) N_("Lojban"), NULL, 0, 0, (void *) CHR('J','B','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
579     { (unichar_t *) N_("Krymchak"), NULL, 0, 0, (void *) CHR('J','C','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
580     { (unichar_t *) N_("Yiddish"), NULL, 0, 0, (void *) CHR('J','I','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
581     { (unichar_t *) N_("Judezmo"), NULL, 0, 0, (void *) CHR('J','U','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
582     { (unichar_t *) N_("Jula"), NULL, 0, 0, (void *) CHR('J','U','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
583     { (unichar_t *) N_("Kabardian"), NULL, 0, 0, (void *) CHR('K','A','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
584     { (unichar_t *) N_("Kabyle"), NULL, 0, 0, (void *) CHR('K','A','B','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
585     { (unichar_t *) N_("Kachchi"), NULL, 0, 0, (void *) CHR('K','A','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
586     { (unichar_t *) N_("Kalenjin"), NULL, 0, 0, (void *) CHR('K','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
587     { (unichar_t *) N_("Lang|Kannada"), NULL, 0, 0, (void *) CHR('K','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
588     { (unichar_t *) N_("Karachay"), NULL, 0, 0, (void *) CHR('K','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
589     { (unichar_t *) N_("Lang|Georgian"), NULL, 0, 0, (void *) CHR('K','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
590     { (unichar_t *) N_("Kazakh"), NULL, 0, 0, (void *) CHR('K','A','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
591     { (unichar_t *) N_("Makonde"), NULL, 0, 0, (void *) CHR('K','D','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
592     { (unichar_t *) N_("Kabuverdianu"), NULL, 0, 0, (void *) CHR('K','E','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
593     { (unichar_t *) N_("Kebena"), NULL, 0, 0, (void *) CHR('K','E','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
594     { (unichar_t *) N_("Kekchi"), NULL, 0, 0, (void *) CHR('K','E','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
595     { (unichar_t *) N_("Khutsuri Georgian"), NULL, 0, 0, (void *) CHR('K','G','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
596     { (unichar_t *) N_("Khakass"), NULL, 0, 0, (void *) CHR('K','H','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
597     { (unichar_t *) N_("Khanty-Kazim"), NULL, 0, 0, (void *) CHR('K','H','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
598     { (unichar_t *) N_("Lang|Khmer"), NULL, 0, 0, (void *) CHR('K','H','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
599     { (unichar_t *) N_("Khanty-Shurishkar"), NULL, 0, 0, (void *) CHR('K','H','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
600     { (unichar_t *) N_("Khamti Shan"), NULL, 0, 0, (void *) CHR('K','H','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
601     { (unichar_t *) N_("Khanty-Vakhi"), NULL, 0, 0, (void *) CHR('K','H','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
602     { (unichar_t *) N_("Khowar"), NULL, 0, 0, (void *) CHR('K','H','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
603     { (unichar_t *) N_("Kikuyu"), NULL, 0, 0, (void *) CHR('K','I','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
604     { (unichar_t *) N_("Kirghiz"), NULL, 0, 0, (void *) CHR('K','I','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
605     { (unichar_t *) N_("Kisii"), NULL, 0, 0, (void *) CHR('K','I','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
606     { (unichar_t *) N_("Kirmanjki"), NULL, 0, 0, (void *) CHR('K','I','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
607     { (unichar_t *) N_("Southern Kiwai"), NULL, 0, 0, (void *) CHR('K','J','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
608     { (unichar_t *) N_("Eastern Pwo Karen"), NULL, 0, 0, (void *) CHR('K','J','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
609     { (unichar_t *) N_("Bumthangkha"), NULL, 0, 0, (void *) CHR('K','J','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
610     { (unichar_t *) N_("Kokni"), NULL, 0, 0, (void *) CHR('K','K','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
611     { (unichar_t *) N_("Kalmyk"), NULL, 0, 0, (void *) CHR('K','L','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
612     { (unichar_t *) N_("Kamba"), NULL, 0, 0, (void *) CHR('K','M','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
613     { (unichar_t *) N_("Kumaoni"), NULL, 0, 0, (void *) CHR('K','M','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
614     { (unichar_t *) N_("Komo"), NULL, 0, 0, (void *) CHR('K','M','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
615     { (unichar_t *) N_("Komso"), NULL, 0, 0, (void *) CHR('K','M','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
616     { (unichar_t *) N_("Khorasani Turkic"), NULL, 0, 0, (void *) CHR('K','M','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
617     { (unichar_t *) N_("Kanuri"), NULL, 0, 0, (void *) CHR('K','N','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
618     { (unichar_t *) N_("Kodagu"), NULL, 0, 0, (void *) CHR('K','O','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
619     { (unichar_t *) N_("Korean Old Hangul"), NULL, 0, 0, (void *) CHR('K','O','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
620     { (unichar_t *) N_("Konkani"), NULL, 0, 0, (void *) CHR('K','O','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
621     { (unichar_t *) N_("Komi"), NULL, 0, 0, (void *) CHR('K','O','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
622     { (unichar_t *) N_("Kikongo"), NULL, 0, 0, (void *) CHR('K','O','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
623     { (unichar_t *) N_("Kongo"), NULL, 0, 0, (void *) CHR('K','O','N','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
624     { (unichar_t *) N_("Komi-Permyak"), NULL, 0, 0, (void *) CHR('K','O','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
625     { (unichar_t *) N_("Korean"), NULL, 0, 0, (void *) CHR('K','O','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
626     { (unichar_t *) N_("Kosraean"), NULL, 0, 0, (void *) CHR('K','O','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
627     { (unichar_t *) N_("Komi-Zyrian"), NULL, 0, 0, (void *) CHR('K','O','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
628     { (unichar_t *) N_("Kpelle"), NULL, 0, 0, (void *) CHR('K','P','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
629     { (unichar_t *) N_("Krio"), NULL, 0, 0, (void *) CHR('K','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
630     { (unichar_t *) N_("Karakalpak"), NULL, 0, 0, (void *) CHR('K','R','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
631     { (unichar_t *) N_("Karelian"), NULL, 0, 0, (void *) CHR('K','R','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
632     { (unichar_t *) N_("Karaim"), NULL, 0, 0, (void *) CHR('K','R','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
633     { (unichar_t *) N_("Karen"), NULL, 0, 0, (void *) CHR('K','R','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
634     { (unichar_t *) N_("Koorete"), NULL, 0, 0, (void *) CHR('K','R','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
635     { (unichar_t *) N_("Kashmiri"), NULL, 0, 0, (void *) CHR('K','S','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
636     { (unichar_t *) N_("Ripuarian"), NULL, 0, 0, (void *) CHR('K','S','H','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
637     { (unichar_t *) N_("Khasi"), NULL, 0, 0, (void *) CHR('K','S','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
638     { (unichar_t *) N_("Kildin Sami"), NULL, 0, 0, (void *) CHR('K','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
639     { (unichar_t *) N_("S'gaw Karen"), NULL, 0, 0, (void *) CHR('K','S','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
640     { (unichar_t *) N_("Kuanyama"), NULL, 0, 0, (void *) CHR('K','U','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
641     { (unichar_t *) N_("Kui"), NULL, 0, 0, (void *) CHR('K','U','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
642     { (unichar_t *) N_("Kulvi"), NULL, 0, 0, (void *) CHR('K','U','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
643     { (unichar_t *) N_("Kumyk"), NULL, 0, 0, (void *) CHR('K','U','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
644     { (unichar_t *) N_("Kurdish"), NULL, 0, 0, (void *) CHR('K','U','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
645     { (unichar_t *) N_("Kurukh"), NULL, 0, 0, (void *) CHR('K','U','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
646     { (unichar_t *) N_("Kuy"), NULL, 0, 0, (void *) CHR('K','U','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
647     { (unichar_t *) N_("Koryak"), NULL, 0, 0, (void *) CHR('K','Y','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
648     { (unichar_t *) N_("Western Kayah"), NULL, 0, 0, (void *) CHR('K','Y','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
649     { (unichar_t *) N_("Ladin"), NULL, 0, 0, (void *) CHR('L','A','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
650     { (unichar_t *) N_("Lahuli"), NULL, 0, 0, (void *) CHR('L','A','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
651     { (unichar_t *) N_("Lak"), NULL, 0, 0, (void *) CHR('L','A','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
652     { (unichar_t *) N_("Lambani"), NULL, 0, 0, (void *) CHR('L','A','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
653     { (unichar_t *) N_("Lang|Lao"), NULL, 0, 0, (void *) CHR('L','A','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
654     { (unichar_t *) N_("Lang|Latin"), NULL, 0, 0, (void *) CHR('L','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
655     { (unichar_t *) N_("Laz"), NULL, 0, 0, (void *) CHR('L','A','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
656     { (unichar_t *) N_("L-Cree"), NULL, 0, 0, (void *) CHR('L','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
657     { (unichar_t *) N_("Ladakhi"), NULL, 0, 0, (void *) CHR('L','D','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
658     { (unichar_t *) N_("Lezgi"), NULL, 0, 0, (void *) CHR('L','E','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
659     { (unichar_t *) N_("Ligurian"), NULL, 0, 0, (void *) CHR('L','I','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
660     { (unichar_t *) N_("Limburgish"), NULL, 0, 0, (void *) CHR('L','I','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
661     { (unichar_t *) N_("Lingala"), NULL, 0, 0, (void *) CHR('L','I','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
662     { (unichar_t *) N_("Lang|Lisu"), NULL, 0, 0, (void *) CHR('L','I','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
663     { (unichar_t *) N_("Lampung"), NULL, 0, 0, (void *) CHR('L','J','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
664     { (unichar_t *) N_("Laki"), NULL, 0, 0, (void *) CHR('L','K','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
665     { (unichar_t *) N_("Low Mari"), NULL, 0, 0, (void *) CHR('L','M','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
666     { (unichar_t *) N_("Lang|Limbu"), NULL, 0, 0, (void *) CHR('L','M','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
667     { (unichar_t *) N_("Lombard"), NULL, 0, 0, (void *) CHR('L','M','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
668     { (unichar_t *) N_("Lomwe"), NULL, 0, 0, (void *) CHR('L','M','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
669     { (unichar_t *) N_("Lang|Loma"), NULL, 0, 0, (void *) CHR('L','O','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
670     { (unichar_t *) N_("Luri"), NULL, 0, 0, (void *) CHR('L','R','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
671     { (unichar_t *) N_("Lower Sorbian"), NULL, 0, 0, (void *) CHR('L','S','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
672     { (unichar_t *) N_("Lule Sami"), NULL, 0, 0, (void *) CHR('L','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
673     { (unichar_t *) N_("Lithuanian"), NULL, 0, 0, (void *) CHR('L','T','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
674     { (unichar_t *) N_("Luxembourgish"), NULL, 0, 0, (void *) CHR('L','T','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
675     { (unichar_t *) N_("Luba-Lulua"), NULL, 0, 0, (void *) CHR('L','U','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
676     { (unichar_t *) N_("Luba-Katanga"), NULL, 0, 0, (void *) CHR('L','U','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
677     { (unichar_t *) N_("Luganda"), NULL, 0, 0, (void *) CHR('L','U','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
678     { (unichar_t *) N_("Luhya"), NULL, 0, 0, (void *) CHR('L','U','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
679     { (unichar_t *) N_("Luo"), NULL, 0, 0, (void *) CHR('L','U','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
680     { (unichar_t *) N_("Latvian"), NULL, 0, 0, (void *) CHR('L','V','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
681     { (unichar_t *) N_("Madura"), NULL, 0, 0, (void *) CHR('M','A','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
682     { (unichar_t *) N_("Magahi"), NULL, 0, 0, (void *) CHR('M','A','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
683     { (unichar_t *) N_("Marshallese"), NULL, 0, 0, (void *) CHR('M','A','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
684     { (unichar_t *) N_("Majang"), NULL, 0, 0, (void *) CHR('M','A','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
685     { (unichar_t *) N_("Makua"), NULL, 0, 0, (void *) CHR('M','A','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
686     { (unichar_t *) N_("Malayalam Traditional"), NULL, 0, 0, (void *) CHR('M','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
687     { (unichar_t *) N_("Mam"), NULL, 0, 0, (void *) CHR('M','A','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
688     { (unichar_t *) N_("Mansi"), NULL, 0, 0, (void *) CHR('M','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
689     { (unichar_t *) N_("Mapudungun"), NULL, 0, 0, (void *) CHR('M','A','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
690     { (unichar_t *) N_("Marathi"), NULL, 0, 0, (void *) CHR('M','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
691     { (unichar_t *) N_("Marwari"), NULL, 0, 0, (void *) CHR('M','A','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
692     { (unichar_t *) N_("Mbundu"), NULL, 0, 0, (void *) CHR('M','B','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
693     { (unichar_t *) N_("Mbo"), NULL, 0, 0, (void *) CHR('M','B','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
694     { (unichar_t *) N_("Lang|Manchu"), NULL, 0, 0, (void *) CHR('M','C','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
695     { (unichar_t *) N_("Moose Cree"), NULL, 0, 0, (void *) CHR('M','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
696     { (unichar_t *) N_("Mende"), NULL, 0, 0, (void *) CHR('M','D','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
697     { (unichar_t *) N_("Mandar"), NULL, 0, 0, (void *) CHR('M','D','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
698     { (unichar_t *) N_("Me'en"), NULL, 0, 0, (void *) CHR('M','E','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
699     { (unichar_t *) N_("Meru"), NULL, 0, 0, (void *) CHR('M','E','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
700     { (unichar_t *) N_("Pattani Malay"), NULL, 0, 0, (void *) CHR('M','F','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
701     { (unichar_t *) N_("Morisyen"), NULL, 0, 0, (void *) CHR('M','F','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
702     { (unichar_t *) N_("Minangkabau"), NULL, 0, 0, (void *) CHR('M','I','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
703     { (unichar_t *) N_("Mizo"), NULL, 0, 0, (void *) CHR('M','I','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
704     { (unichar_t *) N_("Macedonian"), NULL, 0, 0, (void *) CHR('M','K','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
705     { (unichar_t *) N_("Lang|Makasar"), NULL, 0, 0, (void *) CHR('M','K','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
706     { (unichar_t *) N_("Kituba"), NULL, 0, 0, (void *) CHR('M','K','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
707     { (unichar_t *) N_("Male"), NULL, 0, 0, (void *) CHR('M','L','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
708     { (unichar_t *) N_("Malagasy"), NULL, 0, 0, (void *) CHR('M','L','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
709     { (unichar_t *) N_("Malinke"), NULL, 0, 0, (void *) CHR('M','L','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
710     { (unichar_t *) N_("Malayalam Reformed"), NULL, 0, 0, (void *) CHR('M','L','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
711     { (unichar_t *) N_("Malay"), NULL, 0, 0, (void *) CHR('M','L','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
712     { (unichar_t *) N_("Mandinka"), NULL, 0, 0, (void *) CHR('M','N','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
713     { (unichar_t *) N_("Lang|Mongolian"), NULL, 0, 0, (void *) CHR('M','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
714     { (unichar_t *) N_("Manipuri"), NULL, 0, 0, (void *) CHR('M','N','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
715     { (unichar_t *) N_("Maninka"), NULL, 0, 0, (void *) CHR('M','N','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
716     { (unichar_t *) N_("Manx Gaelic"), NULL, 0, 0, (void *) CHR('M','N','X',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
717     { (unichar_t *) N_("Mohawk"), NULL, 0, 0, (void *) CHR('M','O','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
718     { (unichar_t *) N_("Moksha"), NULL, 0, 0, (void *) CHR('M','O','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
719     { (unichar_t *) N_("Moldavian"), NULL, 0, 0, (void *) CHR('M','O','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
720     { (unichar_t *) N_("Mon"), NULL, 0, 0, (void *) CHR('M','O','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
721     { (unichar_t *) N_("Moroccan"), NULL, 0, 0, (void *) CHR('M','O','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
722     { (unichar_t *) N_("Mossi"), NULL, 0, 0, (void *) CHR('M','O','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
723     { (unichar_t *) N_("Maori"), NULL, 0, 0, (void *) CHR('M','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
724     { (unichar_t *) N_("Maithili"), NULL, 0, 0, (void *) CHR('M','T','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
725     { (unichar_t *) N_("Maltese"), NULL, 0, 0, (void *) CHR('M','T','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
726     { (unichar_t *) N_("Mundari"), NULL, 0, 0, (void *) CHR('M','U','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
727     { (unichar_t *) N_("Muscogee"), NULL, 0, 0, (void *) CHR('M','U','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
728     { (unichar_t *) N_("Mirandese"), NULL, 0, 0, (void *) CHR('M','W','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
729     { (unichar_t *) N_("Hmong Daw"), NULL, 0, 0, (void *) CHR('M','W','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
730     { (unichar_t *) N_("Lang|Mayan"), NULL, 0, 0, (void *) CHR('M','Y','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
731     { (unichar_t *) N_("Mazanderani"), NULL, 0, 0, (void *) CHR('M','Z','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
732     { (unichar_t *) N_("Naga-Assamese"), NULL, 0, 0, (void *) CHR('N','A','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
733     { (unichar_t *) N_("Nahuatl"), NULL, 0, 0, (void *) CHR('N','A','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
734     { (unichar_t *) N_("Nanai"), NULL, 0, 0, (void *) CHR('N','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
735     { (unichar_t *) N_("Neapolitan"), NULL, 0, 0, (void *) CHR('N','A','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
736     { (unichar_t *) N_("Naskapi"), NULL, 0, 0, (void *) CHR('N','A','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
737     { (unichar_t *) N_("Nauruan"), NULL, 0, 0, (void *) CHR('N','A','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
738     { (unichar_t *) N_("Navajo"), NULL, 0, 0, (void *) CHR('N','A','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
739     { (unichar_t *) N_("N-Cree"), NULL, 0, 0, (void *) CHR('N','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
740     { (unichar_t *) N_("Ndebele"), NULL, 0, 0, (void *) CHR('N','D','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
741     { (unichar_t *) N_("Ndau"), NULL, 0, 0, (void *) CHR('N','D','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
742     { (unichar_t *) N_("Ndonga"), NULL, 0, 0, (void *) CHR('N','D','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
743     { (unichar_t *) N_("Low Saxon"), NULL, 0, 0, (void *) CHR('N','D','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
744     { (unichar_t *) N_("Nepali"), NULL, 0, 0, (void *) CHR('N','E','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
745     { (unichar_t *) N_("Newari"), NULL, 0, 0, (void *) CHR('N','E','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
746     { (unichar_t *) N_("Ngbaka"), NULL, 0, 0, (void *) CHR('N','G','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
747     { (unichar_t *) N_("Nagari"), NULL, 0, 0, (void *) CHR('N','G','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
748     { (unichar_t *) N_("Norway House Cree"), NULL, 0, 0, (void *) CHR('N','H','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
749     { (unichar_t *) N_("Nisi"), NULL, 0, 0, (void *) CHR('N','I','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
750     { (unichar_t *) N_("Niuean"), NULL, 0, 0, (void *) CHR('N','I','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
751     { (unichar_t *) N_("Nkole"), NULL, 0, 0, (void *) CHR('N','K','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
752     { (unichar_t *) N_("N'Ko"), NULL, 0, 0, (void *) CHR('N','K','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
753     { (unichar_t *) N_("Dutch"), NULL, 0, 0, (void *) CHR('N','L','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
754     { (unichar_t *) N_("Nimadi"), NULL, 0, 0, (void *) CHR('N','O','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
755     { (unichar_t *) N_("Nogai"), NULL, 0, 0, (void *) CHR('N','O','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
756     { (unichar_t *) N_("Norwegian"), NULL, 0, 0, (void *) CHR('N','O','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
757     { (unichar_t *) N_("Novial"), NULL, 0, 0, (void *) CHR('N','O','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
758     { (unichar_t *) N_("Northern Sami"), NULL, 0, 0, (void *) CHR('N','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
759     { (unichar_t *) N_("Northern Sotho"), NULL, 0, 0, (void *) CHR('N','S','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
760     { (unichar_t *) N_("Northern Tai"), NULL, 0, 0, (void *) CHR('N','T','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
761     { (unichar_t *) N_("Esperanto"), NULL, 0, 0, (void *) CHR('N','T','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
762     { (unichar_t *) N_("Nyamwezi"), NULL, 0, 0, (void *) CHR('N','Y','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
763     { (unichar_t *) N_("Nynorsk"), NULL, 0, 0, (void *) CHR('N','Y','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
764     { (unichar_t *) N_("Mbembe Tigon"), NULL, 0, 0, (void *) CHR('N','Z','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
765     { (unichar_t *) N_("Occitan"), NULL, 0, 0, (void *) CHR('O','C','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
766     { (unichar_t *) N_("Oji-Cree"), NULL, 0, 0, (void *) CHR('O','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
767     { (unichar_t *) N_("Ojibway"), NULL, 0, 0, (void *) CHR('O','J','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
768     { (unichar_t *) N_("Lang|Oriya"), NULL, 0, 0, (void *) CHR('O','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
769     { (unichar_t *) N_("Oromo"), NULL, 0, 0, (void *) CHR('O','R','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
770     { (unichar_t *) N_("Ossetian"), NULL, 0, 0, (void *) CHR('O','S','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
771     { (unichar_t *) N_("Palestinian Aramaic"), NULL, 0, 0, (void *) CHR('P','A','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
772     { (unichar_t *) N_("Pangasinan"), NULL, 0, 0, (void *) CHR('P','A','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
773     { (unichar_t *) N_("Pali"), NULL, 0, 0, (void *) CHR('P','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
774     { (unichar_t *) N_("Pampangan"), NULL, 0, 0, (void *) CHR('P','A','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
775     { (unichar_t *) N_("Punjabi"), NULL, 0, 0, (void *) CHR('P','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
776     { (unichar_t *) N_("Palpa"), NULL, 0, 0, (void *) CHR('P','A','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
777     { (unichar_t *) N_("Papiamentu"), NULL, 0, 0, (void *) CHR('P','A','P','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
778     { (unichar_t *) N_("Pashto"), NULL, 0, 0, (void *) CHR('P','A','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
779     { (unichar_t *) N_("Palauan"), NULL, 0, 0, (void *) CHR('P','A','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
780     { (unichar_t *) N_("Bouyei"), NULL, 0, 0, (void *) CHR('P','C','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
781     { (unichar_t *) N_("Picard"), NULL, 0, 0, (void *) CHR('P','C','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
782     { (unichar_t *) N_("Pennsylvania German"), NULL, 0, 0, (void *) CHR('P','D','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
783     { (unichar_t *) N_("Polytonic Greek"), NULL, 0, 0, (void *) CHR('P','G','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
784     { (unichar_t *) N_("Phake"), NULL, 0, 0, (void *) CHR('P','H','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
785     { (unichar_t *) N_("Norfolk"), NULL, 0, 0, (void *) CHR('P','I','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
786     { (unichar_t *) N_("Pilipino (Filipino)"), NULL, 0, 0, (void *) CHR('P','I','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
787     { (unichar_t *) N_("Palaung"), NULL, 0, 0, (void *) CHR('P','L','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
788     { (unichar_t *) N_("Polish"), NULL, 0, 0, (void *) CHR('P','L','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
789     { (unichar_t *) N_("Piemontese"), NULL, 0, 0, (void *) CHR('P','M','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
790     { (unichar_t *) N_("Western Panjabi"), NULL, 0, 0, (void *) CHR('P','N','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
791     { (unichar_t *) N_("Pocomchi"), NULL, 0, 0, (void *) CHR('P','O','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
792     { (unichar_t *) N_("Pohnpeian"), NULL, 0, 0, (void *) CHR('P','O','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
793     { (unichar_t *) N_("Provencal"), NULL, 0, 0, (void *) CHR('P','R','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
794     { (unichar_t *) N_("Portuguese"), NULL, 0, 0, (void *) CHR('P','T','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
795     { (unichar_t *) N_("Western Pwo Karen"), NULL, 0, 0, (void *) CHR('P','W','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
796     { (unichar_t *) N_("Chin"), NULL, 0, 0, (void *) CHR('Q','I','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
797     { (unichar_t *) N_("K'iche'"), NULL, 0, 0, (void *) CHR('Q','U','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
798     { (unichar_t *) N_("Quechua (Bolivia)"), NULL, 0, 0, (void *) CHR('Q','U','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
799     { (unichar_t *) N_("Quechua"), NULL, 0, 0, (void *) CHR('Q','U','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
800     { (unichar_t *) N_("Quechua (Ecuador)"), NULL, 0, 0, (void *) CHR('Q','V','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
801     { (unichar_t *) N_("Quechua (Peru)"), NULL, 0, 0, (void *) CHR('Q','W','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
802     { (unichar_t *) N_("Rajasthani"), NULL, 0, 0, (void *) CHR('R','A','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
803     { (unichar_t *) N_("Rarotongan"), NULL, 0, 0, (void *) CHR('R','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
804     { (unichar_t *) N_("Russian Buriat"), NULL, 0, 0, (void *) CHR('R','B','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
805     { (unichar_t *) N_("R-Cree"), NULL, 0, 0, (void *) CHR('R','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
806     { (unichar_t *) N_("Rejang"), NULL, 0, 0, (void *) CHR('R','E','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
807     { (unichar_t *) N_("Riang"), NULL, 0, 0, (void *) CHR('R','I','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
808     { (unichar_t *) N_("Tarifit"), NULL, 0, 0, (void *) CHR('R','I','F',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
809     { (unichar_t *) N_("Ritarungo"), NULL, 0, 0, (void *) CHR('R','I','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
810     { (unichar_t *) N_("Arakwal"), NULL, 0, 0, (void *) CHR('R','K','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
811     { (unichar_t *) N_("Romansh"), NULL, 0, 0, (void *) CHR('R','M','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
812     { (unichar_t *) N_("Vlax Romani"), NULL, 0, 0, (void *) CHR('R','M','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
813     { (unichar_t *) N_("Romanian"), NULL, 0, 0, (void *) CHR('R','O','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
814     { (unichar_t *) N_("Romany"), NULL, 0, 0, (void *) CHR('R','O','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
815     { (unichar_t *) N_("Rusyn"), NULL, 0, 0, (void *) CHR('R','S','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
816     { (unichar_t *) N_("Rotuman"), NULL, 0, 0, (void *) CHR('R','T','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
817     { (unichar_t *) N_("Ruanda"), NULL, 0, 0, (void *) CHR('R','U','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
818     { (unichar_t *) N_("Rundi"), NULL, 0, 0, (void *) CHR('R','U','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
819     { (unichar_t *) N_("Aromanian"), NULL, 0, 0, (void *) CHR('R','U','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
820     { (unichar_t *) N_("Russian"), NULL, 0, 0, (void *) CHR('R','U','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
821     { (unichar_t *) N_("Sadri"), NULL, 0, 0, (void *) CHR('S','A','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
822     { (unichar_t *) N_("Sanskrit"), NULL, 0, 0, (void *) CHR('S','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
823     { (unichar_t *) N_("Sasak"), NULL, 0, 0, (void *) CHR('S','A','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
824     { (unichar_t *) N_("Santali"), NULL, 0, 0, (void *) CHR('S','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
825     { (unichar_t *) N_("Sayisi"), NULL, 0, 0, (void *) CHR('S','A','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
826     { (unichar_t *) N_("Sicilian"), NULL, 0, 0, (void *) CHR('S','C','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
827     { (unichar_t *) N_("Scots"), NULL, 0, 0, (void *) CHR('S','C','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
828     { (unichar_t *) N_("North Slavey"), NULL, 0, 0, (void *) CHR('S','C','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
829     { (unichar_t *) N_("Sekota"), NULL, 0, 0, (void *) CHR('S','E','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
830     { (unichar_t *) N_("Selkup"), NULL, 0, 0, (void *) CHR('S','E','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
831     { (unichar_t *) N_("Old Irish"), NULL, 0, 0, (void *) CHR('S','G','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
832     { (unichar_t *) N_("Sango"), NULL, 0, 0, (void *) CHR('S','G','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
833     { (unichar_t *) N_("Samogitian"), NULL, 0, 0, (void *) CHR('S','G','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
834     { (unichar_t *) N_("Tachelhit"), NULL, 0, 0, (void *) CHR('S','H','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
835     { (unichar_t *) N_("Shan"), NULL, 0, 0, (void *) CHR('S','H','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
836     { (unichar_t *) N_("Sibe"), NULL, 0, 0, (void *) CHR('S','I','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
837     { (unichar_t *) N_("Sidamo"), NULL, 0, 0, (void *) CHR('S','I','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
838     { (unichar_t *) N_("Silte Gurage"), NULL, 0, 0, (void *) CHR('S','I','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
839     { (unichar_t *) N_("Skolt Sami"), NULL, 0, 0, (void *) CHR('S','K','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
840     { (unichar_t *) N_("Slovak"), NULL, 0, 0, (void *) CHR('S','K','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
841     { (unichar_t *) N_("Slavey"), NULL, 0, 0, (void *) CHR('S','L','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
842     { (unichar_t *) N_("Slovenian"), NULL, 0, 0, (void *) CHR('S','L','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
843     { (unichar_t *) N_("Somali"), NULL, 0, 0, (void *) CHR('S','M','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
844     { (unichar_t *) N_("Samoan"), NULL, 0, 0, (void *) CHR('S','M','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
845     { (unichar_t *) N_("Sena"), NULL, 0, 0, (void *) CHR('S','N','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
846     { (unichar_t *) N_("Shona"), NULL, 0, 0, (void *) CHR('S','N','A','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
847     { (unichar_t *) N_("Sindhi"), NULL, 0, 0, (void *) CHR('S','N','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
848     { (unichar_t *) N_("Lang|Sinhalese"), NULL, 0, 0, (void *) CHR('S','N','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
849     { (unichar_t *) N_("Soninke"), NULL, 0, 0, (void *) CHR('S','N','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
850     { (unichar_t *) N_("Sodo Gurage"), NULL, 0, 0, (void *) CHR('S','O','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
851     { (unichar_t *) N_("Songe"), NULL, 0, 0, (void *) CHR('S','O','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
852     { (unichar_t *) N_("Southern Sotho"), NULL, 0, 0, (void *) CHR('S','O','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
853     { (unichar_t *) N_("Albanian"), NULL, 0, 0, (void *) CHR('S','Q','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
854     { (unichar_t *) N_("Serbian"), NULL, 0, 0, (void *) CHR('S','R','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
855     { (unichar_t *) N_("Sardinian"), NULL, 0, 0, (void *) CHR('S','R','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
856     { (unichar_t *) N_("Saraiki"), NULL, 0, 0, (void *) CHR('S','R','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
857     { (unichar_t *) N_("Serer"), NULL, 0, 0, (void *) CHR('S','R','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
858     { (unichar_t *) N_("South Slavey"), NULL, 0, 0, (void *) CHR('S','S','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
859     { (unichar_t *) N_("Southern Sami"), NULL, 0, 0, (void *) CHR('S','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
860     { (unichar_t *) N_("Saterland Frisian"), NULL, 0, 0, (void *) CHR('S','T','Q',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
861     { (unichar_t *) N_("Sukuma"), NULL, 0, 0, (void *) CHR('S','U','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
862     { (unichar_t *) N_("Lang|Sundanese"), NULL, 0, 0, (void *) CHR('S','U','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
863     { (unichar_t *) N_("Suri"), NULL, 0, 0, (void *) CHR('S','U','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
864     { (unichar_t *) N_("Svan"), NULL, 0, 0, (void *) CHR('S','V','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
865     { (unichar_t *) N_("Swedish"), NULL, 0, 0, (void *) CHR('S','V','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
866     { (unichar_t *) N_("Swadaya Aramaic"), NULL, 0, 0, (void *) CHR('S','W','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
867     { (unichar_t *) N_("Swahili"), NULL, 0, 0, (void *) CHR('S','W','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
868     { (unichar_t *) N_("Swazi"), NULL, 0, 0, (void *) CHR('S','W','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
869     { (unichar_t *) N_("Sutu"), NULL, 0, 0, (void *) CHR('S','X','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
870     { (unichar_t *) N_("Upper Saxon"), NULL, 0, 0, (void *) CHR('S','X','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
871     { (unichar_t *) N_("Sylheti"), NULL, 0, 0, (void *) CHR('S','Y','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
872     { (unichar_t *) N_("Lang|Syriac"), NULL, 0, 0, (void *) CHR('S','Y','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
873     { (unichar_t *) N_("Syriac (Estrangela)"), NULL, 0, 0, (void *) CHR('S','Y','R','E'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
874     { (unichar_t *) N_("Syriac (Western script)"), NULL, 0, 0, (void *) CHR('S','Y','R','J'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
875     { (unichar_t *) N_("Syriac (Eastern script)"), NULL, 0, 0, (void *) CHR('S','Y','R','N'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
876     { (unichar_t *) N_("Silesian"), NULL, 0, 0, (void *) CHR('S','Z','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
877     { (unichar_t *) N_("Tabasaran"), NULL, 0, 0, (void *) CHR('T','A','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
878     { (unichar_t *) N_("Tajiki"), NULL, 0, 0, (void *) CHR('T','A','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
879     { (unichar_t *) N_("Lang|Tamil"), NULL, 0, 0, (void *) CHR('T','A','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
880     { (unichar_t *) N_("Tatar"), NULL, 0, 0, (void *) CHR('T','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
881     { (unichar_t *) N_("TH-Cree"), NULL, 0, 0, (void *) CHR('T','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
882     { (unichar_t *) N_("Dehong Dai"), NULL, 0, 0, (void *) CHR('T','D','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
883     { (unichar_t *) N_("Lang|Telugu"), NULL, 0, 0, (void *) CHR('T','E','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
884     { (unichar_t *) N_("Tetum"), NULL, 0, 0, (void *) CHR('T','E','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
885     { (unichar_t *) N_("Lang|Tagalog"), NULL, 0, 0, (void *) CHR('T','G','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
886     { (unichar_t *) N_("Tongan"), NULL, 0, 0, (void *) CHR('T','G','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
887     { (unichar_t *) N_("Tigre"), NULL, 0, 0, (void *) CHR('T','G','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
888     { (unichar_t *) N_("Tigrinya"), NULL, 0, 0, (void *) CHR('T','G','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
889     { (unichar_t *) N_("Lang|Thai"), NULL, 0, 0, (void *) CHR('T','H','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
890     { (unichar_t *) N_("Tahitian"), NULL, 0, 0, (void *) CHR('T','H','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
891     { (unichar_t *) N_("Lang|Tibetan"), NULL, 0, 0, (void *) CHR('T','I','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
892     { (unichar_t *) N_("Tiv"), NULL, 0, 0, (void *) CHR('T','I','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
893     { (unichar_t *) N_("Turkmen"), NULL, 0, 0, (void *) CHR('T','K','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
894     { (unichar_t *) N_("Tamashek"), NULL, 0, 0, (void *) CHR('T','M','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
895     { (unichar_t *) N_("Temne"), NULL, 0, 0, (void *) CHR('T','M','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
896     { (unichar_t *) N_("Tswana"), NULL, 0, 0, (void *) CHR('T','N','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
897     { (unichar_t *) N_("Tundra Nenets"), NULL, 0, 0, (void *) CHR('T','N','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
898     { (unichar_t *) N_("Tonga"), NULL, 0, 0, (void *) CHR('T','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
899     { (unichar_t *) N_("Todo"), NULL, 0, 0, (void *) CHR('T','O','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
900     { (unichar_t *) N_("Toma"), NULL, 0, 0, (void *) CHR('T','O','D','0'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
901     { (unichar_t *) N_("Tok Pisin"), NULL, 0, 0, (void *) CHR('T','P','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
902     { (unichar_t *) N_("Turkish"), NULL, 0, 0, (void *) CHR('T','R','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
903     { (unichar_t *) N_("Tsonga"), NULL, 0, 0, (void *) CHR('T','S','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
904     { (unichar_t *) N_("Tshangla"), NULL, 0, 0, (void *) CHR('T','S','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
905     { (unichar_t *) N_("Turoyo Aramaic"), NULL, 0, 0, (void *) CHR('T','U','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
906     { (unichar_t *) N_("Tumbuka"), NULL, 0, 0, (void *) CHR('T','U','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
907     { (unichar_t *) N_("Tulu"), NULL, 0, 0, (void *) CHR('T','U','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
908     { (unichar_t *) N_("Tuvin"), NULL, 0, 0, (void *) CHR('T','U','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
909     { (unichar_t *) N_("Tuvalu"), NULL, 0, 0, (void *) CHR('T','V','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
910     { (unichar_t *) N_("Twi"), NULL, 0, 0, (void *) CHR('T','W','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
911     { (unichar_t *) NU_("Tày"), NULL, 0, 0, (void *) CHR('T','Y','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
912     { (unichar_t *) N_("Tamazight"), NULL, 0, 0, (void *) CHR('T','Z','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
913     { (unichar_t *) N_("Tzotzil"), NULL, 0, 0, (void *) CHR('T','Z','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
914     { (unichar_t *) N_("Udmurt"), NULL, 0, 0, (void *) CHR('U','D','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
915     { (unichar_t *) N_("Ukrainian"), NULL, 0, 0, (void *) CHR('U','K','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
916     { (unichar_t *) N_("Umbundu"), NULL, 0, 0, (void *) CHR('U','M','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
917     { (unichar_t *) N_("Urdu"), NULL, 0, 0, (void *) CHR('U','R','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
918     { (unichar_t *) N_("Upper Sorbian"), NULL, 0, 0, (void *) CHR('U','S','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
919     { (unichar_t *) N_("Uyghur"), NULL, 0, 0, (void *) CHR('U','Y','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
920     { (unichar_t *) N_("Uzbek"), NULL, 0, 0, (void *) CHR('U','Z','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
921     { (unichar_t *) N_("Venetian"), NULL, 0, 0, (void *) CHR('V','E','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
922     { (unichar_t *) N_("Venda"), NULL, 0, 0, (void *) CHR('V','E','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
923     { (unichar_t *) N_("Vietnamese"), NULL, 0, 0, (void *) CHR('V','I','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
924     { (unichar_t *) NU_("Volapük"), NULL, 0, 0, (void *) CHR('V','O','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
925     { (unichar_t *) NU_("Võro"), NULL, 0, 0, (void *) CHR('V','R','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
926     { (unichar_t *) N_("Wa"), NULL, 0, 0, (void *) CHR('W','A',' ',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
927     { (unichar_t *) N_("Wagdi"), NULL, 0, 0, (void *) CHR('W','A','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
928     { (unichar_t *) N_("Waray-Waray"), NULL, 0, 0, (void *) CHR('W','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
929     { (unichar_t *) N_("West-Cree"), NULL, 0, 0, (void *) CHR('W','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
930     { (unichar_t *) N_("Welsh"), NULL, 0, 0, (void *) CHR('W','E','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
931     { (unichar_t *) N_("Wolof"), NULL, 0, 0, (void *) CHR('W','L','F',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
932     { (unichar_t *) N_("Walloon"), NULL, 0, 0, (void *) CHR('W','L','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
933     { (unichar_t *) N_("Mewati"), NULL, 0, 0, (void *) CHR('W','T','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
934     { (unichar_t *) N_("Tai Lue"), NULL, 0, 0, (void *) CHR('X','B','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
935     { (unichar_t *) N_("Xhosa"), NULL, 0, 0, (void *) CHR('X','H','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
936     { (unichar_t *) N_("Minjangbal"), NULL, 0, 0, (void *) CHR('X','J','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
937     { (unichar_t *) N_("Khengkha"), NULL, 0, 0, (void *) CHR('X','K','F',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
938     { (unichar_t *) N_("Soga"), NULL, 0, 0, (void *) CHR('X','O','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
939     { (unichar_t *) N_("Kpelle (Liberia)"), NULL, 0, 0, (void *) CHR('X','P','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
940     { (unichar_t *) N_("Yakut"), NULL, 0, 0, (void *) CHR('Y','A','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
941     { (unichar_t *) N_("Yao"), NULL, 0, 0, (void *) CHR('Y','A','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
942     { (unichar_t *) N_("Yapese"), NULL, 0, 0, (void *) CHR('Y','A','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
943     { (unichar_t *) N_("Yoruba"), NULL, 0, 0, (void *) CHR('Y','B','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
944     { (unichar_t *) N_("Y-Cree"), NULL, 0, 0, (void *) CHR('Y','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
945     { (unichar_t *) N_("Yi Classic"), NULL, 0, 0, (void *) CHR('Y','I','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
946     { (unichar_t *) N_("Yi Modern"), NULL, 0, 0, (void *) CHR('Y','I','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
947     { (unichar_t *) N_("Zealandic"), NULL, 0, 0, (void *) CHR('Z','E','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
948     { (unichar_t *) N_("Standard Moroccan Tamazight"), NULL, 0, 0, (void *) CHR('Z','G','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
949     { (unichar_t *) N_("Zhuang"), NULL, 0, 0, (void *) CHR('Z','H','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
950     { (unichar_t *) N_("Chinese Hong Kong"), NULL, 0, 0, (void *) CHR('Z','H','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
951     { (unichar_t *) N_("Chinese Phonetic"), NULL, 0, 0, (void *) CHR('Z','H','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
952     { (unichar_t *) N_("Chinese Simplified"), NULL, 0, 0, (void *) CHR('Z','H','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
953     { (unichar_t *) N_("Chinese Traditional"), NULL, 0, 0, (void *) CHR('Z','H','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
954     { (unichar_t *) N_("Zande"), NULL, 0, 0, (void *) CHR('Z','N','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
955     { (unichar_t *) N_("Zulu"), NULL, 0, 0, (void *) CHR('Z','U','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
956     { (unichar_t *) N_("Zazaki"), NULL, 0, 0, (void *) CHR('Z','Z','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
957     GTEXTINFO_EMPTY
958 };
959 
960 static char *LK_LangsDlg(GGadget *, int r, int c);
961 static char *LK_ScriptsDlg(GGadget *, int r, int c);
962 static struct col_init scriptci[] = {
963 /* GT: English uses "script" to mean a general writing system (latin, greek, kanji) */
964 /* GT: and the cursive handwriting style. Here we mean the general writing system. */
965     { me_stringchoicetag , NULL, scripts, NULL, N_("writing system|Script") },
966     { me_funcedit, LK_LangsDlg, NULL, NULL, N_("Language(s)") },
967     COL_INIT_EMPTY
968 };
969 static struct col_init featureci[] = {
970     { me_stringchoicetrans , NULL, NULL, NULL, N_("Feature") },
971     { me_funcedit, LK_ScriptsDlg, NULL, NULL, N_("Script(s) & Language(s)") },
972     COL_INIT_EMPTY
973 };
974 
LookupUIInit(void)975 void LookupUIInit(void) {
976     static int done = false;
977     int i, j;
978     static GTextInfo *needswork[] = {
979 	scripts, languages, gpos_lookuptypes, gsub_lookuptypes,
980 	NULL
981     };
982 
983     if ( done )
984 return;
985     done = true;
986     for ( j=0; needswork[j]!=NULL; ++j ) {
987 	for ( i=0; needswork[j][i].text!=NULL || needswork[j][i].line; ++i )
988 	    if ( needswork[j][i].text!=NULL )
989 		needswork[j][i].text = (unichar_t *) S_((char *) needswork[j][i].text);
990     }
991     LookupInit();
992 
993     featureci[0].title = S_(featureci[0].title);
994     featureci[1].title = S_(featureci[1].title);
995     scriptci[0].title = S_(scriptci[0].title);
996     scriptci[1].title = S_(scriptci[1].title);
997 }
998 
999 #define CID_LookupType		1000
1000 #define CID_LookupFeatures	1001
1001 #define CID_Lookup_R2L		1002
1002 #define CID_Lookup_IgnBase	1003
1003 #define CID_Lookup_IgnLig	1004
1004 #define CID_Lookup_IgnMark	1005
1005 #define CID_Lookup_ProcessMark	1006
1006 #define CID_LookupName		1007
1007 #define CID_LookupAfm		1008
1008 #define CID_OK			1009
1009 #define CID_Cancel		1010
1010 #define CID_Lookup_ProcessSet	1011
1011 
1012 #define CID_FeatureScripts	1020
1013 #define CID_ShowAnchors		1021
1014 
1015 struct lookup_dlg {
1016     OTLookup *orig;
1017     SplineFont *sf;
1018     GWindow gw, scriptgw;
1019     int isgpos;
1020     int done, scriptdone, name_has_been_set;
1021     int ok;
1022     char *scriptret;
1023 };
1024 
langs_e_h(GWindow gw,GEvent * event)1025 static int langs_e_h(GWindow gw, GEvent *event) {
1026     int *done = GDrawGetUserData(gw);
1027 
1028     if ( event->type==et_close ) {
1029 	*done = true;
1030     } else if ( event->type==et_char ) {
1031 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
1032 	    help("ui/dialogs/lookups.html", "#lookups-scripts-dlg");
1033 return( true );
1034 	}
1035 return( false );
1036     } else if ( event->type==et_controlevent && event->u.control.subtype == et_buttonactivate ) {
1037 	switch ( GGadgetGetCid(event->u.control.g)) {
1038 	  case CID_OK:
1039 	    *done = 2;
1040 	  break;
1041 	  case CID_Cancel:
1042 	    *done = true;
1043 	  break;
1044 	}
1045     }
1046 return( true );
1047 }
1048 
LK_LangsDlg(GGadget * g,int r,int c)1049 static char *LK_LangsDlg(GGadget *g, int r, int c) {
1050     int rows, i;
1051     struct matrix_data *strings = GMatrixEditGet(g, &rows);
1052     char *langstr = strings[2*r+c].u.md_str, *pt, *start;
1053     unsigned char tagstr[4], warnstr[8];
1054     uint32 tag;
1055     int warn_cnt = 0;
1056     int j,done=0;
1057     GWindowAttrs wattrs;
1058     GGadgetCreateData gcd[5], *varray[6], *harray[7], boxes[3];
1059     GTextInfo label[5];
1060     GRect pos;
1061     GWindow gw;
1062     int32 len;
1063     GTextInfo **ti;
1064     char *ret;
1065 
1066     for ( i=0; languages[i].text!=NULL; ++i )
1067 	languages[i].selected = false;
1068 
1069     for ( start= langstr; *start; ) {
1070 	memset(tagstr,' ',sizeof(tagstr));
1071 	for ( pt=start, j=0; *pt!='\0' && *pt!=','; ++pt, ++j ) {
1072 	    if ( j<4 )
1073 		tagstr[j] = *pt;
1074 	}
1075 	if ( *pt==',' ) ++pt;
1076 	tag = (tagstr[0]<<24) | (tagstr[1]<<16) | (tagstr[2]<<8) | tagstr[3];
1077 	for ( i=0; languages[i].text!=NULL; ++i )
1078 	    if ( languages[i].userdata == (void *) (intpt) tag ) {
1079 		languages[i].selected = true;
1080 	break;
1081 	    }
1082 	if ( languages[i].text==NULL ) {
1083 	    ++warn_cnt;
1084 	    memcpy(warnstr,tagstr,4);
1085 	    warnstr[4] = '\0';
1086 	}
1087 	start = pt;
1088     }
1089     if ( warn_cnt!=0 ) {
1090 	if ( warn_cnt==1 )
1091 	    ff_post_error(_("Unknown Language"),_("The language, '%s', is not in the list of known languages and will be omitted"), warnstr );
1092 	else
1093 	    ff_post_error(_("Unknown Language"),_("Several language tags, including '%s', are not in the list of known languages and will be omitted"), warnstr );
1094     }
1095 
1096 	memset(&wattrs,0,sizeof(wattrs));
1097 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
1098 	wattrs.event_masks = ~(1<<et_charup);
1099 	wattrs.restrict_input_to_me = 1;
1100 	wattrs.undercursor = 1;
1101 	wattrs.cursor = ct_pointer;
1102 	wattrs.utf8_window_title =  _("Language List");
1103 	wattrs.is_dlg = true;
1104 	pos.x = pos.y = 0;
1105 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,150));
1106 	pos.height = GDrawPointsToPixels(NULL,193);
1107 	gw = GDrawCreateTopWindow(NULL,&pos,langs_e_h,&done,&wattrs);
1108 
1109 	memset(&gcd,0,sizeof(gcd));
1110 	memset(&boxes,0,sizeof(boxes));
1111 	memset(&label,0,sizeof(label));
1112 
1113 	i = 0;
1114 	gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = 5;
1115 	gcd[i].gd.pos.height = 12*12+6;
1116 	gcd[i].gd.flags = gg_enabled|gg_visible|gg_list_alphabetic|gg_list_multiplesel;
1117 	gcd[i].gd.u.list = languages;
1118 	gcd[i].gd.cid = 0;
1119 	gcd[i].gd.popup_msg = _(
1120 	    "Select as many languages as needed\n"
1121 	    "Hold down the control key when clicking\n"
1122 	    "to make disjoint selections.");
1123 	varray[0] = &gcd[i]; varray[1] = NULL;
1124 	gcd[i++].creator = GListCreate;
1125 
1126 	gcd[i].gd.pos.x = 15-3; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+gcd[i-1].gd.pos.height+5;
1127 	gcd[i].gd.pos.width = -1; gcd[i].gd.pos.height = 0;
1128 	gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
1129 	label[i].text = (unichar_t *) _("_OK");
1130 	label[i].text_is_1byte = true;
1131 	label[i].text_in_resource = true;
1132 	gcd[i].gd.mnemonic = 'O';
1133 	gcd[i].gd.label = &label[i];
1134 	gcd[i].gd.cid = CID_OK;
1135 	harray[0] = GCD_Glue; harray[1] = &gcd[i]; harray[2] = GCD_Glue;
1136 	gcd[i++].creator = GButtonCreate;
1137 
1138 	gcd[i].gd.pos.x = -15; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+3;
1139 	gcd[i].gd.pos.width = -1; gcd[i].gd.pos.height = 0;
1140 	gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
1141 	label[i].text = (unichar_t *) _("_Cancel");
1142 	label[i].text_is_1byte = true;
1143 	label[i].text_in_resource = true;
1144 	gcd[i].gd.label = &label[i];
1145 	gcd[i].gd.mnemonic = 'C';
1146 	gcd[i].gd.cid = CID_Cancel;
1147 	harray[3] = GCD_Glue; harray[4] = &gcd[i]; harray[5] = GCD_Glue; harray[6] = NULL;
1148 	gcd[i++].creator = GButtonCreate;
1149 
1150 	boxes[2].gd.flags = gg_enabled|gg_visible;
1151 	boxes[2].gd.u.boxelements = harray;
1152 	boxes[2].creator = GHBoxCreate;
1153 	varray[2] = &boxes[2]; varray[3] = NULL; varray[4] = NULL;
1154 
1155 	boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
1156 	boxes[0].gd.flags = gg_enabled|gg_visible;
1157 	boxes[0].gd.u.boxelements = varray;
1158 	boxes[0].creator = GHVGroupCreate;
1159 
1160 	GGadgetsCreate(gw,boxes);
1161 	GHVBoxSetExpandableRow(boxes[0].ret,0);
1162 	GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
1163 	GHVBoxFitWindow(boxes[0].ret);
1164 
1165     GDrawSetVisible(gw,true);
1166  retry:
1167     while ( !done )
1168 	GDrawProcessOneEvent(NULL);
1169     ret = NULL;
1170     ti = GGadgetGetList(gcd[0].ret,&len);
1171     if ( done==2 ) {
1172 	int lcnt=0;
1173 	for ( i=0; i<len; ++i ) {
1174 	    if ( ti[i]->selected )
1175 		++lcnt;
1176 	}
1177 	if ( lcnt==0 ) {
1178 	    ff_post_error(_("Language Missing"),_("You must select at least one language.\nUse the \"Default\" language if nothing else fits."));
1179 	    done = 0;
1180  goto retry;
1181 	}
1182 	ret = malloc(5*lcnt+1);
1183 	*ret = '\0';
1184 	pt = ret;
1185 	for ( i=0; i<len; ++i ) {
1186 	    if ( done==2 && ti[i]->selected ) {
1187 		uint32 tag = (uint32) (intpt) (ti[i]->userdata);
1188 		*pt++ = tag>>24;
1189 		*pt++ = tag>>16;
1190 		*pt++ = tag>>8;
1191 		*pt++ = tag&0xff;
1192 		*pt++ = ',';
1193 	    }
1194 	}
1195 	if ( pt!=ret )
1196 	    pt[-1] = '\0';
1197     }
1198     GDrawDestroyWindow(gw);
1199 return( ret );
1200 }
1201 
LK_NewScript(GGadget * g,int row)1202 static void LK_NewScript(GGadget *g,int row) {
1203     int rows;
1204     struct matrix_data *strings = GMatrixEditGet(g, &rows);
1205     /* What's a good default lang list for a new script? */
1206     /*  well it depends on what the script is, but we don't know that yet */
1207     /* dflt is safe */
1208 
1209     strings[2*row+1].u.md_str = copy("dflt");
1210 }
1211 
ScriptMatrixInit(struct matrixinit * mi,char * scriptstr)1212 static void ScriptMatrixInit(struct matrixinit *mi,char *scriptstr) {
1213     struct matrix_data *md;
1214     int k, cnt;
1215     char *start, *scriptend, *langsend;
1216 
1217     memset(mi,0,sizeof(*mi));
1218     mi->col_cnt = 2;
1219     mi->col_init = scriptci;
1220 
1221     md = NULL;
1222     for ( k=0; k<2; ++k ) {
1223 	cnt = 0;
1224 	for ( start = scriptstr; *start; ) {
1225 	    for ( scriptend=start; *scriptend!='\0' && *scriptend!='{'; ++scriptend );
1226 	    langsend = scriptend;
1227 	    if ( *scriptend=='{' )
1228 		for ( langsend=scriptend+1; *langsend!='\0' && *langsend!='}'; ++langsend );
1229 	    if ( k ) {
1230 		md[2*cnt+0].u.md_str = copyn(start,scriptend-start);
1231 		if ( *scriptend=='{' )
1232 		    md[2*cnt+1].u.md_str = copyn(scriptend+1,langsend-(scriptend+1));
1233 		else
1234 		    md[2*cnt+1].u.md_str = copy("");
1235 	    }
1236 	    ++cnt;
1237 	    if ( *langsend=='}' ) ++langsend;
1238 	    if ( *langsend==' ' ) ++langsend;
1239 	    start = langsend;
1240 	}
1241 	if ( md==NULL )
1242 	    md = calloc(2*(cnt+10),sizeof(struct matrix_data));
1243     }
1244     mi->matrix_data = md;
1245     mi->initial_row_cnt = cnt;
1246 
1247     mi->initrow = LK_NewScript;
1248     mi->finishedit = NULL;
1249     mi->candelete = NULL;
1250     mi->popupmenu = NULL;
1251     mi->handle_key = NULL;
1252     mi->bigedittitle = NULL;
1253 }
1254 
Script_OK(GGadget * g,GEvent * e)1255 static int Script_OK(GGadget *g, GEvent *e) {
1256 
1257     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1258 	struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1259 	int rows, i, j, script_cnt, lang_cnt;
1260 	struct matrix_data *strings = GMatrixEditGet(GWidgetGetControl(ld->scriptgw,CID_FeatureScripts), &rows);
1261 	char *pt, *start, *ret, *rpt;
1262 	char foo[4];
1263 
1264 	if ( rows==0 ) {
1265 	    ff_post_error(_("No scripts"),_("You must select at least one script if you provide a feature tag."));
1266 return(true);
1267 	}
1268 	script_cnt = rows;
1269 	lang_cnt = 0;
1270 	for ( i=0; i<rows; ++i ) {
1271 	    if ( strlen(strings[2*i+0].u.md_str)>4 ) {
1272 		ff_post_error(_("Bad script tag"),_("The script tag on line %d (%s) is too long.  It may be at most 4 letters"),
1273 			i+1, strings[2*i+0].u.md_str);
1274 return(true);
1275 	    } else {
1276 		for ( pt=strings[2*i+0].u.md_str; *pt!='\0' ; ++pt )
1277 		    if ( *pt>0x7e ) {
1278 			ff_post_error(_("Bad script tag"),_("The script tag on line %d (%s) should be in ASCII.\n"),
1279 				i+1, strings[2*i+0].u.md_str);
1280 return( true );
1281 		    }
1282 	    }
1283 	    /* Now check the languages */
1284 	    if ( *strings[2*i+1].u.md_str=='\0' ) {
1285 		ff_post_error(_("No languages"),_("You must select at least one language for each script."));
1286 return(true);
1287 	    }
1288 	    for ( start=strings[2*i+1].u.md_str; *start!='\0'; ) {
1289 		for ( pt=start; *pt!=',' && *pt!='\0'; ++pt ) {
1290 		    if ( *pt>0x7e ) {
1291 			ff_post_error(_("Bad language tag"),_("A language tag on line %d (%s) should be in ASCII.\n"),
1292 				i+1, strings[2*i+1].u.md_str);
1293 return( true );
1294 		    }
1295 		}
1296 		if ( pt-start>4 ) {
1297 		    ff_post_error(_("Bad language tag"),_("A language tag on line %d (%s) is too long.  It may be at most 4 letters"),
1298 			    i+1, strings[2*i+0].u.md_str);
1299 return(true);
1300 		}
1301 		++lang_cnt;
1302 		start = pt;
1303 		if ( *start==',' ) ++start;
1304 	    }
1305 	}
1306 
1307 	/* Ok, we validated the script lang list. Now parse it */
1308 	rpt = ret = malloc(6*script_cnt+5*lang_cnt+10);
1309 	for ( i=0; i<rows; ++i ) {
1310 	    memset(foo,' ',sizeof(foo));
1311 	    for ( j=0, pt = strings[2*i+0].u.md_str; j<4 && *pt; foo[j++] = *pt++ );
1312 	    strncpy(rpt,foo,4);
1313 	    rpt += 4;
1314 	    *rpt++ = '{';
1315 	    /* Now do the languages */
1316 	    for ( start=strings[2*i+1].u.md_str; *start!='\0'; ) {
1317 		memset(foo,' ',sizeof(foo));
1318 		for ( j=0,pt=start; *pt!=',' && *pt!='\0'; ++pt )
1319 		    foo[j++] = *pt;
1320 		strncpy(rpt,foo,4);
1321 		rpt += 4; *rpt++ = ',';
1322 		if ( *pt==',' ) ++pt;
1323 		start = pt;
1324 	    }
1325 	    if ( rpt>ret && rpt[-1]==',' )
1326 		rpt[-1] = '}';
1327 	    *rpt++ = ' ';
1328 	}
1329 	if ( rpt>ret && rpt[-1]==' ' ) rpt[-1] = '\0';
1330 	else *rpt = '\0';
1331 	ld->scriptdone = true;
1332 	ld->scriptret = ret;
1333     }
1334 return( true );
1335 }
1336 
Script_Cancel(GGadget * g,GEvent * e)1337 static int Script_Cancel(GGadget *g, GEvent *e) {
1338 
1339     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1340 	struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1341 	ld->scriptdone = true;
1342 	ld->scriptret = NULL;
1343     }
1344 return( true );
1345 }
1346 
script_e_h(GWindow gw,GEvent * event)1347 static int script_e_h(GWindow gw, GEvent *event) {
1348 
1349     if ( event->type==et_close ) {
1350 	struct lookup_dlg *ld = GDrawGetUserData(gw);
1351 	ld->scriptdone = true;
1352 	ld->scriptret = NULL;
1353     } else if ( event->type == et_char ) {
1354 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
1355 	    help("ui/dialogs/lookups.html", "#lookups-scripts-dlg");
1356 return( true );
1357 	}
1358 return( false );
1359     }
1360 
1361 return( true );
1362 }
1363 
LK_ScriptsDlg(GGadget * g,int r,int c)1364 static char *LK_ScriptsDlg(GGadget *g, int r, int c) {
1365     int rows, i, k, j;
1366     struct matrix_data *strings = GMatrixEditGet(g, &rows);
1367     char *scriptstr = strings[2*r+c].u.md_str;
1368     struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1369     GRect pos;
1370     GWindow gw;
1371     GWindowAttrs wattrs;
1372     GGadgetCreateData gcd[4], boxes[3];
1373     GGadgetCreateData *varray[6], *harray3[8];
1374     GTextInfo label[4];
1375     struct matrixinit mi;
1376 
1377     ld->scriptdone = 0;
1378     ld->scriptret = NULL;
1379 
1380     memset(&wattrs,0,sizeof(wattrs));
1381     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
1382     wattrs.event_masks = ~(1<<et_charup);
1383     wattrs.is_dlg = true;
1384     wattrs.restrict_input_to_me = 1;
1385     wattrs.undercursor = 1;
1386     wattrs.cursor = ct_pointer;
1387     wattrs.utf8_window_title = _("Script(s)");
1388     pos.x = pos.y = 0;
1389     pos.width =GDrawPointsToPixels(NULL,GGadgetScale(268));
1390     pos.height = GDrawPointsToPixels(NULL,375);
1391     ld->scriptgw = gw = GDrawCreateTopWindow(NULL,&pos,script_e_h,ld,&wattrs);
1392 
1393     ScriptMatrixInit(&mi,scriptstr);
1394 
1395     memset(&gcd,0,sizeof(gcd));
1396     memset(&boxes,0,sizeof(boxes));
1397     memset(&label,0,sizeof(label));
1398     k=j=0;
1399     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[1].gd.pos.y+14;
1400     gcd[k].gd.pos.width = 300; gcd[k].gd.pos.height = 90;
1401     gcd[k].gd.flags = gg_enabled | gg_visible;
1402     gcd[k].gd.cid = CID_FeatureScripts;
1403     gcd[k].gd.u.matrix = &mi;
1404     gcd[k].gd.popup_msg = _(
1405 	"Each feature is active for a specific set of\n"
1406 	"scripts and languages.\n"
1407 	"Usually only one script is specified, but\n"
1408 	"occasionally more will be.\n"
1409 	"A script is a four letter OpenType script tag\n");
1410     gcd[k].creator = GMatrixEditCreate;
1411     varray[j++] = &gcd[k++]; varray[j++] = NULL;
1412 
1413     gcd[k].gd.pos.x = 30-3;
1414     gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
1415     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
1416     label[k].text = (unichar_t *) _("_OK");
1417     label[k].text_is_1byte = true;
1418     label[k].text_in_resource = true;
1419     gcd[k].gd.label = &label[k];
1420     gcd[k].gd.handle_controlevent = Script_OK;
1421     gcd[k].gd.cid = CID_OK;
1422     gcd[k++].creator = GButtonCreate;
1423 
1424     gcd[k].gd.pos.x = -30;
1425     gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
1426     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
1427     label[k].text = (unichar_t *) _("_Cancel");
1428     label[k].text_is_1byte = true;
1429     label[k].text_in_resource = true;
1430     gcd[k].gd.label = &label[k];
1431     gcd[k].gd.handle_controlevent = Script_Cancel;
1432     gcd[k].gd.cid = CID_Cancel;
1433     gcd[k++].creator = GButtonCreate;
1434 
1435     harray3[0] = harray3[2] = harray3[3] = harray3[4] = harray3[6] = GCD_Glue;
1436     harray3[7] = NULL;
1437     harray3[1] = &gcd[k-2]; harray3[5] = &gcd[k-1];
1438 
1439     boxes[0].gd.flags = gg_enabled|gg_visible;
1440     boxes[0].gd.u.boxelements = harray3;
1441     boxes[0].creator = GHBoxCreate;
1442     varray[j++] = &boxes[0]; varray[j++] = NULL; varray[j] = NULL;
1443 
1444     boxes[1].gd.pos.x = boxes[1].gd.pos.y = 2;
1445     boxes[1].gd.flags = gg_enabled|gg_visible;
1446     boxes[1].gd.u.boxelements = varray;
1447     boxes[1].creator = GHVGroupCreate;
1448 
1449     GGadgetsCreate(gw,boxes+1);
1450 
1451     for ( i=0; i<mi.initial_row_cnt; ++i ) {
1452 	free( mi.matrix_data[2*i+0].u.md_str );
1453 	free( mi.matrix_data[2*i+1].u.md_str );
1454     }
1455     free( mi.matrix_data );
1456 
1457     GMatrixEditSetNewText(gcd[0].ret,S_("OpenTypeFeature|New"));
1458     GHVBoxSetExpandableRow(boxes[1].ret,0);
1459     GHVBoxSetExpandableCol(boxes[0].ret,gb_expandgluesame);
1460 
1461     GHVBoxFitWindow(boxes[1].ret);
1462 
1463     GDrawSetVisible(gw,true);
1464     while ( !ld->scriptdone )
1465 	GDrawProcessOneEvent(NULL);
1466     GDrawDestroyWindow(gw);
1467 return( ld->scriptret );
1468 }
1469 
LK_ParseFL(struct matrix_data * strings,int rows)1470 static FeatureScriptLangList *LK_ParseFL(struct matrix_data *strings, int rows ) {
1471     int i,j;
1472     char *pt, *start;
1473     FeatureScriptLangList *fl, *fhead, *flast;
1474     struct scriptlanglist *sl, *slast;
1475     unsigned char foo[4];
1476     uint32 *langs=NULL;
1477     int lmax=0, lcnt=0;
1478     int feature, setting;
1479 
1480     fhead = flast = NULL;
1481     for ( i=0; i<rows; ++i ) {
1482 	fl = chunkalloc(sizeof(FeatureScriptLangList));
1483 	if ( sscanf(strings[2*i+0].u.md_str,"<%d,%d>", &feature, &setting )== 2 ) {
1484 	    fl->ismac = true;
1485 	    fl->featuretag = (feature<<16)|setting;
1486 	} else {
1487 	    memset(foo,' ',sizeof(foo));
1488 	    for ( j=0, pt = strings[2*i+0].u.md_str; j<4 && *pt; foo[j++] = *pt++ );
1489 	    fl->featuretag = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
1490 	}
1491 	if ( flast==NULL )
1492 	    fhead = fl;
1493 	else
1494 	    flast->next = fl;
1495 	flast = fl;
1496 	/* Now do the script langs */
1497 	slast = NULL;
1498 	for ( start=strings[2*i+1].u.md_str; *start!='\0'; ) {
1499 	    memset(foo,' ',sizeof(foo));
1500 	    for ( j=0,pt=start; *pt!='{' && *pt!='\0'; ++pt )
1501 		foo[j++] = *pt;
1502 	    sl = chunkalloc(sizeof( struct scriptlanglist ));
1503 	    sl->script = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
1504 	    if ( slast==NULL )
1505 		fl->scripts = sl;
1506 	    else
1507 		slast->next = sl;
1508 	    slast = sl;
1509 	    if ( *pt!='{' ) {
1510 		sl->lang_cnt = 1;
1511 		sl->langs[0] = DEFAULT_LANG;
1512 		start = pt;
1513 	    } else {
1514 		lcnt=0;
1515 		for ( start=pt+1; *start!='}' && *start!='\0' ; ) {
1516 		    memset(foo,' ',sizeof(foo));
1517 		    for ( j=0,pt=start; *pt!='}' && *pt!=',' && *pt!='\0'; foo[j++] = *pt++ );
1518 		    if ( lcnt>=lmax )
1519 			langs = realloc(langs,(lmax+=20)*sizeof(uint32));
1520 		    langs[lcnt++] = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
1521 		    start =  ( *pt==',' ) ? pt+1 : pt;
1522 		}
1523 		if ( *start=='}' ) ++start;
1524 		for ( j=0; j<lcnt && j<MAX_LANG; ++j )
1525 		    sl->langs[j] = langs[j];
1526 		if ( lcnt>MAX_LANG ) {
1527 		    sl->morelangs = malloc((lcnt-MAX_LANG)*sizeof(uint32));
1528 		    for ( ; j<lcnt; ++j )
1529 			sl->morelangs[j-MAX_LANG] = langs[j];
1530 		}
1531 		sl->lang_cnt = lcnt;
1532 	    }
1533 	    while ( *start==' ' ) ++start;
1534 	}
1535     }
1536     free(langs);
1537 return( fhead );
1538 }
1539 
LK_FinishEdit(GGadget * g,int row,int col,int wasnew)1540 static void LK_FinishEdit(GGadget *g,int row, int col, int wasnew) {
1541     struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1542 
1543     if ( col==0 ) {
1544 	int rows;
1545 	struct matrix_data *strings = GMatrixEditGet(g, &rows);
1546 
1547 	if ( !ld->isgpos &&
1548 		(strcmp(strings[row].u.md_str,"liga")==0 ||
1549 		 strcmp(strings[row].u.md_str,"rlig")==0 ))
1550 	    GGadgetSetChecked( GWidgetGetControl(ld->gw,CID_LookupAfm ), true );
1551     }
1552     if ( row==0 && !ld->name_has_been_set && ld->orig->features==NULL ) {
1553 	int rows;
1554 	struct matrix_data *strings = GMatrixEditGet(g, &rows);
1555 	OTLookup *otl = ld->orig;
1556 	int old_type = otl->lookup_type;
1557 	FeatureScriptLangList *oldfl = otl->features;
1558 
1559 	otl->lookup_type = (intpt) GGadgetGetListItemSelected(GWidgetGetControl(ld->gw,CID_LookupType))->userdata;
1560 	otl->features = LK_ParseFL(strings,rows);
1561 	NameOTLookup(otl,ld->sf);
1562 	GGadgetSetTitle8(GWidgetGetControl(ld->gw,CID_LookupName),otl->lookup_name);
1563 	free(otl->lookup_name); otl->lookup_name = NULL;
1564 	FeatureScriptLangListFree(otl->features); otl->features = oldfl;
1565 	otl->lookup_type = old_type;
1566     }
1567 }
1568 
LK_NewFeature(GGadget * g,int row)1569 static void LK_NewFeature(GGadget *g,int row) {
1570     int rows;
1571     struct matrix_data *strings = GMatrixEditGet(g, &rows);
1572     struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1573     SplineFont *sf, *_sf = ld->sf;
1574     SplineChar *sc;
1575     /* What's a good default script / lang list for a new feature? */
1576     /*  well it depends on what the feature is, and on what's inside the lookup */
1577     /*  Neither of those has been set yet. */
1578     /* So... give them everything we can think of. They can remove stuff */
1579     /*  which is inappropriate */
1580     uint32 scripts[20], script, *langs;
1581     int scnt = 0, i, l;
1582     int gid, k;
1583     char *buf;
1584     int bmax, bpos;
1585 
1586     k=0;
1587     do {
1588 	sf = k<_sf->subfontcnt ? _sf->subfonts[k] : _sf;
1589 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
1590 	    script = SCScriptFromUnicode(sc);
1591 	    for ( i=0; i<scnt; ++i )
1592 		if ( script == scripts[i] )
1593 	    break;
1594 	    if ( i==scnt ) {
1595 		scripts[scnt++] = script;
1596 		if ( scnt>=20 )	/* If they've got lots of scripts, enumerating them all won't be much use... */
1597 	break;
1598 	    }
1599 	}
1600 	++k;
1601     } while ( k<_sf->subfontcnt && scnt<20 );
1602 
1603     if ( scnt==0 )
1604 	scripts[scnt++] = DEFAULT_SCRIPT;
1605 
1606     buf = malloc(bmax = 100);
1607     bpos = 0;
1608     for ( i=0; i<scnt; ++i ) {
1609 	langs = SFLangsInScript(sf,-1,scripts[i]);
1610 	for ( l=0; langs[l]!=0; ++l );
1611 	if ( bpos + 5+5*l+4 > bmax )
1612 	    buf = realloc( buf, bmax += 5+5*l+100 );
1613 	sprintf( buf+bpos, "%c%c%c%c{", scripts[i]>>24, scripts[i]>>16, scripts[i]>>8, scripts[i] );
1614 	bpos+=5;
1615 	for ( l=0; langs[l]!=0; ++l ) {
1616 	    sprintf( buf+bpos, "%c%c%c%c,", langs[l]>>24, langs[l]>>16, langs[l]>>8, langs[l] );
1617 	    bpos += 5;
1618 	}
1619 	if ( l>0 )
1620 	    --bpos;
1621 	strcpy(buf+bpos,"} ");
1622 	bpos += 2;
1623 	free(langs);
1624     }
1625     if ( bpos>0 )
1626 	buf[bpos-1] = '\0';
1627 
1628     strings[2*row+1].u.md_str = copy(buf);
1629     free(buf);
1630 }
1631 
LKMatrixInit(struct matrixinit * mi,OTLookup * otl)1632 static void LKMatrixInit(struct matrixinit *mi,OTLookup *otl) {
1633     struct matrix_data *md;
1634     int k, cnt,j;
1635     FeatureScriptLangList *fl;
1636     struct scriptlanglist *sl;
1637     char featbuf[32], *buf=NULL;
1638     int blen=0, bpos=0;
1639 
1640     memset(mi,0,sizeof(*mi));
1641     mi->col_cnt = 2;
1642     mi->col_init = featureci;
1643 
1644     md = NULL;
1645     for ( k=0; k<2; ++k ) {
1646 	cnt = 0;
1647 	for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
1648 	    if ( k ) {
1649 		if ( fl->ismac )
1650 		    sprintf( featbuf, "<%d,%d>", fl->featuretag>>16, fl->featuretag&0xffff );
1651 		else
1652 		    sprintf( featbuf, "%c%c%c%c", fl->featuretag>>24, fl->featuretag>>16,
1653 			    fl->featuretag>>8, fl->featuretag );
1654 		md[2*cnt+0].u.md_str = copy(featbuf);
1655 		bpos=0;
1656 		for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
1657 		    if ( bpos+4/*script*/+1/*open brace*/+5*sl->lang_cnt+1+2 > blen )
1658 			buf = realloc(buf,blen+=5+5*sl->lang_cnt+3+200);
1659 		    sprintf( buf+bpos, "%c%c%c%c{", sl->script>>24, sl->script>>16,
1660 			    sl->script>>8, sl->script );
1661 		    bpos+=5;
1662 		    for ( j=0; j<sl->lang_cnt; ++j ) {
1663 			uint32 tag = j<MAX_LANG ? sl->langs[j] : sl->morelangs[j-MAX_LANG];
1664 			sprintf( buf+bpos, "%c%c%c%c,", tag>>24, tag>>16,
1665 				tag>>8, tag );
1666 			bpos+=5;
1667 		    }
1668 		    if ( sl->lang_cnt!=0 )
1669 			--bpos;
1670 		    buf[bpos++] = '}';
1671 		    buf[bpos++] = ' ';
1672 		}
1673 		if ( bpos>0 )
1674 		    --bpos;
1675         if (buf) {
1676             buf[bpos] = '\0';
1677             md[2*cnt+1].u.md_str = copy(buf);
1678         }
1679         else {
1680             md[2*cnt+1].u.md_str = NULL;
1681         }
1682 	    }
1683 	    ++cnt;
1684 	}
1685 	if ( md==NULL )
1686 	    md = calloc(2*(cnt+10),sizeof(struct matrix_data));
1687     }
1688     mi->matrix_data = md;
1689     mi->initial_row_cnt = cnt;
1690 
1691     mi->initrow = LK_NewFeature;
1692     mi->finishedit = LK_FinishEdit;
1693     mi->candelete = NULL;
1694     mi->popupmenu = NULL;
1695     mi->handle_key = NULL;
1696     mi->bigedittitle = NULL;
1697     free(buf);
1698 }
1699 
SFMarkClassList(SplineFont * sf,int class)1700 static GTextInfo *SFMarkClassList(SplineFont *sf,int class) {
1701     int i;
1702     GTextInfo *ti;
1703 
1704     if ( sf->cidmaster ) sf = sf->cidmaster;
1705     else if ( sf->mm!=NULL ) sf=sf->mm->normal;
1706 
1707     i = sf->mark_class_cnt;
1708     ti = calloc(i+4,sizeof( GTextInfo ));
1709     ti[0].text = utf82u_copy( _("All"));
1710     ti[0].selected = class==0;
1711     for ( i=1; i<sf->mark_class_cnt; ++i ) {
1712 	ti[i].text = (unichar_t *) copy(sf->mark_class_names[i]);
1713 	ti[i].userdata = (void *) (intpt) i;
1714 	ti[i].text_is_1byte = true;
1715 	if ( i==class ) ti[i].selected = true;
1716     }
1717 return( ti );
1718 }
1719 
SFMarkSetList(SplineFont * sf,int set)1720 static GTextInfo *SFMarkSetList(SplineFont *sf,int set) {
1721     int i;
1722     GTextInfo *ti;
1723 
1724     if ( sf->cidmaster ) sf = sf->cidmaster;
1725     else if ( sf->mm!=NULL ) sf=sf->mm->normal;
1726 
1727     i = sf->mark_set_cnt;
1728     ti = calloc(i+4,sizeof( GTextInfo ));
1729     ti[0].text = utf82u_copy( _("All"));
1730     ti[0].userdata = (void *) (intpt) -1;
1731     ti[0].selected = set==-1;
1732     for ( i=0; i<sf->mark_set_cnt; ++i ) {
1733 	ti[i+1].text = (unichar_t *) copy(sf->mark_set_names[i]);
1734 	ti[i+1].userdata = (void *) (intpt) i;
1735 	ti[i+1].text_is_1byte = true;
1736 	if ( i==set ) ti[i+1].selected = true;
1737     }
1738 return( ti );
1739 }
1740 
MaskFromLookupType(int lookup_type)1741 static int MaskFromLookupType(int lookup_type ) {
1742     switch ( lookup_type ) {
1743       case gsub_single: case gsub_multiple: case gsub_alternate:
1744       case gsub_ligature: case gsub_context: case gsub_contextchain:
1745 return( 1<<(lookup_type-1));
1746       case gsub_reversecchain:
1747 return( gsub_reversecchain_mask );
1748       case morx_indic: case morx_context: case morx_insert:
1749 return( morx_indic_mask<<(lookup_type-morx_indic) );
1750       case gpos_single: case gpos_pair: case gpos_cursive:
1751       case gpos_mark2base: case gpos_mark2ligature: case gpos_mark2mark:
1752       case gpos_context: case gpos_contextchain:
1753 return( gpos_single_mask<<(lookup_type-gpos_single) );
1754       case kern_statemachine:
1755 return( kern_statemachine_mask );
1756       default:
1757 return( 0 );
1758     }
1759 }
1760 
FeatureListFromLookupType(int lookup_type)1761 static GTextInfo *FeatureListFromLookupType(int lookup_type) {
1762     int k, i, cnt, mask;
1763     GTextInfo *ti;
1764 
1765     mask = MaskFromLookupType( lookup_type );
1766     for ( k=0; k<2; ++k ) {
1767 	cnt = 0;
1768 	for ( i=0; friendlies[i].tag!=0; ++i ) if ( friendlies[i].masks&mask ) {
1769 	    if ( k ) {
1770 		char buf[128];
1771 		snprintf(buf, sizeof buf, "%s %s", friendlies[i].tagstr, friendlies[i].friendlyname);
1772 		ti[cnt].text = (unichar_t *) copy( buf );
1773 		ti[cnt].text_is_1byte = true;
1774 		ti[cnt].userdata = friendlies[i].tagstr;
1775 	    }
1776 	    ++cnt;
1777 	}
1778 	if ( cnt==0 ) {
1779 	    if ( k ) {
1780 		ti[cnt].text = (unichar_t *) copy( _("You must choose a lookup type") );
1781 		ti[cnt].text_is_1byte = true;
1782 		ti[cnt].userdata = "????";
1783 	    }
1784 	    ++cnt;
1785 	}
1786 	if ( !k )
1787 	    ti = calloc(cnt+1,sizeof(GTextInfo));
1788     }
1789 return( ti );
1790 }
1791 
Lookup_NameChanged(GGadget * g,GEvent * e)1792 static int Lookup_NameChanged(GGadget *g, GEvent *e) {
1793 
1794     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
1795 	struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1796 	if ( *_GGadgetGetTitle(g)!='\0' )
1797 	    ld->name_has_been_set = true;
1798     }
1799 return( true );
1800 }
1801 
LK_TypeChanged(GGadget * g,GEvent * e)1802 static int LK_TypeChanged(GGadget *g, GEvent *e) {
1803 
1804     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
1805 	int lookup_type = (intpt) GGadgetGetListItemSelected(g)->userdata;
1806 	struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1807 	GTextInfo *ti = FeatureListFromLookupType( lookup_type );
1808 	GMatrixEditSetColumnChoices( GWidgetGetControl(ld->gw,CID_LookupFeatures), 0, ti );
1809 	GTextInfoListFree(ti);
1810 	if ( !ld->isgpos ) {
1811 	    GGadgetSetEnabled( GWidgetGetControl(ld->gw,CID_LookupAfm ), lookup_type==gsub_ligature );
1812 	    if ( lookup_type!=gsub_ligature )
1813 		GGadgetSetChecked( GWidgetGetControl(ld->gw,CID_LookupAfm ), false );
1814 	}
1815     }
1816 return( true );
1817 }
1818 
Lookup_OK(GGadget * g,GEvent * e)1819 static int Lookup_OK(GGadget *g, GEvent *e) {
1820 
1821     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1822 	struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1823 	int lookup_type = (intpt) GGadgetGetListItemSelected(GWidgetGetControl(ld->gw,CID_LookupType))->userdata;
1824 	int rows, i, isgpos;
1825 	struct matrix_data *strings = GMatrixEditGet(GWidgetGetControl(ld->gw,CID_LookupFeatures), &rows);
1826 	char *pt, *start, *name;
1827 	OTLookup *otl = ld->orig, *test;
1828 	int flags, afm;
1829 	FeatureScriptLangList *fhead;
1830 	int feat, set;
1831 
1832 	if ( lookup_type==ot_undef ) {
1833 	    ff_post_error(_("No Lookup Type Selected"),_("You must select a Lookup Type."));
1834 return(true);
1835 	}
1836 	if ( *_GGadgetGetTitle(GWidgetGetControl(ld->gw,CID_LookupName))=='\0' ) {
1837 	    ff_post_error(_("Unnamed lookup"),_("You must name the lookup."));
1838 return(true);
1839 	}
1840 	for ( i=0; i<rows; ++i ) {
1841 	    if ( sscanf(strings[2*i+0].u.md_str,"<%d,%d>", &feat, &set )== 2 )
1842 		/* It's a mac feature/setting */;
1843 	    else if ( strlen(strings[2*i+0].u.md_str)>4 ) {
1844 		ff_post_error(_("Bad feature tag"),_("The feature tag on line %d (%s) is too long.  It may be at most 4 letters (or it could be a mac feature setting, two numbers in brokets <3,4>)"),
1845 			i+1, strings[2*i+0].u.md_str);
1846 return(true);
1847 	    } else {
1848 		for ( pt=strings[2*i+0].u.md_str; *pt!='\0' ; ++pt )
1849 		    if ( *pt>0x7e ) {
1850 			ff_post_error(_("Bad feature tag"),_("The feature tag on line %d (%s) should be in ASCII.\n"),
1851 				i+1, strings[2*i+0].u.md_str);
1852 return( true );
1853 		    }
1854 	    }
1855 	    /* Now check the script langs */
1856 	    for ( start=strings[2*i+1].u.md_str; *start!='\0'; ) {
1857 		for ( pt=start; *pt!='{' && *pt!='\0'; ++pt ) {
1858 		    if ( *pt>0x7e ) {
1859 			ff_post_error(_("Bad script tag"),_("A script tag on line %d (%s) should be in ASCII.\n"),
1860 				i+1, strings[2*i+1].u.md_str);
1861 return( true );
1862 		    }
1863 		}
1864 		if ( pt-start>4 ) {
1865 		    ff_post_error(_("Bad script tag"),_("A script tag on line %d (%s) is too long.  It may be at most 4 letters"),
1866 			    i+1, strings[2*i+0].u.md_str);
1867 return(true);
1868 		}
1869 		if ( *pt=='{' ) {
1870 		    for ( start=pt+1; *start!='}' && *start!='\0' ; ) {
1871 			for ( pt=start; *pt!='}' && *pt!=',' && *pt!='\0'; ++pt ) {
1872 			    if ( *pt>0x7e ) {
1873 				ff_post_error(_("Bad language tag"),_("A language tag on line %d (%s) should be in ASCII.\n"),
1874 					i+1, strings[2*i+1].u.md_str);
1875 return( true );
1876 			    }
1877 			}
1878 			if ( pt-start>4 ) {
1879 			    ff_post_error(_("Bad language tag"),_("A language tag on line %d (%s) is too long.  It may be at most 4 letters"),
1880 				    i+1, strings[2*i+0].u.md_str);
1881 return(true);
1882 			}
1883 			start =  ( *pt==',' ) ? pt+1 : pt;
1884 		    }
1885 		    if ( *start=='}' ) ++start;
1886 		} else
1887 		    start = pt;
1888 		while ( *start==' ' ) ++start;
1889 	    }
1890 	}
1891 	name = GGadgetGetTitle8(GWidgetGetControl(ld->gw,CID_LookupName));
1892 	for ( isgpos=0; isgpos<2; ++isgpos ) {
1893 	    for ( test = isgpos ? ld->sf->gpos_lookups : ld->sf->gsub_lookups; test!=NULL; test=test->next ) {
1894 		if ( test!=otl && strcmp(test->lookup_name,name)==0 ) {
1895 		    ff_post_error(_("Lookup name already used"),_("This name has already been used for another lookup.\nLookup names must be unique.") );
1896 		    free(name);
1897 return(true);
1898 		}
1899 	    }
1900 	}
1901 
1902 	flags = 0;
1903 	if ( GGadgetIsChecked(GWidgetGetControl(ld->gw,CID_Lookup_R2L)) ) flags |= pst_r2l;
1904 	if ( GGadgetIsChecked(GWidgetGetControl(ld->gw,CID_Lookup_IgnBase)) ) flags |= pst_ignorebaseglyphs;
1905 	if ( GGadgetIsChecked(GWidgetGetControl(ld->gw,CID_Lookup_IgnLig)) ) flags |= pst_ignoreligatures;
1906 	if ( GGadgetIsChecked(GWidgetGetControl(ld->gw,CID_Lookup_IgnMark)) ) flags |= pst_ignorecombiningmarks;
1907 	flags |= ((intpt) GGadgetGetListItemSelected(GWidgetGetControl(ld->gw,CID_Lookup_ProcessMark))->userdata)<<8;
1908 	set = ((intpt) GGadgetGetListItemSelected(GWidgetGetControl(ld->gw,CID_Lookup_ProcessSet))->userdata);
1909 	if ( set!=-1 )
1910 	    flags |= pst_usemarkfilteringset | (set<<16);
1911 
1912 	if ( !ld->isgpos )
1913 	    afm = GGadgetIsChecked( GWidgetGetControl(ld->gw,CID_LookupAfm ));
1914 	else
1915 	    afm = false;
1916 
1917 	/* Ok, we validated the feature script lang list. Now parse it */
1918 	fhead = LK_ParseFL(strings,rows);
1919 	free( otl->lookup_name );
1920 	FeatureScriptLangListFree( otl->features );
1921 	otl->lookup_name = name;
1922 	otl->lookup_type = lookup_type;
1923 	otl->lookup_flags = flags;
1924 	otl->features = FLOrder(fhead);
1925 	otl->store_in_afm = afm;
1926 	ld->done = true;
1927 	ld->ok = true;
1928     }
1929 return( true );
1930 }
1931 
Lookup_Cancel(GGadget * g,GEvent * e)1932 static int Lookup_Cancel(GGadget *g, GEvent *e) {
1933 
1934     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1935 	struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1936 	ld->done = true;
1937 	ld->ok = false;
1938     }
1939 return( true );
1940 }
1941 
lookup_e_h(GWindow gw,GEvent * event)1942 static int lookup_e_h(GWindow gw, GEvent *event) {
1943 
1944     if ( event->type==et_close ) {
1945 	struct lookup_dlg *ld = GDrawGetUserData(gw);
1946 	ld->done = true;
1947 	ld->ok = false;
1948     } else if ( event->type == et_char ) {
1949 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
1950 	    help("ui/dialogs/lookups.html", "#lookups-add-lookup");
1951 return( true );
1952 	}
1953 return( false );
1954     }
1955 
1956 return( true );
1957 }
1958 
EditLookup(OTLookup * otl,int isgpos,SplineFont * sf)1959 int EditLookup(OTLookup *otl,int isgpos,SplineFont *sf) {
1960     /* Ok we must provide a lookup type (but only if otl->type==ot_undef) */
1961     /* a set of lookup flags */
1962     /* a name */
1963     /* A potentially empty set of features, scripts and languages */
1964     /* afm */
1965     /* check to make sure the name isn't a duplicate */
1966     GRect pos;
1967     GWindow gw;
1968     GWindowAttrs wattrs;
1969     GGadgetCreateData gcd[16], boxes[7];
1970     GGadgetCreateData *harray1[4], *varray[14], *flaghvarray[10], *flagarray[12],
1971 	*harray2[4], *harray3[8];
1972     GTextInfo label[16];
1973     struct lookup_dlg ld;
1974     struct matrixinit mi;
1975     int class, i, k, vpos;
1976 
1977     LookupUIInit();
1978 
1979     memset(&ld,0,sizeof(ld));
1980     memset(&wattrs,0,sizeof(wattrs));
1981     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
1982     wattrs.event_masks = ~(1<<et_charup);
1983     wattrs.is_dlg = true;
1984     wattrs.restrict_input_to_me = 1;
1985     wattrs.undercursor = 1;
1986     wattrs.cursor = ct_pointer;
1987     wattrs.utf8_window_title = _("Lookup");
1988     pos.x = pos.y = 0;
1989     pos.width =GDrawPointsToPixels(NULL,GGadgetScale(268));
1990     pos.height = GDrawPointsToPixels(NULL,375);
1991     ld.gw = gw = GDrawCreateTopWindow(NULL,&pos,lookup_e_h,&ld,&wattrs);
1992 
1993     ld.orig = otl;
1994     ld.sf = sf;
1995     ld.isgpos = isgpos;
1996 
1997     memset(boxes,0,sizeof(boxes));
1998     memset(gcd,0,sizeof(gcd));
1999     memset(label,0,sizeof(label));
2000 
2001     label[0].text = (unichar_t *) _("Type:");
2002     label[0].text_is_1byte = true;
2003     label[0].text_in_resource = true;
2004     gcd[0].gd.label = &label[0];
2005     gcd[0].gd.pos.x = 12; gcd[0].gd.pos.y = 6+6;
2006     gcd[0].gd.flags = gg_visible | gg_enabled;
2007     gcd[0].creator = GLabelCreate;
2008 
2009     gcd[1].gd.pos.x = 100; gcd[1].gd.pos.y = 6; gcd[1].gd.pos.width = 200;
2010     gcd[1].gd.flags = otl->lookup_type!=ot_undef && otl->subtables!=NULL?
2011 		(gg_visible) : (gg_visible | gg_enabled);
2012     gcd[1].gd.cid = CID_LookupType;
2013     gcd[1].gd.u.list = lookuptypes[isgpos];
2014     gcd[1].gd.handle_controlevent = LK_TypeChanged;
2015     gcd[1].gd.popup_msg = _(
2016 	"Each lookup may contain many transformations,\n"
2017 	"but each transformation must be of the same type.");
2018     gcd[1].creator = GListButtonCreate;
2019 
2020     for ( i=0; lookuptypes[isgpos][i].text!=NULL || lookuptypes[isgpos][i].line; ++i ) {
2021 	if ( (void *) otl->lookup_type == lookuptypes[isgpos][i].userdata &&
2022 		!lookuptypes[isgpos][i].line) {
2023 	    lookuptypes[isgpos][i].selected = true;
2024 	    gcd[1].gd.label = &lookuptypes[isgpos][i];
2025 	} else
2026 	    lookuptypes[isgpos][i].selected = false;
2027     }
2028     harray1[0] = &gcd[0]; harray1[1] = &gcd[1]; harray1[2] = GCD_Glue; harray1[3] = NULL;
2029     boxes[0].gd.flags = gg_enabled|gg_visible;
2030     boxes[0].gd.u.boxelements = harray1;
2031     boxes[0].creator = GHBoxCreate;
2032     varray[0] = &boxes[0]; varray[1] = NULL;
2033 
2034     LKMatrixInit(&mi,otl);
2035     featureci[0].enum_vals = FeatureListFromLookupType(otl->lookup_type);
2036 
2037     gcd[2].gd.pos.x = 10; gcd[2].gd.pos.y = gcd[1].gd.pos.y+14;
2038     gcd[2].gd.pos.width = 300; gcd[2].gd.pos.height = 90;
2039     gcd[2].gd.flags = gg_enabled | gg_visible;
2040     gcd[2].gd.cid = CID_LookupFeatures;
2041     gcd[2].gd.u.matrix = &mi;
2042     gcd[2].gd.popup_msg = _(
2043 	"Most lookups will be attached to a feature\n"
2044 	"active in a specific script for certain languages.\n"
2045 	"In some cases lookups will not be attached to any\n"
2046 	"feature, but will be invoked by another lookup,\n"
2047 	"a conditional one. In other cases a lookup might\n"
2048 	"be attached to several features.\n"
2049 	"A feature is either a four letter OpenType feature\n"
2050 	"tag, or a two number mac <feature,setting> combination.");
2051     gcd[2].creator = GMatrixEditCreate;
2052     varray[2] = &gcd[2]; varray[3] = NULL;
2053 
2054 
2055 	gcd[3].gd.pos.x = 5; gcd[3].gd.pos.y = 5;
2056 	gcd[3].gd.flags = gg_visible | gg_enabled | (otl->lookup_flags&pst_r2l?gg_cb_on:0);
2057 	label[3].text = (unichar_t *) _("Right To Left");
2058 	label[3].text_is_1byte = true;
2059 	gcd[3].gd.label = &label[3];
2060 	gcd[3].gd.cid = CID_Lookup_R2L;
2061 	gcd[3].creator = GCheckBoxCreate;
2062 	flagarray[0] = &gcd[3]; flagarray[1] = NULL;
2063 
2064 	gcd[4].gd.pos.x = 5; gcd[4].gd.pos.y = gcd[3].gd.pos.y+15;
2065 	gcd[4].gd.flags = gg_visible | gg_enabled | (otl->lookup_flags&pst_ignorebaseglyphs?gg_cb_on:0);
2066 	label[4].text = (unichar_t *) _("Ignore Base Glyphs");
2067 	label[4].text_is_1byte = true;
2068 	gcd[4].gd.label = &label[4];
2069 	gcd[4].gd.cid = CID_Lookup_IgnBase;
2070 	gcd[4].creator = GCheckBoxCreate;
2071 	flagarray[2] = &gcd[4]; flagarray[3] = NULL;
2072 
2073 	gcd[5].gd.pos.x = 5; gcd[5].gd.pos.y = gcd[4].gd.pos.y+15;
2074 	gcd[5].gd.flags = gg_visible | gg_enabled | (otl->lookup_flags&pst_ignoreligatures?gg_cb_on:0);
2075 	label[5].text = (unichar_t *) _("Ignore Ligatures");
2076 	label[5].text_is_1byte = true;
2077 	gcd[5].gd.label = &label[5];
2078 	gcd[5].gd.cid = CID_Lookup_IgnLig;
2079 	gcd[5].creator = GCheckBoxCreate;
2080 	flagarray[4] = &gcd[5]; flagarray[5] = NULL;
2081 
2082 	gcd[6].gd.pos.x = 5; gcd[6].gd.pos.y = gcd[5].gd.pos.y+15;
2083 	gcd[6].gd.flags = gg_visible | gg_enabled | (otl->lookup_flags&pst_ignorecombiningmarks?gg_cb_on:0);
2084 	label[6].text = (unichar_t *) _("Ignore Combining Marks");
2085 	label[6].text_is_1byte = true;
2086 	gcd[6].gd.label = &label[6];
2087 	gcd[6].gd.cid = CID_Lookup_IgnMark;
2088 	gcd[6].creator = GCheckBoxCreate;
2089 	flagarray[6] = &gcd[6]; flagarray[7] = NULL;
2090 
2091 /* GT: Process is a verb here and Mark is a noun. */
2092 /* GT: Marks of the given mark class are to be processed */
2093 	label[7].text = (unichar_t *) _("Mark Class:");
2094 	label[7].text_is_1byte = true;
2095 	gcd[7].gd.label = &label[7];
2096 	gcd[7].gd.pos.x = 5; gcd[7].gd.pos.y = gcd[6].gd.pos.y+16;
2097 	gcd[7].gd.flags = sf->mark_class_cnt<=1 ? gg_visible : (gg_enabled|gg_visible);
2098 	gcd[7].creator = GLabelCreate;
2099 	flaghvarray[0] = &gcd[7];
2100 
2101 	gcd[8].gd.pos.x = 10; gcd[8].gd.pos.y = gcd[7].gd.pos.y;
2102 	gcd[8].gd.pos.width = 140;
2103 	gcd[8].gd.flags = gcd[7].gd.flags;
2104 	if ( (class = ((otl->lookup_flags&0xff00)>>8) )>= sf->mark_class_cnt )
2105 	    class = 0;
2106 	gcd[8].gd.u.list = SFMarkClassList(sf,class);
2107 	gcd[8].gd.label = &gcd[8].gd.u.list[class];
2108 	gcd[8].gd.cid = CID_Lookup_ProcessMark;
2109 	gcd[8].creator = GListButtonCreate;
2110 	flaghvarray[1] = &gcd[8]; flaghvarray[2] = GCD_Glue; flaghvarray[3] = NULL;
2111 
2112 /* GT: Mark is a noun here and Set is also a noun. */
2113 	label[9].text = (unichar_t *) _("Mark Set:");
2114 	label[9].text_is_1byte = true;
2115 	gcd[9].gd.label = &label[9];
2116 	gcd[9].gd.flags = sf->mark_set_cnt<1 ? gg_visible : (gg_enabled|gg_visible);
2117 	gcd[9].creator = GLabelCreate;
2118 	flaghvarray[4] = &gcd[9];
2119 
2120 	gcd[10].gd.pos.width = 140;
2121 	gcd[10].gd.flags = gcd[9].gd.flags;
2122 	class = (otl->lookup_flags>>16) & 0xffff;
2123 	if ( !(otl->lookup_flags&pst_usemarkfilteringset) || class >= sf->mark_set_cnt )
2124 	    class = -1;
2125 	gcd[10].gd.u.list = SFMarkSetList(sf,class);
2126 	gcd[10].gd.label = &gcd[10].gd.u.list[class+1];
2127 	gcd[10].gd.cid = CID_Lookup_ProcessSet;
2128 	gcd[10].creator = GListButtonCreate;
2129 	flaghvarray[5] = &gcd[10]; flaghvarray[6] = GCD_Glue; flaghvarray[7] = NULL; flaghvarray[8] = NULL;
2130 
2131 	boxes[1].gd.flags = gg_enabled|gg_visible;
2132 	boxes[1].gd.u.boxelements = flaghvarray;
2133 	boxes[1].creator = GHVBoxCreate;
2134 
2135 	flagarray[8] = &boxes[1]; flagarray[9] = NULL; flagarray[10] = NULL;
2136 
2137 	boxes[2].gd.pos.x = boxes[2].gd.pos.y = 2;
2138 	boxes[2].gd.flags = gg_enabled|gg_visible;
2139 	boxes[2].gd.u.boxelements = flagarray;
2140 	boxes[2].creator = GHVGroupCreate;
2141     varray[4] = &boxes[2]; varray[5] = NULL;
2142 
2143     label[11].text = (unichar_t *) _("Lookup Name:");
2144     label[11].text_is_1byte = true;
2145     gcd[11].gd.label = &label[11];
2146     gcd[11].gd.pos.x = 5; gcd[11].gd.pos.y = gcd[8].gd.pos.y+16;
2147     gcd[11].gd.flags = gg_enabled|gg_visible;
2148     gcd[11].creator = GLabelCreate;
2149     harray2[0] = &gcd[11];
2150 
2151     label[12].text = (unichar_t *) otl->lookup_name;
2152     label[12].text_is_1byte = true;
2153     gcd[12].gd.pos.x = 10; gcd[12].gd.pos.y = gcd[11].gd.pos.y;
2154     gcd[12].gd.pos.width = 140;
2155     gcd[12].gd.flags = gcd[11].gd.flags|gg_text_xim;
2156     gcd[12].gd.label = otl->lookup_name==NULL ? NULL : &label[12];
2157     gcd[12].gd.cid = CID_LookupName;
2158     gcd[12].gd.handle_controlevent = Lookup_NameChanged;
2159     gcd[12].creator = GTextFieldCreate;
2160     harray2[1] = &gcd[12]; harray2[2] = NULL;
2161     if ( otl->lookup_name!=NULL && *otl->lookup_name!='\0' )
2162 	ld.name_has_been_set = true;
2163 
2164     boxes[3].gd.flags = gg_enabled|gg_visible;
2165     boxes[3].gd.u.boxelements = harray2;
2166     boxes[3].creator = GHBoxCreate;
2167     varray[6] = &boxes[3]; varray[7] = NULL;
2168 
2169     k = 13; vpos = 8;
2170     if ( !isgpos ) {
2171 	gcd[13].gd.pos.x = 5; gcd[13].gd.pos.y = gcd[5].gd.pos.y+15;
2172 	gcd[13].gd.flags = otl->lookup_type!=gsub_ligature ? gg_visible :
2173 		otl->store_in_afm ? (gg_visible | gg_enabled | gg_cb_on) :
2174 		(gg_visible | gg_enabled);
2175 	label[13].text = (unichar_t *) _("Store ligature data in AFM files");
2176 	label[13].text_is_1byte = true;
2177 	gcd[13].gd.label = &label[13];
2178 	gcd[13].gd.cid = CID_LookupAfm;
2179 	gcd[13].creator = GCheckBoxCreate;
2180 	varray[8] = &gcd[13]; varray[9] = NULL;
2181 	k = 14; vpos = 10;
2182     }
2183 
2184     gcd[k].gd.pos.x = 30-3;
2185     gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
2186     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
2187     label[k].text = (unichar_t *) _("_OK");
2188     label[k].text_is_1byte = true;
2189     label[k].text_in_resource = true;
2190     gcd[k].gd.label = &label[k];
2191     gcd[k].gd.handle_controlevent = Lookup_OK;
2192     gcd[k].gd.cid = CID_OK;
2193     gcd[k].creator = GButtonCreate;
2194 
2195     gcd[k+1].gd.pos.x = -30;
2196     gcd[k+1].gd.pos.width = -1; gcd[k+1].gd.pos.height = 0;
2197     gcd[k+1].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
2198     label[k+1].text = (unichar_t *) _("_Cancel");
2199     label[k+1].text_is_1byte = true;
2200     label[k+1].text_in_resource = true;
2201     gcd[k+1].gd.label = &label[k+1];
2202     gcd[k+1].gd.handle_controlevent = Lookup_Cancel;
2203     gcd[k+1].gd.cid = CID_Cancel;
2204     gcd[k+1].creator = GButtonCreate;
2205 
2206     harray3[0] = harray3[2] = harray3[3] = harray3[4] = harray3[6] = GCD_Glue;
2207     harray3[7] = NULL;
2208     harray3[1] = &gcd[k]; harray3[5] = &gcd[k+1];
2209 
2210     boxes[4].gd.flags = gg_enabled|gg_visible;
2211     boxes[4].gd.u.boxelements = harray3;
2212     boxes[4].creator = GHBoxCreate;
2213     varray[vpos++] = &boxes[4]; varray[vpos++] = NULL; varray[vpos] = NULL;
2214 
2215     boxes[5].gd.pos.x = boxes[5].gd.pos.y = 2;
2216     boxes[5].gd.flags = gg_enabled|gg_visible;
2217     boxes[5].gd.u.boxelements = varray;
2218     boxes[5].creator = GHVGroupCreate;
2219 
2220     GGadgetsCreate(gw,boxes+5);
2221 
2222     GTextInfoListFree(gcd[8].gd.u.list);
2223     GTextInfoListFree(gcd[10].gd.u.list);
2224 
2225     for ( i=0; i<mi.initial_row_cnt; ++i ) {
2226 	free( mi.matrix_data[2*i+0].u.md_str );
2227 	free( mi.matrix_data[2*i+1].u.md_str );
2228     }
2229     free( mi.matrix_data );
2230 
2231     GMatrixEditSetNewText(gcd[2].ret,S_("OpenTypeFeature|New"));
2232     GHVBoxSetExpandableRow(boxes[5].ret,1);
2233     GHVBoxSetExpandableCol(boxes[4].ret,gb_expandgluesame);
2234     GHVBoxSetExpandableCol(boxes[3].ret,1);
2235     GHVBoxSetExpandableCol(boxes[1].ret,1);
2236     GHVBoxSetExpandableCol(boxes[0].ret,gb_expandglue);
2237 
2238     GHVBoxFitWindow(boxes[5].ret);
2239 
2240     GDrawSetVisible(gw,true);
2241     while ( !ld.done )
2242 	GDrawProcessOneEvent(NULL);
2243     GDrawDestroyWindow(gw);
2244 
2245 return( ld.ok );
2246 }
2247 
CreateAndSortNewLookupOfType(SplineFont * sf,int lookup_type)2248 static OTLookup *CreateAndSortNewLookupOfType(SplineFont *sf, int lookup_type) {
2249     OTLookup *newotl;
2250     int isgpos = lookup_type>=gpos_start;
2251 
2252     newotl = chunkalloc(sizeof(OTLookup));
2253     newotl->lookup_type = lookup_type;
2254     if ( !EditLookup(newotl,isgpos,sf)) {
2255 	chunkfree(newotl,sizeof(OTLookup));
2256 return( NULL );
2257     }
2258     SortInsertLookup(sf, newotl);
2259 return( newotl );
2260 }
2261 
2262 /* ************************************************************************** */
2263 /* ***************************** Anchor Subtable **************************** */
2264 /* ************************************************************************** */
2265 typedef struct anchorclassdlg {
2266     SplineFont *sf;
2267     int def_layer;
2268     struct lookup_subtable *sub;
2269     GWindow gw;
2270     int mag, pixelsize;
2271     int orig_pos, orig_value, down;
2272     BDFFont *display;
2273     int done;
2274     int popup_r;
2275     GCursor cursor_current;
2276     /* Used during autokern */
2277     int rows_at_start;
2278     int rows,cols;		/* Total rows, columns allocated */
2279     int next_row;
2280     struct matrix_data *psts;
2281 } AnchorClassDlg, PSTKernDlg;
2282 #define CID_Anchors	2001
2283 
2284 #define CID_PSTList	2001
2285 #define CID_Alpha	2002
2286 #define CID_Unicode	2003
2287 #define CID_Scripts	2004
2288 #define CID_BaseChar	2005
2289 #define CID_Suffix	2006
2290 #define CID_AllSame	2007
2291 #define CID_Separation	2008
2292 #define CID_MinKern	2009
2293 #define CID_Touched	2010
2294 #define CID_OnlyCloser	2011
2295 #define CID_Autokern	2012
2296 
2297 #define CID_KernDisplay		2022
2298 #define CID_PixelSize		2023
2299 #define CID_Magnification	2024
2300 
2301 static GTextInfo magnifications[] = {
2302     { (unichar_t *) "100%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 1, 0, 1, 0, 0, '\0'},
2303     { (unichar_t *) "200%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
2304     { (unichar_t *) "300%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
2305     { (unichar_t *) "400%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
2306     GTEXTINFO_EMPTY
2307 };
2308 
SFAddAnchorClass(SplineFont * sf,struct lookup_subtable * sub,char * name)2309 static AnchorClass *SFAddAnchorClass(SplineFont *sf,struct lookup_subtable *sub,
2310 	char *name) {
2311     AnchorClass *ac;
2312 
2313     for ( ac=sf->anchor; ac!=NULL; ac=ac->next) {
2314         if ( strcmp(ac->name,name)==0 && ac->subtable==NULL )
2315     break;
2316     }
2317     if ( ac==NULL ) {
2318         ac = chunkalloc(sizeof(AnchorClass));
2319         ac->name = copy(name);
2320         ac->next = sf->anchor;
2321         sf->anchor = ac;
2322     }
2323     ac->type = sub->lookup->lookup_type == gpos_mark2base ? act_mark :
2324 		sub->lookup->lookup_type == gpos_mark2ligature ? act_mklg :
2325 		sub->lookup->lookup_type == gpos_cursive ? act_curs :
2326 							act_mkmk;
2327     ac->subtable = sub;
2328 return( ac );
2329 }
2330 
AnchorClassD_ShowAnchors(GGadget * g,GEvent * e)2331 static int AnchorClassD_ShowAnchors(GGadget *g, GEvent *e) {
2332     AnchorClassDlg *acd;
2333 
2334     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2335 	struct matrix_data *classes;
2336 	int32 class_cnt;
2337 	int row;
2338 	AnchorClass *ac;
2339 
2340 	acd = GDrawGetUserData(GGadgetGetWindow(g));
2341 	classes = GMatrixEditGet(GWidgetGetControl(acd->gw,CID_Anchors), &class_cnt);
2342 	row = GMatrixEditGetActiveRow(GWidgetGetControl(acd->gw,CID_Anchors));
2343 	if ( row==-1 )
2344 return( true );
2345 	ac = classes[2*row+1].u.md_addr;
2346 	if ( ac==NULL || ac->subtable==NULL) {
2347 	    ac = SFAddAnchorClass(acd->sf,acd->sub,classes[2*row+0].u.md_str);
2348 	} else if ( ac->subtable!=acd->sub ) {
2349 	    ff_post_error(_("Name in use"),_("The name, %.80s, has already been used to identify an anchor class in a different lookup subtable (%.80s)"),
2350 		    ac->name, ac->subtable->subtable_name );
2351 return( true );
2352 	}
2353 	AnchorControlClass(acd->sf,ac,acd->def_layer);
2354     }
2355 return( true );
2356 }
2357 
AC_EnableOtherButtons(GGadget * g,int r,int c)2358 static void AC_EnableOtherButtons(GGadget *g,int r, int c) {
2359     GGadgetSetEnabled(GWidgetGetControl(GGadgetGetWindow(g),CID_ShowAnchors),
2360 	    r!=-1 );
2361 }
2362 
AC_OK(GGadget * g,GEvent * e)2363 static int AC_OK(GGadget *g, GEvent *e) {
2364     AnchorClassDlg *acd;
2365 
2366     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2367 	struct matrix_data *classes;
2368 	int32 class_cnt;
2369 	int i,j;
2370 	AnchorClass *ac, *acnext, *actest;
2371 
2372 	acd = GDrawGetUserData(GGadgetGetWindow(g));
2373 	classes = GMatrixEditGet(GWidgetGetControl(acd->gw,CID_Anchors), &class_cnt);
2374 	acd->sub->anchor_classes = true /*class_cnt!=0*/;
2375 	for ( i=0; i<class_cnt; ++i ) {
2376 	    if ( *classes[2*i+0].u.md_str=='\0' )
2377 	continue;			/* Ignore blank lines. They pressed return once too often or something */
2378 	    for ( j=i+1; j<class_cnt; ++j ) {
2379 		if ( strcmp(classes[2*i+0].u.md_str,classes[2*j+0].u.md_str)==0 ) {
2380 		    ff_post_error(_("Name used twice"),_("The name, %.80s, appears twice in this list.\nEach anchor class must have a distinct name."),
2381 			    classes[2*i+0].u.md_str );
2382 return( true );
2383 		}
2384 	    }
2385 	    for ( actest = acd->sf->anchor; actest!=NULL; actest=actest->next )
2386 		if ( actest->subtable!=NULL && actest->subtable!=acd->sub &&
2387 			strcmp(actest->name,classes[2*i+0].u.md_str)==0 )
2388 	    break;
2389 	    if ( actest!=NULL ) {
2390 		ff_post_error(_("Name in use"),_("The name, %.80s, has already been used to identify an anchor class in a different lookup subtable (%.80s)"),
2391 			actest->name, actest->subtable->subtable_name );
2392 return( true );
2393 	    }
2394 	}
2395 
2396 	for ( ac = acd->sf->anchor; ac!=NULL; ac=ac->next )
2397 	    ac->processed = false;
2398 	for ( i=0; i<class_cnt; ++i ) {
2399 	    ac = classes[2*i+1].u.md_addr;
2400 	    if ( ac!=NULL )
2401 		ac->processed = true;
2402 	}
2403 	for ( ac = acd->sf->anchor; ac!=NULL; ac=ac->next ) {
2404 	    if ( !ac->processed && ac->subtable == acd->sub ) {
2405 		char *buts[3];
2406                 int askresult = 0;
2407     /* want to support keeping APs when class is removed from subtable */
2408 		buts[0] = _("_Remove"); buts[1] = _("_Cancel"); buts[2]=NULL;
2409 		askresult = gwwv_ask(_("Remove Anchor Class?"),(const char **) buts,0,1,_("Do you really want to remove the anchor class, %.80s?\nThis will remove all anchor points associated with that class."),
2410 			ac->name );
2411                 if ( askresult==1 )
2412 return( true );
2413                 else
2414                     ac->ticked = askresult==2 ? 0 : 1;
2415 	    }
2416 	}
2417 
2418 	for ( i=0; i<class_cnt; ++i ) {
2419 	    if ( *classes[2*i+0].u.md_str=='\0' )
2420 	continue;			/* Ignore blank lines. They pressed return once too much or something */
2421 	    ac = classes[2*i+1].u.md_addr;
2422 	    if ( ac==NULL || ac->subtable==NULL ) {
2423 		ac = SFAddAnchorClass(acd->sf,acd->sub,classes[2*i+0].u.md_str);
2424 		ac->processed = true;
2425 	    } else {
2426 		free(ac->name);
2427 		ac->name = copy(classes[2*i+0].u.md_str);
2428 		ac->processed = true;
2429 	    }
2430 	}
2431 	for ( ac = acd->sf->anchor; ac!=NULL; ac=acnext ) {
2432 	    acnext = ac->next;
2433 	    if ( !ac->processed && ac->subtable == acd->sub ) {
2434                 if ( ac->ticked )
2435 		    SFRemoveAnchorClass(acd->sf,ac);
2436                 else {
2437                     ac->ticked = 0;
2438                     ac->subtable = NULL;
2439                 }
2440 	    }
2441 	}
2442 	acd->done = true;
2443     }
2444 return( true );
2445 }
2446 
AC_DoCancel(AnchorClassDlg * acd)2447 static void AC_DoCancel(AnchorClassDlg *acd) {
2448     acd->done = true;
2449 }
2450 
AC_Cancel(GGadget * g,GEvent * e)2451 static int AC_Cancel(GGadget *g, GEvent *e) {
2452     AnchorClassDlg *acd;
2453 
2454     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2455 	acd = GDrawGetUserData(GGadgetGetWindow(g));
2456 	AC_DoCancel(acd);
2457     }
2458 return( true );
2459 }
2460 
acd_e_h(GWindow gw,GEvent * event)2461 static int acd_e_h(GWindow gw, GEvent *event) {
2462     AnchorClassDlg *acd = GDrawGetUserData(gw);
2463 
2464     switch ( event->type ) {
2465       case et_close:
2466 	AC_DoCancel(acd);
2467       break;
2468       case et_char:
2469 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
2470 	    help("ui/dialogs/lookups.html", "#lookups-anchor");
2471 return( true );
2472 	}
2473 return( false );
2474       break;
2475       case et_destroy:
2476       break;
2477       case et_mouseup: case et_mousemove: case et_mousedown:
2478       break;
2479       case et_expose:
2480       break;
2481       case et_resize:
2482       break;
2483     }
2484 return( true );
2485 }
2486 
ACDMatrixInit(struct matrixinit * mi,SplineFont * sf,struct lookup_subtable * sub)2487 static void ACDMatrixInit(struct matrixinit *mi,SplineFont *sf, struct lookup_subtable *sub) {
2488     int cnt;
2489     AnchorClass *ac;
2490     struct matrix_data *md;
2491     static struct col_init ci[] = {
2492 	{ me_string , NULL, NULL, NULL, N_("Anchor Class Name") },
2493 	{ me_addr   , NULL, NULL, NULL, "Anchor Class Pointer, hidden" },
2494 	COL_INIT_EMPTY
2495     };
2496     static int initted = false;
2497 
2498     if ( !initted ) {
2499 	initted = true;
2500 	ci[0].title = S_(ci[0].title);
2501     }
2502 
2503     memset(mi,0,sizeof(*mi));
2504     mi->col_cnt = 2;
2505     mi->col_init = ci;
2506 
2507     for ( ac=sf->anchor, cnt=0; ac!=NULL; ac=ac->next )
2508 	if ( ac->subtable == sub )
2509 	    ++cnt;
2510     if ( cnt==0 ) {
2511 	md = calloc(1,sizeof(struct matrix_data));
2512 	mi->initial_row_cnt = 0;
2513     } else {
2514 	md = calloc(2*cnt,sizeof(struct matrix_data));
2515 	for ( ac=sf->anchor, cnt=0; ac!=NULL; ac=ac->next )
2516 	    if ( ac->subtable == sub ) {
2517 		md[2*cnt   +0].u.md_str  = ac->name;
2518 		md[2*cnt++ +1].u.md_addr = ac;
2519 	    }
2520 	mi->initial_row_cnt = cnt;
2521     }
2522     mi->matrix_data = md;
2523 }
2524 
2525 /* Anchor list, [new], [delete], [edit], [show first mark/entry], [show first base/exit] */
2526 /*  [ok], [cancel] */
AnchorClassD(SplineFont * sf,struct lookup_subtable * sub,int def_layer)2527 static void AnchorClassD(SplineFont *sf, struct lookup_subtable *sub, int def_layer) {
2528     GRect pos;
2529     GWindowAttrs wattrs;
2530     AnchorClassDlg acd;
2531     GWindow gw;
2532     char buffer[200];
2533     GGadgetCreateData gcd[6], buttonbox, mainbox[2];
2534     GGadgetCreateData *buttonarray[8], *varray[7];
2535     GTextInfo label[6];
2536     int i;
2537     struct matrixinit mi;
2538 
2539     memset(&acd,0,sizeof(acd));
2540     acd.sf = sf;
2541     acd.def_layer = def_layer;
2542     acd.sub = sub;
2543 
2544     memset(&wattrs,0,sizeof(wattrs));
2545     memset(&gcd,0,sizeof(gcd));
2546     memset(&buttonbox,0,sizeof(buttonbox));
2547     memset(&mainbox,0,sizeof(mainbox));
2548     memset(&label,0,sizeof(label));
2549 
2550     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
2551     wattrs.event_masks = ~(1<<et_charup);
2552     wattrs.restrict_input_to_me = true;
2553     wattrs.undercursor = 1;
2554     wattrs.cursor = ct_pointer;
2555     snprintf(buffer,sizeof(buffer),_("Anchor classes in subtable %.80s"),
2556 	    sub->subtable_name );
2557     wattrs.utf8_window_title = buffer;
2558     wattrs.is_dlg = true;
2559     pos.x = pos.y = 0;
2560     pos.width = GGadgetScale(GDrawPointsToPixels(NULL,325));
2561     pos.height = GDrawPointsToPixels(NULL,250);
2562     acd.gw = gw = GDrawCreateTopWindow(NULL,&pos,acd_e_h,&acd,&wattrs);
2563 
2564     i = 0;
2565 
2566     ACDMatrixInit(&mi,sf,sub);
2567     gcd[i].gd.pos.width = 300; gcd[i].gd.pos.height = 200;
2568     gcd[i].gd.flags = gg_enabled | gg_visible;
2569     gcd[i].gd.cid = CID_Anchors;
2570     gcd[i].gd.u.matrix = &mi;
2571     gcd[i].data = &acd;
2572     gcd[i++].creator = GMatrixEditCreate;
2573     varray[0] = &gcd[i-1]; varray[1] = NULL;
2574 
2575     gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+24+3;
2576     gcd[i].gd.pos.width = -1;
2577     gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
2578     label[i].text = (unichar_t *) _("_OK");
2579     label[i].text_is_1byte = true;
2580     label[i].text_in_resource = true;
2581     gcd[i].gd.label = &label[i];
2582     gcd[i].gd.handle_controlevent = AC_OK;
2583     gcd[i].gd.cid = CID_OK;
2584     gcd[i++].creator = GButtonCreate;
2585 
2586     gcd[i].gd.pos.x = -10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+3;
2587     gcd[i].gd.pos.width = -1;
2588     gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
2589     label[i].text = (unichar_t *) _("_Cancel");
2590     label[i].text_is_1byte = true;
2591     label[i].text_in_resource = true;
2592     gcd[i].gd.label = &label[i];
2593     gcd[i].gd.handle_controlevent = AC_Cancel;
2594     gcd[i].gd.cid = CID_Cancel;
2595     gcd[i++].creator = GButtonCreate;
2596 
2597     buttonarray[0] = GCD_Glue; buttonarray[1] = &gcd[i-2]; buttonarray[2] = GCD_Glue;
2598     buttonarray[3] = GCD_Glue; buttonarray[4] = &gcd[i-1]; buttonarray[5] = GCD_Glue;
2599     buttonarray[6] = NULL;
2600     buttonbox.gd.flags = gg_enabled|gg_visible;
2601     buttonbox.gd.u.boxelements = buttonarray;
2602     buttonbox.creator = GHBoxCreate;
2603     varray[2] = &buttonbox; varray[3] = NULL; varray[4] = NULL;
2604 
2605     mainbox[0].gd.pos.x = mainbox[0].gd.pos.y = 2;
2606     mainbox[0].gd.flags = gg_enabled|gg_visible;
2607     mainbox[0].gd.u.boxelements = varray;
2608     mainbox[0].creator = GHVGroupCreate;
2609 
2610     GGadgetsCreate(acd.gw,mainbox);
2611     GHVBoxSetExpandableRow(mainbox[0].ret,0);
2612     GHVBoxSetExpandableCol(buttonbox.ret,gb_expandgluesame);
2613     GMatrixEditShowColumn(gcd[0].ret,1,false);
2614 
2615     gcd[i].gd.flags = gg_visible | gg_enabled;
2616     label[i].text = (unichar_t *) S_("Anchor Control...");
2617     label[i].text_is_1byte = true;
2618     label[i].text_in_resource = true;
2619     gcd[i].gd.label = &label[i];
2620     gcd[i].gd.cid = CID_ShowAnchors;
2621     gcd[i].gd.handle_controlevent = AnchorClassD_ShowAnchors;
2622     gcd[i].creator = GButtonCreate;
2623 
2624     GMatrixEditAddButtons(gcd[0].ret,gcd+i);
2625     GMatrixEditSetOtherButtonEnable(gcd[0].ret,AC_EnableOtherButtons);
2626     GMatrixEditSetNewText(gcd[0].ret,S_("New Anchor Class"));
2627 
2628     GDrawSetVisible(acd.gw,true);
2629     while ( !acd.done )
2630 	GDrawProcessOneEvent(NULL);
2631     GDrawDestroyWindow(acd.gw);
2632 }
2633 
2634 /* ************************************************************************** */
2635 /* ********************** Single/Double Glyph Subtables ********************* */
2636 /* ************************************************************************** */
2637 static int isalphabetic = true, byscripts = true, stemming=true;
2638 int lookup_hideunused = true;
2639 static int ispair;
2640 
2641 struct sortinfo {
2642     char *glyphname;		/* We replace the glyph name with a pointer to*/
2643 				/* this structure, but must restore it when */
2644 			        /* finished sorting */
2645     SplineChar *sc;		/* The glyph itself */
2646     SplineChar *base;		/* The base of Agrave would be A */
2647     uint32 script;
2648 };
2649 
SortPrep(PSTKernDlg * pstkd,struct matrix_data * md,struct sortinfo * si)2650 static void SortPrep(PSTKernDlg *pstkd, struct matrix_data *md, struct sortinfo *si) {
2651 
2652     si->glyphname = md->u.md_str;
2653     md->u.md_ival = (intpt) si;
2654 
2655     si->sc = SFGetChar(pstkd->sf,-1,si->glyphname);
2656     if ( si->sc==NULL )
2657 return;
2658     if ( byscripts )
2659 	si->script = SCScriptFromUnicode(si->sc);
2660     if ( stemming ) {
2661 	const unichar_t *alt=NULL;
2662 	int uni = si->sc->unicodeenc;
2663 	char *pt;
2664 
2665         if ( uni!=-1 && uni<0x10000 &&
2666         	isdecompositionnormative(uni) &&
2667 		unicode_alternates[uni>>8]!=NULL &&
2668 		(alt = unicode_alternates[uni>>8][uni&0xff])!=NULL )
2669 	    si->base = SFGetChar(pstkd->sf,alt[0],NULL);
2670 	if ( si->base==NULL &&
2671 		((pt=strchr(si->glyphname,'.'))!=NULL ||
2672 		 (pt=strchr(si->glyphname,'_'))!=NULL )) {
2673 	    int ch = *pt;
2674 	    *pt = '\0';
2675 	    si->base = SFGetChar(pstkd->sf,-1,si->glyphname);
2676 	    *pt = ch;
2677 	}
2678 	if ( si->base==NULL )
2679 	    si->base = si->sc;
2680     }
2681 }
2682 
SortUnPrep(struct matrix_data * md)2683 static void SortUnPrep(struct matrix_data *md) {
2684     struct sortinfo *si = (struct sortinfo *) (md->u.md_ival);
2685 
2686     md->u.md_str = si->glyphname;
2687 }
2688 
_md_cmp(const struct sortinfo * md1,const struct sortinfo * md2)2689 static int _md_cmp(const struct sortinfo *md1, const struct sortinfo *md2) {
2690 
2691     if ( md1->sc==NULL || md2->sc==NULL ) {
2692 	if ( md1->sc!=NULL )
2693 return( 1 );
2694 	else if ( md2->sc!=NULL )
2695 return( -1 );
2696 	else
2697 return( strcmp( md1->glyphname,md2->glyphname ));
2698     }
2699 
2700     if ( byscripts && md1->script!=md2->script ) {
2701 	if ( md1->script==DEFAULT_SCRIPT )
2702 return( 1 );
2703 	else if ( md2->script==DEFAULT_SCRIPT )
2704 return( -1 );
2705 	if ( md1->script>md2->script )
2706 return( 1 );
2707 	else
2708 return( -1 );
2709     }
2710 
2711     if ( !isalphabetic ) {
2712 	int uni1;
2713 	int uni2;
2714 
2715 	if ( stemming ) {
2716 	    /* First ignore case */
2717 	    uni1 = (md1->base->unicodeenc!=-1)? md1->base->unicodeenc : 0xffffff;
2718 	    uni2 = (md2->base->unicodeenc!=-1)? md2->base->unicodeenc : 0xffffff;
2719 	    if ( uni1<0x10000 && islower(uni1)) uni1 = toupper(uni1);
2720 	    if ( uni2<0x10000 && islower(uni2)) uni2 = toupper(uni2);
2721 
2722 	    if ( uni1>uni2 )
2723 return( 1 );
2724 	    else if ( uni1<uni2 )
2725 return( -1 );
2726 
2727 	    uni1 = (md1->base->unicodeenc!=-1)? md1->base->unicodeenc : 0xffffff;
2728 	    uni2 = (md2->base->unicodeenc!=-1)? md2->base->unicodeenc : 0xffffff;
2729 	    if ( uni1>uni2 )
2730 return( 1 );
2731 	    else if ( uni1<uni2 )
2732 return( -1 );
2733 	}
2734 
2735 	uni1 = (md1->sc->unicodeenc!=-1)? md1->sc->unicodeenc : 0xffffff;
2736 	uni2 = (md2->sc->unicodeenc!=-1)? md2->sc->unicodeenc : 0xffffff;
2737 	if ( uni1>uni2 )
2738 return( 1 );
2739 	else if ( uni1<uni2 )
2740 return( -1 );
2741     } else {
2742 	if ( stemming ) {
2743 	    int ret;
2744 	    ret = strcasecmp(md1->base->name,md2->base->name);
2745 	    if ( ret!=0 )
2746 return( ret );
2747 	    ret = strcmp(md1->base->name,md2->base->name);
2748 	    if ( ret!=0 )
2749 return( ret );
2750 	}
2751     }
2752 return( strcmp(md1->glyphname,md2->glyphname));
2753 }
2754 
md_cmp(const void * _md1,const void * _md2)2755 static int md_cmp(const void *_md1, const void *_md2) {
2756     const struct matrix_data *md1 = _md1, *md2 = _md2;
2757     int ret = _md_cmp((struct sortinfo *) md1->u.md_ival,(struct sortinfo *) md2->u.md_ival);
2758 
2759     if ( ret==0 && ispair )
2760 	ret = _md_cmp((struct sortinfo *) md1[1].u.md_ival,(struct sortinfo *) md2[1].u.md_ival);
2761 return( ret );
2762 }
2763 
PSTKD_DoSort(PSTKernDlg * pstkd,struct matrix_data * psts,int rows,int cols)2764 static void PSTKD_DoSort(PSTKernDlg *pstkd,struct matrix_data *psts,int rows,int cols) {
2765     struct sortinfo *primary, *secondary=NULL;
2766     int i;
2767 
2768     if ( pstkd->gw!=NULL && GWidgetGetControl(pstkd->gw,CID_Alpha)!=NULL ) {
2769 	isalphabetic = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Alpha));
2770 	byscripts = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Scripts));
2771 	stemming = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_BaseChar));
2772     }
2773     primary = calloc(rows,sizeof(struct sortinfo));
2774     ispair = pstkd->sub->lookup->lookup_type == gpos_pair;
2775     if ( ispair )
2776 	secondary = calloc(rows,sizeof(struct sortinfo));
2777     for ( i=0; i<rows; ++i ) {
2778 	SortPrep(pstkd,&psts[i*cols+0],&primary[i]);
2779 	if ( ispair )
2780 	    SortPrep(pstkd,&psts[i*cols+1],&secondary[i]);
2781     }
2782     qsort( psts, rows, cols*sizeof(struct matrix_data), md_cmp);
2783     for ( i=0; i<rows; ++i ) {
2784 	SortUnPrep(&psts[i*cols+0]);
2785 	if ( ispair )
2786 	    SortUnPrep(&psts[i*cols+1]);
2787     }
2788     free(primary);
2789     free(secondary);
2790 }
2791 
PST_FreeImage(const void * _pstkd,GImage * img)2792 static void PST_FreeImage(const void *_pstkd, GImage *img) {
2793     GImageDestroy(img);
2794 }
2795 
_PST_GetImage(const void * _pstkd)2796 static GImage *_PST_GetImage(const void *_pstkd) {
2797     PSTKernDlg *pstkd = (PSTKernDlg *) _pstkd;
2798     GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
2799     int rows, cols = GMatrixEditGetColCnt(pstk);
2800     struct matrix_data *old = GMatrixEditGet(pstk,&rows);
2801     SplineChar *sc = SFGetChar(pstkd->sf,-1, old[cols*pstkd->popup_r].u.md_str);
2802 
2803 return( PST_GetImage(pstk,pstkd->sf,pstkd->def_layer,pstkd->sub,pstkd->popup_r,sc) );
2804 }
2805 
PST_PopupPrepare(GGadget * g,int r,int c)2806 static void PST_PopupPrepare(GGadget *g, int r, int c) {
2807     PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
2808     int rows, cols = GMatrixEditGetColCnt(g);
2809     struct matrix_data *old = GMatrixEditGet(g,&rows);
2810     if ( c!=0 && pstkd->sub->lookup->lookup_type == gpos_single )
2811 return;
2812     if ( c<0 || c>=cols || r<0 || r>=rows || old[cols*r].u.md_str==NULL ||
2813 	SFGetChar(pstkd->sf,-1, old[cols*r].u.md_str)==NULL )
2814 return;
2815     pstkd->popup_r = r;
2816     GGadgetPreparePopupImage(GGadgetGetWindow(g),NULL,pstkd,_PST_GetImage,PST_FreeImage);
2817 }
2818 
2819 static void PSTKD_FinishSuffixedEdit(GGadget *g, int row, int col, int wasnew);
2820 static void PSTKD_FinishBoundsEdit(GGadget *g, int row, int col, int wasnew);
2821 static void PSTKD_FinishKernEdit(GGadget *g, int row, int col, int wasnew);
2822 static void PSTKD_InitSameAsRow(GGadget *g, int row);
2823 
is_boundsFeat(struct lookup_subtable * sub)2824 static int is_boundsFeat( struct lookup_subtable *sub) {
2825     FeatureScriptLangList *features = sub->lookup->features, *testf;
2826 
2827     if ( sub->lookup->lookup_type == gpos_single ) {
2828 	for ( testf=features; testf!=NULL; testf=testf->next ) {
2829 	    if ( testf->featuretag==CHR('l','f','b','d'))
2830 return( 1 );
2831 	    else if ( testf->featuretag==CHR('r','t','b','d'))
2832 return( -1 );
2833 	}
2834     }
2835 return( 0 );
2836 }
2837 
PSTMatrixInit(struct matrixinit * mi,SplineFont * _sf,struct lookup_subtable * sub,PSTKernDlg * pstkd)2838 static void PSTMatrixInit(struct matrixinit *mi,SplineFont *_sf, struct lookup_subtable *sub, PSTKernDlg *pstkd) {
2839     int cnt;
2840     struct matrix_data *md;
2841     static struct col_init simplesubsci[] = {
2842 	{ me_string , NULL, NULL, NULL, N_("Base Glyph Name") },
2843 	{ me_string, NULL, NULL, NULL, N_("Replacement Glyph Name") },
2844 	COL_INIT_EMPTY
2845     };
2846     static struct col_init ligatureci[] = {
2847 	{ me_string , NULL, NULL, NULL, N_("Ligature Glyph Name") },
2848 	{ me_string, NULL, NULL, NULL, N_("Source Glyph Names") },
2849 	COL_INIT_EMPTY
2850     };
2851     static struct col_init altmultsubsci[] = {
2852 	{ me_string , NULL, NULL, NULL, N_("Base Glyph Name") },
2853 	{ me_string, NULL, NULL, NULL, N_("Replacement Glyph Names") },
2854 	COL_INIT_EMPTY
2855     };
2856 #define SIM_DX		1
2857 #define SIM_DY		3
2858 #define SIM_DX_ADV	5
2859 #define SIM_DY_ADV	7
2860 #define PAIR_DX1	2
2861 #define PAIR_DY1	4
2862 #define PAIR_DX_ADV1	6
2863 #define PAIR_DY_ADV1	8
2864 #define PAIR_DX2	10
2865 #define PAIR_DY2	12
2866 #define PAIR_DX_ADV2	14
2867 #define PAIR_DY_ADV2	16
2868     static struct col_init simpleposci[] = {
2869 	{ me_string , NULL, NULL, NULL, N_("Base Glyph Name") },
2870 	{ me_int, NULL, NULL, NULL, NU_("∆x") },	/* delta-x */
2871 /* GT: "Adjust" here means Device Table based pixel adjustments, an OpenType */
2872 /* GT: concept which allows small corrections for small pixel sizes where */
2873 /* GT: rounding errors (in kerning for example) may smush too glyphs together */
2874 /* GT: or space them too far apart. Generally not a problem for big pixelsizes*/
2875 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2876 	{ me_int, NULL, NULL, NULL, NU_("∆y") },	/* delta-y */
2877 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2878 	{ me_int, NULL, NULL, NULL, NU_("∆x_adv") },	/* delta-x-adv */
2879 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2880 	{ me_int, NULL, NULL, NULL, NU_("∆y_adv") },	/* delta-y-adv */
2881 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2882 	COL_INIT_EMPTY
2883     };
2884     static struct col_init pairposci[] = {
2885 	{ me_string , NULL, NULL, NULL, N_("First Glyph Name") },
2886 	{ me_string , NULL, NULL, NULL, N_("Second Glyph Name") },
2887 	{ me_int, NULL, NULL, NULL, NU_("∆x #1") },	/* delta-x */
2888 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2889 	{ me_int, NULL, NULL, NULL, NU_("∆y #1") },	/* delta-y */
2890 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2891 	{ me_int, NULL, NULL, NULL, NU_("∆x_adv #1") },	/* delta-x-adv */
2892 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2893 	{ me_int, NULL, NULL, NULL, NU_("∆y_adv #1") },	/* delta-y-adv */
2894 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2895 	{ me_int, NULL, NULL, NULL, NU_("∆x #2") },	/* delta-x */
2896 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2897 	{ me_int, NULL, NULL, NULL, NU_("∆y #2") },	/* delta-y */
2898 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2899 	{ me_int, NULL, NULL, NULL, NU_("∆x_adv #2") },	/* delta-x-adv */
2900 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2901 	{ me_int, NULL, NULL, NULL, NU_("∆y_adv #2") },	/* delta-y-adv */
2902 	{ me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2903 	COL_INIT_EMPTY
2904     };
2905     static struct { int ltype; int cnt; struct col_init *ci; } fuf[] = {
2906 	{ gsub_single, sizeof(simplesubsci)/sizeof(struct col_init)-1, simplesubsci },
2907 	{ gsub_multiple, sizeof(altmultsubsci)/sizeof(struct col_init)-1, altmultsubsci },
2908 	{ gsub_alternate, sizeof(altmultsubsci)/sizeof(struct col_init)-1, altmultsubsci },
2909 	{ gsub_ligature, sizeof(ligatureci)/sizeof(struct col_init)-1, ligatureci },
2910 	{ gpos_single, sizeof(simpleposci)/sizeof(struct col_init)-1, simpleposci },
2911 	{ gpos_pair, sizeof(pairposci)/sizeof(struct col_init)-1, pairposci },
2912 	{ 0, 0, NULL }
2913     };
2914     static int initted = false;
2915     int lookup_type = sub->lookup->lookup_type;
2916     int isv, r2l = sub->lookup->lookup_flags&pst_r2l;
2917     int i,j, gid,k;
2918     SplineFont *sf;
2919     SplineChar *sc;
2920     PST *pst;
2921     KernPair *kp;
2922 
2923     if ( !initted ) {
2924 	initted = true;
2925 	for ( i=0; fuf[i].ltype!=0; ++i ) if ( fuf[i].ltype!=gsub_alternate ) {
2926 	    for ( j=0; j<fuf[i].cnt; ++j )
2927 		fuf[i].ci[j].title = S_(fuf[i].ci[j].title);
2928 	}
2929     }
2930 
2931     memset(mi,0,sizeof(*mi));
2932     for ( i=0; fuf[i].ltype!=0 && fuf[i].ltype!=lookup_type; ++i );
2933     if ( fuf[i].ltype==0 ) {
2934 	IError( "Unknown lookup type in PSTMatrixInit");
2935 	i -= 2;
2936     }
2937     mi->col_cnt = fuf[i].cnt;
2938     mi->col_init = fuf[i].ci;
2939 
2940     for ( j=0; j<2; ++j ) {
2941 	cnt = 0;
2942 	k = 0;
2943 	do {
2944 	    sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k];
2945 	    for ( gid = 0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
2946 		for ( pst = sc->possub; pst!=NULL; pst=pst->next ) {
2947 		    if ( pst->subtable == sub ) {
2948 			if ( j ) {
2949 			    md[cnt*mi->col_cnt].u.md_str = SCNameUniStr( sc );
2950 			    switch ( lookup_type ) {
2951 			      case gsub_single:
2952 				md[cnt*mi->col_cnt+1].u.md_str = SFNameList2NameUni(sf,pst->u.subs.variant);
2953 			      break;
2954 			      case gsub_multiple:
2955 			      case gsub_alternate:
2956 				md[cnt*mi->col_cnt+1].u.md_str = SFNameList2NameUni(sf,pst->u.mult.components);
2957 			      break;
2958 			      case gsub_ligature:
2959 				md[cnt*mi->col_cnt+1].u.md_str = SFNameList2NameUni(sf,pst->u.lig.components);
2960 			      break;
2961 			      case gpos_single:
2962 				md[cnt*mi->col_cnt+SIM_DX].u.md_ival = pst->u.pos.xoff;
2963 				md[cnt*mi->col_cnt+SIM_DY].u.md_ival = pst->u.pos.yoff;
2964 				md[cnt*mi->col_cnt+SIM_DX_ADV].u.md_ival = pst->u.pos.h_adv_off;
2965 				md[cnt*mi->col_cnt+SIM_DY_ADV].u.md_ival = pst->u.pos.v_adv_off;
2966 				ValDevTabToStrings(md,cnt*mi->col_cnt+SIM_DX+1,pst->u.pos.adjust);
2967 			      break;
2968 			      case gpos_pair:
2969 				md[cnt*mi->col_cnt+1].u.md_str = SFNameList2NameUni( sf,pst->u.pair.paired );
2970 				md[cnt*mi->col_cnt+PAIR_DX1].u.md_ival = pst->u.pair.vr[0].xoff;
2971 				md[cnt*mi->col_cnt+PAIR_DY1].u.md_ival = pst->u.pair.vr[0].yoff;
2972 				md[cnt*mi->col_cnt+PAIR_DX_ADV1].u.md_ival = pst->u.pair.vr[0].h_adv_off;
2973 				md[cnt*mi->col_cnt+PAIR_DY_ADV1].u.md_ival = pst->u.pair.vr[0].v_adv_off;
2974 				md[cnt*mi->col_cnt+PAIR_DX2].u.md_ival = pst->u.pair.vr[1].xoff;
2975 				md[cnt*mi->col_cnt+PAIR_DY2].u.md_ival = pst->u.pair.vr[1].yoff;
2976 				md[cnt*mi->col_cnt+PAIR_DX_ADV2].u.md_ival = pst->u.pair.vr[1].h_adv_off;
2977 				md[cnt*mi->col_cnt+PAIR_DY_ADV2].u.md_ival = pst->u.pair.vr[1].v_adv_off;
2978 				ValDevTabToStrings(md,cnt*mi->col_cnt+PAIR_DX1+1,pst->u.pair.vr[0].adjust);
2979 				ValDevTabToStrings(md,cnt*mi->col_cnt+PAIR_DX2+1,pst->u.pair.vr[1].adjust);
2980 			      break;
2981 			    }
2982 			}
2983 			++cnt;
2984 		    }
2985 		}
2986 		if ( lookup_type==gpos_pair ) {
2987 		    for ( isv=0; isv<2; ++isv ) {
2988 			for ( kp = isv ? sc->vkerns : sc->kerns ; kp!=NULL; kp=kp->next ) {
2989 			    if ( kp->subtable == sub ) {
2990 				if ( j ) {
2991 				    md[cnt*mi->col_cnt+0].u.md_str = SCNameUniStr( sc );
2992 				    md[cnt*mi->col_cnt+1].u.md_str = SCNameUniStr( kp->sc );
2993 			            if ( isv ) {
2994 					md[cnt*mi->col_cnt+PAIR_DY_ADV1].u.md_ival = kp->off;
2995 					DevTabToString(&md[cnt*mi->col_cnt+PAIR_DY_ADV1+1].u.md_str,kp->adjust);
2996 				    } else if ( r2l ) {
2997 					md[cnt*mi->col_cnt+PAIR_DX_ADV1].u.md_ival = kp->off;
2998 					DevTabToString(&md[cnt*mi->col_cnt+PAIR_DX_ADV1+1].u.md_str,kp->adjust);
2999 					md[cnt*mi->col_cnt+PAIR_DX1].u.md_ival = kp->off;
3000 					DevTabToString(&md[cnt*mi->col_cnt+PAIR_DX1+1].u.md_str,kp->adjust);
3001 				    } else {
3002 					md[cnt*mi->col_cnt+PAIR_DX_ADV1].u.md_ival = kp->off;
3003 					DevTabToString(&md[cnt*mi->col_cnt+PAIR_DX_ADV1+1].u.md_str,kp->adjust);
3004 				    }
3005 			        }
3006 			        ++cnt;
3007 			    }
3008 			}
3009 		    }
3010 		}
3011 	    }
3012 	    ++k;
3013 	} while ( k<_sf->subfontcnt );
3014 	if ( !j ) {
3015 	    mi->initial_row_cnt = cnt;
3016 	    if ( cnt==0 ) {
3017 		md = calloc(mi->col_cnt,sizeof(struct matrix_data));
3018     break;
3019 	    } else {
3020 		md = calloc(mi->col_cnt*cnt,sizeof(struct matrix_data));
3021 	    }
3022 	}
3023     }
3024     PSTKD_DoSort(pstkd,md,cnt,mi->col_cnt);
3025     mi->matrix_data = md;
3026     if ( lookup_type==gsub_single )
3027 	mi->finishedit = PSTKD_FinishSuffixedEdit;
3028     else if ( lookup_type==gpos_single ) {
3029 	mi->initrow = PSTKD_InitSameAsRow;
3030 	if ( is_boundsFeat(sub))
3031 	    mi->finishedit = PSTKD_FinishBoundsEdit;
3032     } else if ( lookup_type==gpos_pair && !sub->vertical_kerning )
3033 	mi->finishedit = PSTKD_FinishKernEdit;
3034 }
3035 
PSTKD_FinishSuffixedEdit(GGadget * g,int row,int col,int wasnew)3036 static void PSTKD_FinishSuffixedEdit(GGadget *g, int row, int col, int wasnew) {
3037     PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3038     int rows, cols = GMatrixEditGetColCnt(g);
3039     struct matrix_data *psts = _GMatrixEditGet(g,&rows);
3040     char *suffix = GGadgetGetTitle8(GWidgetGetControl(pstkd->gw,CID_Suffix));
3041     SplineChar *alt, *sc;
3042 
3043     if ( col!=0 || !wasnew || psts[row*cols+0].u.md_str==NULL )
3044 return;
3045     if ( *suffix=='\0' || ( suffix[0]=='.' && suffix[1]=='\0' ))
3046 return;
3047     sc = SFGetChar(pstkd->sf,-1,psts[row*cols+0].u.md_str);
3048     if ( sc==NULL )
3049 return;
3050     alt = SuffixCheck(sc,suffix);
3051     if ( alt!=NULL )
3052 	psts[row*cols+1].u.md_str = copy(alt->name);
3053 }
3054 
PSTKD_FinishBoundsEdit(GGadget * g,int row,int col,int wasnew)3055 static void PSTKD_FinishBoundsEdit(GGadget *g, int row, int col, int wasnew) {
3056     PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3057     int rows, cols = GMatrixEditGetColCnt(g);
3058     struct matrix_data *psts = _GMatrixEditGet(g,&rows);
3059     int is_bounds = is_boundsFeat(pstkd->sub);
3060     SplineChar *sc;
3061     real loff, roff;
3062 
3063     if ( col!=0 || !wasnew || psts[row*cols+0].u.md_str==NULL )
3064 return;
3065     if ( is_bounds==0 )
3066 return;
3067     sc = SFGetChar(pstkd->sf,-1,psts[row*cols+0].u.md_str);
3068     if ( sc==NULL )
3069 return;
3070 
3071     GuessOpticalOffset(sc,pstkd->def_layer,&loff,&roff,0);
3072     if ( is_bounds>0 ) {
3073 	psts[row*cols+SIM_DX].u.md_ival = -loff;
3074 	psts[row*cols+SIM_DX_ADV].u.md_ival = -loff;
3075     } else
3076 	psts[row*cols+SIM_DX_ADV].u.md_ival = -roff;
3077 }
3078 
PSTKD_AddKP(void * data,SplineChar * left,SplineChar * right,int off)3079 static void PSTKD_AddKP(void *data,SplineChar *left, SplineChar *right, int off) {
3080     PSTKernDlg *pstkd = data;
3081     struct matrix_data *psts = pstkd->psts;
3082     int cols = pstkd->cols;
3083     int i;
3084     SplineChar *first=left, *second=right;
3085 
3086     if ( pstkd->sub->lookup->lookup_flags & pst_r2l ) {
3087 	first = right;
3088 	second = left;
3089     }
3090 
3091     for ( i=0; i<pstkd->rows_at_start; ++i ) {
3092 	/* If the user has already got this combination in the lookup */
3093 	/*  then don't add a new guess, and don't override the old */
3094 	if ( psts[i*cols+0].u.md_str!=NULL && psts[i*cols+1].u.md_str!=NULL &&
3095 	    strcmp(psts[i*cols+0].u.md_str,first->name)==0 &&
3096 	    strcmp(psts[i*cols+1].u.md_str,second->name)==0 )
3097 return;
3098     }
3099 
3100     i = pstkd->next_row++;
3101     if ( i >= pstkd->rows )
3102 	pstkd->psts = psts = realloc(psts,(pstkd->rows += 100)*cols*sizeof(struct matrix_data));
3103     memset(psts+i*cols,0,cols*sizeof(struct matrix_data));
3104     psts[i*cols+0].u.md_str = copy(first->name);
3105     psts[i*cols+1].u.md_str = copy(second->name);
3106     if ( pstkd->sub->lookup->lookup_flags & pst_r2l )
3107 	psts[i*cols+PAIR_DX_ADV1].u.md_ival = psts[i*cols+PAIR_DX1].u.md_ival = off;
3108     /* else if ( pstkd->sub->vertical_kerning ) */	/* We don't do vertical stuff */
3109     else
3110 	psts[i*cols+PAIR_DX_ADV1].u.md_ival = off;
3111     if ( strcmp(psts[0+0].u.md_str,"T")!=0 )
3112 	second = left;
3113 }
3114 
PSTKD_FinishKernEdit(GGadget * g,int row,int col,int wasnew)3115 static void PSTKD_FinishKernEdit(GGadget *g, int row, int col, int wasnew) {
3116     PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3117     int rows, cols = GMatrixEditGetColCnt(g);
3118     struct matrix_data *psts = _GMatrixEditGet(g,&rows);
3119     SplineChar *sc1, *sc2;
3120     SplineChar *lefts[2], *rights[2];
3121     int err, touch, separation;
3122 
3123     if ( col>1 || psts[row*cols+0].u.md_str==NULL ||
3124 	    psts[row*cols+1].u.md_str==NULL ||
3125 	    psts[row*cols+PAIR_DX1].u.md_ival!=0 ||
3126 	    psts[row*cols+PAIR_DX_ADV1].u.md_ival!=0 ||
3127 	    psts[row*cols+PAIR_DY_ADV1].u.md_ival!=0 ||
3128 	    psts[row*cols+PAIR_DX_ADV2].u.md_ival!=0 )
3129 return;
3130     sc1 = SFGetChar(pstkd->sf,-1,psts[row*cols+0].u.md_str);
3131     sc2 = SFGetChar(pstkd->sf,-1,psts[row*cols+1].u.md_str);
3132     if ( sc1==NULL || sc2==NULL )
3133 return;
3134     lefts[1] = rights[1] = NULL;
3135     if ( pstkd->sub->lookup->lookup_flags & pst_r2l ) {
3136 	lefts[0] = sc2;
3137 	rights[0] = sc1;
3138     } else {
3139 	lefts[0] = sc1;
3140 	rights[0] = sc2;
3141     }
3142 
3143     err = false;
3144     touch = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Touched));
3145     separation = GetInt8(pstkd->gw,CID_Separation,_("Separation"),&err);
3146     if ( err )
3147 return;
3148 
3149     pstkd->cols = GMatrixEditGetColCnt(g);
3150     pstkd->psts = psts;
3151     pstkd->rows_at_start = 0;
3152     pstkd->next_row = row;
3153     pstkd->rows = rows;
3154 
3155     AutoKern2(pstkd->sf,pstkd->def_layer,lefts,rights,pstkd->sub,
3156 	    separation,0,touch,0,0,	/* Don't bother with minkern,onlyCloser they asked for this, they get it, whatever it may be */
3157 	    PSTKD_AddKP,pstkd);
3158     if ( pstkd->psts != psts )
3159 	IError("AutoKern added too many pairs, was only supposed to add one");
3160     else
3161 	GGadgetRedraw(g);
3162 }
3163 
PSTKD_InitSameAsRow(GGadget * g,int row)3164 static void PSTKD_InitSameAsRow(GGadget *g, int row) {
3165     GWidget gw = GGadgetGetWindow(g);
3166     int rows, cols = GMatrixEditGetColCnt(g);
3167     struct matrix_data *psts = GMatrixEditGet(g,&rows);
3168 
3169     if ( row==0 )
3170 return;
3171     if ( !GGadgetIsChecked(GWidgetGetControl(gw,CID_AllSame)))
3172 return;
3173     psts[row*cols+SIM_DX].u.md_ival = psts[0+SIM_DX].u.md_ival;
3174     psts[row*cols+SIM_DY].u.md_ival = psts[0+SIM_DY].u.md_ival;
3175     psts[row*cols+SIM_DX_ADV].u.md_ival = psts[0+SIM_DX_ADV].u.md_ival;
3176     psts[row*cols+SIM_DY_ADV].u.md_ival = psts[0+SIM_DY_ADV].u.md_ival;
3177 }
3178 
PSTKD_Sort(GGadget * g,GEvent * e)3179 static int PSTKD_Sort(GGadget *g, GEvent *e) {
3180     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
3181 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3182 	GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3183 	int rows, cols = GMatrixEditGetColCnt(pstk);
3184 	struct matrix_data *old = GMatrixEditGet(pstk,&rows);
3185 	PSTKD_DoSort(pstkd,old,rows,cols);
3186 	GGadgetRedraw(pstk);
3187     }
3188 return( true );
3189 }
3190 
PSTKD_DoHideUnused(PSTKernDlg * pstkd)3191 static void PSTKD_DoHideUnused(PSTKernDlg *pstkd) {
3192     GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3193     int rows, cols = GMatrixEditGetColCnt(pstk);
3194     struct matrix_data *old = GMatrixEditGet(pstk,&rows);
3195     uint8 cols_used[20];
3196     int r, col, startc, tot;
3197 
3198     startc = ( pstkd->sub->lookup->lookup_type == gpos_single ) ? 1 : 2;
3199     if ( lookup_hideunused ) {
3200 	memset(cols_used,0,sizeof(cols_used));
3201 	for ( r=0; r<rows; ++r ) {
3202 	    for ( col=startc; col<cols; col+=2 ) {
3203 		if ( old[cols*r+col].u.md_ival!=0 )
3204 		    cols_used[col] = true;
3205 		if ( old[cols*r+col+1].u.md_str!=NULL && *old[cols*r+col+1].u.md_str!='\0' )
3206 		    cols_used[col+1] = true;
3207 	    }
3208 	}
3209 	/* If no columns used (no info yet, all info is to preempt a kernclass and sets to 0) */
3210 	/*  then show what we expect to be the default column for this kerning mode*/
3211 	for ( col=startc, tot=0; col<cols; ++col )
3212 	    tot += cols_used[col];
3213 	if ( tot==0 ) {
3214 	    if ( startc==1 ) {
3215 		cols_used[SIM_DX] = cols_used[SIM_DY] =
3216 			cols_used[SIM_DX_ADV] = cols_used[SIM_DY_ADV] = true;
3217 	    } else {
3218 		if ( pstkd->sub->vertical_kerning )
3219 		    cols_used[PAIR_DY_ADV1] = true;
3220 		else if ( pstkd->sub->lookup->lookup_flags&pst_r2l )
3221 		    cols_used[PAIR_DX_ADV1] = cols_used[PAIR_DX1] = true;
3222 		else
3223 		    cols_used[PAIR_DX_ADV1] = true;
3224 	    }
3225 	}
3226 	for ( col=startc; col<cols; ++col )
3227 	    GMatrixEditShowColumn(pstk,col,cols_used[col]);
3228     } else {
3229 	for ( col=startc; col<cols; ++col )
3230 	    GMatrixEditShowColumn(pstk,col,true);
3231     }
3232     GWidgetToDesiredSize(pstkd->gw);
3233 
3234     GGadgetRedraw(pstk);
3235 }
3236 
PSTKD_HideUnused(GGadget * g,GEvent * e)3237 static int PSTKD_HideUnused(GGadget *g, GEvent *e) {
3238     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
3239 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3240 	lookup_hideunused = GGadgetIsChecked(g);
3241 	PSTKD_DoHideUnused(pstkd);
3242 	GGadgetRedraw(GWidgetGetControl(pstkd->gw,CID_PSTList));
3243     }
3244 return( true );
3245 }
3246 
PSTKD_MagnificationChanged(GGadget * g,GEvent * e)3247 static int PSTKD_MagnificationChanged(GGadget *g, GEvent *e) {
3248     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
3249 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3250 	int mag = GGadgetGetFirstListSelectedItem(g);
3251 
3252 	if ( mag!=-1 && mag!=pstkd->mag-1 ) {
3253 	    pstkd->mag = mag+1;
3254 	    GGadgetRedraw(GWidgetGetControl(pstkd->gw,CID_KernDisplay));
3255 	}
3256     }
3257 return( true );
3258 }
3259 
PSTKD_DisplaySizeChanged(GGadget * g,GEvent * e)3260 static int PSTKD_DisplaySizeChanged(GGadget *g, GEvent *e) {
3261     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
3262 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3263 	const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(pstkd->gw,CID_PixelSize));
3264 	unichar_t *end;
3265 	int pixelsize = u_strtol(ret,&end,10);
3266 
3267 	while ( *end==' ' ) ++end;
3268 	if ( pixelsize>4 && pixelsize<400 && *end=='\0' ) {
3269 	    pstkd->pixelsize = pixelsize;
3270 	    if ( pstkd->display!=NULL ) {
3271 		BDFFontFree(pstkd->display);
3272 		pstkd->display = NULL;
3273 	    }
3274 	    GGadgetRedraw(GWidgetGetControl(pstkd->gw,CID_KernDisplay));
3275 	}
3276     }
3277 return( true );
3278 }
3279 
PSTKD_METextChanged(GGadget * g,int r,int c,GGadget * text)3280 static void PSTKD_METextChanged(GGadget *g, int r, int c, GGadget *text) {
3281     PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3282     GGadgetRedraw(GWidgetGetControl(pstkd->gw,CID_KernDisplay));
3283 }
3284 
FigureValue(struct matrix_data * old,int rcol,int c,int startc,GGadget * tf,double scale,int pixelsize)3285 static int FigureValue(struct matrix_data *old,int rcol,int c, int startc,
3286 	GGadget *tf,double scale, int pixelsize) {
3287     int val;
3288     char *str, *freeme=NULL;
3289     DeviceTable dt;
3290 
3291     if ( c==startc && tf!=NULL )
3292 	val = u_strtol(_GGadgetGetTitle(tf),NULL,10);
3293     else
3294 	val = old[rcol+startc].u.md_ival;
3295     val = rint(val*scale);
3296     if ( c==startc+1 && tf!=NULL )
3297 	str = freeme = GGadgetGetTitle8(tf);
3298     else
3299 	str = old[rcol+startc+1].u.md_str;
3300     memset(&dt,0,sizeof(dt));
3301     DeviceTableParse(&dt,str);
3302     if ( pixelsize>=dt.first_pixel_size && pixelsize<=dt.last_pixel_size && dt.corrections!=NULL )
3303 	val += dt.corrections[pixelsize-dt.first_pixel_size];
3304     free(dt.corrections);
3305     free(freeme);
3306 return( val );
3307 }
3308 
ParsePSTKVR(PSTKernDlg * pstkd,GGadget * pstk,int startc,struct vr * vr)3309 static int ParsePSTKVR(PSTKernDlg *pstkd,GGadget *pstk,int startc,struct vr *vr) {
3310     int rows, cols = GMatrixEditGetColCnt(pstk);
3311     struct matrix_data *old = _GMatrixEditGet(pstk,&rows);
3312     GGadget *tf = _GMatrixEditGetActiveTextField(pstk);
3313     int r = GMatrixEditGetActiveRow(pstk);
3314     int c = GMatrixEditGetActiveCol(pstk);
3315     double scale = pstkd->pixelsize/(double) (pstkd->sf->ascent+pstkd->sf->descent);
3316 
3317     vr->xoff = FigureValue(old,r*cols,c,startc,tf,scale,pstkd->pixelsize);
3318     vr->yoff = FigureValue(old,r*cols,c,startc+2,tf,scale,pstkd->pixelsize);
3319     vr->h_adv_off = FigureValue(old,r*cols,c,startc+4,tf,scale,pstkd->pixelsize);
3320     vr->v_adv_off = FigureValue(old,r*cols,c,startc+6,tf,scale,pstkd->pixelsize);
3321 return( true );
3322 }
3323 
PSTKern_DrawGlyph(GWindow pixmap,int x,int y,BDFChar * bc,int mag)3324 static void PSTKern_DrawGlyph(GWindow pixmap,int x,int y, BDFChar *bc, int mag) {
3325     /* x,y is the location of the origin of the glyph, they need to */
3326     /*  be adjusted by the images xmin, ymax, etc. */
3327     struct _GImage base;
3328     GImage gi;
3329     GClut clut;
3330     int scale, l;
3331     Color fg, bg;
3332 
3333     scale = bc->depth == 8 ? 8 : 4;
3334 
3335     memset(&gi,'\0',sizeof(gi));
3336     memset(&base,'\0',sizeof(base));
3337     memset(&clut,'\0',sizeof(clut));
3338     gi.u.image = &base;
3339     base.clut = &clut;
3340     base.data = bc->bitmap;
3341     base.bytes_per_line = bc->bytes_per_line;
3342     base.width = bc->xmax-bc->xmin+1;
3343     base.height = bc->ymax-bc->ymin+1;
3344     base.image_type = it_index;
3345     clut.clut_len = 1<<scale;
3346     bg = GDrawGetDefaultBackground(NULL);
3347     fg = GDrawGetDefaultForeground(NULL);
3348     for ( l=0; l<(1<<scale); ++l )
3349 	clut.clut[l] =
3350 	    COLOR_CREATE(
3351 	     COLOR_RED(bg) + (l*(COLOR_RED(fg)-COLOR_RED(bg)))/((1<<scale)-1),
3352 	     COLOR_GREEN(bg) + (l*(COLOR_GREEN(fg)-COLOR_GREEN(bg)))/((1<<scale)-1),
3353 	     COLOR_BLUE(bg) + (l*(COLOR_BLUE(fg)-COLOR_BLUE(bg)))/((1<<scale)-1) );
3354 
3355     x += bc->xmin;
3356     y -= bc->ymax;
3357 
3358     if ( mag==1 )
3359 	GDrawDrawImage(pixmap,&gi,NULL,x,y);
3360     else
3361 	GDrawDrawImageMagnified(pixmap, &gi, NULL,
3362 		x*mag,y*mag,
3363 		base.width*mag,base.height*mag);
3364 }
3365 
PSTKern_Expose(GWindow pixmap,PSTKernDlg * pstkd)3366 static void PSTKern_Expose(GWindow pixmap, PSTKernDlg *pstkd) {
3367     GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3368     int r;
3369     int rows, cols = GMatrixEditGetColCnt(pstk);
3370     struct matrix_data *old = _GMatrixEditGet(pstk,&rows);
3371     SplineChar *sc1, *sc2;
3372     BDFChar *bc1, *bc2;
3373     struct vr vr1, vr2;
3374     int xorig, yorig;
3375     GRect size;
3376     int mag = pstkd->mag;
3377 
3378     if ( (r = GMatrixEditGetActiveRow(pstk))==-1 )
3379 return;		/* No kerning pair is active */
3380     if ( old[r*cols+0].u.md_str==NULL || old[r*cols+1].u.md_str==NULL )
3381 return;		/* No glyphs specified to kern */
3382     sc1 = SFGetChar(pstkd->sf,-1,old[r*cols+0].u.md_str);
3383     sc2 = SFGetChar(pstkd->sf,-1,old[r*cols+1].u.md_str);
3384     if ( sc1==NULL || sc2==NULL )
3385 return;		/* The names specified weren't in the font */
3386 
3387     if ( !ParsePSTKVR(pstkd,pstk,PAIR_DX1,&vr1) ||
3388 	    !ParsePSTKVR(pstkd,pstk,PAIR_DX2,&vr2) )
3389 return;		/* Couldn't parse the numeric kerning info */
3390 
3391     if ( pstkd->display==NULL )
3392 	pstkd->display = SplineFontPieceMeal(pstkd->sf,pstkd->def_layer,pstkd->pixelsize,72,pf_antialias,NULL);
3393     bc1 = BDFPieceMealCheck(pstkd->display,sc1->orig_pos);
3394     bc2 = BDFPieceMealCheck(pstkd->display,sc2->orig_pos);
3395 
3396     GDrawGetSize(GDrawableGetWindow(GWidgetGetControl(pstkd->gw,CID_KernDisplay)),&size);
3397     if ( pstkd->sub->vertical_kerning ) {
3398 	double scale = pstkd->pixelsize/(double) (pstkd->sf->ascent+pstkd->sf->descent);
3399 	int vwidth1 = rint( sc1->vwidth*scale ), vwidth2 = rint( sc2->vwidth*scale );
3400 	xorig = size.width/10;
3401 	yorig = size.height/20;
3402 	xorig /= mag; yorig /= mag;
3403 	PSTKern_DrawGlyph(pixmap,xorig+vr1.xoff,yorig+vwidth1-vr1.yoff, bc1, mag);
3404 	PSTKern_DrawGlyph(pixmap,xorig+vr2.xoff,yorig+vwidth1+vwidth2+vr1.v_adv_off-vr2.yoff, bc2, mag);
3405     } else if ( pstkd->sub->lookup->lookup_flags&pst_r2l ) {
3406 	xorig = 9*size.width/10;
3407 	yorig = pstkd->sf->ascent*size.height/(pstkd->sf->ascent+pstkd->sf->descent);
3408 	GDrawDrawLine(pixmap,xorig+vr1.h_adv_off-vr1.xoff,0,xorig+vr1.h_adv_off-vr1.xoff,size.height, 0x808080);
3409 	GDrawDrawLine(pixmap,0,yorig,size.width,yorig, 0x808080);
3410 	xorig /= mag; yorig /= mag;
3411 	PSTKern_DrawGlyph(pixmap,xorig-bc1->width,yorig-vr1.yoff, bc1, mag);
3412 	PSTKern_DrawGlyph(pixmap,xorig-bc1->width-bc2->width-vr2.h_adv_off-vr2.xoff-vr1.xoff,yorig-vr2.yoff, bc2, mag);
3413     } else {
3414 	xorig = size.width/10;
3415 	yorig = pstkd->sf->ascent*size.height/(pstkd->sf->ascent+pstkd->sf->descent);
3416 	GDrawDrawLine(pixmap,xorig,0,xorig,size.height, 0x808080);
3417 	GDrawDrawLine(pixmap,0,yorig,size.width,yorig, 0x808080);
3418 	xorig /= mag; yorig /= mag;
3419 	PSTKern_DrawGlyph(pixmap,xorig+vr1.xoff,yorig-vr1.yoff, bc1, mag);
3420 	PSTKern_DrawGlyph(pixmap,xorig+bc1->width+vr1.h_adv_off+vr2.xoff,yorig-vr2.yoff, bc2, mag);
3421     }
3422 }
3423 
PSTKern_Mouse(PSTKernDlg * pstkd,GEvent * event)3424 static void PSTKern_Mouse(PSTKernDlg *pstkd,GEvent *event) {
3425     GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3426     int r;
3427     int rows, cols = GMatrixEditGetColCnt(pstk);
3428     struct matrix_data *old = _GMatrixEditGet(pstk,&rows);
3429     int c = GMatrixEditGetActiveCol(pstk);
3430     GGadget *tf = _GMatrixEditGetActiveTextField(pstk);
3431     double scale = pstkd->pixelsize/(double) (pstkd->sf->ascent+pstkd->sf->descent);
3432     int diff, col, col2;
3433     int r2l = pstkd->sub->lookup->lookup_flags&pst_r2l;
3434     char buffer[20];
3435     GCursor ct = ct_mypointer;
3436     GRect size;
3437 
3438     if ( (r = GMatrixEditGetActiveRow(pstk))==-1 )
3439 return;		/* No kerning pair is active */
3440 
3441     if ( pstkd->sub->vertical_kerning ) {
3442 	diff = event->u.mouse.y - pstkd->orig_pos;
3443 	col = PAIR_DY_ADV1;
3444     } else if ( r2l ) {
3445 	diff = pstkd->orig_pos - event->u.mouse.x ;
3446 	col = PAIR_DX_ADV1;
3447 	col2 = PAIR_DX1;
3448     } else {
3449 	diff = event->u.mouse.x - pstkd->orig_pos;
3450 	col = PAIR_DX_ADV1;
3451     }
3452 
3453     if ( event->type == et_mousedown ) {
3454 	pstkd->down = true;
3455 	pstkd->orig_pos = pstkd->sub->vertical_kerning ? event->u.mouse.y : event->u.mouse.x;
3456 	pstkd->orig_value = FigureValue(old,r*cols,c,col,tf,1.0,-1);
3457     } else if ( pstkd->down ) {
3458 	diff = rint(diff/scale);
3459 	if ( col==c && tf!=NULL ) {
3460 	    sprintf( buffer, "%d", pstkd->orig_value + diff);
3461 	    GGadgetSetTitle8(tf,buffer);
3462 	    GGadgetRedraw(tf);
3463 	} else {
3464 	    old[r*cols+col].u.md_ival = pstkd->orig_value + diff;
3465 	    GGadgetRedraw(pstk);
3466 	}
3467 	if ( r2l ) {
3468 	    if ( col2==c && tf!=NULL ) {
3469 		sprintf( buffer, "%d", pstkd->orig_value + diff);
3470 		GGadgetSetTitle8(tf,buffer);
3471 		GGadgetRedraw(tf);
3472 	    } else {
3473 		old[r*cols+col2].u.md_ival = pstkd->orig_value + diff;
3474 		GGadgetRedraw(pstk);
3475 	    }
3476 	}
3477 	GGadgetRedraw(GWidgetGetControl(pstkd->gw,CID_KernDisplay));
3478 	if ( event->type == et_mouseup )
3479 	    pstkd->down = false;
3480     } else if ( event->type == et_mousemove ) {
3481 	SplineChar *sc1 = SFGetChar(pstkd->sf,-1,old[r*cols+0].u.md_str);
3482 	if (sc1!=NULL ) {
3483 	    GDrawGetSize(event->w,&size);
3484 	    if ( r2l ) {
3485 		if ( event->u.mouse.x < 9*size.width/10-sc1->width*scale )
3486 		    ct = ct_kerning;
3487 	    } else if ( col==PAIR_DX_ADV1 && event->u.mouse.x-size.width/10 > sc1->width*scale ) {
3488 		ct = ct_kerning;
3489 	    }
3490 	}
3491     }
3492     if ( ct!=pstkd->cursor_current ) {
3493 	GDrawSetCursor(event->w,ct);
3494 	pstkd->cursor_current = ct;
3495     }
3496 }
3497 
pstkern_e_h(GWindow gw,GEvent * event)3498 static int pstkern_e_h(GWindow gw, GEvent *event) {
3499     switch ( event->type ) {
3500       case et_char:
3501 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
3502 	    help("ui/dialogs/lookups.html", "#lookups-pair");
3503 return( true );
3504 	}
3505 return( false );
3506       case et_expose:
3507 	PSTKern_Expose(gw,GDrawGetUserData(gw));
3508 return( true );
3509       case et_mousedown: case et_mouseup: case et_mousemove:
3510 	PSTKern_Mouse(GDrawGetUserData(gw),event);
3511 return( true );
3512     }
3513 return( true );
3514 }
3515 
SCReasonable(SplineChar * sc)3516 static int SCReasonable(SplineChar *sc) {
3517     if ( sc==NULL )
3518 return( false );
3519     if ( strcmp(sc->name,".notdef")==0 ||
3520 	    strcmp(sc->name,".null")==0 ||
3521 	    strcmp(sc->name,"nonmarkingreturn")==0 )
3522 return( false );
3523 
3524 return( true );
3525 }
3526 
MDCopy(struct matrix_data * old,int rows,int cols)3527 static struct matrix_data *MDCopy(struct matrix_data *old,int rows,int cols) {
3528     struct matrix_data *md = malloc(rows*cols*sizeof(struct matrix_data));
3529     int r;
3530 
3531     memcpy(md,old,rows*cols*sizeof(struct matrix_data));
3532     for ( r=0; r<rows; ++r ) {
3533 	md[r*cols+0].u.md_str = copy(md[r*cols+0].u.md_str);
3534 	if ( cols==2 /* subs, lig, alt, etc. */ || cols>=10 /* kerning */ )
3535 	    md[r*cols+1].u.md_str = copy(md[r*cols+1].u.md_str);
3536     }
3537 return( md );
3538 }
3539 
SCNameUnused(char * name,struct matrix_data * old,int rows,int cols)3540 static int SCNameUnused(char *name,struct matrix_data *old,int rows,int cols) {
3541     int r;
3542 
3543     for ( r=0; r<rows; ++r ) {
3544 	if ( old[r*cols+0].u.md_str!=NULL && strcmp(old[r*cols+0].u.md_str,name)==0 ) {
3545 	    if (( cols==2 && (old[r*cols+1].u.md_str == NULL || *old[r*cols+1].u.md_str=='\0')) ||
3546 		    (cols==5 && old[r*cols+1].u.md_ival==0 &&
3547 				old[r*cols+2].u.md_ival==0 &&
3548 				old[r*cols+3].u.md_ival==0 &&
3549 				old[r*cols+4].u.md_ival==0 ))
3550 return( r );		/* There's an entry, but it's blank, fill it if we can */
3551 	    else
3552 return( -1 );
3553 	}
3554     }
3555 return( r );		/* Off end of list */
3556 }
3557 
SCIsLigature(SplineChar * sc)3558 static int SCIsLigature(SplineChar *sc) {
3559     int len;
3560     const unichar_t *alt=NULL;
3561 
3562     if ( strchr(sc->name,'_')!=NULL )
3563 return( true );
3564     len = strlen( sc->name );
3565     if ( strncmp(sc->name,"uni",3)==0 && (len-3)%4==0 && len>7 )
3566 return( true );
3567 
3568     if ( sc->unicodeenc==-1 || sc->unicodeenc>=0x10000 )
3569 return( false );
3570     else if ( isdecompositionnormative(sc->unicodeenc) &&
3571 		unicode_alternates[sc->unicodeenc>>8]!=NULL &&
3572 		(alt = unicode_alternates[sc->unicodeenc>>8][sc->unicodeenc&0xff])!=NULL ) {
3573 	if ( alt[1]=='\0' )
3574 return( false );		/* Single replacements aren't ligatures */
3575 	else if ( iscombining(alt[1]) && ( alt[2]=='\0' || iscombining(alt[2])))
3576 return( false );		/* Nor am I interested in accented letters */
3577 	else
3578 return( true );
3579     } else
3580 return( false );
3581 }
3582 
3583 enum pop_type { pt_all, pt_suffixed, pt_selected };
3584 
PSTKD_DoPopulate(PSTKernDlg * pstkd,char * suffix,enum pop_type pt)3585 static void PSTKD_DoPopulate(PSTKernDlg *pstkd,char *suffix, enum pop_type pt) {
3586     GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3587     int rows, row_max, old_rows, cols = GMatrixEditGetColCnt(pstk);
3588     struct matrix_data *old = GMatrixEditGet(pstk,&rows), *psts;
3589     int pos;
3590     int gid,k;
3591     SplineChar *sc, *alt;
3592     int enc;
3593     SplineFont *sf = pstkd->sf;
3594     FontView *fv = (FontView *) sf->fv;
3595     EncMap *map = fv->b.map;
3596     GGadget *gallsame = GWidgetGetControl(pstkd->gw,CID_AllSame);
3597     int allsame = false;
3598     FeatureScriptLangList *features = pstkd->sub->lookup->features;
3599 
3600     if ( gallsame!=NULL )
3601 	allsame = GGadgetIsChecked(gallsame);
3602 
3603     psts = MDCopy(old,rows,cols);
3604     old_rows = row_max = rows;
3605     k = 0;
3606     do {
3607 	sf = pstkd->sf->subfontcnt==0 ? pstkd->sf : pstkd->sf->subfonts[k];
3608 	for ( gid=0; gid<sf->glyphcnt; ++gid ) {
3609 	    if ( SCReasonable(sc = sf->glyphs[gid]) &&
3610 		    (pt==pt_selected || ScriptInFeatureScriptList(SCScriptFromUnicode(sc),
3611 			    features)) &&
3612 		    (pt!=pt_selected || (gid<fv->b.sf->glyphcnt &&
3613 			    (enc = map->backmap[gid])!=-1 &&
3614 			    fv->b.selected[enc])) &&
3615 		    (pos = SCNameUnused(sc->name,old,old_rows,cols))!=-1 &&
3616 		    (pstkd->sub->lookup->lookup_type!=gsub_ligature ||
3617 			    SCIsLigature(sc)) ) {
3618 		alt = NULL;
3619 		if ( suffix!=NULL ) {
3620 		    alt = SuffixCheck(sc,suffix);
3621 		    if ( pt==pt_suffixed && alt==NULL )
3622 	    continue;
3623 		}
3624 		if ( pos==old_rows ) {
3625 		    pos = rows;
3626 		    if ( rows>=row_max ) {
3627 			if ( row_max< sf->glyphcnt-10 )
3628 			    row_max = sf->glyphcnt;
3629 			else
3630 			    row_max += 15;
3631 			psts = realloc(psts,row_max*cols*sizeof(struct matrix_data));
3632 		    }
3633 		    memset(psts+rows*cols,0,cols*sizeof(struct matrix_data));
3634 		    psts[rows*cols+0].u.md_str = copy(sc->name);
3635 		    ++rows;
3636 		}
3637 		if ( alt!=NULL )
3638 		    psts[pos*cols+1].u.md_str = copy(alt->name);
3639 		else if ( allsame && pos!=0 ) {
3640 		    psts[pos*cols+SIM_DX].u.md_ival = psts[0+SIM_DX].u.md_ival;
3641 		    psts[pos*cols+SIM_DY].u.md_ival = psts[0+SIM_DY].u.md_ival;
3642 		    psts[pos*cols+SIM_DX_ADV].u.md_ival = psts[0+SIM_DX_ADV].u.md_ival;
3643 		    psts[pos*cols+SIM_DY_ADV].u.md_ival = psts[0+SIM_DY_ADV].u.md_ival;
3644 		} else if ( pstkd->sub->lookup->lookup_type!=gpos_pair )
3645 		    SCSubtableDefaultSubsCheck(sc,pstkd->sub,psts,cols,pos,pstkd->def_layer);
3646 	    }
3647 	}
3648 	++k;
3649     } while ( k<pstkd->sf->subfontcnt );
3650     if ( rows<row_max )
3651 	psts = realloc(psts,rows*cols*sizeof(struct matrix_data));
3652     PSTKD_DoSort(pstkd,psts,rows,cols);
3653     GMatrixEditSet(pstk,psts,rows,false);
3654     GGadgetRedraw(pstk);
3655 }
3656 
PSTKD_SetSuffix(PSTKernDlg * pstkd)3657 static void PSTKD_SetSuffix(PSTKernDlg *pstkd) {
3658     char *suffix;
3659 
3660     if ( pstkd->sub->lookup->lookup_type!=gsub_single )
3661 return;		/* Not applicable */
3662 
3663     suffix = GGadgetGetTitle8(GWidgetGetControl(pstkd->gw,CID_Suffix));
3664     if ( *suffix!='\0' && ( suffix[0]!='.' || suffix[1]!='\0' )) {
3665 	free(pstkd->sub->suffix);
3666 	pstkd->sub->suffix = ( *suffix=='.' ) ? copy(suffix+1): copy(suffix);
3667 	free(suffix);
3668     }
3669 }
3670 
PSTKD_PopulateWithSuffix(GGadget * g,GEvent * e)3671 static int PSTKD_PopulateWithSuffix(GGadget *g, GEvent *e) {
3672     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3673 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3674 	char *suffix = GGadgetGetTitle8(GWidgetGetControl(pstkd->gw,CID_Suffix));
3675 	if ( *suffix!='\0' && ( suffix[0]!='.' || suffix[1]!='\0' )) {
3676 	    PSTKD_DoPopulate(pstkd,suffix, pt_suffixed);
3677 	    PSTKD_SetSuffix(pstkd);
3678 	}
3679 	free(suffix);
3680     }
3681 return( true );
3682 }
3683 
PSTKD_Populate(GGadget * g,GEvent * e)3684 static int PSTKD_Populate(GGadget *g, GEvent *e) {
3685     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3686 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3687 	GGadget *gsuffix = GWidgetGetControl(pstkd->gw,CID_Suffix);
3688 	char *suffix = NULL;
3689 	if ( gsuffix != NULL ) {
3690 	    suffix = GGadgetGetTitle8(gsuffix);
3691 	    if ( *suffix=='\0' || ( suffix[0]=='.' && suffix[1]=='\0' )) {
3692 		free(suffix);
3693 		suffix = NULL;
3694 	    }
3695 	}
3696 	PSTKD_SetSuffix(pstkd);
3697 	PSTKD_DoPopulate(pstkd,suffix,pt_all);
3698 	free(suffix);
3699     }
3700 return( true );
3701 }
3702 
PSTKD_PopulateSelected(GGadget * g,GEvent * e)3703 static int PSTKD_PopulateSelected(GGadget *g, GEvent *e) {
3704     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3705 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3706 	GGadget *gsuffix = GWidgetGetControl(pstkd->gw,CID_Suffix);
3707 	char *suffix = NULL;
3708 	if ( gsuffix != NULL ) {
3709 	    suffix = GGadgetGetTitle8(gsuffix);
3710 	    if ( *suffix=='\0' || ( suffix[0]=='.' && suffix[1]=='\0' )) {
3711 		free(suffix);
3712 		suffix = NULL;
3713 	    }
3714 	}
3715 	PSTKD_SetSuffix(pstkd);
3716 	PSTKD_DoPopulate(pstkd,suffix,pt_selected);
3717 	free(suffix);
3718     }
3719 return( true );
3720 }
3721 
PSTKD_DoAutoKern(PSTKernDlg * pstkd,SplineChar ** glyphlist)3722 static int PSTKD_DoAutoKern(PSTKernDlg *pstkd,SplineChar **glyphlist) {
3723     int err, touch, separation, minkern, onlyCloser;
3724 
3725     if ( !GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Autokern)) )
3726 return( false );
3727 
3728     err = false;
3729     touch = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Touched));
3730     separation = GetInt8(pstkd->gw,CID_Separation,_("Separation"),&err);
3731     minkern = GetInt8(pstkd->gw,CID_MinKern,_("Min Kern"),&err);
3732     onlyCloser = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_OnlyCloser));
3733     if ( err )
3734 return( false );
3735     AutoKern2(pstkd->sf,pstkd->def_layer,glyphlist,glyphlist,pstkd->sub,
3736 	    separation,minkern,touch,onlyCloser,0,
3737 	    PSTKD_AddKP,pstkd);
3738 return( true );
3739 }
3740 
PSTKD_AutoKern(GGadget * g,GEvent * e)3741 static int PSTKD_AutoKern(GGadget *g, GEvent *e) {
3742     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3743 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3744 	GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3745 	int rows, cols = GMatrixEditGetColCnt(pstk);
3746 	struct matrix_data *old = GMatrixEditGet(pstk,&rows);
3747 	SplineFont *sf = pstkd->sf;
3748 	int i,gid,cnt;
3749 	SplineChar **list, *sc;
3750 	FeatureScriptLangList *features = pstkd->sub->lookup->features, *testf;
3751 	struct scriptlanglist *scripts;
3752 	uint32 *scripttags;
3753 
3754 	pstkd->cols = GMatrixEditGetColCnt(pstk);
3755 	pstkd->psts = MDCopy(old,rows,cols);
3756 	pstkd->rows_at_start = pstkd->rows = pstkd->next_row = rows;
3757 
3758 	for ( testf=features,cnt=0; testf!=NULL; testf=testf->next ) {
3759 	    for ( scripts=testf->scripts; scripts!=NULL; scripts=scripts->next )
3760 		++cnt;
3761 	}
3762 	if ( cnt==0 ) {
3763 	    ff_post_error(_("No scripts"),_("There are no scripts bound to features bound to this lookup. So nothing happens." ));
3764 return(true);
3765 	}
3766 	scripttags = malloc((cnt+1)*sizeof(uint32));
3767 	for ( testf=features,cnt=0; testf!=NULL; testf=testf->next ) {
3768 	    for ( scripts=testf->scripts; scripts!=NULL; scripts=scripts->next ) {
3769 		for ( i=0; i<cnt; ++i )
3770 		    if ( scripttags[i]==scripts->script )
3771 		break;
3772 		if ( i==cnt )
3773 		    scripttags[cnt++] = scripts->script;
3774 	    }
3775 	}
3776 	scripttags[cnt] = 0;
3777 
3778 	list = malloc((sf->glyphcnt+1)*sizeof(SplineChar *));
3779 	for ( i=0; scripttags[i]!=0; ++i ) {
3780 	    uint32 script = scripttags[i];
3781 
3782 	    for ( cnt=gid=0; gid<sf->glyphcnt; ++gid ) {
3783 		if ( (sc = sf->glyphs[gid])!=NULL &&
3784 			SCWorthOutputting(sc) &&
3785 			SCScriptFromUnicode(sc)==script )
3786 		    list[cnt++] = sc;
3787 	    }
3788 	    list[cnt] = NULL;
3789 	    PSTKD_DoAutoKern(pstkd,list);
3790 	}
3791 	free(list);
3792 	free(scripttags);
3793 	if ( pstkd->next_row<pstkd->rows )
3794 	    pstkd->psts = realloc(pstkd->psts,pstkd->rows*cols*sizeof(struct matrix_data));
3795 	PSTKD_DoSort(pstkd,pstkd->psts,pstkd->next_row,cols);
3796 	GMatrixEditSet(pstk,pstkd->psts,pstkd->next_row,false);
3797 	GGadgetRedraw(pstk);
3798     }
3799 return( true );
3800 }
3801 
PSTKD_AutoKernSelected(GGadget * g,GEvent * e)3802 static int PSTKD_AutoKernSelected(GGadget *g, GEvent *e) {
3803     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3804 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3805 	GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3806 	int rows, cols = GMatrixEditGetColCnt(pstk);
3807 	struct matrix_data *old = GMatrixEditGet(pstk,&rows);
3808 	SplineFont *sf = pstkd->sf;
3809 	FontViewBase *fv = sf->fv;
3810 	int enc,gid,cnt;
3811 	SplineChar **list, *sc;
3812 
3813 	pstkd->cols = GMatrixEditGetColCnt(pstk);
3814 	pstkd->psts = MDCopy(old,rows,cols);
3815 	pstkd->rows_at_start = pstkd->rows = pstkd->next_row = rows;
3816 
3817 	for ( enc=0,cnt=0; enc<fv->map->enccount; ++enc ) {
3818 	    if ( fv->selected[enc] && (gid=fv->map->map[enc])!=-1 &&
3819 		    SCWorthOutputting(sc = sf->glyphs[gid]))
3820 		++cnt;
3821 	}
3822 	list = malloc((cnt+1)*sizeof(SplineChar *));
3823 	for ( enc=0,cnt=0; enc<fv->map->enccount; ++enc ) {
3824 	    if ( fv->selected[enc] && (gid=fv->map->map[enc])!=-1 &&
3825 		    SCWorthOutputting(sc = sf->glyphs[gid]))
3826 		list[cnt++] = sc;
3827 	}
3828 	list[cnt] = NULL;
3829 	PSTKD_DoAutoKern(pstkd,list);
3830 	free(list);
3831 	if ( pstkd->next_row<pstkd->rows )
3832 	    pstkd->psts = realloc(pstkd->psts,pstkd->rows*cols*sizeof(struct matrix_data));
3833 	PSTKD_DoSort(pstkd,pstkd->psts,pstkd->next_row,cols);
3834 	GMatrixEditSet(pstk,pstkd->psts,pstkd->next_row,false);
3835 	GGadgetRedraw(pstk);
3836     }
3837 return( true );
3838 }
3839 
PSTKD_RemoveAll(GGadget * g,GEvent * e)3840 static int PSTKD_RemoveAll(GGadget *g, GEvent *e) {
3841     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3842 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3843 	GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3844 	int cols = GMatrixEditGetColCnt(pstk);
3845 	struct matrix_data *psts=NULL;
3846 
3847 	psts = calloc(cols,sizeof(struct matrix_data));
3848 	GMatrixEditSet(pstk,psts,0,false);
3849     }
3850 return( true );
3851 }
3852 
PSTKD_RemoveEmpty(GGadget * g,GEvent * e)3853 static int PSTKD_RemoveEmpty(GGadget *g, GEvent *e) {
3854     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3855 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3856 	GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3857 	int rows, cols = GMatrixEditGetColCnt(pstk);
3858 	struct matrix_data *old = GMatrixEditGet(pstk,&rows), *psts=NULL;
3859 	int r, empty, rm_cnt, j;
3860 
3861 	for ( r=rows-1, rm_cnt=0; r>=0; --r ) {
3862 	    if ( pstkd->sub->lookup->lookup_type==gpos_single )
3863 		empty = old[r*cols+SIM_DX].u.md_ival==0 &&
3864 			old[r*cols+SIM_DY].u.md_ival==0 &&
3865 			old[r*cols+SIM_DX_ADV].u.md_ival==0 &&
3866 			old[r*cols+SIM_DY_ADV].u.md_ival==0;
3867 	    else
3868 		empty = old[r*cols+1].u.md_str == NULL || *old[r*cols+1].u.md_str=='\0';
3869 	    if ( empty ) {
3870 		if ( psts==NULL )
3871 		    psts = MDCopy(old,rows,cols);
3872 		free(psts[r*cols+0].u.md_str);
3873 		if ( cols!=5 )
3874 		    free(psts[r*cols+1].u.md_str);
3875 		for ( j=r+1; j<rows-rm_cnt; ++j )
3876 		    memcpy(psts+(j-1)*cols,psts+j*cols,
3877 			    cols*sizeof(struct matrix_data));
3878 		++rm_cnt;
3879 	    }
3880 	}
3881 	if ( rm_cnt!=0 ) {
3882 	    /* Some reallocs explode if given a size of 0 */
3883 	    psts = realloc(psts,(rows-rm_cnt+1)*cols*sizeof(struct matrix_data));
3884 	    GMatrixEditSet(pstk,psts,rows-rm_cnt,false);
3885 	}
3886     }
3887 return( true );
3888 }
3889 
SFUntickAllPSTandKern(SplineFont * sf)3890 void SFUntickAllPSTandKern(SplineFont *sf)
3891 {
3892     SplineChar *sc;
3893     PST *pst;
3894     KernPair *kp;
3895     int gid, isv;
3896     for ( gid=0; gid<sf->glyphcnt; ++gid ) {
3897 	if ( (sc = sf->glyphs[gid])!=NULL ) {
3898 	    for ( pst = sc->possub; pst!=NULL; pst=pst->next )
3899 		pst->ticked = false;
3900 	    for ( isv=0; isv<2; ++isv )
3901 		for ( kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kp->next )
3902 		    kp->kcid = 0;
3903 	}
3904     }
3905 }
3906 
3907 
3908 
3909 /**
3910  * Return true of the splinechar has any horizontal or vert kerns.
3911  */
SCHasAnyKerns(SplineChar * sc)3912 static int SCHasAnyKerns(SplineChar *sc) {
3913     KernPair *kp;
3914     int v;
3915     int ret = 0;
3916     for ( v=0; v<2; ++v ) {
3917 	kp = v ? sc->vkerns : sc->kerns;
3918 	if ( kp!=NULL ) {
3919 	    ret = 1;
3920 	}
3921     }
3922     return ret;
3923 }
3924 
3925 
kernsLength(KernPair * kp)3926 int kernsLength( KernPair *kp ) {
3927     if( !kp )
3928 	return 0;
3929     int i = 0;
3930     for ( ; kp; kp=kp->next ) {
3931 	++i;
3932     }
3933     return i;
3934 }
3935 
kerncomp(const void * _r1,const void * _r2)3936 static int kerncomp(const void *_r1, const void *_r2) {
3937     const KernPair *kp1 = *(KernPair * const *)_r1;
3938     const KernPair *kp2 = *(KernPair * const *)_r2;
3939     if( kp1->sc->orig_pos == kp2->sc->orig_pos )
3940 	return 0;
3941     if( kp1->sc->orig_pos <  kp2->sc->orig_pos )
3942 	return -1;
3943     return 1;
3944 }
3945 
3946 /**
3947  * This will sort both the kerns and vkerns for the splinefont so that
3948  * the list is in the order of "orig_pos" for each kernpair.
3949  *
3950  * Sometimes FontForge had two KernPair lists which were identical but
3951  * where not sorted the same way, using this function will force a
3952  * specific ordering on the KernPair list to make performing a lexical
3953  * "diff" between two serialized KernPair lists work as expected.
3954  */
SCKernsSort(SplineChar * sc)3955 static void SCKernsSort( SplineChar *sc ) {
3956     KernPair *kp;
3957     int v;
3958     KernPair *cur;
3959     KernPair *newlist = 0;
3960     for ( v=0; v<2; ++v ) {
3961 	kp = v ? sc->vkerns : sc->kerns;
3962 	if ( kp!=NULL ) {
3963 	    int idx = 0;
3964 	    int length = kernsLength(kp);
3965 	    if( length == 0 ) {
3966 		// nothing is always sorted already!
3967 		continue;
3968 	    }
3969 
3970 	    KernPair** ordered = malloc( sizeof(KernPair*) * length+1 );
3971 	    // copy to temp array
3972 	    idx = 0;
3973 	    for ( cur = kp; cur; cur=cur->next ) {
3974 		ordered[idx] = cur;
3975 		idx++;
3976 	    }
3977 	    // sort array using easy swap()
3978 	    qsort(ordered,length,sizeof(KernPair*),kerncomp);
3979 
3980 	    // and back to a linked list again
3981 	    newlist = ordered[0];
3982 	    ordered[length-1]->next = 0;
3983 	    for( idx = 0; idx < length-1; idx++ ) {
3984 		ordered[idx]->next = ordered[idx+1];
3985 	    }
3986 	    free( ordered );
3987 
3988 	    // now change the right kerns member of sc
3989 	    if( v )  sc->vkerns = newlist;
3990 	    else     sc->kerns  = newlist;
3991 	}
3992     }
3993 }
3994 
3995 extern char* SFDCreateUndoForLookup( SplineFont *sf, int lookup_type );
3996 
3997 /**
3998  * Create a fragment of SFD file which represents the state of the
3999  * lookup_type for the given splinefont.
4000  *
4001  * Note that the kernpair lists may be modified by this function in
4002  * that those lists might be sorted on return.
4003  *
4004  * The caller needs to free() the returned string value.
4005  */
SFDCreateUndoForLookup(SplineFont * sf,int lookup_type)4006 char* SFDCreateUndoForLookup( SplineFont *sf, int lookup_type )
4007 {
4008     int gid = 0;
4009     SplineChar *sc = 0;
4010     PST *pst = 0;
4011     FILE* sfd = MakeTemporaryFile();
4012     SFD_DumpLookup( sfd, sf );
4013     for ( gid=0; gid<sf->glyphcnt; ++gid )
4014     {
4015 	if ( (sc = sf->glyphs[gid])!=NULL )
4016 	{
4017 	    int haveStartMarker = 0;
4018 	    if(lookup_type==gpos_pair)
4019 	    {
4020 		haveStartMarker = 1;
4021 		SFDDumpCharStartingMarker( sfd, sc );
4022 
4023 		if( SCHasAnyKerns(sc) )
4024 		{
4025 		    SCKernsSort( sc );
4026 		    SFD_DumpKerns( sfd, sc, 0 );
4027 		}
4028 	    }
4029 	    else
4030 	    {
4031 		for ( pst = sc->possub; pst; pst=pst->next )
4032 		{
4033 		    if( !haveStartMarker )
4034 		    {
4035 			haveStartMarker = 1;
4036 			SFDDumpCharStartingMarker( sfd, sc );
4037 		    }
4038 		    SFD_DumpPST( sfd, sc );
4039 		}
4040 	    }
4041 	    if( haveStartMarker )
4042 	    {
4043 		fprintf(sfd,"EndChar\n" );
4044 	    }
4045 	}
4046     }
4047 
4048     char* str = FileToAllocatedString( sfd );
4049     fclose(sfd);
4050     return(str);
4051 }
4052 
SCFindByGlyphName(SplineFont * sf,char * n)4053 static SplineChar* SCFindByGlyphName( SplineFont *sf, char* n ) {
4054     int i=0;
4055     for ( i=0; i<sf->glyphcnt; ++i ) {
4056 	if ( sf->glyphs[i]!=NULL ) {
4057 	    SplineChar *sc = sf->glyphs[i];
4058 	    if( !strcmp( sc->name, n )) {
4059 		return sc;
4060 	    }
4061 	}
4062     }
4063     return 0;
4064 }
4065 
4066 
SFDTrimUndoOldToNew_Output(FILE * retf,char * glyph,char * line)4067 static void SFDTrimUndoOldToNew_Output( FILE* retf, char* glyph, char* line ) {
4068     fwrite( "StartChar:", strlen("StartChar:"), 1, retf );
4069     fwrite( glyph,       strlen(glyph),         1, retf );
4070     fwrite( "\n",         1,                    1, retf );
4071     fwrite( line,        strlen(line),          1, retf );
4072     fwrite( "\n",         1,                    1, retf );
4073     fwrite( "EndChar\n", strlen("EndChar\n"),   1, retf );
4074 }
4075 
4076 /**
4077  * Given two SFD fragments, oldstr and newstr, compare them and remove
4078  * the SFD for glyphs which have not changed state significantly
4079  * between old and new.
4080  *
4081  * This can be very handy for fonts like DroidSans which have kerning
4082  * table which is about 2mb in SFD format. If you only change the
4083  * kerning for a 10 glyphs then the resulting SFD from this function
4084  * might be only in the 10-50kb size instead of megabytes.
4085  *
4086  * The caller needs to free the return value.
4087  */
SFDTrimUndoOldToNew(SplineFont * sf,char * oldstr,char * newstr)4088 static char* SFDTrimUndoOldToNew( SplineFont *sf, char* oldstr, char* newstr ) {
4089 
4090     if( !oldstr || !newstr ) {
4091 	    return 0;
4092     }
4093 
4094     /* open temporary files */
4095     FILE *of, *nf, *retf;
4096     if ( !(retf=MakeTemporaryFile()) )
4097 	    goto error0SFDTrimUndoOldToNew;
4098     if ( !(of=MakeTemporaryFile()) )
4099 	    goto error1SFDTrimUndoOldToNew;
4100     if ( !(nf=MakeTemporaryFile()) )
4101 	    goto error2SFDTrimUndoOldToNew;
4102 
4103     int glyphsWithUndoInfoCount = 0;
4104     fwrite( oldstr, strlen(oldstr), 1, of );
4105     fwrite( newstr, strlen(newstr), 1, nf );
4106     fseek( of, 0, SEEK_SET );
4107     fseek( nf, 0, SEEK_SET );
4108 
4109     char* oglyph = 0;
4110     char* nglyph = 0;
4111     char* oline = 0;
4112     char* nline = 0;
4113 
4114     // copy the header from the old SFD fragment
4115     while((oline = getquotedeol(of)) && !feof(of)) {
4116 	    if( !strnmatch( oline, "StartChar:", strlen( "StartChar:" ))) {
4117 	        int len = strlen("StartChar:");
4118 	        while( oline[len] && oline[len] == ' ' )
4119 	    	len++;
4120 	        oglyph = copy( oline+len );
4121 	        break;
4122 	    }
4123 	    fwrite( oline, strlen(oline), 1, retf );
4124 	    fwrite( "\n", 1, 1, retf );
4125 	    free(oline);
4126     }
4127 
4128     while (oglyph) {
4129 	    oline = getquotedeol(of);
4130 	    SplineChar* oldsc = SCFindByGlyphName( sf, oglyph );
4131 	    int newGlyphsSeen = 0;
4132 
4133 	    while ((nglyph = SFDMoveToNextStartChar(nf))) {
4134 	        newGlyphsSeen++;
4135 	        SplineChar* newsc = SCFindByGlyphName( sf, nglyph );
4136 	        if( !newsc || !oldsc ) {
4137 	    	    goto error3SFDTrimUndoOldToNew;
4138 	        }
4139 	        /**
4140 	         * If the user has deleted a whole glyph from the SFD
4141 	         * fragment then the nglyph in the new list will be
4142 	         * *after* oglyph in the ordering. If that's the case then
4143 	         * we have to output oglyph and read another glyph from
4144 	         * the old list.
4145 	         */
4146 	        while( newsc->orig_pos > oldsc->orig_pos ) {
4147 	    	    glyphsWithUndoInfoCount++;
4148 	    	    SFDTrimUndoOldToNew_Output( retf, oglyph, oline );
4149 	    	    free(oline);
4150 	    	    oglyph = SFDMoveToNextStartChar(of);
4151 	    	    oline = getquotedeol(of);
4152 	    	    oldsc = SCFindByGlyphName( sf, oglyph );
4153 	        }
4154 
4155 	        nline = getquotedeol(nf);
4156 	        if( !oline || !nline ) {
4157 	    	    fprintf(stderr,"failed to read new or old files during SFD diff. Returning entire new data as diff!\n");
4158 	    	    goto error3SFDTrimUndoOldToNew;
4159 	        }
4160 	        if( strcmp( oglyph, nglyph )) {
4161 //	    	    fprintf(stderr,"mismatch between old and new SFD fragments. Skipping new glyph that is not in old...\n");
4162 	    	    glyphsWithUndoInfoCount++;
4163 	    	    SFDTrimUndoOldToNew_Output( retf, nglyph, "Kerns2: " );
4164 	    	    free(nline);
4165 	    	    continue;
4166 	        }
4167 
4168 	        if( !strcmp( oline, nline )) {
4169 	    	    // printf("old and new have the same data, skipping glyph:%s\n", oglyph );
4170 	    	    break;
4171 	        }
4172 
4173 	        glyphsWithUndoInfoCount++;
4174 	        SFDTrimUndoOldToNew_Output( retf, oglyph, oline );
4175 	        free(nline);
4176 	        break;
4177 	    }
4178 
4179 	    /**
4180 	     * There is one or more old glyphs which do not have a new glyph
4181 	     * in the SFD fragments, preserve those old SFD fragments.
4182 	     */
4183 	    if( !newGlyphsSeen ) {
4184 	        glyphsWithUndoInfoCount++;
4185 	        SFDTrimUndoOldToNew_Output( retf, oglyph, oline );
4186 	    }
4187 
4188 	    free(oline);
4189 	    oglyph = SFDMoveToNextStartChar(of);
4190     }
4191     fclose(of);
4192 
4193     /* Trailing new glyph data that has nothing in the old file.
4194      *
4195      * we don't care what the data is if its new,
4196      * we only want to be able to revert to the old state (nothing)
4197      */
4198     while ((nglyph = SFDMoveToNextStartChar(nf))) {
4199 	    SCFindByGlyphName( sf, nglyph );
4200 	    nline = getquotedeol(nf);
4201 	    glyphsWithUndoInfoCount++;
4202 	    SFDTrimUndoOldToNew_Output( retf, nglyph, "Kerns2: " );
4203     }
4204     fclose(nf);
4205 
4206     if( !glyphsWithUndoInfoCount )
4207 	    goto error1SFDTrimUndoOldToNew;
4208 
4209     char* ret = FileToAllocatedString( retf );
4210     fclose(retf);
4211     return ret;
4212 
4213 error3SFDTrimUndoOldToNew: fclose(of);
4214 error2SFDTrimUndoOldToNew: fclose(nf);
4215 error1SFDTrimUndoOldToNew: fclose(retf);
4216 error0SFDTrimUndoOldToNew:
4217     return 0;
4218 }
4219 
4220 
4221 
PSTKD_Ok(GGadget * g,GEvent * e)4222 static int PSTKD_Ok(GGadget *g, GEvent *e) {
4223 
4224     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
4225 	PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
4226 	GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
4227 	int rows, cols = GMatrixEditGetColCnt(pstk);
4228 	struct matrix_data *psts = GMatrixEditGet(pstk,&rows);
4229 	int r, r1, k, gid, isv, ch;
4230 	char *pt, *start;
4231 	int lookup_type = pstkd->sub->lookup->lookup_type;
4232 	SplineFont *sf = NULL;
4233 	SplineChar *sc, *found;
4234 	char *buts[3];
4235 	KernPair *kp, *kpprev, *kpnext;
4236 	PST *pst, *pstprev, *pstnext;
4237 	int err, touch=0, separation=0, minkern=0, onlyCloser=0, autokern=0;
4238 	int _t = lookup_type == gpos_single ? pst_position
4239 		: lookup_type == gpos_pair ? pst_pair
4240 		: lookup_type == gsub_single ? pst_substitution
4241 		: lookup_type == gsub_alternate ? pst_alternate
4242 		: lookup_type == gsub_multiple ? pst_multiple
4243 		:                            pst_ligature;
4244 
4245 	/* First check for errors */
4246 	if ( lookup_type==gpos_pair ) {
4247 	    /* bad metadata */
4248 	    err = false;
4249 	    touch = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Touched));
4250 	    separation = GetInt8(pstkd->gw,CID_Separation,_("Separation"),&err);
4251 	    minkern = GetInt8(pstkd->gw,CID_MinKern,_("Min Kern"),&err);
4252 	    onlyCloser = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_OnlyCloser));
4253 	    autokern = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Autokern));
4254 	    if ( err )
4255 return( true );
4256 	}
4257 
4258 	    /* Glyph names that aren't in the font */
4259 	for ( r=0; r<rows; ++r ) {
4260 	    if ( SFGetChar(pstkd->sf,-1,psts[r*cols+0].u.md_str)==NULL ) {
4261 		ff_post_error( _("Missing glyph"),_("There is no glyph named %s in the font"),
4262 			psts[cols*r+0].u.md_str );
4263 return( true );
4264 	    }
4265 	}
4266 	    /* Empty entries */
4267 	if ( cols==2 || cols==10 ) {
4268 	    for ( r=0; r<rows; ++r ) {
4269 		start = psts[cols*r+1].u.md_str;
4270 		if ( start==NULL ) start="";
4271 		while ( *start== ' ' ) ++start;
4272 		if ( *start=='\0' ) {
4273 		    ff_post_error( _("Missing glyph name"),_("You must specify a replacement glyph for %s"),
4274 			    psts[cols*r+0].u.md_str );
4275 return( true );
4276 		}
4277 		/* Replacements which aren't in the font */
4278 		while ( *start ) {
4279 		    for ( pt=start; *pt!='\0' && *pt!=' ' && *pt!='('; ++pt );
4280 		    ch = *pt; *pt='\0';
4281 		    found = SFGetChar(pstkd->sf,-1,start);
4282 		    if ( found==NULL ) {
4283 			buts[0] = _("_Yes");
4284 			buts[1] = _("_Cancel");
4285 			buts[2] = NULL;
4286 			if ( gwwv_ask(_("Missing glyph"),(const char **) buts,0,1,_("For glyph %.60s you refer to a glyph named %.80s, which is not in the font yet. Was this intentional?"),
4287 				psts[cols*r+0].u.md_str, start)==1 ) {
4288 			    *pt = ch;
4289 return( true );
4290 			}
4291 		    }
4292 		    *pt = ch;
4293 		    if ( ch=='(' ) {
4294 			while ( *pt!=')' && *pt!='\0' ) ++pt;
4295 			if ( *pt==')' ) ++pt;
4296 		    }
4297 		    while ( *pt== ' ' ) ++pt;
4298 		    start = pt;
4299 		}
4300 	    }
4301 	}
4302 	    /* Duplicate entries */
4303 	for ( r=0; r<rows; ++r ) {
4304 	    for ( r1=r+1; r1<rows; ++r1 ) {
4305 		if ( strcmp(psts[r*cols+0].u.md_str,psts[r1*cols+0].u.md_str)==0 ) {
4306 		    if ( lookup_type==gpos_pair || lookup_type==gsub_ligature ) {
4307 			if ( strcmp(psts[r*cols+1].u.md_str,psts[r1*cols+1].u.md_str)==0 ) {
4308 			    ff_post_error( _("Duplicate data"),_("There are two entries for the same glyph set (%.80s and %.80s)"),
4309 				    psts[cols*r+0].u.md_str, psts[cols*r+1].u.md_str );
4310 return( true );
4311 			}
4312 		    } else {
4313 			ff_post_error( _("Duplicate data"),_("There are two entries for the same glyph (%.80s)"),
4314 				psts[cols*r+0].u.md_str );
4315 return( true );
4316 		    }
4317 		}
4318 	    }
4319 	}
4320 
4321 	/* Check for badly specified device tables */
4322 	if ( _t==pst_position || _t==pst_pair ) {
4323 	    int startc = _t==pst_position ? SIM_DX+1 : PAIR_DX1+1;
4324 	    int low, high, c;
4325 	    for ( r=0; r<rows; ++r ) {
4326 		for ( c=startc; c<cols; c+=2 ) {
4327 		    if ( !DeviceTableOK(psts[r*cols+c].u.md_str,&low,&high) ) {
4328 			ff_post_error( _("Bad Device Table Adjustment"),_("A device table adjustment specified for %.80s is invalid"),
4329 				psts[cols*r+0].u.md_str );
4330 return( true );
4331 		    }
4332 		}
4333 	    }
4334 	}
4335 
4336 	/* Ok, if we get here then there should be no errors and we can parse */
4337 
4338 	/* First grab a snapshot of the state of the system as it is
4339 	 * now so that we can "undo" the lookup table edits as a
4340 	 * single operation if desired */
4341 	char* oldsfd = 0;
4342 	if( !pstkd->sf->subfontcnt ) {
4343 	    sf = pstkd->sf;
4344 	    oldsfd = SFDCreateUndoForLookup( sf, lookup_type );
4345 
4346 	    if( DEBUG && oldsfd )
4347 		GFileWriteAll( "/tmp/old-lookup-table.sfd", oldsfd );
4348 	}
4349 
4350 	/* Then mark all the current things as unused */
4351 	k=0;
4352 	do {
4353 	    sf = pstkd->sf->subfontcnt==0 ? pstkd->sf : pstkd->sf->subfonts[k];
4354 	    SFUntickAllPSTandKern( sf );
4355 	    ++k;
4356 	} while ( k<pstkd->sf->subfontcnt );
4357 
4358 	/* Write the changes to each SplineChar. For example, updating
4359 	 * the ligaments will update the splinechar's
4360 	 * pst->u.subs.variant and pst->u.lig.lig
4361 	 */
4362 	if ( lookup_type!=gpos_pair ) {
4363 	    for ( r=0; r<rows; ++r ) {
4364 
4365 		sc = SFGetChar(pstkd->sf,-1,psts[cols*r+0].u.md_str);
4366 		for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
4367 		    if ( pst->subtable == pstkd->sub && !pst->ticked )
4368 		break;
4369 		}
4370 		if ( pst==NULL ) {
4371 		    pst = chunkalloc(sizeof(PST));
4372 		    pst->type = _t;
4373 		    pst->subtable = pstkd->sub;
4374 		    pst->next = sc->possub;
4375 		    sc->possub = pst;
4376 		} else if ( lookup_type!=gpos_single )
4377 		    free( pst->u.subs.variant );
4378 		pst->ticked = true;
4379 		if ( lookup_type==gpos_single ) {
4380 		    VRDevTabParse(&pst->u.pos,&psts[cols*r+SIM_DX+1]);
4381 		    pst->u.pos.xoff = psts[cols*r+SIM_DX].u.md_ival;
4382 		    pst->u.pos.yoff = psts[cols*r+SIM_DY].u.md_ival;
4383 		    pst->u.pos.h_adv_off = psts[cols*r+SIM_DX_ADV].u.md_ival;
4384 		    pst->u.pos.v_adv_off = psts[cols*r+SIM_DY_ADV].u.md_ival;
4385 		} else {
4386 		    pst->u.subs.variant = GlyphNameListDeUnicode( psts[cols*r+1].u.md_str );
4387 		    if ( lookup_type==gsub_ligature )
4388 			pst->u.lig.lig = sc;
4389 		}
4390 	    }
4391 	} else if ( lookup_type==gpos_pair ) {
4392 	    for ( r=0; r<rows; ++r ) {
4393 		sc = SFGetChar(pstkd->sf,-1,psts[cols*r+0].u.md_str);
4394 		KpMDParse(sc,pstkd->sub,psts,rows,cols,r);
4395 	    }
4396 	}
4397 
4398 	/* Now free anything with this subtable which did not get ticked
4399 	 * during the above update */
4400 	k=0;
4401 	do {
4402 	    sf = pstkd->sf->subfontcnt==0 ? pstkd->sf : pstkd->sf->subfonts[k];
4403 	    for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
4404 		for ( pstprev=NULL, pst = sc->possub; pst!=NULL; pst=pstnext ) {
4405 		    pstnext = pst->next;
4406 		    if ( pst->ticked || pst->subtable!=pstkd->sub )
4407 			pstprev = pst;
4408 		    else {
4409 			if ( pstprev==NULL )
4410 			    sc->possub = pstnext;
4411 			else
4412 			    pstprev->next = pstnext;
4413 			pst->next = NULL;
4414 			PSTFree(pst);
4415 		    }
4416 		}
4417 		for ( isv=0; isv<2; ++isv ) {
4418 		    for ( kpprev=NULL, kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kpnext ) {
4419 			kpnext = kp->next;
4420 			if ( kp->kcid!=0 || kp->subtable!=pstkd->sub )
4421 			    kpprev = kp;
4422 			else {
4423 			    if ( kpprev!=NULL )
4424 				kpprev->next = kpnext;
4425 			    else if ( isv )
4426 				sc->vkerns = kpnext;
4427 			    else
4428 				sc->kerns = kpnext;
4429 			    kp->next = NULL;
4430 			    KernPairsFree(kp);
4431 			}
4432 		    }
4433 		}
4434 	    }
4435 	    ++k;
4436 	} while ( k<pstkd->sf->subfontcnt );
4437 
4438 	/* The field we use to tick kern pairs must be reset to false */
4439 	k=0;
4440 	do {
4441 	    for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
4442 		for ( isv=0; isv<2; ++isv ) {
4443 		    for ( kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kp->next ) {
4444 			kp->kcid = false;
4445 		    }
4446 		}
4447 	    }
4448 	    ++k;
4449 	} while ( k<pstkd->sf->subfontcnt );
4450 	PSTKD_SetSuffix(pstkd);
4451 	if ( lookup_type==gpos_pair ) {
4452 	    pstkd->sub->separation = separation;
4453 	    pstkd->sub->minkern = minkern;
4454 	    pstkd->sub->kerning_by_touch = touch;
4455 	    pstkd->sub->onlyCloser = onlyCloser;
4456 	    pstkd->sub->dontautokern = !autokern;
4457 	}
4458 
4459 	/* compare the updated data to the snapshot we took before and
4460 	 * trim the undo operation of superfluious data
4461 	 */
4462 	if( oldsfd ) {
4463 
4464 	    int shouldCreateUndoEntry = 1;
4465 	    sf = pstkd->sf;
4466 	    char* str = SFDCreateUndoForLookup( sf, lookup_type );
4467 
4468 	    if( !pstkd->sf->subfontcnt ) {
4469 		char* diffstr = SFDTrimUndoOldToNew( sf, oldsfd, str );
4470 		if( !diffstr ) {
4471 		    // If nothing has changed after all,
4472 		    // don't create an empty undo
4473 		    shouldCreateUndoEntry = 0;
4474 		} else {
4475 		    free(str);
4476 		    str = diffstr;
4477 		}
4478 	    }
4479 
4480 	    if( shouldCreateUndoEntry ) {
4481 
4482 		dlist_trim_to_limit( (struct dlistnode **)&sf->undoes, fontlevel_undo_limit,
4483 				     (dlist_visitor_func_type)SFUndoFreeAssociated );
4484 
4485 		enum sfundotype t = sfut_lookups;
4486 		if( lookup_type == gpos_pair ) {
4487 		    t = sfut_lookups_kerns;
4488 		}
4489 		SFUndoes* undo = SFUndoCreateSFD( t, _("Lookup Table Edit"), str );
4490 		dlist_pushfront( (struct dlistnode **)&sf->undoes, (struct dlistnode *)undo );
4491 	    }
4492 //	    printf("we now have %d splinefont level undoes\n", dlist_size((struct dlistnode **)&sf->undoes));
4493 	}
4494 
4495 	pstkd->done = true;
4496     }
4497 return( true );
4498 }
4499 
PSTKD_DoCancel(PSTKernDlg * pstkd)4500 static void PSTKD_DoCancel(PSTKernDlg *pstkd) {
4501     pstkd->done = true;
4502 }
4503 
PSTKD_Cancel(GGadget * g,GEvent * e)4504 static int PSTKD_Cancel(GGadget *g, GEvent *e) {
4505     PSTKernDlg *pstkd;
4506 
4507     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
4508 	pstkd = GDrawGetUserData(GGadgetGetWindow(g));
4509 	PSTKD_DoCancel(pstkd);
4510     }
4511 return( true );
4512 }
4513 
GlyphNameListDeUnicode(char * str)4514 char *GlyphNameListDeUnicode( char *str ) {
4515     char *pt;
4516     char *ret, *rpt;
4517 
4518     rpt = ret = malloc(strlen(str)+1);
4519     while ( *str==' ' ) ++str;
4520     for ( pt=str; *pt!='\0'; ) {
4521 	if ( *pt==' ' ) {
4522 	    while ( *pt==' ' ) ++pt;
4523 	    --pt;
4524 	}
4525 	if ( *pt=='(' ) {
4526 	    while ( *pt!=')' && *pt!='\0' ) ++pt;
4527 	    if ( *pt==')' ) ++pt;
4528 	} else
4529 	    *rpt++ = *pt++;
4530     }
4531     *rpt = '\0';
4532 return( ret );
4533 }
4534 
SFNameList2NameUni(SplineFont * sf,char * str)4535 char *SFNameList2NameUni(SplineFont *sf, char *str) {
4536     char *start, *pt, *ret, *rpt;
4537     int cnt, ch;
4538     SplineChar *sc;
4539 
4540     if ( str==NULL )
4541 return( NULL );
4542     if ( !add_char_to_name_list )
4543 return( copy(str));
4544 
4545     cnt = 0;
4546     for ( pt=str; *pt!='\0'; ++pt )
4547 	if ( *pt==' ' )
4548 	    ++cnt;
4549     rpt = ret = malloc(strlen(str) + (cnt+1)*7 + 1);
4550     for ( start=str; *start!='\0'; ) {
4551 	while ( *start==' ' ) ++start;
4552 	if ( *start=='\0' )
4553     break;
4554 	for ( pt=start; *pt!='\0' && *pt!=' ' && *pt!='('; ++pt );
4555 	ch = *pt; *pt='\0';
4556 	sc = SFGetChar(sf,-1,start);
4557 	strcpy(rpt,start);
4558 	rpt += strlen(rpt);
4559 	*pt = ch;
4560 	/* don't show control characters, or space or parens, or */
4561 	/*  latin letters (their names are themselves, no need to duplicate) */
4562 	/*  or things in the private use area */
4563 	if ( sc!=NULL && sc->unicodeenc>32 && sc->unicodeenc!=')' &&
4564 		!( sc->unicodeenc<0x7f && isalpha(sc->unicodeenc)) &&
4565 	        !issurrogate(sc->unicodeenc) &&
4566 		!isprivateuse(sc->unicodeenc)) {
4567 	    *rpt++ = '(';
4568 	    rpt = utf8_idpb(rpt,sc->unicodeenc,0);
4569 	    *rpt++ = ')';
4570 	}
4571 	*rpt++ = ' ';
4572 	if ( ch=='(' )
4573 	    while ( *pt!=')' && *pt!='\0' ) ++pt;
4574 	while ( *pt==' ' ) ++pt;
4575 	start = pt;
4576     }
4577     if ( rpt>ret )
4578 	rpt[-1] = '\0';
4579     else
4580 	ret[0] = '\0';
4581 return( ret );
4582 }
4583 
SCNameUniStr(SplineChar * sc)4584 char *SCNameUniStr(SplineChar *sc) {
4585     char *temp, *pt;
4586     int len;
4587 
4588     if ( sc==NULL )
4589 return( NULL );
4590     if ( !add_char_to_name_list )
4591 return( copy(sc->name));
4592 
4593     len = strlen(sc->name);
4594     temp = malloc(len + 8);
4595     strcpy(temp,sc->name);
4596     if ( sc->unicodeenc>32 && sc->unicodeenc!=')' && add_char_to_name_list &&
4597 	    !( sc->unicodeenc<0x7f && isalpha(sc->unicodeenc)) &&
4598 	    !issurrogate(sc->unicodeenc) &&
4599 	    !isprivateuse(sc->unicodeenc)) {
4600 	pt = temp+len;
4601 	*pt++ = '(';
4602 	pt = utf8_idpb(pt,sc->unicodeenc,0);
4603 	*pt++ = ')';
4604 	*pt = '\0';
4605     }
4606 return( temp );
4607 }
4608 
uSCNameUniStr(SplineChar * sc)4609 unichar_t *uSCNameUniStr(SplineChar *sc) {
4610     unichar_t *temp;
4611     int len;
4612 
4613     if ( sc==NULL )
4614 return( NULL );
4615     temp = malloc((strlen(sc->name) + 5) * sizeof(unichar_t));
4616     utf82u_strcpy(temp,sc->name);
4617     if ( sc->unicodeenc>32 && sc->unicodeenc!=')' && add_char_to_name_list &&
4618 	    !( sc->unicodeenc<0x7f && isalpha(sc->unicodeenc)) &&
4619 	    !issurrogate(sc->unicodeenc) &&
4620 	    !isprivateuse(sc->unicodeenc)) {
4621 	len = u_strlen(temp);
4622 	temp[len] = '(';
4623 	temp[len+1] = sc->unicodeenc;
4624 	temp[len+2] = ')';
4625 	temp[len+3] = '\0';
4626     }
4627 return( temp );
4628 }
4629 
SFGlyphNameCompletion(SplineFont * sf,GGadget * t,int from_tab,int new_name_after_space)4630 unichar_t **SFGlyphNameCompletion(SplineFont *sf,GGadget *t,int from_tab,
4631 	int new_name_after_space) {
4632     unichar_t *pt, *spt, *basept, *wild; unichar_t **ret;
4633     int gid, cnt, doit, match_len;
4634     SplineChar *sc;
4635     int do_wildcards;
4636 
4637     pt = spt = basept = (unichar_t *) _GGadgetGetTitle(t);
4638     if ( pt==NULL || *pt=='\0' )
4639 return( NULL );
4640     if ( new_name_after_space ) {
4641 	if (( spt = u_strrchr(spt,' '))== NULL )
4642 	    spt = basept;
4643 	else {
4644 	    pt = ++spt;
4645 	    if ( *pt=='\0' )
4646 return( NULL );
4647 	}
4648     }
4649     while ( *pt && *pt!='*' && *pt!='?' && *pt!='[' && *pt!='{' )
4650 	++pt;
4651     do_wildcards = *pt!='\0';
4652 
4653     if (( !do_wildcards && pt-spt==1 && ( *spt>=0x10000 || !isalpha(*spt))) ||
4654 	    (!from_tab && do_wildcards && pt-spt==2 && spt[1]==' ')) {
4655 	sc = SFGetChar(sf,*spt,NULL);
4656 	/* One unicode character which isn't a glyph name (so not "A") and */
4657 	/*  isn't a wildcard (so not "*") gets expanded to its glyph name. */
4658 	/*  (so "," becomes "comma(,)" */
4659 	/* Or, a single wildcard followed by a space gets expanded to glyph name */
4660 	if ( sc!=NULL ) {
4661 	    ret = malloc((2)*sizeof(unichar_t *));
4662 	    ret[0] = uSCNameUniStr(sc);
4663 	    ret[1] = NULL;
4664 return( ret );
4665 	}
4666     }
4667     if ( do_wildcards && !from_tab )
4668 return( NULL );
4669 
4670     wild = NULL;
4671     if ( do_wildcards ) {
4672 	pt = spt;
4673 	wild = malloc((u_strlen(spt)+2)*sizeof(unichar_t));
4674 	u_strcpy(wild,pt);
4675 	uc_strcat(wild,"*");
4676     }
4677 
4678     match_len = u_strlen(spt);
4679     ret = NULL;
4680     for ( doit=0; doit<2; ++doit ) {
4681 	cnt=0;
4682 	for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
4683 	    int matched;
4684 	    if ( do_wildcards ) {
4685 		unichar_t *temp = utf82u_copy(sc->name);
4686 		matched = GGadgetWildMatch((unichar_t *) wild,temp,false);
4687 		free(temp);
4688 	    } else
4689 		matched = uc_strncmp(spt,sc->name,match_len)==0;
4690 	    if ( matched ) {
4691 		if ( doit ) {
4692 		    if ( spt==basept ) {
4693 			ret[cnt] = uSCNameUniStr(sc);
4694 		    } else {
4695 			unichar_t *temp = malloc((spt-basept+strlen(sc->name)+4)*sizeof(unichar_t));
4696 			int len;
4697 			u_strncpy(temp,basept,spt-basept);
4698 			utf82u_strcpy(temp+(spt-basept),sc->name);
4699 			len = u_strlen(temp);
4700 			if ( sc->unicodeenc>32 && add_char_to_name_list &&
4701 				!( sc->unicodeenc<0x7f && isalpha(sc->unicodeenc)) &&
4702 				!issurrogate(sc->unicodeenc) &&
4703 			        !isprivateuse(sc->unicodeenc)) {
4704 			    temp[len] = '(';
4705 			    temp[len+1] = sc->unicodeenc;
4706 			    temp[len+2] = ')';
4707 			    temp[len+3] = '\0';
4708 			}
4709 			ret[cnt] = temp;
4710 		    }
4711 		}
4712 		++cnt;
4713 	    }
4714 	}
4715 	if ( doit )
4716 	    ret[cnt] = NULL;
4717 	else if ( cnt==0 )
4718     break;
4719 	else
4720 	    ret = malloc((cnt+1)*sizeof(unichar_t *));
4721     }
4722     free(wild);
4723 return( ret );
4724 }
4725 
PSTKD_GlyphNameCompletion(GGadget * t,int from_tab)4726 static unichar_t **PSTKD_GlyphNameCompletion(GGadget *t,int from_tab) {
4727     PSTKernDlg *pstkd = GDrawGetUserData(GDrawGetParentWindow(GGadgetGetWindow(t)));
4728     SplineFont *sf = pstkd->sf;
4729 
4730 return( SFGlyphNameCompletion(sf,t,from_tab,false));
4731 }
4732 
PSTKD_GlyphListCompletion(GGadget * t,int from_tab)4733 static unichar_t **PSTKD_GlyphListCompletion(GGadget *t,int from_tab) {
4734     PSTKernDlg *pstkd = GDrawGetUserData(GDrawGetParentWindow(GGadgetGetWindow(t)));
4735     SplineFont *sf = pstkd->sf;
4736 
4737 return( SFGlyphNameCompletion(sf,t,from_tab,true));
4738 }
4739 
pstkd_e_h(GWindow gw,GEvent * event)4740 static int pstkd_e_h(GWindow gw, GEvent *event) {
4741     PSTKernDlg *pstkd = GDrawGetUserData(gw);
4742 
4743     switch ( event->type ) {
4744       case et_close:
4745 	PSTKD_DoCancel(pstkd);
4746       break;
4747       case et_char:
4748 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
4749 	    int lookup_type = pstkd->sub->lookup->lookup_type;
4750 	    if ( lookup_type==gpos_single )
4751 		help("ui/dialogs/lookups.html", "#lookups-single-pos");
4752 	    else if ( lookup_type==gpos_pair )
4753 		help("ui/dialogs/lookups.html", "#lookups-pair");
4754 	    else
4755 		help("ui/dialogs/lookups.html", "#lookups-basic-subs");
4756 return( true );
4757 	}
4758 return( false );
4759       break;
4760       case et_destroy:
4761       break;
4762       case et_mouseup: case et_mousemove: case et_mousedown:
4763       break;
4764       case et_expose:
4765       break;
4766       case et_resize:
4767       break;
4768     }
4769 return( true );
4770 }
4771 
PSTKernD(SplineFont * sf,struct lookup_subtable * sub,int def_layer)4772 static void PSTKernD(SplineFont *sf, struct lookup_subtable *sub, int def_layer) {
4773     PSTKernDlg pstkd;
4774     GRect pos;
4775     GWindowAttrs wattrs;
4776     char title[300];
4777     struct matrixinit mi;
4778     GGadgetCreateData gcd[23], buttongcd[6], box[6], hbox;
4779     GGadgetCreateData *h1array[8], *h2array[7], *h3array[7], *varray[20], *h4array[8], *h5array[4];
4780     GTextInfo label[23], buttonlabel[6];
4781     int i,k,mi_pos, mi_k;
4782     enum otlookup_type lookup_type = sub->lookup->lookup_type;
4783     char sepbuf[40], mkbuf[40];
4784 
4785     if ( sub->separation==0 && !sub->kerning_by_touch ) {
4786 	sub->separation = sf->width_separation;
4787 	if ( sf->width_separation==0 )
4788 	    sub->separation = 15*(sf->ascent+sf->descent)/100;
4789 	sub->minkern = sub->separation/10;
4790     }
4791 
4792     memset(&pstkd,0,sizeof(pstkd));
4793     pstkd.sf = sf;
4794     pstkd.def_layer = def_layer;
4795     pstkd.sub = sub;
4796 
4797     memset(&wattrs,0,sizeof(wattrs));
4798     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
4799     wattrs.event_masks = ~(1<<et_charup);
4800     wattrs.restrict_input_to_me = true;
4801     wattrs.undercursor = 1;
4802     wattrs.cursor = ct_pointer;
4803     snprintf(title,sizeof(title), _("Lookup Subtable, %s"), sub->subtable_name );
4804     wattrs.utf8_window_title =  title;
4805     wattrs.is_dlg = true;
4806     pos.x = pos.y = 0;
4807     pos.width = GGadgetScale(GDrawPointsToPixels(NULL,300));
4808     pos.height = GDrawPointsToPixels(NULL,400);
4809     pstkd.gw = GDrawCreateTopWindow(NULL,&pos,pstkd_e_h,&pstkd,&wattrs);
4810 
4811     memset(&gcd,0,sizeof(gcd));
4812     memset(&buttongcd,0,sizeof(buttongcd));
4813     memset(&box,0,sizeof(box));
4814     memset(&label,0,sizeof(label));
4815     memset(&buttonlabel,0,sizeof(buttonlabel));
4816 
4817     i = k = 0;
4818     label[i].text = (unichar_t *) _("_Alphabetic");
4819     label[i].text_is_1byte = true;
4820     label[i].text_in_resource = true;
4821     gcd[i].gd.label = &label[i];
4822     gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4823     gcd[i].gd.flags = isalphabetic ? (gg_enabled|gg_visible|gg_cb_on) : (gg_enabled|gg_visible);
4824     gcd[i].gd.popup_msg = _("Sort this display based on the alphabetic name of the glyph");
4825     gcd[i].gd.handle_controlevent = PSTKD_Sort;
4826     gcd[i].gd.cid = CID_Alpha;
4827     gcd[i].creator = GRadioCreate;
4828     h1array[0] = &gcd[i++];
4829 
4830     label[i].text = (unichar_t *) _("_Unicode");
4831     label[i].text_is_1byte = true;
4832     label[i].text_in_resource = true;
4833     gcd[i].gd.label = &label[i];
4834     gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4835     gcd[i].gd.flags = !isalphabetic ? (gg_enabled|gg_visible|gg_cb_on) : (gg_enabled|gg_visible);
4836     gcd[i].gd.popup_msg = _("Sort this display based on the unicode code of the glyph");
4837     gcd[i].gd.handle_controlevent = PSTKD_Sort;
4838     gcd[i].gd.cid = CID_Unicode;
4839     gcd[i].creator = GRadioCreate;
4840     h1array[1] = &gcd[i++]; h1array[2] = GCD_HPad10;
4841 
4842     label[i].text = (unichar_t *) _("_By Base Char");
4843     label[i].text_is_1byte = true;
4844     label[i].text_in_resource = true;
4845     gcd[i].gd.label = &label[i];
4846     gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4847     gcd[i].gd.flags = stemming ? (gg_enabled|gg_visible|gg_cb_on) : (gg_enabled|gg_visible);
4848     gcd[i].gd.popup_msg = _("Sort first using the base glyph (if any).\nThus Agrave would sort with A");
4849     gcd[i].gd.handle_controlevent = PSTKD_Sort;
4850     gcd[i].gd.cid = CID_BaseChar;
4851     gcd[i].creator = GCheckBoxCreate;
4852     h1array[3] = &gcd[i++]; h1array[4] = GCD_HPad10;
4853 
4854     label[i].text = (unichar_t *) _("By _Scripts");
4855     label[i].text_is_1byte = true;
4856     label[i].text_in_resource = true;
4857     gcd[i].gd.label = &label[i];
4858     gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4859     gcd[i].gd.flags = byscripts ? (gg_enabled|gg_visible|gg_cb_on) : (gg_enabled|gg_visible);
4860     gcd[i].gd.popup_msg = _("Sort first using the glyph's script.\nThus A and Z would sort together\nwhile Alpha would sort with Omega and not A");
4861     if ( sub->lookup->features==NULL ||
4862 	    (sub->lookup->features->next==NULL &&
4863 	     (sub->lookup->features->scripts==NULL ||
4864 	      sub->lookup->features->scripts->next==NULL)))
4865 	gcd[i].gd.flags = gg_visible|gg_cb_on;	/* If there is only one script, we can't really sort by it */
4866     gcd[i].gd.handle_controlevent = PSTKD_Sort;
4867     gcd[i].gd.cid = CID_Scripts;
4868     gcd[i].creator = GCheckBoxCreate;
4869     h1array[5] = &gcd[i++]; h1array[6] = GCD_Glue; h1array[7] = NULL;
4870 
4871     box[0].gd.flags = gg_enabled|gg_visible;
4872     box[0].gd.u.boxelements = h1array;
4873     box[0].creator = GHBoxCreate;
4874     varray[k++] = &box[0]; varray[k++] = NULL;
4875 
4876     if ( sub->lookup->lookup_type == gpos_pair || sub->lookup->lookup_type == gpos_single ) {
4877 	label[i].text = (unichar_t *) _("_Hide Unused Columns");
4878 	label[i].text_is_1byte = true;
4879 	label[i].text_in_resource = true;
4880 	gcd[i].gd.label = &label[i];
4881 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4882 	gcd[i].gd.flags = lookup_hideunused ? (gg_enabled|gg_visible|gg_cb_on) : (gg_enabled|gg_visible);
4883 	gcd[i].gd.popup_msg = _("Don't display columns of 0s.\nThe OpenType lookup allows for up to 8 kinds\nof data, but almost all lookups will use just one or two.\nOmitting the others makes the behavior clearer.");
4884 	gcd[i].gd.handle_controlevent = PSTKD_HideUnused;
4885 	gcd[i].creator = GCheckBoxCreate;
4886 	varray[k++] = &gcd[i++]; varray[k++] = NULL;
4887     }
4888 
4889     PSTMatrixInit(&mi,sf,sub,&pstkd);
4890     mi_pos = i;
4891     gcd[i].gd.pos.height = 200;
4892     gcd[i].gd.flags = gg_enabled | gg_visible;
4893     gcd[i].gd.cid = CID_PSTList;
4894     gcd[i].gd.u.matrix = &mi;
4895     gcd[i].data = &pstkd;
4896     gcd[i].creator = GMatrixEditCreate;
4897     mi_k = k;
4898     varray[k++] = &gcd[i++]; varray[k++] = NULL;
4899 
4900     buttonlabel[0].text = (unichar_t *) _("_Populate");
4901     if ( lookup_type==gpos_pair )
4902 	buttonlabel[0].text = (unichar_t *) _("Auto_Kern");
4903     buttonlabel[0].text_is_1byte = true;
4904     buttonlabel[0].text_in_resource = true;
4905     buttongcd[0].gd.label = &buttonlabel[0];
4906     buttongcd[0].gd.pos.x = 5; buttongcd[0].gd.pos.y = 5+4;
4907     buttongcd[0].gd.flags =
4908 	    sub->lookup->features==NULL || sub->vertical_kerning ?
4909 	     gg_visible:
4910 	     gg_enabled|gg_visible;
4911     if ( lookup_type==gpos_pair ) {
4912 	buttongcd[0].gd.popup_msg = _("For each script to which this lookup applies, look at all pairs of\n"
4913 						    "glyphs in that script and try to guess a reasonable kerning value\n"
4914 			                            "for that pair.");
4915 	buttongcd[0].gd.handle_controlevent = PSTKD_AutoKern;
4916     } else {
4917 	buttongcd[0].gd.popup_msg = _("Add entries for all glyphs in the scripts to which this lookup applies.\nWhen FontForge can find a default value it will add that too.");
4918 	buttongcd[0].gd.handle_controlevent = PSTKD_Populate;
4919     }
4920     buttongcd[0].creator = GButtonCreate;
4921 
4922     buttonlabel[1].text = (unichar_t *) _("_Add Selected");
4923     if ( lookup_type==gpos_pair )
4924 	buttonlabel[1].text = (unichar_t *) _("_AutoKern Selected");
4925     buttonlabel[1].text_is_1byte = true;
4926     buttonlabel[1].text_in_resource = true;
4927     buttongcd[1].gd.label = &buttonlabel[1];
4928     buttongcd[1].gd.pos.x = 5; buttongcd[1].gd.pos.y = 5+4;
4929     buttongcd[1].gd.flags = gg_enabled|gg_visible;
4930     if ( lookup_type==gpos_pair ) {
4931 	if ( sub->vertical_kerning )
4932 	    buttongcd[1].gd.flags = gg_visible;
4933 	buttongcd[1].gd.popup_msg = _("Add kerning info between all pairs of selected glyphs" );
4934 	buttongcd[1].gd.handle_controlevent = PSTKD_AutoKernSelected;
4935     } else {
4936 	buttongcd[1].gd.popup_msg = _("Add entries for all selected glyphs.");
4937 	buttongcd[1].gd.handle_controlevent = PSTKD_PopulateSelected;
4938     }
4939     buttongcd[1].creator = GButtonCreate;
4940 
4941     buttonlabel[2].text = (unichar_t *) _("_Remove Empty");
4942     buttonlabel[2].text_is_1byte = true;
4943     buttonlabel[2].text_in_resource = true;
4944     buttongcd[2].gd.label = &buttonlabel[2];
4945     buttongcd[2].gd.pos.x = 5; buttongcd[2].gd.pos.y = 5+4;
4946     buttongcd[2].gd.flags = gg_enabled|gg_visible;
4947     buttongcd[2].gd.popup_msg =
4948 	    (sub->lookup->lookup_type == gpos_single ? _("Remove all \"empty\" entries -- those where all fields are 0") :
4949 	     sub->lookup->lookup_type == gpos_pair ? _("Remove all \"empty\" entries -- entries with no second glyph") :
4950 	     sub->lookup->lookup_type == gsub_ligature ? _("Remove all \"empty\" entries -- those with no source glyphs") :
4951 		_("Remove all \"empty\" entries -- those with no replacement glyphs"));
4952     buttongcd[2].gd.handle_controlevent = PSTKD_RemoveEmpty;
4953     buttongcd[2].creator = GButtonCreate;
4954 
4955     buttonlabel[3].text = (unichar_t *) _("Remove All");
4956     buttonlabel[3].text_is_1byte = true;
4957     buttonlabel[3].text_in_resource = true;
4958     buttongcd[3].gd.label = &buttonlabel[3];
4959     buttongcd[3].gd.pos.x = 5; buttongcd[3].gd.pos.y = 5+4;
4960     buttongcd[3].gd.flags = gg_enabled|gg_visible;
4961     buttongcd[3].gd.popup_msg = _("Remove all entries.");
4962     buttongcd[3].gd.handle_controlevent = PSTKD_RemoveAll;
4963     buttongcd[3].creator = GButtonCreate;
4964 
4965     if ( sub->lookup->lookup_type == gsub_single ) {
4966 	label[i].text = (unichar_t *) _("_Default Using Suffix:");
4967 	label[i].text_is_1byte = true;
4968 	label[i].text_in_resource = true;
4969 	gcd[i].gd.label = &label[i];
4970 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4971 	gcd[i].gd.flags = gg_enabled|gg_visible;
4972 	gcd[i].gd.popup_msg = _(
4973 	    "Add entries to the lookup based on the following suffix.\n"
4974 	    "So if the suffix is set to \"superior\" and the font\n"
4975 	    "contains glyphs named \"A\" and \"A.superior\" (and the\n"
4976 	    "lookup applies to the latin script), then FontForge will\n"
4977 	    "add an entry mapping \"A\" -> \"A.superior\"." );
4978 	gcd[i].gd.handle_controlevent = PSTKD_PopulateWithSuffix;
4979 	gcd[i].creator = GButtonCreate;
4980 	h2array[0] = &gcd[i++];
4981 
4982 	label[i].text = (unichar_t *) sub->suffix;
4983 	label[i].text_is_1byte = true;
4984 	label[i].text_in_resource = true;
4985 	gcd[i].gd.label = sub->suffix==NULL ? NULL : &label[i];
4986 	gcd[i].gd.flags = gg_enabled|gg_visible;
4987 	gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
4988 	gcd[i].gd.cid = CID_Suffix;
4989 	gcd[i].creator = GTextFieldCreate;
4990 	h2array[1] = &gcd[i++]; h2array[2] = GCD_Glue; h2array[3] = NULL;
4991 
4992 	box[1].gd.flags = gg_enabled|gg_visible;
4993 	box[1].gd.u.boxelements = h2array;
4994 	box[1].creator = GHBoxCreate;
4995 	varray[k++] = &box[1]; varray[k++] = NULL;
4996     } else if ( sub->lookup->lookup_type == gpos_single ) {
4997 	label[i].text = (unichar_t *) _("_Default New Entries to First");
4998 	label[i].text_is_1byte = true;
4999 	label[i].text_in_resource = true;
5000 	gcd[i].gd.label = &label[i];
5001 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5002 	gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
5003 	if ( is_boundsFeat(sub)!=0 )
5004 	    gcd[i].gd.flags = gg_enabled|gg_visible;
5005 	gcd[i].gd.popup_msg = _("When adding new entries, give them the same\ndelta values as those on the first line.");
5006 	gcd[i].gd.cid = CID_AllSame;
5007 	gcd[i].creator = GCheckBoxCreate;
5008 	varray[k++] = &gcd[i++]; varray[k++] = NULL;
5009 
5010     } else if ( sub->lookup->lookup_type == gpos_pair ) {
5011 	label[i].text = (unichar_t *) _("_Default Separation:");
5012 	label[i].text_is_1byte = true;
5013 	label[i].text_in_resource = true;
5014 	gcd[i].gd.label = &label[i];
5015 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5016 	gcd[i].gd.flags = gg_enabled|gg_visible;
5017 	gcd[i].gd.popup_msg = _(
5018 	    "Add entries to the lookup trying to make the optical\n"
5019 	    "separation between all pairs of glyphs equal to this\n"
5020 	    "value." );
5021 	gcd[i].creator = GLabelCreate;
5022 	h4array[0] = &gcd[i++];
5023 
5024 	sprintf( sepbuf, "%d", sub->separation );
5025 	label[i].text = (unichar_t *) sepbuf;
5026 	label[i].text_is_1byte = true;
5027 	label[i].text_in_resource = true;
5028 	gcd[i].gd.label = &label[i];
5029 	gcd[i].gd.pos.width = 50;
5030 	gcd[i].gd.flags = gg_enabled|gg_visible;
5031 	gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
5032 	gcd[i].gd.cid = CID_Separation;
5033 	gcd[i].creator = GTextFieldCreate;
5034 	h4array[1] = &gcd[i++];
5035 
5036 	label[i].text = (unichar_t *) _("_Min Kern:");
5037 	label[i].text_is_1byte = true;
5038 	label[i].text_in_resource = true;
5039 	gcd[i].gd.label = &label[i];
5040 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5041 	gcd[i].gd.flags = gg_enabled|gg_visible;
5042 	gcd[i].gd.popup_msg = _(
5043 	    "Any computed kerning change whose absolute value is less\n"
5044 	    "that this will be ignored.\n" );
5045 	gcd[i].creator = GLabelCreate;
5046 	h4array[2] = &gcd[i++];
5047 
5048 	sprintf( mkbuf, "%d", sub->minkern );
5049 	label[i].text = (unichar_t *) mkbuf;
5050 	label[i].text_is_1byte = true;
5051 	label[i].text_in_resource = true;
5052 	gcd[i].gd.label = &label[i];
5053 	gcd[i].gd.pos.width = 50;
5054 	gcd[i].gd.flags = gg_enabled|gg_visible;
5055 	gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
5056 	gcd[i].gd.cid = CID_MinKern;
5057 	gcd[i].creator = GTextFieldCreate;
5058 	h4array[3] = &gcd[i++];
5059 
5060 	label[i].text = (unichar_t *) _("_Touching");
5061 	label[i].text_is_1byte = true;
5062 	label[i].text_in_resource = true;
5063 	gcd[i].gd.label = &label[i];
5064 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5065 	gcd[i].gd.flags = gg_enabled|gg_visible;
5066 	if ( sub->kerning_by_touch )
5067 	    gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
5068 	gcd[i].gd.popup_msg = _(
5069 	    "Normally kerning is based on achieving a constant (optical)\n"
5070 	    "separation between glyphs, but occasionally it is desirable\n"
5071 	    "to have a kerning table where the kerning is based on the\n"
5072 	    "closest approach between two glyphs (So if the desired separ-\n"
5073 	    "ation is 0 then the glyphs will actually be touching.");
5074 	gcd[i].gd.cid = CID_Touched;
5075 	gcd[i].creator = GCheckBoxCreate;
5076 	h4array[4] = &gcd[i++];
5077 
5078 	h4array[5] = GCD_Glue; h4array[6] = NULL;
5079 
5080 	box[1].gd.flags = gg_enabled|gg_visible;
5081 	box[1].gd.u.boxelements = h4array;
5082 	box[1].creator = GHBoxCreate;
5083 	varray[k++] = &box[1]; varray[k++] = NULL;
5084 
5085 	label[i].text = (unichar_t *) _("Only kern glyphs closer");
5086 	label[i].text_is_1byte = true;
5087 	label[i].text_in_resource = true;
5088 	gcd[i].gd.label = &label[i];
5089 	gcd[i].gd.flags = gg_enabled|gg_visible;
5090 	if ( sub->onlyCloser )
5091 	    gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
5092 	gcd[i].gd.popup_msg = _(
5093 	    "When doing autokerning, only move glyphs closer together,\n"
5094 	    "so the kerning offset will be negative.");
5095 	gcd[i].gd.cid = CID_OnlyCloser;
5096 	gcd[i].creator = GCheckBoxCreate;
5097 	h5array[0] = &gcd[i++];
5098 
5099 	label[i].text = (unichar_t *) _("Autokern new entries");
5100 	label[i].text_is_1byte = true;
5101 	label[i].text_in_resource = true;
5102 	gcd[i].gd.label = &label[i];
5103 	gcd[i].gd.flags = gg_enabled|gg_visible;
5104 	if ( !sub->dontautokern )
5105 	    gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
5106 	gcd[i].gd.popup_msg = _(
5107 	    "When adding new entries provide default kerning values.");
5108 	gcd[i].gd.cid = CID_Autokern;
5109 	gcd[i].creator = GCheckBoxCreate;
5110 	h5array[1] = &gcd[i++]; h5array[2] = NULL;
5111 
5112 	memset(&hbox,0,sizeof(hbox));
5113 	hbox.gd.flags = gg_enabled|gg_visible;
5114 	hbox.gd.u.boxelements = h5array;
5115 	hbox.creator = GHBoxCreate;
5116 	varray[k++] = &hbox; varray[k++] = NULL;
5117 
5118 	label[i].text = (unichar_t *) _("Size:");
5119 	label[i].text_is_1byte = true;
5120 	gcd[i].gd.label = &label[i];
5121 	gcd[i].gd.pos.x = 30; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+30;
5122 	gcd[i].gd.flags = gg_visible|gg_enabled ;
5123 	gcd[i++].creator = GLabelCreate;
5124 	h2array[0] = &gcd[i-1];
5125 
5126 	pstkd.pixelsize = 150;
5127 	label[i].text = (unichar_t *) "150";
5128 	label[i].text_is_1byte = true;
5129 	gcd[i].gd.label = &label[i];
5130 	gcd[i].gd.pos.x = 92; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y-4;
5131 	gcd[i].gd.pos.width = 80;
5132 	gcd[i].gd.flags = gg_visible|gg_enabled ;
5133 	gcd[i].gd.cid = CID_PixelSize;
5134 	gcd[i].gd.handle_controlevent = PSTKD_DisplaySizeChanged;
5135 	gcd[i++].creator = GTextFieldCreate;
5136 	h2array[1] = &gcd[i-1]; h2array[2] = GCD_HPad10;
5137 
5138 /* GT: Short for "Magnification" */
5139 	label[i].text = (unichar_t *) _("Mag:");
5140 	label[i].text_is_1byte = true;
5141 	gcd[i].gd.label = &label[i];
5142 	gcd[i].gd.pos.x = 185; gcd[i].gd.pos.y = gcd[i-2].gd.pos.y;
5143 	gcd[i].gd.flags = gg_visible|gg_enabled ;
5144 	gcd[i++].creator = GLabelCreate;
5145 	h2array[3] = &gcd[i-1];
5146 
5147 	pstkd.mag = 1;
5148 	gcd[i].gd.flags = gg_visible|gg_enabled ;
5149 	gcd[i].gd.cid = CID_Magnification;
5150 	gcd[i].gd.u.list = magnifications;
5151 	gcd[i].gd.handle_controlevent = PSTKD_MagnificationChanged;
5152 	gcd[i++].creator = GListButtonCreate;
5153 	h2array[4] = &gcd[i-1]; h2array[5] = GCD_Glue; h2array[6] = NULL;
5154 
5155 	box[2].gd.flags = gg_enabled|gg_visible;
5156 	box[2].gd.u.boxelements = h2array;
5157 	box[2].creator = GHBoxCreate;
5158 	varray[k++] = &box[2]; varray[k++] = NULL;
5159 
5160 	gcd[i].gd.pos.width = 200;
5161 	gcd[i].gd.pos.height = 200;
5162 	gcd[i].gd.flags = gg_visible | gg_enabled;
5163 	gcd[i].gd.u.drawable_e_h = pstkern_e_h;
5164 	gcd[i].gd.cid = CID_KernDisplay;
5165 	gcd[i].creator = GDrawableCreate;
5166 	varray[k++] = &gcd[i++]; varray[k++] = NULL;
5167     }
5168 
5169     gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+24+3;
5170     gcd[i].gd.pos.width = -1;
5171     gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
5172     label[i].text = (unichar_t *) _("_OK");
5173     label[i].text_is_1byte = true;
5174     label[i].text_in_resource = true;
5175     gcd[i].gd.label = &label[i];
5176     gcd[i].gd.handle_controlevent = PSTKD_Ok;
5177     gcd[i++].creator = GButtonCreate;
5178 
5179     gcd[i].gd.pos.x = -10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+3;
5180     gcd[i].gd.pos.width = -1;
5181     gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
5182     label[i].text = (unichar_t *) _("_Cancel");
5183     label[i].text_is_1byte = true;
5184     label[i].text_in_resource = true;
5185     gcd[i].gd.label = &label[i];
5186     gcd[i].gd.handle_controlevent = PSTKD_Cancel;
5187     gcd[i].gd.cid = CID_Cancel;
5188     gcd[i++].creator = GButtonCreate;
5189 
5190     h3array[0] = GCD_Glue; h3array[1] = &gcd[i-2]; h3array[2] = GCD_Glue;
5191     h3array[3] = GCD_Glue; h3array[4] = &gcd[i-1]; h3array[5] = GCD_Glue;
5192     h3array[6] = NULL;
5193 
5194     box[3].gd.flags = gg_enabled|gg_visible;
5195     box[3].gd.u.boxelements = h3array;
5196     box[3].creator = GHBoxCreate;
5197     varray[k++] = &box[3]; varray[k++] = NULL; varray[k++] = NULL;
5198 
5199     box[4].gd.pos.x = box[4].gd.pos.y = 2;
5200     box[4].gd.flags = gg_enabled|gg_visible;
5201     box[4].gd.u.boxelements = varray;
5202     box[4].creator = GHVGroupCreate;
5203 
5204     GGadgetsCreate(pstkd.gw,box+4);
5205     GHVBoxSetExpandableRow(box[4].ret,mi_k/2);
5206     GHVBoxSetExpandableCol(box[3].ret,gb_expandgluesame);
5207     if ( sub->lookup->lookup_type == gsub_single )
5208 	GHVBoxSetExpandableCol(box[1].ret,gb_expandglue);
5209     else if ( sub->lookup->lookup_type==gpos_pair ) {
5210 	GHVBoxSetExpandableCol(box[1].ret,gb_expandglue);
5211 	GHVBoxSetExpandableCol(box[2].ret,gb_expandglue);
5212     }
5213     GHVBoxSetExpandableCol(box[0].ret,gb_expandglue);
5214     GMatrixEditAddButtons(gcd[mi_pos].ret,buttongcd);
5215     GMatrixEditSetColumnCompletion(gcd[mi_pos].ret,0,PSTKD_GlyphNameCompletion);
5216     if ( sub->lookup->lookup_type == gsub_single || sub->lookup->lookup_type==gpos_pair )
5217 	GMatrixEditSetColumnCompletion(gcd[mi_pos].ret,1,PSTKD_GlyphNameCompletion);
5218     else if ( sub->lookup->lookup_type == gsub_multiple ||
5219 	    sub->lookup->lookup_type==gsub_alternate ||
5220 	    sub->lookup->lookup_type==gsub_ligature )
5221 	GMatrixEditSetColumnCompletion(gcd[mi_pos].ret,1,PSTKD_GlyphListCompletion);
5222 
5223     if ( sub->lookup->lookup_type == gpos_pair )
5224 	GMatrixEditSetTextChangeReporter(gcd[mi_pos].ret,PSTKD_METextChanged);
5225     else
5226 	GMatrixEditSetMouseMoveReporter(gcd[mi_pos].ret,PST_PopupPrepare);
5227     if ( sub->lookup->lookup_type == gpos_pair || sub->lookup->lookup_type == gpos_single )
5228 	PSTKD_DoHideUnused(&pstkd);
5229     else
5230 	GHVBoxFitWindow(box[4].ret);
5231 
5232     GDrawSetVisible(pstkd.gw,true);
5233 
5234     while ( !pstkd.done )
5235 	GDrawProcessOneEvent(NULL);
5236     GDrawDestroyWindow(pstkd.gw);
5237     if ( pstkd.display!=NULL ) {
5238 	BDFFontFree(pstkd.display);
5239 	pstkd.display = NULL;
5240     }
5241 }
5242 /* ************************************************************************** */
5243 /* *************************** Subtable Selection *************************** */
5244 /* ************************************************************************** */
5245 
SubtableNameInUse(char * subname,SplineFont * sf,struct lookup_subtable * exclude)5246 static int SubtableNameInUse(char *subname, SplineFont *sf, struct lookup_subtable *exclude) {
5247     int isgpos, i, j;
5248     OTLookup *otl;
5249     struct lookup_subtable *sub;
5250 
5251     if ( sf->fontinfo!=NULL ) {
5252 	for ( isgpos=0; isgpos<2; ++isgpos ) {
5253 	    struct lkdata *lk = &sf->fontinfo->tables[isgpos];
5254 	    for ( i=0; i<lk->cnt; ++i ) {
5255 		if ( lk->all[i].deleted )
5256 	    continue;
5257 		for ( j=0; j<lk->all[i].subtable_cnt; ++j ) {
5258 		    if ( lk->all[i].subtables[j].deleted || lk->all[i].subtables[j].subtable==exclude )
5259 		continue;
5260 		    if ( strcmp(lk->all[i].subtables[j].subtable->subtable_name,subname)==0 )
5261 return( true );
5262 		}
5263 	    }
5264 	}
5265     } else {
5266 	for ( isgpos=0; isgpos<2; ++isgpos ) {
5267 	    for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
5268 		for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
5269 		    if ( sub==exclude )
5270 		continue;
5271 		    if ( strcmp(sub->subtable_name,subname)==0 )
5272 return( true );
5273 		}
5274 	    }
5275 	}
5276     }
5277 return( false );
5278 }
5279 
EditSubtable(struct lookup_subtable * sub,int isgpos,SplineFont * sf,struct subtable_data * sd,int def_layer)5280 int EditSubtable(struct lookup_subtable *sub,int isgpos,SplineFont *sf,
5281 	struct subtable_data *sd, int def_layer) {
5282     char *def = sub->subtable_name;
5283     int new = def==NULL;
5284     char *freeme = NULL;
5285     int name_search;
5286 
5287     if ( new ) {
5288 	def = freeme = malloc(strlen(sub->lookup->lookup_name)+10);
5289 	name_search = 1;
5290 	do {
5291 	    sprintf( def, "%s-%d", sub->lookup->lookup_name, name_search++ );
5292 	} while ( SubtableNameInUse(def,sf,sub));
5293     }
5294     for (;;) {
5295 	def = gwwv_ask_string(_("Please name this subtable"),def,_("Please name this subtable"));
5296 	free(freeme);
5297 	if ( def==NULL )
5298 return( false );
5299 	freeme = def;
5300 	if ( SubtableNameInUse(def,sf,sub) )
5301 	    ff_post_notice(_("Duplicate name"),_("There is already a subtable with that name, please pick another."));
5302 	else
5303     break;
5304     }
5305     free(sub->subtable_name);
5306     sub->subtable_name = def;
5307     if ( new && sub->lookup->lookup_type == gsub_single )
5308 	sub->suffix = SuffixFromTags(sub->lookup->features);
5309     if ( new && (sd==NULL || !(sd->flags&sdf_dontedit)) )
5310 	_LookupSubtableContents(sf, sub, sd, def_layer);
5311 return( true );
5312 }
5313 
NewSubtable(OTLookup * otl,int isgpos,SplineFont * sf,struct subtable_data * sd,int def_layer)5314 static struct lookup_subtable *NewSubtable(OTLookup *otl,int isgpos,SplineFont *sf, struct subtable_data *sd,int def_layer) {
5315     struct lookup_subtable *sub, *last;
5316     int i,j;
5317 
5318     sub = chunkalloc(sizeof(struct lookup_subtable));
5319     sub->lookup = otl;
5320     sub->separation = 15*(sf->ascent+sf->descent)/100;
5321     sub->minkern = sub->separation/10;
5322     if ( !EditSubtable(sub,isgpos,sf,sd,def_layer)) {
5323 	chunkfree(sub,sizeof(struct lookup_subtable));
5324 return( NULL );
5325     }
5326     if ( otl->subtables==NULL )
5327 	otl->subtables = sub;
5328     else {
5329 	for ( last=otl->subtables; last->next!=NULL; last=last->next );
5330 	last->next = sub;
5331     }
5332     if ( sf->fontinfo!=NULL ) {
5333 	struct lkdata *lk = &sf->fontinfo->tables[isgpos];
5334 	for ( i=0; i<lk->cnt && lk->all[i].lookup!=otl; ++i );
5335 	if ( i==lk->cnt ) {
5336 	    IError( "Lookup missing from FontInfo lookup list");
5337 	} else {
5338 	    if ( lk->all[i].subtable_cnt>=lk->all[i].subtable_max )
5339 		lk->all[i].subtables = realloc(lk->all[i].subtables,(lk->all[i].subtable_max+=10)*sizeof(struct lksubinfo));
5340 	    j = lk->all[i].subtable_cnt++;
5341 	    memset(&lk->all[i].subtables[j],0,sizeof(struct lksubinfo));
5342 	    lk->all[i].subtables[j].subtable = sub;
5343 	    GFI_LookupScrollbars(sf->fontinfo,isgpos, true);
5344 	    GFI_LookupEnableButtons(sf->fontinfo,isgpos);
5345 	}
5346     }
5347 return( sub );
5348 }
5349 
SFSubtablesOfType(SplineFont * sf,int lookup_type,int kernclass,int add_none)5350 GTextInfo **SFSubtablesOfType(SplineFont *sf, int lookup_type, int kernclass,
5351 	int add_none) {
5352     int isgpos = (lookup_type>=gpos_start);
5353     int k, cnt, lcnt, pos;
5354     OTLookup *otl;
5355     struct lookup_subtable *sub;
5356     GTextInfo **ti;
5357 
5358     if ( sf->cidmaster != NULL ) sf=sf->cidmaster;
5359     else if ( sf->mm != NULL ) sf = sf->mm->normal;
5360 
5361     for ( k=0; k<2; ++k ) {
5362 	cnt = lcnt = pos = 0;
5363 	if ( k && add_none ) {
5364 	    ti[pos] = calloc(1,sizeof(GTextInfo));
5365 	    ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5366 	    ti[pos]->userdata = (void *) -1;
5367 	    ti[pos++]->text = utf82u_copy(_("No Subtable"));
5368 	    ti[pos] = calloc(1,sizeof(GTextInfo));
5369 	    ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5370 	    ti[pos++]->line = true;
5371 	}
5372 	for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
5373 	    if ( otl->lookup_type==lookup_type && otl->subtables!=NULL ) {
5374 		if ( k ) {
5375 		    ti[pos] = calloc(1,sizeof(GTextInfo));
5376 		    ti[pos]->text = malloc((utf82u_strlen(otl->lookup_name)+2)*sizeof(unichar_t));
5377 		    ti[pos]->text[0] = ' ';
5378 		    utf82u_strcpy(ti[pos]->text+1,otl->lookup_name);
5379 		    ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5380 		    ti[pos++]->disabled = true;
5381 		}
5382 		++lcnt;
5383 		for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
5384 		    if ( lookup_type!=gpos_pair || kernclass==-1 ||
5385 			    (kernclass && sub->kc!=NULL) ||
5386 			    (!kernclass && sub->per_glyph_pst_or_kern)) {
5387 			if ( k ) {
5388 			    ti[pos] = calloc(1,sizeof(GTextInfo));
5389 			    ti[pos]->text = utf82u_copy(sub->subtable_name);
5390 			    ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5391 			    ti[pos++]->userdata = sub;
5392 			}
5393 			++cnt;
5394 		    }
5395 		}
5396 	    }
5397 	}
5398 	if ( !k ) {
5399 	    ti = calloc(cnt+lcnt+3+2*add_none,sizeof(GTextInfo*));
5400 	} else {
5401 	    ti[pos] = calloc(1,sizeof(GTextInfo));
5402 	    ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5403 	    ti[pos++]->line = true;
5404 	    ti[pos] = calloc(1,sizeof(GTextInfo));
5405 	    ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5406 	    ti[pos++]->text = utf82u_copy(_("New Lookup Subtable..."));
5407 	    ti[pos] = calloc(1,sizeof(GTextInfo));
5408 return( ti );
5409 	}
5410     }
5411     /* We'll never get here */
5412 return( NULL );
5413 }
5414 
SFSubtableListOfType(SplineFont * sf,int lookup_type,int kernclass,int add_none)5415 GTextInfo *SFSubtableListOfType(SplineFont *sf, int lookup_type, int kernclass,int add_none) {
5416     GTextInfo **temp, *ti;
5417     int cnt;
5418 
5419     temp = SFSubtablesOfType(sf,lookup_type,kernclass,add_none);
5420     if ( temp==NULL )
5421 return( NULL );
5422     for ( cnt=0; temp[cnt]->text!=NULL || temp[cnt]->line; ++cnt );
5423     ti = calloc(cnt+1,sizeof(GTextInfo));
5424     for ( cnt=0; temp[cnt]->text!=NULL || temp[cnt]->line; ++cnt ) {
5425 	ti[cnt] = *temp[cnt];
5426 	free(temp[cnt]);
5427     }
5428     free(temp);
5429 return( ti );
5430 }
5431 
SFNewLookupSubtableOfType(SplineFont * sf,int lookup_type,struct subtable_data * sd,int def_layer)5432 struct lookup_subtable *SFNewLookupSubtableOfType(SplineFont *sf, int lookup_type, struct subtable_data *sd, int def_layer ) {
5433     int isgpos = (lookup_type>=gpos_start);
5434     OTLookup *otl, *found=NULL;
5435     int cnt, ans;
5436     struct lookup_subtable *sub;
5437     char **choices;
5438 
5439     if ( sf->cidmaster ) sf=sf->cidmaster;
5440 
5441     cnt = 0;
5442     for ( otl=isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next )
5443 	if ( otl->lookup_type==lookup_type )
5444 	    ++cnt;
5445     if ( cnt==0 ) {
5446 	/* There are no lookups of this type, so there is nothing for them to */
5447 	/*  pick from. So we must create a new lookup for them, and then add */
5448 	/*  a subtable to it */
5449 	found = CreateAndSortNewLookupOfType(sf,lookup_type);
5450 	if ( found==NULL )
5451 return( NULL );
5452 	sub = NewSubtable(found,isgpos,sf,sd,def_layer);
5453 	/* even if they canceled the subtable creation they are now stuck */
5454 	/*  with the lookup */
5455 return( sub );
5456     }
5457 
5458     /* I thought briefly that if cnt were 1 I might want to automagically */
5459     /*  create a subtable in that lookup... but no. Still give them the */
5460     /*  option of creating a new lookup */
5461 
5462     choices = malloc((cnt+2)*sizeof(char *));
5463     for ( cnt=0, otl=isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next )
5464 	if ( otl->lookup_type==lookup_type )
5465 	    choices[cnt++] = otl->lookup_name;
5466     choices[cnt++] = _("Create a new lookup");
5467     choices[cnt] = NULL;
5468     ans = gwwv_choose(_("Add a subtable to which lookup?"),(const char **) choices,cnt,cnt-1,
5469 	    _("Add a subtable to which lookup?"));
5470     if ( ans==-1 )
5471 	found = NULL;
5472     else if ( ans==cnt-1 )
5473 	found = CreateAndSortNewLookupOfType(sf,lookup_type);
5474     else {
5475 	found = NULL;
5476 	for ( cnt=0, otl=isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
5477 	    if ( otl->lookup_type==lookup_type ) {
5478 		if ( cnt==ans ) {
5479 		    found = otl;
5480 	break;
5481 		} else
5482 		    ++cnt;
5483 	    }
5484 	}
5485     }
5486     free(choices);
5487     if ( found==NULL )
5488 return( NULL );
5489 
5490 return( NewSubtable(found,isgpos,sf,sd,def_layer));
5491 }
5492 
kf_activateMe(struct fvcontainer * fvc,FontViewBase * fvb)5493 static void kf_activateMe(struct fvcontainer *fvc,FontViewBase *fvb) {
5494     struct kf_dlg *kf = (struct kf_dlg *) fvc;
5495     FontView *fv = (FontView *) fvb;
5496 
5497     if ( !fv->notactive )
5498 return;
5499 
5500     kf->first_fv->notactive = true;
5501     kf->second_fv->notactive = true;
5502     fv->notactive = false;
5503     kf->active = fv;
5504     GDrawSetUserData(kf->dw,fv);
5505     GDrawRequestExpose(kf->dw,NULL,false);
5506 }
5507 
kf_charEvent(struct fvcontainer * fvc,void * event)5508 static void kf_charEvent(struct fvcontainer *fvc,void *event) {
5509     struct kf_dlg *kf = (struct kf_dlg *) fvc;
5510     FVChar(kf->active,event);
5511 }
5512 
kf_doClose(struct fvcontainer * fvc)5513 static void kf_doClose(struct fvcontainer *fvc) {
5514     struct kf_dlg *kf = (struct kf_dlg *) fvc;
5515     kf->done = true;
5516 }
5517 
kf_doResize(struct fvcontainer * fvc,FontViewBase * fvb,int width,int height)5518 static void kf_doResize(struct fvcontainer *fvc,FontViewBase *fvb,
5519 	int width, int height) {
5520     struct kf_dlg *kf = (struct kf_dlg *) fvc;
5521     FontView *fv = (FontView *) fvb;
5522     FontView *otherfv = fv==kf->first_fv ? kf->second_fv : kf->first_fv;
5523     static int nested=0;
5524     GRect size;
5525 
5526     if ( fv->filled==NULL || otherfv->filled == NULL )
5527 return;		/* Not initialized yet */
5528     if ( nested )
5529 return;
5530     nested = 1;
5531     FVSetUIToMatch(otherfv,fv);
5532     nested = 0;
5533 
5534     memset(&size,0,sizeof(size));
5535     size.height = fv->mbh + kf->infoh + kf->fh+4 + 2*(height-fv->mbh) + kf->fh + 2;
5536     size.width = width;
5537     GGadgetSetDesiredSize(kf->guts,NULL, &size);
5538     GHVBoxFitWindow(kf->topbox);
5539 }
5540 
5541 static struct fvcontainer_funcs kernformat_funcs = {
5542     fvc_kernformat,
5543     true,			/* Modal dialog. No charviews, etc. */
5544     kf_activateMe,
5545     kf_charEvent,
5546     kf_doClose,
5547     kf_doResize
5548 };
5549 
kf_FVSetSize(struct kf_dlg * kf,FontView * fv,int width,int height,int y,int sbsize)5550 static void kf_FVSetSize(struct kf_dlg *kf,FontView *fv,int width, int height,
5551 	int y, int sbsize) {
5552     int cc, rc, topchar;
5553     GRect subsize;
5554 
5555     topchar = fv->rowoff*fv->colcnt;
5556     cc = (width-1) / fv->cbw;
5557     if ( cc<1 ) cc=1;
5558     rc = (height-1)/ fv->cbh;
5559     if ( rc<1 ) rc = 1;
5560     subsize.x = 0; subsize.y = 0;
5561     subsize.width = cc*fv->cbw + 1;
5562     subsize.height = rc*fv->cbh + 1;
5563     GDrawResize(fv->v,subsize.width,subsize.height);
5564     GDrawMove(fv->v,0,y);
5565     GGadgetMove(fv->vsb,subsize.width,y);
5566     GGadgetResize(fv->vsb,sbsize,subsize.height);
5567 
5568     fv->colcnt = cc; fv->rowcnt = rc;
5569     fv->width = subsize.width; fv->height = subsize.height;
5570     fv->rowltot = (fv->b.map->enccount+fv->colcnt-1)/fv->colcnt;
5571     GScrollBarSetBounds(fv->vsb,0,fv->rowltot,fv->rowcnt);
5572     fv->rowoff = topchar/fv->colcnt;
5573     if ( fv->rowoff>=fv->rowltot-fv->rowcnt )
5574         fv->rowoff = fv->rowltot-fv->rowcnt;
5575     if ( fv->rowoff<0 ) fv->rowoff =0;
5576     GScrollBarSetPos(fv->vsb,fv->rowoff);
5577 
5578     GDrawRequestExpose(fv->v,NULL,true);
5579 }
5580 
kf_sizeSet(struct kf_dlg * kf,GWindow dw)5581 static void kf_sizeSet(struct kf_dlg *kf,GWindow dw) {
5582     GRect size, gsize;
5583     int width, height, y;
5584 
5585     GDrawGetSize(dw,&size);
5586     GGadgetGetSize(kf->first_fv->vsb,&gsize);
5587     width = size.width - gsize.width;
5588     height = size.height - kf->mbh - kf->first_fv->infoh - 2*(kf->fh+4);
5589     height /= 2;
5590 
5591     y = kf->mbh + kf->first_fv->infoh + (kf->fh + 4);
5592     kf_FVSetSize(kf,kf->first_fv,width,height,y,gsize.width);
5593 
5594     kf->label2_y = y + height+2;
5595     y = kf->label2_y + kf->fh + 2;
5596     kf_FVSetSize(kf,kf->second_fv,width,height,y,gsize.width);
5597 }
5598 
kf_sub_e_h(GWindow pixmap,GEvent * event)5599 static int kf_sub_e_h(GWindow pixmap, GEvent *event) {
5600     FontView *active_fv;
5601     struct kf_dlg *kf;
5602 
5603     if ( event->type==et_destroy )
5604 return( true );
5605 
5606     active_fv = (FontView *) GDrawGetUserData(pixmap);
5607     kf = (struct kf_dlg *) (active_fv->b.container);
5608 
5609     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
5610 	    (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
5611 return( GGadgetDispatchEvent(active_fv->vsb,event));
5612     }
5613 
5614     switch ( event->type ) {
5615       case et_expose:
5616 	FVDrawInfo(active_fv,pixmap,event);
5617 	GDrawSetFont(pixmap, kf->first_fv->notactive? kf->plain : kf->bold );
5618 	GDrawDrawText8(pixmap,10,kf->mbh+kf->first_fv->infoh+kf->as,
5619 		_("Select glyphs for the first part of the kern pair"),-1,0x000000);
5620 	GDrawSetFont(pixmap, kf->second_fv->notactive? kf->plain : kf->bold );
5621 	GDrawDrawText8(pixmap,10,kf->label2_y+kf->as,
5622 		_("Select glyphs for the second part of the kern pair"),-1,0x000000);
5623       break;
5624       case et_char:
5625 	kf_charEvent(&kf->base,event);
5626       break;
5627       case et_mousedown:
5628 	if ( event->u.mouse.y<kf->mbh )
5629 return(false);
5630 	kf_activateMe(&kf->base,(struct fontviewbase *)
5631 		(( event->u.mouse.y > kf->label2_y ) ?
5632 			kf->second_fv : kf->first_fv) );
5633 return(false);
5634       break;
5635       case et_mouseup: case et_mousemove:
5636 return(false);
5637       case et_resize:
5638         kf_sizeSet(kf,pixmap);
5639       break;
5640     }
5641 return( true );
5642 }
5643 
5644 #undef CID_Separation
5645 #undef CID_MinKern
5646 #undef CID_Touched
5647 #undef CID_OnlyCloser
5648 #undef CID_Autokern
5649 #define CID_KPairs	1000
5650 #define CID_KClasses	1001
5651 #define CID_KCBuild	1002
5652 #define CID_Separation	1003
5653 #define CID_MinKern	1004
5654 #define CID_Touched	1005
5655 #define CID_ClassDistance	1006
5656 #define CID_Guts	1008
5657 #define CID_OnlyCloser	1009
5658 #define CID_Autokern	1010
5659 
5660 struct kf_results {
5661     int asked;
5662     int autokern;
5663     int autobuild;
5664     real good_enough;
5665     SplineChar **firstglyphs;
5666     SplineChar **secondglyphs;
5667 };
5668 
KF_FormatChange(GGadget * g,GEvent * e)5669 static int KF_FormatChange(GGadget *g, GEvent *e) {
5670     struct kf_dlg *kf;
5671     char mkbuf[10];
5672 
5673     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
5674 	kf = GDrawGetUserData(GGadgetGetWindow(g));
5675 	if ( GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_KPairs)) ) {
5676 	    GGadgetSetEnabled(GWidgetGetControl(kf->gw,CID_KCBuild),0);
5677 	    sprintf(mkbuf,"%d",15*(kf->sf->ascent+kf->sf->descent)/1000 );
5678 	    GGadgetSetTitle8(GWidgetGetControl(kf->gw,CID_MinKern),mkbuf);
5679 	} else {
5680 	    GGadgetSetEnabled(GWidgetGetControl(kf->gw,CID_KCBuild),1);
5681 	    GGadgetSetTitle8(GWidgetGetControl(kf->gw,CID_MinKern),"0");
5682 	}
5683     }
5684 return( true );
5685 }
5686 
SelectedGlyphs(FontView * _fv)5687 static SplineChar **SelectedGlyphs(FontView *_fv) {
5688     FontViewBase *fv = (FontViewBase *) _fv;
5689     SplineFont *sf;
5690     EncMap *map;
5691     int selcnt;
5692     int enc,gid;
5693     SplineChar **glyphlist, *sc;
5694 
5695     map = fv->map;
5696     sf = fv->sf;
5697     selcnt=0;
5698     for ( enc=0; enc<map->enccount; ++enc ) {
5699 	if ( fv->selected[enc] && (gid=map->map[enc])!=-1 &&
5700 		SCWorthOutputting(sf->glyphs[gid]))
5701 	    ++selcnt;
5702     }
5703     if ( selcnt<1 ) {
5704 	ff_post_error(_("No selection"), _("Please select some glyphs in the font views at the bottom of the dialog for FontForge to put into classes."));
5705 return(NULL);
5706     }
5707 
5708     glyphlist = malloc((selcnt+1)*sizeof(SplineChar *));
5709     selcnt=0;
5710     for ( enc=0; enc<map->enccount; ++enc ) {
5711 	if ( fv->selected[enc] && (gid=map->map[enc])!=-1 &&
5712 		SCWorthOutputting(sc = sf->glyphs[gid]))
5713 	    glyphlist[selcnt++] = sc;
5714     }
5715     glyphlist[selcnt] = NULL;
5716 return( glyphlist );
5717 }
5718 
KF_OK(GGadget * g,GEvent * e)5719 static int KF_OK(GGadget *g, GEvent *e) {
5720 
5721     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
5722 	struct kf_dlg *kf = GDrawGetUserData(GGadgetGetWindow(g));
5723 	int touch, separation, minkern, err, onlyCloser, autokern;
5724 	real good_enough=0;
5725 	int isclass, autobuild=0;
5726 	struct kf_results *results = kf->results;
5727 
5728 	err = false;
5729 	touch = GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_Touched));
5730 	separation = GetInt8(kf->gw,CID_Separation,_("Separation"),&err);
5731 	minkern = GetInt8(kf->gw,CID_MinKern,_("Min Kern"),&err);
5732 	onlyCloser = GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_OnlyCloser));
5733 	autokern = GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_Autokern));
5734 	if ( err )
5735 return( true );
5736 
5737 	isclass = GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_KClasses));
5738 	if ( isclass ) {
5739 	    autobuild = GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_KCBuild));
5740 	    good_enough = GetReal8(kf->gw,CID_ClassDistance,_("Intra Class Distance"),&err);
5741 	    if ( err )
5742 return( true );
5743 	}
5744 	if ( autobuild || autokern ) {
5745 	    results->firstglyphs = SelectedGlyphs(kf->first_fv);
5746 	    if ( results->firstglyphs == NULL )
5747 return( true );
5748 	    results->secondglyphs = SelectedGlyphs(kf->second_fv);
5749 	    if ( results->secondglyphs == NULL ) {
5750 		free(results->firstglyphs); results->firstglyphs=NULL;
5751 return( true );
5752 	    }
5753 	}
5754 	kf->sub->separation = separation;
5755 	kf->sub->minkern = minkern;
5756 	kf->sub->kerning_by_touch = touch;
5757 	kf->sub->onlyCloser = onlyCloser;
5758 	kf->sub->dontautokern = !autokern;
5759 	results->good_enough = good_enough;
5760 	if ( !isclass )
5761 	    results->asked = 0;
5762 	else
5763 	    results->asked = 1;
5764 	results->autobuild = autobuild;
5765 	results->autokern = autokern;
5766 	kf->done = true;
5767     }
5768 return( true );
5769 }
5770 
KF_Cancel(GGadget * g,GEvent * e)5771 static int KF_Cancel(GGadget *g, GEvent *e) {
5772     struct kf_dlg *kf;
5773 
5774     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
5775 	kf = GDrawGetUserData(GGadgetGetWindow(g));
5776 	kf->done = true;
5777     }
5778 return( true );
5779 }
5780 
kf_e_h(GWindow gw,GEvent * event)5781 static int kf_e_h(GWindow gw, GEvent *event) {
5782     struct kf_dlg *kf = GDrawGetUserData(gw);
5783 
5784     switch ( event->type ) {
5785       case et_close:
5786 	kf->done = true;
5787       break;
5788       case et_char:
5789 return( false );
5790       break;
5791     }
5792 return( true );
5793 }
5794 
kern_format_dlg(SplineFont * sf,int def_layer,struct lookup_subtable * sub,struct kf_results * results)5795 static int kern_format_dlg( SplineFont *sf, int def_layer,
5796 	struct lookup_subtable *sub, struct kf_results *results ) {
5797     GRect pos;
5798     GWindowAttrs wattrs;
5799     GGadgetCreateData gcd[16], boxes[7], hbox;
5800     GGadgetCreateData *varray[21], *h2array[6], *h3array[6], *h4array[8], *buttonarray[8], *h5array[4];
5801     GTextInfo label[15];
5802     char sepbuf[40], mkbuf[40], distancebuf[40];
5803     struct kf_dlg kf;
5804     int i,j, guts_row;
5805     /* Returns are 0=>Pairs, 1=>Classes, 2=>Cancel */
5806     FontRequest rq;
5807     int as, ds, ld;
5808     static GFont *plainfont = NULL, *boldfont=NULL;
5809 
5810     if ( sub->separation==0 && !sub->kerning_by_touch ) {
5811 	sub->separation = sf->width_separation;
5812 	if ( sf->width_separation==0 )
5813 	    sub->separation = 15*(sf->ascent+sf->descent)/100;
5814 	sub->minkern = sub->separation/10;
5815     }
5816 
5817     memset(&wattrs,0,sizeof(wattrs));
5818     memset(&gcd,0,sizeof(gcd));
5819     memset(&boxes,0,sizeof(boxes));
5820     memset(&label,0,sizeof(label));
5821     memset(&kf,0,sizeof(kf));
5822 
5823     kf.base.funcs = &kernformat_funcs;
5824     kf.sub = sub;
5825     kf.results = results;
5826     kf.sf = sf;
5827     kf.def_layer = def_layer;
5828     results->asked = 2;			/* Cancel */
5829 
5830     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
5831     wattrs.event_masks = ~(1<<et_charup);
5832     wattrs.restrict_input_to_me = true;
5833     wattrs.undercursor = 1;
5834     wattrs.cursor = ct_pointer;
5835     wattrs.utf8_window_title = _("Kerning format") ;
5836     wattrs.is_dlg = true;
5837     pos.x = pos.y = 0;
5838     pos.width = 100;
5839     pos.height = 100;
5840     kf.gw = GDrawCreateTopWindow(NULL,&pos,kf_e_h,&kf,&wattrs);
5841 
5842     if ( plainfont==NULL ) {
5843 	memset(&rq,0,sizeof(rq));
5844 	rq.utf8_family_name = SANS_UI_FAMILIES;
5845 	rq.point_size = 12;
5846 	rq.weight = 400;
5847 	plainfont = GDrawInstanciateFont(NULL,&rq);
5848 	plainfont = GResourceFindFont("KernFormat.Font",plainfont);
5849 	GDrawDecomposeFont(plainfont, &rq);
5850 	rq.weight = 700;
5851 	boldfont = GDrawInstanciateFont(NULL,&rq);
5852 	boldfont = GResourceFindFont("KernFormat.BoldFont",boldfont);
5853     }
5854     kf.plain = plainfont; kf.bold = boldfont;
5855     GDrawWindowFontMetrics(kf.gw,kf.plain,&as,&ds,&ld);
5856     kf.fh = as+ds; kf.as = as;
5857 
5858     i = j = 0;
5859 
5860     label[i].text = (unichar_t *) _("Use individual kerning pairs");
5861     label[i].text_is_1byte = true;
5862     label[i].text_in_resource = true;
5863     gcd[i].gd.label = &label[i];
5864     gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
5865     gcd[i].gd.popup_msg = _(
5866 	"In this format you specify every kerning pair in which\n"
5867 	"you are interested in." );
5868     gcd[i].gd.cid = CID_KPairs;
5869     gcd[i].gd.handle_controlevent = KF_FormatChange;
5870     gcd[i].creator = GRadioCreate;
5871     varray[j++] = &gcd[i++]; varray[j++] = NULL;
5872 
5873     label[i].text = (unichar_t *) _("Use a matrix of kerning classes");
5874     label[i].text_is_1byte = true;
5875     label[i].text_in_resource = true;
5876     gcd[i].gd.label = &label[i];
5877     gcd[i].gd.flags = gg_enabled|gg_visible|gg_rad_continueold;
5878     gcd[i].gd.popup_msg = _(
5879 	"In this format you define a series of glyph classes and\n"
5880 	"specify a matrix showing how each class interacts with all\n"
5881 	"the others.");
5882     gcd[i].gd.cid = CID_KClasses;
5883     gcd[i].gd.handle_controlevent = KF_FormatChange;
5884     gcd[i].creator = GRadioCreate;
5885     varray[j++] = &gcd[i++]; varray[j++] = NULL;
5886 
5887     label[i].text = (unichar_t *) _("FontForge will guess kerning classes for selected glyphs");
5888     label[i].text_is_1byte = true;
5889     label[i].text_in_resource = true;
5890     gcd[i].gd.label = &label[i];
5891     gcd[i].gd.flags = gg_visible|gg_cb_on;
5892     gcd[i].gd.popup_msg = _(
5893 	"FontForge will look at the glyphs selected in the font view\n"
5894 	"and will try to find groups of glyphs which are most alike\n"
5895 	"and generate kerning classes based on that information." );
5896     gcd[i].gd.cid = CID_KCBuild;
5897     gcd[i].creator = GCheckBoxCreate;
5898     h2array[0] = GCD_HPad10; h2array[1] = &gcd[i++]; h2array[2] = GCD_Glue; h2array[3] = NULL;
5899     boxes[3].gd.flags = gg_enabled|gg_visible;
5900     boxes[3].gd.u.boxelements = h2array;
5901     boxes[3].creator = GHBoxCreate;
5902     varray[j++] = &boxes[3]; varray[j++] = NULL;
5903 
5904     label[i].text = (unichar_t *) _("Intra Class Distance:");
5905     label[i].text_is_1byte = true;
5906     label[i].text_in_resource = true;
5907     gcd[i].gd.label = &label[i];
5908     gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5909     gcd[i].gd.flags = gg_enabled|gg_visible;
5910     gcd[i].gd.popup_msg = _(
5911 	"This is roughly (very roughly) the number off em-units\n"
5912 	"of error that two glyphs may have to belong in the same\n"
5913 	"class. This error is taken by comparing the two glyphs\n"
5914 	"to all other glyphs and summing the differences.\n"
5915 	"A small number here (like 2) means lots of small classes,\n"
5916 	"while a larger number (like 20) will mean fewer classes,\n"
5917 	"each with more glyphs." );
5918     gcd[i].creator = GLabelCreate;
5919     h3array[0] = GCD_HPad10; h3array[1] = &gcd[i++];
5920 
5921     sprintf( distancebuf, "%g", (sf->ascent+sf->descent)/100. );
5922     label[i].text = (unichar_t *) distancebuf;
5923     label[i].text_is_1byte = true;
5924     label[i].text_in_resource = true;
5925     gcd[i].gd.label = &label[i];
5926     gcd[i].gd.pos.width = 50;
5927     gcd[i].gd.flags = gg_enabled|gg_visible;
5928     gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
5929     gcd[i].gd.cid = CID_ClassDistance;
5930     gcd[i].creator = GTextFieldCreate;
5931     h3array[2] = &gcd[i++]; h3array[3] = GCD_Glue; h3array[4] = NULL;
5932 
5933     boxes[5].gd.flags = gg_enabled|gg_visible;
5934     boxes[5].gd.u.boxelements = h3array;
5935     boxes[5].creator = GHBoxCreate;
5936     varray[j++] = &boxes[5]; varray[j++] = NULL;
5937     varray[j++] = GCD_Glue; varray[j++] = NULL;
5938 
5939 	label[i].text = (unichar_t *) _("_Default Separation:");
5940 	label[i].text_is_1byte = true;
5941 	label[i].text_in_resource = true;
5942 	gcd[i].gd.label = &label[i];
5943 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5944 	gcd[i].gd.flags = gg_enabled|gg_visible;
5945 	gcd[i].gd.popup_msg = _(
5946 	    "Add entries to the lookup trying to make the optical\n"
5947 	    "separation between all pairs of glyphs equal to this\n"
5948 	    "value." );
5949 	gcd[i].creator = GLabelCreate;
5950 	h4array[0] = &gcd[i++];
5951 
5952 	sprintf( sepbuf, "%d", sub->separation );
5953 	label[i].text = (unichar_t *) sepbuf;
5954 	label[i].text_is_1byte = true;
5955 	label[i].text_in_resource = true;
5956 	gcd[i].gd.label = &label[i];
5957 	gcd[i].gd.pos.width = 50;
5958 	gcd[i].gd.flags = gg_enabled|gg_visible;
5959 	gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
5960 	gcd[i].gd.cid = CID_Separation;
5961 	gcd[i].creator = GTextFieldCreate;
5962 	h4array[1] = &gcd[i++]; h4array[2] = GCD_Glue;
5963 
5964 	label[i].text = (unichar_t *) _("_Min Kern:");
5965 	label[i].text_is_1byte = true;
5966 	label[i].text_in_resource = true;
5967 	gcd[i].gd.label = &label[i];
5968 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5969 	gcd[i].gd.flags = gg_enabled|gg_visible;
5970 	gcd[i].gd.popup_msg = _(
5971 	    "Any computed kerning change whose absolute value is less\n"
5972 	    "that this will be ignored.\n" );
5973 	gcd[i].creator = GLabelCreate;
5974 	h4array[3] = &gcd[i++];
5975 
5976 	sprintf( mkbuf, "%d", sub->minkern );
5977 	label[i].text = (unichar_t *) mkbuf;
5978 	label[i].text_is_1byte = true;
5979 	label[i].text_in_resource = true;
5980 	gcd[i].gd.label = &label[i];
5981 	gcd[i].gd.pos.width = 50;
5982 	gcd[i].gd.flags = gg_enabled|gg_visible;
5983 	gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
5984 	gcd[i].gd.cid = CID_MinKern;
5985 	gcd[i].creator = GTextFieldCreate;
5986 	h4array[4] = &gcd[i++];
5987 
5988 	label[i].text = (unichar_t *) _("_Touching");
5989 	label[i].text_is_1byte = true;
5990 	label[i].text_in_resource = true;
5991 	gcd[i].gd.label = &label[i];
5992 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5993 	gcd[i].gd.flags = gg_enabled|gg_visible;
5994 	if ( sub->kerning_by_touch )
5995 	    gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
5996 	gcd[i].gd.popup_msg = _(
5997 	    "Normally kerning is based on achieving a constant (optical)\n"
5998 	    "separation between glyphs, but occasionally it is desirable\n"
5999 	    "to have a kerning table where the kerning is based on the\n"
6000 	    "closest approach between two glyphs (So if the desired separ-\n"
6001 	    "ation is 0 then the glyphs will actually be touching.");
6002 	gcd[i].gd.cid = CID_Touched;
6003 	gcd[i].creator = GCheckBoxCreate;
6004 	h4array[5] = &gcd[i++];
6005 
6006 	h4array[6] = GCD_Glue; h4array[7] = NULL;
6007 
6008 	boxes[4].gd.flags = gg_enabled|gg_visible;
6009 	boxes[4].gd.u.boxelements = h4array;
6010 	boxes[4].creator = GHBoxCreate;
6011 	varray[j++] = &boxes[4]; varray[j++] = NULL;
6012 
6013 	label[i].text = (unichar_t *) _("Only kern glyphs closer");
6014 	label[i].text_is_1byte = true;
6015 	label[i].text_in_resource = true;
6016 	gcd[i].gd.label = &label[i];
6017 	gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
6018 	if ( sub->onlyCloser )
6019 	    gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
6020 	gcd[i].gd.popup_msg = _(
6021 	    "When doing autokerning, only move glyphs closer together,\n"
6022 	    "so the kerning offset will be negative.");
6023 	gcd[i].gd.cid = CID_OnlyCloser;
6024 	gcd[i].creator = GCheckBoxCreate;
6025 	h5array[0] = &gcd[i++];
6026 
6027 	label[i].text = (unichar_t *) _("Autokern new entries");
6028 	label[i].text_is_1byte = true;
6029 	label[i].text_in_resource = true;
6030 	gcd[i].gd.label = &label[i];
6031 	gcd[i].gd.flags = gg_enabled|gg_visible;
6032 	if ( !sub->dontautokern )
6033 	    gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
6034 	gcd[i].gd.popup_msg = _(
6035 	    "When adding new entries provide default kerning values.");
6036 	gcd[i].gd.cid = CID_Autokern;
6037 	gcd[i].creator = GCheckBoxCreate;
6038 	h5array[1] = &gcd[i++]; h5array[2] = NULL;
6039 
6040 	memset(&hbox,0,sizeof(hbox));
6041 	hbox.gd.flags = gg_enabled|gg_visible;
6042 	hbox.gd.u.boxelements = h5array;
6043 	hbox.creator = GHBoxCreate;
6044 	varray[j++] = &hbox; varray[j++] = NULL;
6045 
6046     guts_row = j/2;
6047     gcd[i].gd.flags = gg_enabled|gg_visible;
6048     gcd[i].gd.cid = CID_Guts;
6049     gcd[i].gd.u.drawable_e_h = kf_sub_e_h;
6050     gcd[i].creator = GDrawableCreate;
6051     varray[j++] = &gcd[i++]; varray[j++] = NULL;
6052 
6053     gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
6054     label[i].text = (unichar_t *) _("_OK");
6055     label[i].text_is_1byte = true;
6056     label[i].text_in_resource = true;
6057     gcd[i].gd.label = &label[i];
6058     gcd[i].gd.handle_controlevent = KF_OK;
6059     gcd[i++].creator = GButtonCreate;
6060 
6061     gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
6062     label[i].text = (unichar_t *) _("_Cancel");
6063     label[i].text_is_1byte = true;
6064     label[i].text_in_resource = true;
6065     gcd[i].gd.label = &label[i];
6066     gcd[i].gd.handle_controlevent = KF_Cancel;
6067     gcd[i++].creator = GButtonCreate;
6068 
6069     buttonarray[0] = GCD_Glue; buttonarray[1] = &gcd[i-2]; buttonarray[2] = GCD_Glue;
6070     buttonarray[3] = GCD_Glue; buttonarray[4] = &gcd[i-1]; buttonarray[5] = GCD_Glue;
6071     buttonarray[6] = NULL;
6072     boxes[6].gd.flags = gg_enabled|gg_visible;
6073     boxes[6].gd.u.boxelements = buttonarray;
6074     boxes[6].creator = GHBoxCreate;
6075     varray[j++] = &boxes[6]; varray[j++] = NULL; varray[j++] = NULL;
6076 
6077     boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
6078     boxes[0].gd.flags = gg_enabled|gg_visible;
6079     boxes[0].gd.u.boxelements = varray;
6080     boxes[0].creator = GHVGroupCreate;
6081 
6082     GGadgetsCreate(kf.gw,boxes);
6083 
6084     GHVBoxSetExpandableRow(boxes[0].ret,guts_row);
6085     GHVBoxSetExpandableCol(boxes[3].ret,gb_expandglue);
6086     GHVBoxSetExpandableCol(boxes[4].ret,gb_expandglue);
6087     GHVBoxSetExpandableCol(boxes[5].ret,gb_expandglue);
6088     GHVBoxSetExpandableCol(boxes[6].ret,gb_expandgluesame);
6089 
6090     kf.topbox = boxes[0].ret;
6091     KFFontViewInits(&kf, GWidgetGetControl(kf.gw,CID_Guts));
6092     kf_activateMe((struct fvcontainer *) &kf,(struct fontviewbase *) kf.first_fv);
6093 
6094     GHVBoxFitWindow(boxes[0].ret);
6095     GDrawSetVisible(kf.gw,true);
6096     while ( !kf.done )
6097 	GDrawProcessOneEvent(NULL);
6098     FontViewFree(&kf.second_fv->b);
6099     FontViewFree(&kf.first_fv->b);
6100     GDrawSetUserData(kf.gw,NULL);
6101     GDrawDestroyWindow(kf.gw);
6102 return( results->asked );
6103 }
6104 
_LookupSubtableContents(SplineFont * sf,struct lookup_subtable * sub,struct subtable_data * sd,int def_layer)6105 void _LookupSubtableContents(SplineFont *sf, struct lookup_subtable *sub,
6106 	struct subtable_data *sd,int def_layer) {
6107     int lookup_type = sub->lookup->lookup_type;
6108     static int nested=0;
6109     extern int default_autokern_dlg;
6110 
6111     if ( (lookup_type == gsub_context || lookup_type == gsub_contextchain ||
6112 		lookup_type == gsub_reversecchain ||
6113 		lookup_type == gpos_context || lookup_type == gpos_contextchain) &&
6114 	    sub->fpst==NULL ) {
6115 	sub->fpst = chunkalloc(sizeof(FPST));
6116 	sub->fpst->type = lookup_type == gsub_context ? pst_contextsub :
6117 		lookup_type == gsub_contextchain ? pst_chainsub :
6118 		lookup_type == gsub_reversecchain ? pst_reversesub :
6119 		lookup_type == gpos_context ? pst_contextpos :
6120 		 pst_chainpos;
6121 	if ( lookup_type == gsub_reversecchain )
6122 	    sub->fpst->format = pst_reversecoverage;
6123 	sub->fpst->subtable = sub;
6124 	sub->fpst->next = sf->possub;
6125 	sf->possub = sub->fpst;
6126     } else if ( (lookup_type == morx_indic ||
6127 		lookup_type == morx_context ||
6128 		lookup_type == morx_insert ||
6129 		lookup_type == kern_statemachine) &&
6130 	    sub->sm==NULL ) {
6131 	sub->sm = chunkalloc(sizeof(ASM));
6132 	sub->sm->type = lookup_type == morx_indic ? asm_indic :
6133 		lookup_type == morx_context ? asm_context :
6134 		lookup_type == morx_insert ? asm_insert :
6135 		 asm_kern;
6136 	sub->sm->subtable = sub;
6137 	sub->sm->next = sf->sm;
6138 	sf->sm = sub->sm;
6139     } else if ( lookup_type==gpos_pair &&
6140 		sub->kc==NULL &&
6141 		!sub->per_glyph_pst_or_kern ) {
6142 	char *buts[5];
6143 	struct kf_results results;
6144 
6145 	memset(&results,0,sizeof(results));
6146 	if ( sd!=NULL && sd->flags&sdf_verticalkern )
6147 	    sub->vertical_kerning = true;
6148 	else if ( sd!=NULL && sd->flags&sdf_horizontalkern )
6149 	    sub->vertical_kerning = false;
6150 	else
6151 	    sub->vertical_kerning = VerticalKernFeature(sf,sub->lookup,true);
6152 
6153 	if ( sd!=NULL && (sd->flags&sdf_kernclass) )
6154 	    results.asked = 1;
6155 	else if ( sd!=NULL && (sd->flags&sdf_kernpair) )
6156 	    results.asked = 0;
6157 	else {
6158 	    if ( sub->vertical_kerning || nested || !default_autokern_dlg ) {
6159 		buts[0] = _("_Pairs"); buts[1] = _("C_lasses");
6160 		buts[2] = _("_Cancel"); buts[3]=NULL;
6161 		results.asked = gwwv_ask(_("Kerning format"),(const char **) buts,0,1,_("Kerning may be specified either by classes of glyphs\nor by pairwise combinations of individual glyphs.\nWhich do you want for this subtable?") );
6162 	    } else {
6163 		nested = 1;
6164 		kern_format_dlg(sf,def_layer,sub,&results);
6165 		nested = 0;
6166 	    }
6167 	    if ( results.asked==2 )
6168 return;
6169 	}
6170 	if ( results.asked==0 ) {
6171 	    sub->per_glyph_pst_or_kern = true;
6172 	    if ( results.autokern ) {
6173 		SplineChar **lefts, **rights;
6174 		if ( sub->lookup->lookup_flags & pst_r2l ) {
6175 		    lefts = results.secondglyphs;
6176 		    rights = results.firstglyphs;
6177 		} else {
6178 		    lefts = results.firstglyphs;
6179 		    rights = results.secondglyphs;
6180 		}
6181 		AutoKern2(sf, def_layer,lefts,rights,
6182 		    sub,
6183 		    /* If separation==0 and !touch then use default values */
6184 		    0,0,0,0, 0, NULL, NULL);
6185 	    }
6186 	} else {
6187 	    sub->kc = chunkalloc(sizeof(KernClass));
6188 	    if ( sub->vertical_kerning ) {
6189 		sub->kc->next = sf->vkerns;
6190 		sf->vkerns = sub->kc;
6191 	    } else {
6192 		sub->kc->next = sf->kerns;
6193 		sf->kerns = sub->kc;
6194 	    }
6195 	    sub->kc->subtable = sub;
6196 	    sub->kc->first_cnt = sub->kc->second_cnt = 1;
6197 	    sub->kc->firsts = calloc(1,sizeof(char *));
6198 	    sub->kc->seconds = calloc(1,sizeof(char *));
6199 	    sub->kc->offsets = calloc(1,sizeof(int16));
6200 	    sub->kc->adjusts = calloc(1,sizeof(DeviceTable));
6201 		/* Need to fix for Hebrew !!!! */
6202 	    if ( results.autobuild )
6203 		/* Specifying separation==0 and !touching means use default values */
6204 		AutoKern2BuildClasses(sf,def_layer,results.firstglyphs,
6205 			results.secondglyphs,sub,0,0,0,0,0,results.good_enough);
6206 	}
6207 	free(results.firstglyphs);
6208 	free(results.secondglyphs);
6209     }
6210 
6211     if ( sub->fpst && sf->fontinfo!=NULL ) {
6212 	ContextChainEdit(sf,sub->fpst,sf->fontinfo,NULL,def_layer);
6213     } else if ( sub->sm && sf->fontinfo!=NULL ) {
6214 	StateMachineEdit(sf,sub->sm,sf->fontinfo);
6215     } else if ( sub->kc!=NULL ) {
6216 	KernClassD(sub->kc,sf,def_layer,sub->vertical_kerning);
6217     } else if ( sub->lookup->lookup_type>=gpos_cursive &&
6218 	    sub->lookup->lookup_type<=gpos_mark2mark )
6219 	AnchorClassD(sf,sub,def_layer);
6220     else
6221 	PSTKernD(sf,sub,def_layer);
6222 }
6223 /******************************************************************************/
6224 /***************************   Add/Remove Language   **************************/
6225 /******************************************************************************/
StrNextLang(char ** _pt)6226 static uint32 StrNextLang(char **_pt) {
6227     unsigned char *pt = (unsigned char *) *_pt;
6228     unsigned char tag[4];
6229     int i;
6230 
6231     memset(tag,' ',4);
6232     while ( *pt==' ' || *pt==',' ) ++pt;
6233     if ( *pt=='\0' )
6234 return( 0 );
6235 
6236     for ( i=0; i<4 && *pt!='\0' && *pt!=','; ++i )
6237 	tag[i] = *pt++;
6238     while ( *pt==' ' ) ++pt;
6239     if ( *pt!='\0' && *pt!=',' )
6240 return( 0xffffffff );
6241     *_pt = (char *) pt;
6242 return( (tag[0]<<24) | (tag[1]<<16) | (tag[2]<<8) | tag[3] );
6243 }
6244 
lk_AddRm(struct lkdata * lk,int add_lang,uint32 script,char * langs)6245 static void lk_AddRm(struct lkdata *lk,int add_lang,uint32 script, char *langs) {
6246     int i,l;
6247     OTLookup *otl;
6248     FeatureScriptLangList *fl;
6249     struct scriptlanglist *sl;
6250     uint32 lang;
6251     char *pt;
6252 
6253     for ( i=0; i<lk->cnt; ++i ) {
6254 	if ( lk->all[i].deleted || !lk->all[i].selected )
6255     continue;
6256 	otl = lk->all[i].lookup;
6257 	for ( fl= otl->features; fl!=NULL; fl=fl->next ) {
6258 	    for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) if ( sl->script==script ) {
6259 		pt = langs;
6260 		while ( (lang = StrNextLang(&pt))!=0 ) {
6261 		    for ( l=sl->lang_cnt-1; l>=0; --l ) {
6262 			if ( ((l>=MAX_LANG) ? sl->morelangs[l-MAX_LANG] : sl->langs[l]) == lang )
6263 		    break;
6264 		    }
6265 		    if ( add_lang && l<0 ) {
6266 			if ( sl->lang_cnt<MAX_LANG ) {
6267 			    sl->langs[sl->lang_cnt++] = lang;
6268 			} else {
6269 			    sl->morelangs = realloc(sl->morelangs,(++sl->lang_cnt-MAX_LANG)*sizeof(uint32));
6270 			    sl->morelangs[sl->lang_cnt-MAX_LANG-1] = lang;
6271 			}
6272 		    } else if ( !add_lang && l>=0 ) {
6273 			--sl->lang_cnt;
6274 			while ( l<sl->lang_cnt ) {
6275 			    uint32 nlang = l+1>=MAX_LANG ? sl->morelangs[l+1-MAX_LANG] : sl->langs[l+1];
6276 			    if ( l>=MAX_LANG )
6277 				sl->morelangs[l-MAX_LANG] = nlang;
6278 			    else
6279 				sl->langs[l] = nlang;
6280 			    ++l;
6281 			}
6282 			if ( sl->lang_cnt==0 ) {
6283 			    sl->langs[0] = DEFAULT_LANG;
6284 			    sl->lang_cnt = 1;
6285 			}
6286 		    }
6287 		}
6288 	    }
6289 	}
6290     }
6291 }
6292 
6293 #define CID_ScriptTag	1001
6294 #define CID_Langs	1002
6295 
6296 struct addrmlang {
6297     GWindow gw;
6298     int done;
6299     int add_lang;
6300     struct lkdata *lk;
6301 };
6302 
ARL_OK(GGadget * g,GEvent * e)6303 static int ARL_OK(GGadget *g, GEvent *e) {
6304 
6305     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
6306 	struct addrmlang *arl = GDrawGetUserData(GGadgetGetWindow(g));
6307 	const unichar_t *spt;
6308 	char *langs, *pt;
6309 	int i;
6310 	unsigned char tag[4];
6311 	uint32 script_tag, lang;
6312 
6313 	memset(tag,' ',4);
6314 	spt = _GGadgetGetTitle(GWidgetGetControl(arl->gw,CID_ScriptTag));
6315 	while ( *spt==' ' ) ++spt;
6316 	if ( *spt=='\0' ) {
6317 	    ff_post_error(_("No Script Tag"), _("Please specify a 4 letter opentype script tag"));
6318 return( true );
6319 	}
6320 	for ( i=0; i<4 && *spt!='\0'; ++i )
6321 	    tag[i] = *spt++;
6322 	while ( *spt==' ' ) ++spt;
6323 	if ( *spt!='\0' ) {
6324 	    ff_post_error(_("Script Tag too long"), _("Please specify a 4 letter opentype script tag"));
6325 return( true );
6326 	}
6327 	script_tag = (tag[0]<<24) | (tag[1]<<16) | (tag[2]<<8) | tag[3];
6328 
6329 	pt = langs = GGadgetGetTitle8(GWidgetGetControl(arl->gw,CID_Langs));
6330 	while ( (lang = StrNextLang(&pt))!=0 ) {
6331 	    if ( lang==0xffffffff ) {
6332 		ff_post_error(_("Invalid language"), _("Please specify a comma separated list of 4 letter opentype language tags"));
6333 return( true );
6334 	    }
6335 	}
6336 
6337 	lk_AddRm(arl->lk,arl->add_lang,script_tag, langs);
6338 	free(langs);
6339 	arl->done = true;
6340     }
6341 return( true );
6342 }
6343 
ARL_Cancel(GGadget * g,GEvent * e)6344 static int ARL_Cancel(GGadget *g, GEvent *e) {
6345 
6346     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
6347 	struct addrmlang *arl = GDrawGetUserData(GGadgetGetWindow(g));
6348 	arl->done = true;
6349     }
6350 return( true );
6351 }
6352 
ARL_TagChanged(GGadget * g,GEvent * e)6353 static int ARL_TagChanged(GGadget *g, GEvent *e) {
6354     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged &&
6355 	    e->u.control.u.tf_changed.from_pulldown != -1 ) {
6356 	int which = e->u.control.u.tf_changed.from_pulldown;
6357 	int len;
6358 	GTextInfo **ti = GGadgetGetList(g,&len);
6359 	char tag[8];
6360 	uint32 tagval = (intpt) (ti[which]->userdata);
6361 
6362 	tag[0] = tagval>>24;
6363 	tag[1] = tagval>>16;
6364 	tag[2] = tagval>>8;
6365 	tag[3] = tagval;
6366 	tag[4] = 0;
6367 	GGadgetSetTitle8(g,tag);
6368     }
6369 return( true );
6370 }
6371 
arl_e_h(GWindow gw,GEvent * event)6372 static int arl_e_h(GWindow gw, GEvent *event) {
6373     struct addrmlang *arl = GDrawGetUserData(gw);
6374 
6375     switch ( event->type ) {
6376       case et_char:
6377 return( false );
6378       case et_close:
6379 	arl->done = true;
6380       break;
6381     }
6382 return( true );
6383 }
6384 
ScriptListOfFont(SplineFont * sf)6385 static GTextInfo *ScriptListOfFont(SplineFont *sf) {
6386     uint32 *ourscripts = SFScriptsInLookups(sf,-1);
6387     int i,j;
6388     GTextInfo *ti;
6389     char tag[8];
6390 
6391     if ( ourscripts==NULL || ourscripts[0]==0 ) {
6392 	free(ourscripts);
6393 	ourscripts = malloc(2*sizeof(uint32));
6394 	ourscripts[0] = DEFAULT_SCRIPT;
6395 	ourscripts[1] = 0;
6396     }
6397     for ( i=0; ourscripts[i]!=0; ++i );
6398     ti = calloc(i+1,sizeof(GTextInfo));
6399     for ( i=0; ourscripts[i]!=0; ++i ) {
6400 	ti[i].userdata = (void *) (intpt) ourscripts[i];
6401 	for ( j=0; scripts[j].text!=NULL; ++j) {
6402 	    if ( scripts[j].userdata == (void *) (intpt) ourscripts[i])
6403 	break;
6404 	}
6405 	if ( scripts[j].text!=NULL )
6406 	    ti[i].text = (unichar_t *) copy( (char *) scripts[j].text );
6407 	else {
6408 	    tag[0] = ourscripts[i]>>24;
6409 	    tag[1] = ourscripts[i]>>16;
6410 	    tag[2] = ourscripts[i]>>8;
6411 	    tag[3] = ourscripts[i]&0xff;
6412 	    tag[4] = 0;
6413 	    ti[i].text = (unichar_t *) copy( (char *) tag );
6414 	}
6415 	ti[i].text_is_1byte = true;
6416     }
6417     ti[0].selected = true;
6418     free(ourscripts);
6419 return( ti );
6420 }
6421 
AddRmLang(SplineFont * sf,struct lkdata * lk,int add_lang)6422 void AddRmLang(SplineFont *sf, struct lkdata *lk,int add_lang) {
6423     GRect pos;
6424     GWindow gw;
6425     GWindowAttrs wattrs;
6426     struct addrmlang arl;
6427     GGadgetCreateData gcd[14], *hvarray[4][3], *barray[8], boxes[3];
6428     GTextInfo label[14];
6429     int k;
6430     GEvent dummy;
6431 
6432     LookupUIInit();
6433 
6434     memset(&arl,0,sizeof(arl));
6435     arl.lk = lk;
6436     arl.add_lang = add_lang;
6437 
6438     memset(&wattrs,0,sizeof(wattrs));
6439     memset(&gcd,0,sizeof(gcd));
6440     memset(&boxes,0,sizeof(boxes));
6441     memset(&label,0,sizeof(label));
6442 
6443     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
6444     wattrs.event_masks = ~(1<<et_charup);
6445     wattrs.restrict_input_to_me = true;
6446     wattrs.undercursor = 1;
6447     wattrs.cursor = ct_pointer;
6448     wattrs.utf8_window_title = add_lang ? _("Add Language(s) to Script") : _("Remove Language(s) from Script");
6449     wattrs.is_dlg = true;
6450     pos.x = pos.y = 0;
6451     pos.width = 100;
6452     pos.height = 100;
6453     arl.gw = gw = GDrawCreateTopWindow(NULL,&pos,arl_e_h,&arl,&wattrs);
6454 
6455     k = 0;
6456 
6457     label[k].text = (unichar_t *) _("Script Tag:");
6458     label[k].text_is_1byte = true;
6459     gcd[k].gd.label = &label[k];
6460     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6461     gcd[k].gd.flags = gg_visible | gg_enabled;
6462     gcd[k++].creator = GLabelCreate;
6463     hvarray[0][0] = &gcd[k-1];
6464 
6465     gcd[k].gd.u.list = ScriptListOfFont(sf);
6466     gcd[k].gd.flags = gg_visible | gg_enabled | gg_list_alphabetic;
6467     gcd[k].gd.cid = CID_ScriptTag;
6468     gcd[k].gd.handle_controlevent = ARL_TagChanged;
6469     gcd[k++].creator = GListFieldCreate;
6470     hvarray[0][1] = &gcd[k-1]; hvarray[0][2] = NULL;
6471 
6472     label[k].text = (unichar_t *) _("Language Tag:");
6473     label[k].text_is_1byte = true;
6474     gcd[k].gd.label = &label[k];
6475     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6476     gcd[k].gd.flags = gg_visible | gg_enabled;
6477     gcd[k++].creator = GLabelCreate;
6478     hvarray[1][0] = &gcd[k-1];
6479 
6480     gcd[k].gd.u.list = languages;
6481     gcd[k].gd.cid = CID_Langs;
6482     gcd[k].gd.flags = gg_visible | gg_enabled | gg_list_alphabetic;
6483     gcd[k].gd.handle_controlevent = ARL_TagChanged;
6484     gcd[k++].creator = GListFieldCreate;
6485     hvarray[1][1] = &gcd[k-1]; hvarray[1][2] = NULL;
6486 
6487 
6488     label[k].text = (unichar_t *) _("_OK");
6489     label[k].text_is_1byte = true;
6490     label[k].text_in_resource = true;
6491     gcd[k].gd.label = &label[k];
6492     gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_default;
6493     gcd[k].gd.handle_controlevent = ARL_OK;
6494     gcd[k++].creator = GButtonCreate;
6495 
6496     label[k].text = (unichar_t *) _("_Cancel");
6497     label[k].text_is_1byte = true;
6498     label[k].text_in_resource = true;
6499     gcd[k].gd.label = &label[k];
6500     gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_cancel;
6501     gcd[k].gd.handle_controlevent = ARL_Cancel;
6502     gcd[k++].creator = GButtonCreate;
6503 
6504     barray[0] = barray[2] = barray[3] = barray[4] = barray[6] = GCD_Glue; barray[7] = NULL;
6505     barray[1] = &gcd[k-2]; barray[5] = &gcd[k-1];
6506 
6507     boxes[2].gd.flags = gg_enabled|gg_visible;
6508     boxes[2].gd.u.boxelements = barray;
6509     boxes[2].creator = GHBoxCreate;
6510 
6511     hvarray[2][0] = &boxes[2]; hvarray[2][1] = GCD_ColSpan; hvarray[2][2] = NULL;
6512     hvarray[3][0] = NULL;
6513 
6514     boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
6515     boxes[0].gd.flags = gg_enabled|gg_visible;
6516     boxes[0].gd.u.boxelements = hvarray[0];
6517     boxes[0].creator = GHVGroupCreate;
6518 
6519     GGadgetsCreate(gw,boxes);
6520 
6521     memset(&dummy,0,sizeof(dummy));
6522     dummy.type = et_controlevent;
6523     dummy.u.control.subtype = et_textchanged;
6524     dummy.u.control.u.tf_changed.from_pulldown = GGadgetGetFirstListSelectedItem(gcd[1].ret);
6525     ARL_TagChanged(gcd[1].ret,&dummy);
6526 
6527     GTextInfoListFree(gcd[1].gd.u.list);
6528 
6529     GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
6530 
6531     GHVBoxFitWindow(boxes[0].ret);
6532 
6533     GDrawSetVisible(gw,true);
6534     while ( !arl.done )
6535 	GDrawProcessOneEvent(NULL);
6536 
6537     GDrawDestroyWindow(gw);
6538 }
6539 
6540 /******************************************************************************/
6541 /****************************   Mass Glyph Rename   ***************************/
6542 /******************************************************************************/
6543 typedef struct massrenamedlg {
6544     GWindow gw;
6545     int done;
6546     FontView *fv;
6547 } MassRenameDlg;
6548 
6549 #undef CID_Suffix
6550 #define CID_SubTable		1001
6551 #define CID_Suffix		1002
6552 #define CID_StartName		1003
6553 #define CID_ReplaceSuffix	1004
6554 #define CID_Themselves		1005
6555 
MRD_OK(GGadget * g,GEvent * e)6556 static int MRD_OK(GGadget *g, GEvent *e) {
6557 
6558     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
6559 	MassRenameDlg *mrd = GDrawGetUserData(GGadgetGetWindow(g));
6560 	int sel_cnt, enc, enc_max = mrd->fv->b.map->enccount;
6561 	char *start_name, *suffix, *pt;
6562 	int enc_start;
6563 	SplineChar *sc, *sourcesc;
6564 	GTextInfo *subti;
6565 	struct lookup_subtable *sub;
6566 	PST *pst;
6567 	int themselves = GGadgetIsChecked(GWidgetGetControl(mrd->gw,CID_Themselves));
6568 	int rplsuffix = GGadgetIsChecked(GWidgetGetControl(mrd->gw,CID_ReplaceSuffix));
6569 
6570 	for ( enc=sel_cnt=0; enc<enc_max; ++enc ) if ( mrd->fv->b.selected[enc] )
6571 	    ++sel_cnt;
6572 	if ( !themselves ) {
6573 	    char *freeme = GGadgetGetTitle8(GWidgetGetControl(mrd->gw,CID_StartName));
6574 	    start_name = GlyphNameListDeUnicode(freeme);
6575 	    free(freeme);
6576 	    enc_start = SFFindSlot(mrd->fv->b.sf,mrd->fv->b.map,-1,start_name);
6577 	    if ( enc_start==-1 ) {
6578 		ff_post_error(_("No Start Glyph"), _("The encoding does not contain something named %.40s"), start_name );
6579 		free(start_name);
6580 return( true );
6581 	    }
6582 	    free( start_name );
6583 	    if ( enc_start+sel_cnt>=enc_max ) {
6584 		ff_post_error(_("Not enough glyphs"), _("There aren't enough glyphs in the encoding to name all the selected characters"));
6585 return( true );
6586 	    }
6587 	    for ( enc=enc_start; enc<enc_start+sel_cnt; ++enc ) if ( mrd->fv->b.selected[enc]) {
6588 		ff_post_error(_("Bad selection"), _("You may not rename any of the base glyphs, but your selection overlaps the set of base glyphs."));
6589 return( true );
6590 	    }
6591 	} else
6592 	    enc_start = 0;
6593 
6594 	sub = NULL;
6595 	subti = GGadgetGetListItemSelected(GWidgetGetControl(mrd->gw,CID_SubTable));
6596 	if ( subti!=NULL )
6597 	    sub = subti->userdata;
6598 	if ( sub==(struct lookup_subtable *)-1 )
6599 	    sub = NULL;
6600 	if ( sub!=NULL && themselves ) {
6601 	    ff_post_error(_("Can't specify a subtable here"), _("As the selected glyphs are also source glyphs, they will be renamed, so they can't act as source glyphs for a lookup."));
6602 return( true );
6603 	}
6604 
6605 	suffix = GGadgetGetTitle8(GWidgetGetControl(mrd->gw,CID_Suffix));
6606 	if ( *suffix=='\0' || (*suffix=='.' && suffix[1]=='\0')) {
6607 	    ff_post_error(_("Missing suffix"), _("If you don't specify a suffix, the glyphs don't get renamed."));
6608 	    free(suffix);
6609 return( true );
6610 	}
6611 	if ( *suffix!='.' ) {
6612 	    char *old = suffix;
6613 	    suffix = strconcat(".",suffix);
6614 	    free(old);
6615 	}
6616 
6617 	for ( enc=sel_cnt=0; enc<enc_max; ++enc ) if ( mrd->fv->b.selected[enc] ) {
6618 	    char *oldname;
6619 	    sourcesc = sc = SFMakeChar(mrd->fv->b.sf,mrd->fv->b.map,enc);
6620 	    if ( !themselves )
6621 		sourcesc = SFMakeChar(mrd->fv->b.sf,mrd->fv->b.map,enc_start+sel_cnt);
6622 	    oldname = sc->name;
6623 	    if ( rplsuffix && (pt=strchr(sourcesc->name,'.'))!=NULL ) {
6624 		char *name = malloc(pt-sourcesc->name+strlen(suffix)+2);
6625 		strcpy(name,sourcesc->name);
6626 		strcpy(name+(pt-sourcesc->name),suffix);
6627 		sc->name = name;
6628 	    } else
6629 		sc->name = strconcat(sourcesc->name,suffix);
6630 	    free(oldname);
6631 	    sc->unicodeenc = -1;
6632 	    if ( sub!=NULL ) {
6633 		/* There can only be one single subs with this subtable */
6634 		/*  attached to the source glyph */
6635 		for ( pst=sourcesc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
6636 		if ( pst==NULL ) {
6637 		    pst = chunkalloc(sizeof(PST));
6638 		    pst->next = sourcesc->possub;
6639 		    sourcesc->possub = pst;
6640 		    pst->subtable = sub;
6641 		    pst->type = pst_substitution;
6642 		}
6643 		free(pst->u.subs.variant);
6644 		pst->u.subs.variant = copy(sc->name);
6645 	    }
6646 	    ++sel_cnt;
6647 	}
6648 	free(suffix);
6649 	mrd->done = true;
6650     }
6651 return( true );
6652 }
6653 
MRD_Cancel(GGadget * g,GEvent * e)6654 static int MRD_Cancel(GGadget *g, GEvent *e) {
6655 
6656     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
6657 	MassRenameDlg *mrd = GDrawGetUserData(GGadgetGetWindow(g));
6658 	mrd->done = true;
6659     }
6660 return( true );
6661 }
6662 
MRD_SuffixChange(GGadget * g,GEvent * e)6663 static int MRD_SuffixChange(GGadget *g, GEvent *e) {
6664 
6665     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
6666 	MassRenameDlg *mrd = GDrawGetUserData(GGadgetGetWindow(g));
6667 	char *suffix = GGadgetGetTitle8(g);
6668 	int32 len;
6669 	int i;
6670 	GTextInfo **ti = GGadgetGetList(GWidgetGetControl(mrd->gw,CID_SubTable),&len);
6671 	struct lookup_subtable *sub;
6672 
6673 	for ( i=0; i<len; ++i ) {
6674 	    sub = ti[i]->userdata;
6675 	    if ( sub==NULL || sub==(struct lookup_subtable *) -1 )
6676 	continue;
6677 	    if ( sub->suffix==NULL )
6678 	continue;
6679 	    if ( strcmp(suffix,sub->suffix)==0 ) {
6680 		GGadgetSelectOneListItem(GWidgetGetControl(mrd->gw,CID_SubTable),i);
6681 return( true );
6682 	    }
6683 	}
6684     }
6685 return( true );
6686 }
6687 
MRD_SelectSubtable(MassRenameDlg * mrd,struct lookup_subtable * sub)6688 static void MRD_SelectSubtable(MassRenameDlg *mrd,struct lookup_subtable *sub) {
6689     int32 len;
6690     GTextInfo **ti = GGadgetGetList(GWidgetGetControl(mrd->gw,CID_SubTable),&len);
6691     int i, no_pos = -1;
6692 
6693     for ( i=0; i<len; ++i ) if ( !ti[i]->line ) {
6694 	if ( ti[i]->userdata == sub )
6695     break;
6696 	else if ( ti[i]->userdata == (void *) -1 )
6697 	    no_pos = i;
6698     }
6699     if ( i==len )
6700 	i = no_pos;
6701     if ( i!=-1 )
6702 	GGadgetSelectOneListItem(GWidgetGetControl(mrd->gw,CID_SubTable),i);
6703 }
6704 
MRD_Subtable(GGadget * g,GEvent * e)6705 static int MRD_Subtable(GGadget *g, GEvent *e) {
6706     MassRenameDlg *mrd = GDrawGetUserData(GGadgetGetWindow(g));
6707     GTextInfo *ti;
6708     struct lookup_subtable *sub;
6709 
6710     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
6711 	ti = GGadgetGetListItemSelected(g);
6712 	if ( ti!=NULL ) {
6713 	    if ( ti->userdata==NULL ) {
6714 		sub = SFNewLookupSubtableOfType(mrd->fv->b.sf,gsub_single,NULL,mrd->fv->b.active_layer);
6715 		if ( sub!=NULL )
6716 		    GGadgetSetList(g,SFSubtablesOfType(mrd->fv->b.sf,gsub_single,false,true),false);
6717 		MRD_SelectSubtable(mrd,sub);
6718 	    } else if ( (sub = ti->userdata) != (struct lookup_subtable *) -1 &&
6719 		    sub->suffix != NULL )
6720 		GGadgetSetTitle8(GWidgetGetControl(mrd->gw,CID_Suffix),sub->suffix);
6721 	}
6722     }
6723 return( true );
6724 }
6725 
MRD_GlyphNameCompletion(GGadget * t,int from_tab)6726 static unichar_t **MRD_GlyphNameCompletion(GGadget *t,int from_tab) {
6727     MassRenameDlg *mrd = GDrawGetUserData(GGadgetGetWindow(t));
6728     SplineFont *sf = mrd->fv->b.sf;
6729 
6730 return( SFGlyphNameCompletion(sf,t,from_tab,false));
6731 }
6732 
mrd_e_h(GWindow gw,GEvent * event)6733 static int mrd_e_h(GWindow gw, GEvent *event) {
6734     MassRenameDlg *mrd = GDrawGetUserData(gw);
6735 
6736     switch ( event->type ) {
6737       case et_char:
6738 return( false );
6739       case et_close:
6740 	mrd->done = true;
6741       break;
6742     }
6743 return( true );
6744 }
6745 
FVMassGlyphRename(FontView * fv)6746 void FVMassGlyphRename(FontView *fv) {
6747     GRect pos;
6748     GWindow gw;
6749     GWindowAttrs wattrs;
6750     MassRenameDlg mrd;
6751     GGadgetCreateData gcd[14], *hvarray[11][3], *barray[8], boxes[3];
6752     GTextInfo label[14];
6753     int i,k,subtablek, startnamek;
6754 
6755     memset(&mrd,0,sizeof(mrd));
6756     mrd.fv = fv;
6757 
6758     memset(&wattrs,0,sizeof(wattrs));
6759     memset(&gcd,0,sizeof(gcd));
6760     memset(&label,0,sizeof(label));
6761     memset(&boxes,0,sizeof(boxes));
6762 
6763     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
6764     wattrs.event_masks = ~(1<<et_charup);
6765     wattrs.restrict_input_to_me = true;
6766     wattrs.undercursor = 1;
6767     wattrs.cursor = ct_pointer;
6768     wattrs.utf8_window_title = _("Mass Glyph Rename");
6769     wattrs.is_dlg = true;
6770     pos.x = pos.y = 0;
6771     pos.width = 100;
6772     pos.height = 100;
6773     mrd.gw = gw = GDrawCreateTopWindow(NULL,&pos,mrd_e_h,&mrd,&wattrs);
6774 
6775     k = i = 0;
6776 
6777     label[k].text = (unichar_t *) _("Rename all glyphs in the selection");
6778     label[k].text_is_1byte = true;
6779     gcd[k].gd.label = &label[k];
6780     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6781     gcd[k].gd.flags = gg_visible | gg_enabled;
6782     gcd[k++].creator = GLabelCreate;
6783     hvarray[i][0] = &gcd[k-1]; hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
6784 
6785     label[k].text = (unichar_t *) _("By appending the suffix:");
6786     label[k].text_is_1byte = true;
6787     gcd[k].gd.label = &label[k];
6788     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6789     gcd[k].gd.flags = gg_visible | gg_enabled;
6790     gcd[k++].creator = GLabelCreate;
6791     hvarray[i][0] = &gcd[k-1];
6792 
6793     gcd[k].gd.flags = gg_visible | gg_enabled;
6794     gcd[k].gd.cid = CID_Suffix;
6795     gcd[k].gd.handle_controlevent = MRD_SuffixChange;
6796     gcd[k++].creator = GTextFieldCreate;
6797     hvarray[i][1] = &gcd[k-1]; hvarray[i++][2] = NULL;
6798 
6799     label[k].text = (unichar_t *) _("To their own names");
6800     label[k].text_is_1byte = true;
6801     gcd[k].gd.label = &label[k];
6802     gcd[k].gd.cid = CID_Themselves;
6803     gcd[k].gd.flags = gg_visible | gg_enabled | gg_cb_on;
6804     gcd[k++].creator = GRadioCreate;
6805     hvarray[i][0] = &gcd[k-1];
6806     hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
6807 
6808     label[k].text = (unichar_t *) _("To the glyph names starting at:");
6809     label[k].text_is_1byte = true;
6810     gcd[k].gd.label = &label[k];
6811     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6812     gcd[k].gd.flags = gg_visible | gg_enabled;
6813     gcd[k].gd.popup_msg = _("So if you type \"A\" here the first selected glyph would be named \"A.suffix\".\nThe second \"B.suffix\", and so on.");
6814     gcd[k++].creator = GRadioCreate;
6815     hvarray[i][0] = &gcd[k-1];
6816 
6817     startnamek = k;
6818     gcd[k].gd.flags = gg_visible | gg_enabled;
6819     gcd[k].gd.cid = CID_StartName;
6820     gcd[k].gd.popup_msg = _("So if you type \"A\" here the first selected glyph would be named \"A.suffix\".\nThe second \"B.suffix\", and so on.");
6821     gcd[k++].creator = GTextCompletionCreate;
6822     hvarray[i][1] = &gcd[k-1]; hvarray[i++][2] = NULL;
6823 
6824     label[k].text = (unichar_t *) _("If one of those glyphs already has a suffix");
6825     label[k].text_is_1byte = true;
6826     gcd[k].gd.label = &label[k];
6827     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6828     gcd[k].gd.flags = gg_visible | gg_enabled;
6829     gcd[k++].creator = GLabelCreate;
6830     hvarray[i][0] = &gcd[k-1]; hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
6831 
6832     label[k].text = (unichar_t *) _("Append to it");
6833     label[k].text_is_1byte = true;
6834     gcd[k].gd.label = &label[k];
6835     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6836     gcd[k].gd.flags = gg_visible | gg_enabled;
6837     gcd[k++].creator = GRadioCreate;
6838     hvarray[i][0] = &gcd[k-1];
6839 
6840     label[k].text = (unichar_t *) _("Replace it");
6841     label[k].text_is_1byte = true;
6842     gcd[k].gd.label = &label[k];
6843     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6844     gcd[k].gd.flags = gg_visible | gg_enabled | gg_cb_on;
6845     gcd[k].gd.cid = CID_ReplaceSuffix;
6846     gcd[k++].creator = GRadioCreate;
6847     hvarray[i][1] = &gcd[k-1]; hvarray[i++][2] = NULL;
6848 
6849     label[k].text = (unichar_t *) _("Optionally, add this mapping to the lookup subtable:");
6850     label[k].text_is_1byte = true;
6851     gcd[k].gd.label = &label[k];
6852     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6853     gcd[k].gd.flags = gg_visible | gg_enabled;
6854     gcd[k++].creator = GLabelCreate;
6855     hvarray[i][0] = &gcd[k-1]; hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
6856 
6857     subtablek = k;
6858     gcd[k].gd.flags = gg_enabled|gg_visible;
6859     gcd[k].gd.cid = CID_SubTable;
6860     gcd[k].gd.handle_controlevent = MRD_Subtable;
6861     gcd[k++].creator = GListButtonCreate;
6862     hvarray[i][0] = GCD_Glue; hvarray[i][1] = &gcd[k-1]; hvarray[i++][2] = NULL;
6863     hvarray[i][0] = hvarray[i][1] = GCD_Glue; hvarray[i++][2] = NULL;
6864 
6865     label[k].text = (unichar_t *) _("_OK");
6866     label[k].text_is_1byte = true;
6867     label[k].text_in_resource = true;
6868     gcd[k].gd.label = &label[k];
6869     gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_default;
6870     gcd[k].gd.handle_controlevent = MRD_OK;
6871     gcd[k++].creator = GButtonCreate;
6872 
6873     label[k].text = (unichar_t *) _("_Cancel");
6874     label[k].text_is_1byte = true;
6875     label[k].text_in_resource = true;
6876     gcd[k].gd.label = &label[k];
6877     gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_cancel;
6878     gcd[k].gd.handle_controlevent = MRD_Cancel;
6879     gcd[k++].creator = GButtonCreate;
6880 
6881     barray[0] = barray[2] = barray[3] = barray[4] = barray[6] = GCD_Glue; barray[7] = NULL;
6882     barray[1] = &gcd[k-2]; barray[5] = &gcd[k-1];
6883     hvarray[i][0] = &boxes[2]; hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
6884     hvarray[i][0] = NULL;
6885 
6886     boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
6887     boxes[0].gd.flags = gg_enabled|gg_visible;
6888     boxes[0].gd.u.boxelements = hvarray[0];
6889     boxes[0].creator = GHVGroupCreate;
6890 
6891     boxes[2].gd.flags = gg_enabled|gg_visible;
6892     boxes[2].gd.u.boxelements = barray;
6893     boxes[2].creator = GHBoxCreate;
6894 
6895     GGadgetsCreate(gw,boxes);
6896     GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
6897     GHVBoxSetExpandableRow(boxes[0].ret,gb_expandglue);
6898     GHVBoxSetExpandableCol(boxes[0].ret,1);
6899 
6900     GGadgetSetList(gcd[subtablek].ret,SFSubtablesOfType(fv->b.sf,gsub_single,false,true),false);
6901     GGadgetSelectOneListItem(gcd[subtablek].ret,0);
6902     GCompletionFieldSetCompletion(gcd[startnamek].ret,MRD_GlyphNameCompletion);
6903     GWidgetIndicateFocusGadget(GWidgetGetControl(gw,CID_Suffix));
6904 
6905     GHVBoxFitWindow(boxes[0].ret);
6906 
6907     GDrawSetVisible(gw,true);
6908     while ( !mrd.done )
6909 	GDrawProcessOneEvent(NULL);
6910 
6911     GDrawDestroyWindow(gw);
6912 }
6913 
6914 /* ************************************************************************** */
6915 /* ******************** Interface to FontInfo Lookup list ******************* */
6916 /* ************************************************************************** */
6917 
FI_SortInsertLookup(SplineFont * sf,OTLookup * newotl)6918 static void FI_SortInsertLookup(SplineFont *sf, OTLookup *newotl) {
6919     int isgpos = newotl->lookup_type>=gpos_start;
6920     int pos, i, k;
6921 
6922     if ( sf->fontinfo ) {
6923 	struct lkdata *lk = &sf->fontinfo->tables[isgpos];
6924 	pos = FeatureOrderId(isgpos,newotl->features);
6925 	if ( lk->cnt>=lk->max )
6926 	    lk->all = realloc(lk->all,(lk->max+=10)*sizeof(struct lkinfo));
6927 	for ( i=0; i<lk->cnt && FeatureOrderId(isgpos,lk->all[i].lookup->features)<pos; ++i );
6928 	for ( k=lk->cnt; k>i+1; --k )
6929 	    lk->all[k] = lk->all[k-1];
6930 	memset(&lk->all[k],0,sizeof(struct lkinfo));
6931 	lk->all[k].lookup = newotl;
6932 	++lk->cnt;
6933 	GFI_LookupScrollbars(sf->fontinfo,isgpos, true);
6934 	GFI_LookupEnableButtons(sf->fontinfo,isgpos);
6935     }
6936 }
6937 
6938 /* Before may be:
6939     * A lookup in into_sf, in which case insert new lookup before it
6940     * NULL               , in which case insert new lookup at end
6941     * -1                 , in which case insert new lookup at start
6942     * -2                 , try to guess a good position
6943 */
FI_OrderNewLookup(SplineFont * into_sf,OTLookup * otl,OTLookup * before)6944 static void FI_OrderNewLookup(SplineFont *into_sf,OTLookup *otl,OTLookup *before) {
6945     int isgpos = otl->lookup_type>=gpos_start;
6946     OTLookup **head = isgpos ? &into_sf->gpos_lookups : &into_sf->gsub_lookups;
6947     int i, k;
6948 
6949     if ( into_sf->fontinfo ) {
6950 	struct lkdata *lk = &into_sf->fontinfo->tables[isgpos];
6951 
6952 	if ( lk->cnt>=lk->max )
6953 	    lk->all = realloc(lk->all,(lk->max+=10)*sizeof(struct lkinfo));
6954 
6955 	if ( before == (OTLookup *) -2 )
6956 	    FI_SortInsertLookup(into_sf,otl);
6957 	else {
6958 	    if ( before == (OTLookup *) -1 || *head==NULL || *head==before ) {
6959 		i = 0;
6960 	    } else {
6961 		for ( i=0; i<lk->cnt && lk->all[i].lookup!=before; ++i );
6962 	    }
6963 	    for ( k=lk->cnt; k>i; --k )
6964 		lk->all[k] = lk->all[k-1];
6965 	    memset(&lk->all[i],0,sizeof(lk->all[i]));
6966 	    lk->all[i].lookup = otl;
6967 	    ++lk->cnt;
6968 	    GFI_LookupScrollbars(into_sf->fontinfo,isgpos, true);
6969 	    GFI_LookupEnableButtons(into_sf->fontinfo,isgpos);
6970 	}
6971     }
6972 }
6973 
FI_OTLookupCopyInto(SplineFont * into_sf,SplineFont * from_sf,OTLookup * from_otl,OTLookup * to_otl,int scnt,OTLookup * before)6974 static void FI_OTLookupCopyInto(SplineFont *into_sf,SplineFont *from_sf,
6975 	OTLookup *from_otl, OTLookup *to_otl, int scnt, OTLookup *before) {
6976     if ( into_sf->fontinfo ) {
6977 	int isgpos = from_otl->lookup_type>=gpos_start;
6978 	struct lkdata *lk = &into_sf->fontinfo->tables[isgpos];
6979 	struct lookup_subtable *sub;
6980 	int i;
6981 	for ( i=0; i<lk->cnt; ++i )
6982 	    if ( lk->all[i].lookup==to_otl )
6983 	break;
6984 	if ( i==lk->cnt ) {
6985 	    FI_OrderNewLookup(into_sf,to_otl,before);
6986 	    for ( i=0; i<lk->cnt; ++i )
6987 		if ( lk->all[i].lookup==to_otl )
6988 	    break;
6989 	}
6990 	lk->all[i].subtable_cnt = lk->all[i].subtable_max = scnt;
6991 	lk->all[i].subtables = calloc(scnt,sizeof(struct lksubinfo));
6992 	if ( scnt>0 )
6993 	    for ( sub=to_otl->subtables, scnt=0; sub!=NULL; sub=sub->next, ++scnt )
6994 		lk->all[i].subtables[scnt].subtable = sub;
6995     }
6996 }
6997 
6998 struct fi_interface gdraw_fi_interface = {
6999     FI_SortInsertLookup,
7000     FI_OTLookupCopyInto,
7001     FontInfoDestroy
7002 };
7003