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 = ∿
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