Line data Source code
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 : #include "autowidth2.h"
29 : #include "fontforgeui.h"
30 : #include "fvfonts.h"
31 : #include "lookups.h"
32 : #include "sfd.h"
33 : #include "splinefill.h"
34 : #include "splinesaveafm.h"
35 : #include "splineutil.h"
36 : #include "tottfgpos.h"
37 : #include <chardata.h>
38 : #include <utype.h>
39 : #include <ustring.h>
40 : #include <math.h>
41 : #include <locale.h>
42 : #include <stdlib.h>
43 : #include "ttf.h"
44 : #include <gkeysym.h>
45 : #include "gfile.h"
46 : #include "sfundo.h"
47 :
48 : int add_char_to_name_list = true;
49 : int default_autokern_dlg = true;
50 : int fontlevel_undo_limit = 10;
51 :
52 : void SFUntickAllPSTandKern(SplineFont *sf);
53 : int kernsLength( KernPair *kp );
54 :
55 : static int DEBUG = 1;
56 :
57 : /* ************************************************************************** */
58 : /* ******************************* UI routines ****************************** */
59 : /* ************************************************************************** */
60 :
61 0 : GTextInfo **SFLookupListFromType(SplineFont *sf, int lookup_type ) {
62 0 : int isgpos = (lookup_type>=gpos_start);
63 : int k, cnt;
64 : OTLookup *otl;
65 : GTextInfo **ti;
66 :
67 0 : for ( k=0; k<2; ++k ) {
68 0 : cnt = 0;
69 0 : for ( otl= isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
70 0 : if ( lookup_type==gsub_start || lookup_type==gpos_start ||
71 0 : otl->lookup_type == lookup_type ) {
72 0 : if ( k ) {
73 0 : ti[cnt] = calloc(1,sizeof(GTextInfo));
74 0 : ti[cnt]->userdata = (void *) otl;
75 0 : ti[cnt]->fg = ti[cnt]->bg = COLOR_DEFAULT;
76 0 : ti[cnt]->text = utf82u_copy(otl->lookup_name);
77 : }
78 0 : ++cnt;
79 : }
80 : }
81 0 : if ( !k )
82 0 : ti = calloc(cnt+2,sizeof(GTextInfo *));
83 : else
84 0 : ti[cnt] = calloc(1,sizeof(GTextInfo));
85 : }
86 0 : return( ti );
87 : }
88 :
89 0 : GTextInfo *SFLookupArrayFromType(SplineFont *sf, int lookup_type ) {
90 0 : int isgpos = (lookup_type>=gpos_start);
91 : int k, cnt;
92 : OTLookup *otl;
93 : GTextInfo *ti;
94 :
95 0 : for ( k=0; k<2; ++k ) {
96 0 : cnt = 0;
97 0 : for ( otl= isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
98 0 : if ( lookup_type==gsub_start || lookup_type==gpos_start ||
99 0 : otl->lookup_type == lookup_type ) {
100 0 : if ( k ) {
101 0 : ti[cnt].userdata = (void *) otl;
102 0 : ti[cnt].fg = ti[cnt].bg = COLOR_DEFAULT;
103 0 : ti[cnt].text = utf82u_copy(otl->lookup_name);
104 : }
105 0 : ++cnt;
106 : }
107 : }
108 0 : if ( !k )
109 0 : ti = calloc(cnt+2,sizeof(GTextInfo));
110 : }
111 0 : return( ti );
112 : }
113 :
114 0 : GTextInfo *SFLookupArrayFromMask(SplineFont *sf, int mask ) {
115 : int k, cnt;
116 : OTLookup *otl;
117 : GTextInfo *ti;
118 : int isgpos;
119 :
120 0 : for ( k=0; k<2; ++k ) {
121 0 : cnt = 0;
122 0 : for ( isgpos = 0; isgpos<2; ++isgpos ) {
123 0 : for ( otl= isgpos ? sf->gpos_lookups : sf->gsub_lookups ; otl!=NULL; otl=otl->next ) {
124 0 : int lmask = isgpos ? (gpos_single_mask<<(otl->lookup_type-gpos_single))
125 0 : : (gsub_single_mask<<(otl->lookup_type-gsub_single));
126 0 : if ( mask==0 || (mask&lmask)) {
127 0 : if ( k ) {
128 0 : ti[cnt].userdata = (void *) otl;
129 0 : ti[cnt].fg = ti[cnt].bg = COLOR_DEFAULT;
130 0 : ti[cnt].text_is_1byte = true;
131 0 : ti[cnt].text = (unichar_t *) copy(otl->lookup_name);
132 : }
133 0 : ++cnt;
134 : }
135 : }
136 : }
137 0 : if ( !k )
138 0 : ti = calloc(cnt+2,sizeof(GTextInfo ));
139 : }
140 0 : return( ti );
141 : }
142 :
143 : /* ************************************************************************** */
144 : /* ********************** Lookup dialog and subdialogs ********************** */
145 : /* ************************************************************************** */
146 :
147 : static GTextInfo gsub_lookuptypes[] = {
148 : { (unichar_t *) N_("Lookup Type|Unspecified"), NULL, 0, 0, (void *) ot_undef, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
149 : { (unichar_t *) N_("Single Substitution"), NULL, 0, 0, (void *) gsub_single, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
150 : { (unichar_t *) N_("Multiple Substitution"), NULL, 0, 0, (void *) gsub_multiple, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
151 : { (unichar_t *) N_("Alternate Substitution"), NULL, 0, 0, (void *) gsub_alternate, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
152 : { (unichar_t *) N_("Ligature Substitution"), NULL, 0, 0, (void *) gsub_ligature, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
153 : { (unichar_t *) N_("Contextual Substitution"), NULL, 0, 0, (void *) gsub_context, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
154 : { (unichar_t *) N_("Contextual Chaining Substitution"), NULL, 0, 0, (void *) gsub_contextchain, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
155 : { (unichar_t *) N_("Reverse Chaining Substitution"), NULL, 0, 0, (void *) gsub_reversecchain, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
156 : { NULL, NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0'}, /* Line */
157 : { (unichar_t *) N_("Mac Indic State Machine"), NULL, 0, 0, (void *) morx_indic, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
158 : { (unichar_t *) N_("Mac Contextual State Machine"), NULL, 0, 0, (void *) morx_context, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
159 : { (unichar_t *) N_("Mac Insertion State Machine"), NULL, 0, 0, (void *) morx_insert, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
160 : GTEXTINFO_EMPTY
161 : };
162 : static GTextInfo gpos_lookuptypes[] = {
163 : { (unichar_t *) N_("Lookup Type|Unspecified"), NULL, 0, 0, (void *) ot_undef, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
164 : { (unichar_t *) N_("Single Position"), NULL, 0, 0, (void *) gpos_single, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
165 : { (unichar_t *) N_("Pair Position (kerning)"), NULL, 0, 0, (void *) gpos_pair, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
166 : { (unichar_t *) N_("Cursive Position"), NULL, 0, 0, (void *) gpos_cursive, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
167 : { (unichar_t *) N_("Mark to Base Position"), NULL, 0, 0, (void *) gpos_mark2base, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
168 : { (unichar_t *) N_("Mark to Ligature Position"), NULL, 0, 0, (void *) gpos_mark2ligature, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
169 : { (unichar_t *) N_("Mark to Mark Position"), NULL, 0, 0, (void *) gpos_mark2mark, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
170 : { (unichar_t *) N_("Contextual Position"), NULL, 0, 0, (void *) gpos_context, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
171 : { (unichar_t *) N_("Contextual Chaining Position"), NULL, 0, 0, (void *) gpos_contextchain, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
172 : { NULL, NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0'}, /* Line */
173 : { (unichar_t *) N_("Mac Kerning State Machine"), NULL, 0, 0, (void *) kern_statemachine, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
174 : GTEXTINFO_EMPTY
175 : };
176 : static GTextInfo *lookuptypes[2] = { gsub_lookuptypes, gpos_lookuptypes };
177 :
178 : /* see also list in tottfgpos.c mapping code points to scripts */
179 : /* see also list in lookups.c for non-ui access to these data */
180 : GTextInfo scripts[] = {
181 : /* GT: See the long comment at "Property|New" */
182 : /* GT: The msgstr should contain a translation of "Arabic", ignore "Script|" */
183 : { (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'},
184 : { (unichar_t *) N_("Script|Aramaic"), NULL, 0, 0, (void *) CHR('a','r','a','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
185 : { (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'},
186 : { (unichar_t *) N_("Script|Avestan"), NULL, 0, 0, (void *) CHR('a','v','e','s'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
187 : { (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'},
188 : { (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'},
189 : { (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'},
190 : { (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'},
191 : { (unichar_t *) N_("Bliss Symbolics"), NULL, 0, 0, (void *) CHR('b','l','i','s'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
192 : { (unichar_t *) N_("Bopomofo"), NULL, 0, 0, (void *) CHR('b','o','p','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
193 : { (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'},
194 : { (unichar_t *) N_("Braille"), NULL, 0, 0, (void *) CHR('b','r','a','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
195 : { (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'},
196 : { (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'},
197 : { (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'},
198 : { (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'},
199 : { (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'},
200 : { (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'},
201 : { (unichar_t *) N_("Cirth"), NULL, 0, 0, (void *) CHR('c','i','r','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
202 : { (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'},
203 : { (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'},
204 : { (unichar_t *) N_("Cypro-Minoan"), NULL, 0, 0, (void *) CHR('c','p','r','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
205 : { (unichar_t *) N_("Cypriot syllabary"), NULL, 0, 0, (void *) CHR('c','p','m','n'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
206 : { (unichar_t *) N_("Cyrillic"), NULL, 0, 0, (void *) CHR('c','y','r','l'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
207 : { (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'},
208 : { (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'},
209 : { (unichar_t *) N_("Devanagari"), NULL, 0, 0, (void *) CHR('d','e','v','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
210 : { (unichar_t *) N_("Devanagari2"), NULL, 0, 0, (void *) CHR('d','e','v','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
211 : /* { (unichar_t *) N_("Egyptian demotic"), NULL, 0, 0, (void *) CHR('e','g','y','d'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'}, */
212 : /* { (unichar_t *) N_("Egyptian hieratic"), NULL, 0, 0, (void *) CHR('e','g','y','h'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'}, */
213 : /* GT: Someone asked if FontForge actually was prepared generate hieroglyph output */
214 : /* GT: because of this string. No. But OpenType and Unicode have placeholders for */
215 : /* GT: dealing with these scripts against the day someone wants to use them. So */
216 : /* GT: FontForge must be prepared to deal with those placeholders if nothing else. */
217 : /* { (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'}, */
218 : { (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'},
219 : { (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'},
220 : { (unichar_t *) N_("Glagolitic"), NULL, 0, 0, (void *) CHR('g','l','a','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
221 : { (unichar_t *) N_("Gothic"), NULL, 0, 0, (void *) CHR('g','o','t','h'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
222 : { (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'},
223 : { (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'},
224 : { (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'},
225 : { (unichar_t *) N_("Gurmukhi"), NULL, 0, 0, (void *) CHR('g','u','r','u'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
226 : { (unichar_t *) N_("Gurmukhi2"), NULL, 0, 0, (void *) CHR('g','u','r','2'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
227 : { (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'},
228 : { (unichar_t *) N_("Hangul"), NULL, 0, 0, (void *) CHR('h','a','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
229 : { (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'},
230 : { (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'},
231 : /* { (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'},*/
232 : /* { (unichar_t *) N_("Indus (Harappan)"), NULL, 0, 0, (void *) CHR('i','n','d','s'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},*/
233 : { (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'},
234 : /* { (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'},*/
235 : { (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'},
236 : { (unichar_t *) NU_("Kharoṣṭhī"), NULL, 0, 0, (void *) CHR('k','h','a','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
237 : { (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'},
238 : { (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'},
239 : { (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'},
240 : { (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'},
241 : { (unichar_t *) N_("Script|Lao") , NULL, 0, 0, (void *) CHR('l','a','o',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
242 : { (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'},
243 : { (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'},
244 : { (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'}, /* Not in ISO 15924 !!!!!, just guessing */
245 : { (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'},
246 : { (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'},
247 : { (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'},
248 : /* { (unichar_t *) N_("Mayan hieroglyphs"), NULL, 0, 0, (void *) CHR('m','a','y','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},*/
249 : { (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'},
250 : { (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'},
251 : { (unichar_t *) NU_("Mathematical Alphanumeric Symbols"), NULL, 0, 0, (void *) CHR('m','a','t','h'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
252 : { (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'},
253 : { (unichar_t *) N_("Musical"), NULL, 0, 0, (void *) CHR('m','u','s','i'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
254 : { (unichar_t *) N_("Script|Myanmar"), NULL, 0, 0, (void *) CHR('m','y','m','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
255 : { (unichar_t *) N_("N'Ko"), NULL, 0, 0, (void *) CHR('n','k','o',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
256 : { (unichar_t *) N_("Ogham"), NULL, 0, 0, (void *) CHR('o','g','a','m'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
257 : { (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'},
258 : { (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'},
259 : { (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'},
260 : { (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'},
261 : { (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'},
262 : { (unichar_t *) N_("Osmanya"), NULL, 0, 0, (void *) CHR('o','s','m','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
263 : { (unichar_t *) N_("Script|Pahlavi"), NULL, 0, 0, (void *) CHR('p','a','l','v'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
264 : { (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'},
265 : { (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'},
266 : { (unichar_t *) N_("Phaistos"), NULL, 0, 0, (void *) CHR('p','h','s','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
267 : { (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'},
268 : { (unichar_t *) N_("Rongorongo"), NULL, 0, 0, (void *) CHR('r','o','r','o'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
269 : { (unichar_t *) N_("Runic"), NULL, 0, 0, (void *) CHR('r','u','n','r'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
270 : { (unichar_t *) N_("Shavian"), NULL, 0, 0, (void *) CHR('s','h','a','w'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
271 : { (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'},
272 : { (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'},
273 : { (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'},
274 : { (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'},
275 : { (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'},
276 : { (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'},
277 : { (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'}, /* Not in ISO 15924 !!!!!, just guessing */
278 : { (unichar_t *) N_("Tai Lu"), NULL, 0, 0, (void *) CHR('t','a','l','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'}, /* Not in ISO 15924 !!!!!, just guessing */
279 : { (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'},
280 : { (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'},
281 : { (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'},
282 : { (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'},
283 : { (unichar_t *) N_("Tengwar"), NULL, 0, 0, (void *) CHR('t','e','n','g'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
284 : { (unichar_t *) N_("Thaana"), NULL, 0, 0, (void *) CHR('t','h','a','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
285 : { (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'},
286 : { (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'},
287 : { (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'},
288 : { (unichar_t *) N_("Script|Ugaritic"), NULL, 0, 0, (void *) CHR('u','g','r','t'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'}, /* Not in ISO 15924 !!!!!, just guessing */
289 : { (unichar_t *) N_("Script|Vai"), NULL, 0, 0, (void *) CHR('v','a','i',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
290 : /* { (unichar_t *) N_("Visible Speech"), NULL, 0, 0, (void *) CHR('v','i','s','p'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},*/
291 : { (unichar_t *) N_("Cuneiform, Ugaritic"), NULL, 0, 0, (void *) CHR('x','u','g','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
292 : { (unichar_t *) N_("Script|Yi") , NULL, 0, 0, (void *) CHR('y','i',' ',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
293 : /* { (unichar_t *) N_("Private Use Script 1") , NULL, 0, 0, (void *) CHR('q','a','a','a'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},*/
294 : /* { (unichar_t *) N_("Private Use Script 2") , NULL, 0, 0, (void *) CHR('q','a','a','b'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},*/
295 : /* { (unichar_t *) N_("Undetermined Script") , NULL, 0, 0, (void *) CHR('z','y','y','y'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},*/
296 : /* { (unichar_t *) N_("Uncoded Script") , NULL, 0, 0, (void *) CHR('z','z','z','z'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},*/
297 : GTEXTINFO_EMPTY
298 : };
299 :
300 : GTextInfo languages[] = {
301 : { (unichar_t *) N_("Abaza"), NULL, 0, 0, (void *) CHR('A','B','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
302 : { (unichar_t *) N_("Abkhazian"), NULL, 0, 0, (void *) CHR('A','B','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
303 : { (unichar_t *) N_("Adyghe"), NULL, 0, 0, (void *) CHR('A','D','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
304 : { (unichar_t *) N_("Afrikaans"), NULL, 0, 0, (void *) CHR('A','F','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
305 : { (unichar_t *) N_("Afar"), NULL, 0, 0, (void *) CHR('A','F','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
306 : { (unichar_t *) N_("Agaw"), NULL, 0, 0, (void *) CHR('A','G','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
307 : { (unichar_t *) N_("Alsatian"), NULL, 0, 0, (void *) CHR('A','L','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
308 : { (unichar_t *) N_("Altai"), NULL, 0, 0, (void *) CHR('A','L','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
309 : { (unichar_t *) N_("Americanist IPA"), NULL, 0, 0, (void *) CHR('A','M','P','H'), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
310 : /* GT: See the long comment at "Property|New" */
311 : /* GT: The msgstr should contain a translation of "Amharic", ignore "Lang|" */
312 : { (unichar_t *) N_("Lang|Amharic"), NULL, 0, 0, (void *) CHR('A','M','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
313 : { (unichar_t *) N_("Lang|Arabic"), NULL, 0, 0, (void *) CHR('A','R','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
314 : { (unichar_t *) N_("Aari"), NULL, 0, 0, (void *) CHR('A','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
315 : { (unichar_t *) N_("Arakanese"), NULL, 0, 0, (void *) CHR('A','R','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
316 : { (unichar_t *) N_("Assamese"), NULL, 0, 0, (void *) CHR('A','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
317 : { (unichar_t *) N_("Athapaskan"), NULL, 0, 0, (void *) CHR('A','T','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
318 : { (unichar_t *) N_("Lang|Avar"), NULL, 0, 0, (void *) CHR('A','V','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
319 : { (unichar_t *) N_("Awadhi"), NULL, 0, 0, (void *) CHR('A','W','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
320 : { (unichar_t *) N_("Aymara"), NULL, 0, 0, (void *) CHR('A','Y','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
321 : { (unichar_t *) N_("Azeri"), NULL, 0, 0, (void *) CHR('A','Z','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
322 : { (unichar_t *) N_("Badaga"), NULL, 0, 0, (void *) CHR('B','A','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
323 : { (unichar_t *) N_("Baghelkhandi"), NULL, 0, 0, (void *) CHR('B','A','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
324 : { (unichar_t *) N_("Balkar"), NULL, 0, 0, (void *) CHR('B','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
325 : { (unichar_t *) N_("Baule"), NULL, 0, 0, (void *) CHR('B','A','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
326 : { (unichar_t *) N_("Lang|Berber"), NULL, 0, 0, (void *) CHR('B','B','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
327 : { (unichar_t *) N_("Bench"), NULL, 0, 0, (void *) CHR('B','C','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
328 : { (unichar_t *) N_("Bible Cree"), NULL, 0, 0, (void *) CHR('B','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
329 : { (unichar_t *) N_("Belarussian"), NULL, 0, 0, (void *) CHR('B','E','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
330 : { (unichar_t *) N_("Bemba"), NULL, 0, 0, (void *) CHR('B','E','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
331 : { (unichar_t *) N_("Lang|Bengali"), NULL, 0, 0, (void *) CHR('B','E','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
332 : { (unichar_t *) N_("Bulgarian"), NULL, 0, 0, (void *) CHR('B','G','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
333 : { (unichar_t *) N_("Bhili"), NULL, 0, 0, (void *) CHR('B','H','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
334 : { (unichar_t *) N_("Bhojpuri"), NULL, 0, 0, (void *) CHR('B','H','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
335 : { (unichar_t *) N_("Bikol"), NULL, 0, 0, (void *) CHR('B','I','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
336 : { (unichar_t *) N_("Bilen"), NULL, 0, 0, (void *) CHR('B','I','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
337 : { (unichar_t *) N_("Blackfoot"), NULL, 0, 0, (void *) CHR('B','K','F',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
338 : { (unichar_t *) N_("Balochi"), NULL, 0, 0, (void *) CHR('B','L','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
339 : { (unichar_t *) N_("Balante"), NULL, 0, 0, (void *) CHR('B','L','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
340 : { (unichar_t *) N_("Balti"), NULL, 0, 0, (void *) CHR('B','L','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
341 : { (unichar_t *) N_("Bambara"), NULL, 0, 0, (void *) CHR('B','M','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
342 : { (unichar_t *) N_("Bamileke"), NULL, 0, 0, (void *) CHR('B','M','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
343 : { (unichar_t *) N_("Bosnian"), NULL, 0, 0, (void *) CHR('B','O','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
344 : { (unichar_t *) N_("Breton"), NULL, 0, 0, (void *) CHR('B','R','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
345 : { (unichar_t *) N_("Brahui"), NULL, 0, 0, (void *) CHR('B','R','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
346 : { (unichar_t *) N_("Braj Bhasha"), NULL, 0, 0, (void *) CHR('B','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
347 : { (unichar_t *) N_("Burmese"), NULL, 0, 0, (void *) CHR('B','R','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
348 : { (unichar_t *) N_("Bashkir"), NULL, 0, 0, (void *) CHR('B','S','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
349 : { (unichar_t *) N_("Beti"), NULL, 0, 0, (void *) CHR('B','T','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
350 : { (unichar_t *) N_("Catalan"), NULL, 0, 0, (void *) CHR('C','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
351 : { (unichar_t *) N_("Cebuano"), NULL, 0, 0, (void *) CHR('C','E','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
352 : { (unichar_t *) N_("Chechen"), NULL, 0, 0, (void *) CHR('C','H','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
353 : { (unichar_t *) N_("Chaha Gurage"), NULL, 0, 0, (void *) CHR('C','H','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
354 : { (unichar_t *) N_("Chattisgarhi"), NULL, 0, 0, (void *) CHR('C','H','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
355 : { (unichar_t *) N_("Chichewa"), NULL, 0, 0, (void *) CHR('C','H','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
356 : { (unichar_t *) N_("Chukchi"), NULL, 0, 0, (void *) CHR('C','H','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
357 : { (unichar_t *) N_("Chipewyan"), NULL, 0, 0, (void *) CHR('C','H','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
358 : { (unichar_t *) N_("Lang|Cherokee"), NULL, 0, 0, (void *) CHR('C','H','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
359 : { (unichar_t *) N_("Chuvash"), NULL, 0, 0, (void *) CHR('C','H','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
360 : { (unichar_t *) N_("Comorian"), NULL, 0, 0, (void *) CHR('C','M','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
361 : { (unichar_t *) N_("Lang|Coptic"), NULL, 0, 0, (void *) CHR('C','O','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
362 : { (unichar_t *) N_("Corsican"), NULL, 0, 0, (void *) CHR('C','O','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
363 : { (unichar_t *) N_("Cree"), NULL, 0, 0, (void *) CHR('C','R','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
364 : { (unichar_t *) N_("Carrier"), NULL, 0, 0, (void *) CHR('C','R','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
365 : { (unichar_t *) N_("Crimean Tatar"), NULL, 0, 0, (void *) CHR('C','R','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
366 : { (unichar_t *) N_("Church Slavonic"), NULL, 0, 0, (void *) CHR('C','S','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
367 : { (unichar_t *) N_("Czech"), NULL, 0, 0, (void *) CHR('C','S','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
368 : { (unichar_t *) N_("Danish"), NULL, 0, 0, (void *) CHR('D','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
369 : { (unichar_t *) N_("Dargwa"), NULL, 0, 0, (void *) CHR('D','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
370 : { (unichar_t *) N_("Lang|Default"), NULL, 0, 0, (void *) DEFAULT_LANG, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
371 : { (unichar_t *) N_("Woods Cree"), NULL, 0, 0, (void *) CHR('D','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
372 : { (unichar_t *) N_("German (Standard)"), NULL, 0, 0, (void *) CHR('D','E','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
373 : { (unichar_t *) N_("Dogri"), NULL, 0, 0, (void *) CHR('D','G','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
374 : { (unichar_t *) N_("Dhivehi (Obsolete)"), NULL, 0, 0, (void *) CHR('D','H','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
375 : { (unichar_t *) N_("Dhivehi"), NULL, 0, 0, (void *) CHR('D','I','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
376 : { (unichar_t *) N_("Djerma"), NULL, 0, 0, (void *) CHR('D','J','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
377 : { (unichar_t *) N_("Dangme"), NULL, 0, 0, (void *) CHR('D','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
378 : { (unichar_t *) N_("Dinka"), NULL, 0, 0, (void *) CHR('D','N','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
379 : { (unichar_t *) N_("Dari"), NULL, 0, 0, (void *) CHR('D','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
380 : { (unichar_t *) N_("Dungan"), NULL, 0, 0, (void *) CHR('D','U','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
381 : { (unichar_t *) N_("Dzongkha"), NULL, 0, 0, (void *) CHR('D','Z','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
382 : { (unichar_t *) N_("Ebira"), NULL, 0, 0, (void *) CHR('E','B','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
383 : { (unichar_t *) N_("Eastern Cree"), NULL, 0, 0, (void *) CHR('E','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
384 : { (unichar_t *) N_("Edo"), NULL, 0, 0, (void *) CHR('E','D','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
385 : { (unichar_t *) N_("Efik"), NULL, 0, 0, (void *) CHR('E','F','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
386 : { (unichar_t *) N_("Lang|Greek"), NULL, 0, 0, (void *) CHR('E','L','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
387 : { (unichar_t *) N_("English"), NULL, 0, 0, (void *) CHR('E','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
388 : { (unichar_t *) N_("Erzya"), NULL, 0, 0, (void *) CHR('E','R','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
389 : { (unichar_t *) N_("Spanish"), NULL, 0, 0, (void *) CHR('E','S','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
390 : { (unichar_t *) N_("Estonian"), NULL, 0, 0, (void *) CHR('E','T','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
391 : { (unichar_t *) N_("Basque"), NULL, 0, 0, (void *) CHR('E','U','Q',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
392 : { (unichar_t *) N_("Evenki"), NULL, 0, 0, (void *) CHR('E','V','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
393 : { (unichar_t *) N_("Even"), NULL, 0, 0, (void *) CHR('E','V','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
394 : { (unichar_t *) N_("Ewe"), NULL, 0, 0, (void *) CHR('E','W','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
395 : { (unichar_t *) N_("French Antillean"), NULL, 0, 0, (void *) CHR('F','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
396 : { (unichar_t *) N_("Lang|Farsi"), NULL, 0, 0, (void *) CHR('F','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
397 : { (unichar_t *) N_("Finnish"), NULL, 0, 0, (void *) CHR('F','I','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
398 : { (unichar_t *) N_("Fijian"), NULL, 0, 0, (void *) CHR('F','J','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
399 : { (unichar_t *) N_("Flemish"), NULL, 0, 0, (void *) CHR('F','L','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
400 : { (unichar_t *) N_("Forest Nenets"), NULL, 0, 0, (void *) CHR('F','N','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
401 : { (unichar_t *) N_("Fon"), NULL, 0, 0, (void *) CHR('F','O','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
402 : { (unichar_t *) N_("Faroese"), NULL, 0, 0, (void *) CHR('F','O','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
403 : { (unichar_t *) N_("French (Standard)"), NULL, 0, 0, (void *) CHR('F','R','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
404 : { (unichar_t *) N_("Frisian"), NULL, 0, 0, (void *) CHR('F','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
405 : { (unichar_t *) N_("Friulian"), NULL, 0, 0, (void *) CHR('F','R','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
406 : { (unichar_t *) N_("Futa"), NULL, 0, 0, (void *) CHR('F','T','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
407 : { (unichar_t *) N_("Fulani"), NULL, 0, 0, (void *) CHR('F','U','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
408 : { (unichar_t *) N_("Ga"), NULL, 0, 0, (void *) CHR('G','A','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
409 : { (unichar_t *) N_("Gaelic"), NULL, 0, 0, (void *) CHR('G','A','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
410 : { (unichar_t *) N_("Gagauz"), NULL, 0, 0, (void *) CHR('G','A','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
411 : { (unichar_t *) N_("Galician"), NULL, 0, 0, (void *) CHR('G','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
412 : { (unichar_t *) N_("Garshuni"), NULL, 0, 0, (void *) CHR('G','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
413 : { (unichar_t *) N_("Garhwali"), NULL, 0, 0, (void *) CHR('G','A','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
414 : { (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'},
415 : { (unichar_t *) N_("Gilyak"), NULL, 0, 0, (void *) CHR('G','I','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
416 : { (unichar_t *) N_("Gumuz"), NULL, 0, 0, (void *) CHR('G','M','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
417 : { (unichar_t *) N_("Gondi"), NULL, 0, 0, (void *) CHR('G','O','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
418 : { (unichar_t *) N_("Greenlandic"), NULL, 0, 0, (void *) CHR('G','R','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
419 : { (unichar_t *) N_("Garo"), NULL, 0, 0, (void *) CHR('G','R','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
420 : { (unichar_t *) N_("Guarani"), NULL, 0, 0, (void *) CHR('G','U','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
421 : { (unichar_t *) N_("Lang|Gujarati"), NULL, 0, 0, (void *) CHR('G','U','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
422 : { (unichar_t *) N_("Haitian"), NULL, 0, 0, (void *) CHR('H','A','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
423 : { (unichar_t *) N_("Halam"), NULL, 0, 0, (void *) CHR('H','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
424 : { (unichar_t *) N_("Harauti"), NULL, 0, 0, (void *) CHR('H','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
425 : { (unichar_t *) N_("Hausa"), NULL, 0, 0, (void *) CHR('H','A','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
426 : { (unichar_t *) N_("Hawaiin"), NULL, 0, 0, (void *) CHR('H','A','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
427 : { (unichar_t *) N_("Hammer-Banna"), NULL, 0, 0, (void *) CHR('H','B','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
428 : { (unichar_t *) N_("Hiligaynon"), NULL, 0, 0, (void *) CHR('H','I','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
429 : { (unichar_t *) N_("Hindi"), NULL, 0, 0, (void *) CHR('H','I','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
430 : { (unichar_t *) N_("High Mari"), NULL, 0, 0, (void *) CHR('H','M','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
431 : { (unichar_t *) N_("Hindko"), NULL, 0, 0, (void *) CHR('H','N','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
432 : { (unichar_t *) N_("Ho"), NULL, 0, 0, (void *) CHR('H','O',' ',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
433 : { (unichar_t *) N_("Harari"), NULL, 0, 0, (void *) CHR('H','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
434 : { (unichar_t *) N_("Croatian"), NULL, 0, 0, (void *) CHR('H','R','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
435 : { (unichar_t *) N_("Hungarian"), NULL, 0, 0, (void *) CHR('H','U','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
436 : { (unichar_t *) N_("Lang|Armenian"), NULL, 0, 0, (void *) CHR('H','Y','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
437 : { (unichar_t *) N_("Igbo"), NULL, 0, 0, (void *) CHR('I','B','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
438 : { (unichar_t *) N_("Ijo"), NULL, 0, 0, (void *) CHR('I','J','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
439 : { (unichar_t *) N_("Ilokano"), NULL, 0, 0, (void *) CHR('I','L','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
440 : { (unichar_t *) N_("Indonesian"), NULL, 0, 0, (void *) CHR('I','N','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
441 : { (unichar_t *) N_("Ingush"), NULL, 0, 0, (void *) CHR('I','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
442 : { (unichar_t *) N_("Inuktitut"), NULL, 0, 0, (void *) CHR('I','N','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
443 : { (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'},
444 : { (unichar_t *) N_("Irish"), NULL, 0, 0, (void *) CHR('I','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
445 : { (unichar_t *) N_("Irish Traditional"), NULL, 0, 0, (void *) CHR('I','R','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
446 : { (unichar_t *) N_("Icelandic"), NULL, 0, 0, (void *) CHR('I','S','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
447 : { (unichar_t *) N_("Inari Sami"), NULL, 0, 0, (void *) CHR('I','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
448 : { (unichar_t *) N_("Italian"), NULL, 0, 0, (void *) CHR('I','T','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
449 : { (unichar_t *) N_("Lang|Hebrew"), NULL, 0, 0, (void *) CHR('I','W','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
450 : { (unichar_t *) N_("Lang|Javanese"), NULL, 0, 0, (void *) CHR('J','A','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
451 : { (unichar_t *) N_("Yiddish"), NULL, 0, 0, (void *) CHR('J','I','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
452 : { (unichar_t *) N_("Japanese"), NULL, 0, 0, (void *) CHR('J','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
453 : { (unichar_t *) N_("Judezmo"), NULL, 0, 0, (void *) CHR('J','U','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
454 : { (unichar_t *) N_("Jula"), NULL, 0, 0, (void *) CHR('J','U','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
455 : { (unichar_t *) N_("Kabardian"), NULL, 0, 0, (void *) CHR('K','A','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
456 : { (unichar_t *) N_("Kachchi"), NULL, 0, 0, (void *) CHR('K','A','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
457 : { (unichar_t *) N_("Kalenjin"), NULL, 0, 0, (void *) CHR('K','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
458 : { (unichar_t *) N_("Lang|Kannada"), NULL, 0, 0, (void *) CHR('K','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
459 : { (unichar_t *) N_("Karachay"), NULL, 0, 0, (void *) CHR('K','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
460 : { (unichar_t *) N_("Lang|Georgian"), NULL, 0, 0, (void *) CHR('K','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
461 : { (unichar_t *) N_("Kazakh"), NULL, 0, 0, (void *) CHR('K','A','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
462 : { (unichar_t *) N_("Kebena"), NULL, 0, 0, (void *) CHR('K','E','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
463 : { (unichar_t *) N_("Khutsuri Georgian"), NULL, 0, 0, (void *) CHR('K','G','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
464 : { (unichar_t *) N_("Khakass"), NULL, 0, 0, (void *) CHR('K','H','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
465 : { (unichar_t *) N_("Khanty-Kazim"), NULL, 0, 0, (void *) CHR('K','H','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
466 : { (unichar_t *) N_("Lang|Khmer"), NULL, 0, 0, (void *) CHR('K','H','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
467 : { (unichar_t *) N_("Khanty-Shurishkar"), NULL, 0, 0, (void *) CHR('K','H','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
468 : { (unichar_t *) N_("Khanty-Vakhi"), NULL, 0, 0, (void *) CHR('K','H','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
469 : { (unichar_t *) N_("Khowar"), NULL, 0, 0, (void *) CHR('K','H','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
470 : { (unichar_t *) N_("Kikuyu"), NULL, 0, 0, (void *) CHR('K','I','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
471 : { (unichar_t *) N_("Kirghiz"), NULL, 0, 0, (void *) CHR('K','I','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
472 : { (unichar_t *) N_("Kisii"), NULL, 0, 0, (void *) CHR('K','I','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
473 : { (unichar_t *) N_("Kokni"), NULL, 0, 0, (void *) CHR('K','K','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
474 : { (unichar_t *) N_("Kalmyk"), NULL, 0, 0, (void *) CHR('K','L','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
475 : { (unichar_t *) N_("Kamba"), NULL, 0, 0, (void *) CHR('K','M','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
476 : { (unichar_t *) N_("Kumaoni"), NULL, 0, 0, (void *) CHR('K','M','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
477 : { (unichar_t *) N_("Komo"), NULL, 0, 0, (void *) CHR('K','M','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
478 : { (unichar_t *) N_("Komso"), NULL, 0, 0, (void *) CHR('K','M','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
479 : { (unichar_t *) N_("Kanuri"), NULL, 0, 0, (void *) CHR('K','N','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
480 : { (unichar_t *) N_("Kodagu"), NULL, 0, 0, (void *) CHR('K','O','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
481 : { (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'},
482 : { (unichar_t *) N_("Konkani"), NULL, 0, 0, (void *) CHR('K','O','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
483 : { (unichar_t *) N_("Kikongo"), NULL, 0, 0, (void *) CHR('K','O','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
484 : { (unichar_t *) N_("Komi-Permyak"), NULL, 0, 0, (void *) CHR('K','O','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
485 : { (unichar_t *) N_("Korean"), NULL, 0, 0, (void *) CHR('K','O','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
486 : { (unichar_t *) N_("Komi-Zyrian"), NULL, 0, 0, (void *) CHR('K','O','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
487 : { (unichar_t *) N_("Kpelle"), NULL, 0, 0, (void *) CHR('K','P','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
488 : { (unichar_t *) N_("Krio"), NULL, 0, 0, (void *) CHR('K','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
489 : { (unichar_t *) N_("Karakalpak"), NULL, 0, 0, (void *) CHR('K','R','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
490 : { (unichar_t *) N_("Karelian"), NULL, 0, 0, (void *) CHR('K','R','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
491 : { (unichar_t *) N_("Karaim"), NULL, 0, 0, (void *) CHR('K','R','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
492 : { (unichar_t *) N_("Karen"), NULL, 0, 0, (void *) CHR('K','R','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
493 : { (unichar_t *) N_("Koorete"), NULL, 0, 0, (void *) CHR('K','R','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
494 : { (unichar_t *) N_("Kashmiri"), NULL, 0, 0, (void *) CHR('K','S','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
495 : { (unichar_t *) N_("Khasi"), NULL, 0, 0, (void *) CHR('K','S','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
496 : { (unichar_t *) N_("Kildin Sami"), NULL, 0, 0, (void *) CHR('K','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
497 : { (unichar_t *) N_("Kui"), NULL, 0, 0, (void *) CHR('K','U','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
498 : { (unichar_t *) N_("Kulvi"), NULL, 0, 0, (void *) CHR('K','U','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
499 : { (unichar_t *) N_("Kumyk"), NULL, 0, 0, (void *) CHR('K','U','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
500 : { (unichar_t *) N_("Kurdish"), NULL, 0, 0, (void *) CHR('K','U','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
501 : { (unichar_t *) N_("Kurukh"), NULL, 0, 0, (void *) CHR('K','U','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
502 : { (unichar_t *) N_("Kuy"), NULL, 0, 0, (void *) CHR('K','U','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
503 : { (unichar_t *) N_("Koryak"), NULL, 0, 0, (void *) CHR('K','Y','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
504 : { (unichar_t *) N_("Ladin"), NULL, 0, 0, (void *) CHR('L','A','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
505 : { (unichar_t *) N_("Lahuli"), NULL, 0, 0, (void *) CHR('L','A','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
506 : { (unichar_t *) N_("Lak"), NULL, 0, 0, (void *) CHR('L','A','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
507 : { (unichar_t *) N_("Lambani"), NULL, 0, 0, (void *) CHR('L','A','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
508 : { (unichar_t *) N_("Lang|Lao"), NULL, 0, 0, (void *) CHR('L','A','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
509 : { (unichar_t *) N_("Lang|Latin"), NULL, 0, 0, (void *) CHR('L','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
510 : { (unichar_t *) N_("Laz"), NULL, 0, 0, (void *) CHR('L','A','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
511 : { (unichar_t *) N_("L-Cree"), NULL, 0, 0, (void *) CHR('L','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
512 : { (unichar_t *) N_("Ladakhi"), NULL, 0, 0, (void *) CHR('L','D','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
513 : { (unichar_t *) N_("Lezgi"), NULL, 0, 0, (void *) CHR('L','E','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
514 : { (unichar_t *) N_("Lingala"), NULL, 0, 0, (void *) CHR('L','I','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
515 : { (unichar_t *) N_("Low Mari"), NULL, 0, 0, (void *) CHR('L','M','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
516 : { (unichar_t *) N_("Lang|Limbu"), NULL, 0, 0, (void *) CHR('L','M','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
517 : { (unichar_t *) N_("Lomwe"), NULL, 0, 0, (void *) CHR('L','M','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
518 : { (unichar_t *) N_("Lower Sorbian"), NULL, 0, 0, (void *) CHR('L','S','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
519 : { (unichar_t *) N_("Lule Sami"), NULL, 0, 0, (void *) CHR('L','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
520 : { (unichar_t *) N_("Lithuanian"), NULL, 0, 0, (void *) CHR('L','T','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
521 : { (unichar_t *) N_("Luxembourgish"), NULL, 0, 0, (void *) CHR('L','T','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
522 : { (unichar_t *) N_("Luba"), NULL, 0, 0, (void *) CHR('L','U','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
523 : { (unichar_t *) N_("Luganda"), NULL, 0, 0, (void *) CHR('L','U','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
524 : { (unichar_t *) N_("Luhya"), NULL, 0, 0, (void *) CHR('L','U','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
525 : { (unichar_t *) N_("Luo"), NULL, 0, 0, (void *) CHR('L','U','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
526 : { (unichar_t *) N_("Latvian"), NULL, 0, 0, (void *) CHR('L','V','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
527 : { (unichar_t *) N_("Majang"), NULL, 0, 0, (void *) CHR('M','A','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
528 : { (unichar_t *) N_("Makua"), NULL, 0, 0, (void *) CHR('M','A','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
529 : { (unichar_t *) N_("Malayalam Traditional"), NULL, 0, 0, (void *) CHR('M','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
530 : { (unichar_t *) N_("Mansi"), NULL, 0, 0, (void *) CHR('M','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
531 : { (unichar_t *) N_("Marathi"), NULL, 0, 0, (void *) CHR('M','A','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
532 : { (unichar_t *) N_("Mapudungun"), NULL, 0, 0, (void *) CHR('M','A','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
533 : { (unichar_t *) N_("Marwari"), NULL, 0, 0, (void *) CHR('M','A','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
534 : { (unichar_t *) N_("Mbundu"), NULL, 0, 0, (void *) CHR('M','B','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
535 : { (unichar_t *) N_("Lang|Manchu"), NULL, 0, 0, (void *) CHR('M','C','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
536 : { (unichar_t *) N_("Moose Cree"), NULL, 0, 0, (void *) CHR('M','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
537 : { (unichar_t *) N_("Mende"), NULL, 0, 0, (void *) CHR('M','D','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
538 : { (unichar_t *) N_("Me'en"), NULL, 0, 0, (void *) CHR('M','E','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
539 : { (unichar_t *) N_("Mizo"), NULL, 0, 0, (void *) CHR('M','I','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
540 : { (unichar_t *) N_("Macedonian"), NULL, 0, 0, (void *) CHR('M','K','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
541 : { (unichar_t *) N_("Male"), NULL, 0, 0, (void *) CHR('M','L','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
542 : { (unichar_t *) N_("Malagasy"), NULL, 0, 0, (void *) CHR('M','L','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
543 : { (unichar_t *) N_("Malinke"), NULL, 0, 0, (void *) CHR('M','L','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
544 : { (unichar_t *) N_("Malayalam Reformed"), NULL, 0, 0, (void *) CHR('M','L','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
545 : { (unichar_t *) N_("Malay"), NULL, 0, 0, (void *) CHR('M','L','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
546 : { (unichar_t *) N_("Mandinka"), NULL, 0, 0, (void *) CHR('M','N','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
547 : { (unichar_t *) N_("Lang|Mongolian"), NULL, 0, 0, (void *) CHR('M','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
548 : { (unichar_t *) N_("Manipuri"), NULL, 0, 0, (void *) CHR('M','N','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
549 : { (unichar_t *) N_("Maninka"), NULL, 0, 0, (void *) CHR('M','N','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
550 : { (unichar_t *) N_("Manx Gaelic"), NULL, 0, 0, (void *) CHR('M','N','X',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
551 : { (unichar_t *) N_("Mohawk"), NULL, 0, 0, (void *) CHR('M','O','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
552 : { (unichar_t *) N_("Moksha"), NULL, 0, 0, (void *) CHR('M','O','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
553 : { (unichar_t *) N_("Moldavian"), NULL, 0, 0, (void *) CHR('M','O','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
554 : { (unichar_t *) N_("Mon"), NULL, 0, 0, (void *) CHR('M','O','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
555 : { (unichar_t *) N_("Moroccan"), NULL, 0, 0, (void *) CHR('M','O','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
556 : { (unichar_t *) N_("Maori"), NULL, 0, 0, (void *) CHR('M','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
557 : { (unichar_t *) N_("Maithili"), NULL, 0, 0, (void *) CHR('M','T','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
558 : { (unichar_t *) N_("Maltese"), NULL, 0, 0, (void *) CHR('M','T','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
559 : { (unichar_t *) N_("Mundari"), NULL, 0, 0, (void *) CHR('M','U','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
560 : { (unichar_t *) N_("Naga-Assamese"), NULL, 0, 0, (void *) CHR('N','A','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
561 : { (unichar_t *) N_("Nanai"), NULL, 0, 0, (void *) CHR('N','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
562 : { (unichar_t *) N_("Naskapi"), NULL, 0, 0, (void *) CHR('N','A','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
563 : { (unichar_t *) N_("N-Cree"), NULL, 0, 0, (void *) CHR('N','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
564 : { (unichar_t *) N_("Ndebele"), NULL, 0, 0, (void *) CHR('N','D','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
565 : { (unichar_t *) N_("Ndonga"), NULL, 0, 0, (void *) CHR('N','D','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
566 : { (unichar_t *) N_("Nepali"), NULL, 0, 0, (void *) CHR('N','E','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
567 : { (unichar_t *) N_("Newari"), NULL, 0, 0, (void *) CHR('N','E','W',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
568 : { (unichar_t *) N_("Nagari"), NULL, 0, 0, (void *) CHR('N','G','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
569 : { (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'},
570 : { (unichar_t *) N_("Nisi"), NULL, 0, 0, (void *) CHR('N','I','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
571 : { (unichar_t *) N_("Niuean"), NULL, 0, 0, (void *) CHR('N','I','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
572 : { (unichar_t *) N_("Nkole"), NULL, 0, 0, (void *) CHR('N','K','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
573 : { (unichar_t *) N_("N'Ko"), NULL, 0, 0, (void *) CHR('N','K','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
574 : { (unichar_t *) N_("Dutch"), NULL, 0, 0, (void *) CHR('N','L','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
575 : { (unichar_t *) N_("Nogai"), NULL, 0, 0, (void *) CHR('N','O','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
576 : { (unichar_t *) N_("Norwegian"), NULL, 0, 0, (void *) CHR('N','O','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
577 : { (unichar_t *) N_("Northern Sami"), NULL, 0, 0, (void *) CHR('N','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
578 : { (unichar_t *) N_("Northern Tai"), NULL, 0, 0, (void *) CHR('N','T','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
579 : { (unichar_t *) N_("Esperanto"), NULL, 0, 0, (void *) CHR('N','T','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
580 : { (unichar_t *) N_("Nynorsk"), NULL, 0, 0, (void *) CHR('N','Y','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
581 : { (unichar_t *) N_("Occitan"), NULL, 0, 0, (void *) CHR('O','C','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
582 : { (unichar_t *) N_("Oji-Cree"), NULL, 0, 0, (void *) CHR('O','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
583 : { (unichar_t *) N_("Ojibway"), NULL, 0, 0, (void *) CHR('O','J','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
584 : { (unichar_t *) N_("Lang|Oriya"), NULL, 0, 0, (void *) CHR('O','R','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
585 : { (unichar_t *) N_("Oromo"), NULL, 0, 0, (void *) CHR('O','R','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
586 : { (unichar_t *) N_("Ossetian"), NULL, 0, 0, (void *) CHR('O','S','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
587 : { (unichar_t *) N_("Palestinian Aramaic"), NULL, 0, 0, (void *) CHR('P','A','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
588 : { (unichar_t *) N_("Pali"), NULL, 0, 0, (void *) CHR('P','A','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
589 : { (unichar_t *) N_("Punjabi"), NULL, 0, 0, (void *) CHR('P','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
590 : { (unichar_t *) N_("Palpa"), NULL, 0, 0, (void *) CHR('P','A','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
591 : { (unichar_t *) N_("Pashto"), NULL, 0, 0, (void *) CHR('P','A','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
592 : { (unichar_t *) N_("Polytonic Greek"), NULL, 0, 0, (void *) CHR('P','G','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
593 : { (unichar_t *) N_("Pilipino (Filipino)"), NULL, 0, 0, (void *) CHR('P','I','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
594 : { (unichar_t *) N_("Palaung"), NULL, 0, 0, (void *) CHR('P','L','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
595 : { (unichar_t *) N_("Polish"), NULL, 0, 0, (void *) CHR('P','L','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
596 : { (unichar_t *) N_("Provencal"), NULL, 0, 0, (void *) CHR('P','R','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
597 : { (unichar_t *) N_("Portuguese"), NULL, 0, 0, (void *) CHR('P','T','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
598 : { (unichar_t *) N_("Chin"), NULL, 0, 0, (void *) CHR('Q','I','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
599 : { (unichar_t *) N_("Rajasthani"), NULL, 0, 0, (void *) CHR('R','A','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
600 : { (unichar_t *) N_("R-Cree"), NULL, 0, 0, (void *) CHR('R','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
601 : { (unichar_t *) N_("Russian Buriat"), NULL, 0, 0, (void *) CHR('R','B','U',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
602 : { (unichar_t *) N_("Riang"), NULL, 0, 0, (void *) CHR('R','I','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
603 : { (unichar_t *) N_("Rhaeto-Romanic"), NULL, 0, 0, (void *) CHR('R','M','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
604 : { (unichar_t *) N_("Romanian"), NULL, 0, 0, (void *) CHR('R','O','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
605 : { (unichar_t *) N_("Romany"), NULL, 0, 0, (void *) CHR('R','O','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
606 : { (unichar_t *) N_("Rusyn"), NULL, 0, 0, (void *) CHR('R','S','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
607 : { (unichar_t *) N_("Ruanda"), NULL, 0, 0, (void *) CHR('R','U','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
608 : { (unichar_t *) N_("Russian"), NULL, 0, 0, (void *) CHR('R','U','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
609 : { (unichar_t *) N_("Sadri"), NULL, 0, 0, (void *) CHR('S','A','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
610 : { (unichar_t *) N_("Sanskrit"), NULL, 0, 0, (void *) CHR('S','A','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
611 : { (unichar_t *) N_("Santali"), NULL, 0, 0, (void *) CHR('S','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
612 : { (unichar_t *) N_("Sayisi"), NULL, 0, 0, (void *) CHR('S','A','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
613 : { (unichar_t *) N_("Sekota"), NULL, 0, 0, (void *) CHR('S','E','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
614 : { (unichar_t *) N_("Selkup"), NULL, 0, 0, (void *) CHR('S','E','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
615 : { (unichar_t *) N_("Sango"), NULL, 0, 0, (void *) CHR('S','G','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
616 : { (unichar_t *) N_("Shan"), NULL, 0, 0, (void *) CHR('S','H','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
617 : { (unichar_t *) N_("Sibe"), NULL, 0, 0, (void *) CHR('S','I','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
618 : { (unichar_t *) N_("Sidamo"), NULL, 0, 0, (void *) CHR('S','I','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
619 : { (unichar_t *) N_("Silte Gurage"), NULL, 0, 0, (void *) CHR('S','I','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
620 : { (unichar_t *) N_("Skolt Sami"), NULL, 0, 0, (void *) CHR('S','K','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
621 : { (unichar_t *) N_("Slovak"), NULL, 0, 0, (void *) CHR('S','K','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
622 : { (unichar_t *) N_("Slavey"), NULL, 0, 0, (void *) CHR('S','L','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
623 : { (unichar_t *) N_("Slovenian"), NULL, 0, 0, (void *) CHR('S','L','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
624 : { (unichar_t *) N_("Somali"), NULL, 0, 0, (void *) CHR('S','M','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
625 : { (unichar_t *) N_("Samoan"), NULL, 0, 0, (void *) CHR('S','M','O',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
626 : { (unichar_t *) N_("Sena"), NULL, 0, 0, (void *) CHR('S','N','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
627 : { (unichar_t *) N_("Sindhi"), NULL, 0, 0, (void *) CHR('S','N','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
628 : { (unichar_t *) N_("Lang|Sinhalese"), NULL, 0, 0, (void *) CHR('S','N','H',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
629 : { (unichar_t *) N_("Soninke"), NULL, 0, 0, (void *) CHR('S','N','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
630 : { (unichar_t *) N_("Sodo Gurage"), NULL, 0, 0, (void *) CHR('S','O','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
631 : { (unichar_t *) N_("Sotho"), NULL, 0, 0, (void *) CHR('S','O','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
632 : { (unichar_t *) N_("Albanian"), NULL, 0, 0, (void *) CHR('S','Q','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
633 : { (unichar_t *) N_("Serbian"), NULL, 0, 0, (void *) CHR('S','R','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
634 : { (unichar_t *) N_("Saraiki"), NULL, 0, 0, (void *) CHR('S','R','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
635 : { (unichar_t *) N_("Serer"), NULL, 0, 0, (void *) CHR('S','R','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
636 : { (unichar_t *) N_("South Slavey"), NULL, 0, 0, (void *) CHR('S','S','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
637 : { (unichar_t *) N_("Southern Sami"), NULL, 0, 0, (void *) CHR('S','S','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
638 : { (unichar_t *) N_("Suri"), NULL, 0, 0, (void *) CHR('S','U','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
639 : { (unichar_t *) N_("Svan"), NULL, 0, 0, (void *) CHR('S','V','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
640 : { (unichar_t *) N_("Swedish"), NULL, 0, 0, (void *) CHR('S','V','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
641 : { (unichar_t *) N_("Swadaya Aramaic"), NULL, 0, 0, (void *) CHR('S','W','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
642 : { (unichar_t *) N_("Swahili"), NULL, 0, 0, (void *) CHR('S','W','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
643 : { (unichar_t *) N_("Swazi"), NULL, 0, 0, (void *) CHR('S','W','Z',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
644 : { (unichar_t *) N_("Sutu"), NULL, 0, 0, (void *) CHR('S','X','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
645 : { (unichar_t *) N_("Lang|Syriac"), NULL, 0, 0, (void *) CHR('S','Y','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
646 : { (unichar_t *) N_("Tabasaran"), NULL, 0, 0, (void *) CHR('T','A','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
647 : { (unichar_t *) N_("Tajiki"), NULL, 0, 0, (void *) CHR('T','A','J',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
648 : { (unichar_t *) N_("Lang|Tamil"), NULL, 0, 0, (void *) CHR('T','A','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
649 : { (unichar_t *) N_("Tatar"), NULL, 0, 0, (void *) CHR('T','A','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
650 : { (unichar_t *) N_("TH-Cree"), NULL, 0, 0, (void *) CHR('T','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
651 : { (unichar_t *) N_("Lang|Telugu"), NULL, 0, 0, (void *) CHR('T','E','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
652 : { (unichar_t *) N_("Tongan"), NULL, 0, 0, (void *) CHR('T','G','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
653 : { (unichar_t *) N_("Tigre"), NULL, 0, 0, (void *) CHR('T','G','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
654 : { (unichar_t *) N_("Tigrinya"), NULL, 0, 0, (void *) CHR('T','G','Y',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
655 : { (unichar_t *) N_("Lang|Thai"), NULL, 0, 0, (void *) CHR('T','H','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
656 : { (unichar_t *) N_("Tahitian"), NULL, 0, 0, (void *) CHR('T','H','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
657 : { (unichar_t *) N_("Lang|Tibetan"), NULL, 0, 0, (void *) CHR('T','I','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
658 : { (unichar_t *) N_("Turkmen"), NULL, 0, 0, (void *) CHR('T','K','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
659 : { (unichar_t *) N_("Temne"), NULL, 0, 0, (void *) CHR('T','M','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
660 : { (unichar_t *) N_("Tswana"), NULL, 0, 0, (void *) CHR('T','N','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
661 : { (unichar_t *) N_("Tundra Nenets"), NULL, 0, 0, (void *) CHR('T','N','E',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
662 : { (unichar_t *) N_("Tonga"), NULL, 0, 0, (void *) CHR('T','N','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
663 : { (unichar_t *) N_("Todo"), NULL, 0, 0, (void *) CHR('T','O','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
664 : { (unichar_t *) N_("Turkish"), NULL, 0, 0, (void *) CHR('T','R','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
665 : { (unichar_t *) N_("Tsonga"), NULL, 0, 0, (void *) CHR('T','S','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
666 : { (unichar_t *) N_("Turoyo Aramaic"), NULL, 0, 0, (void *) CHR('T','U','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
667 : { (unichar_t *) N_("Tulu"), NULL, 0, 0, (void *) CHR('T','U','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
668 : { (unichar_t *) N_("Tuvin"), NULL, 0, 0, (void *) CHR('T','U','V',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
669 : { (unichar_t *) N_("Twi"), NULL, 0, 0, (void *) CHR('T','W','I',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
670 : { (unichar_t *) N_("Udmurt"), NULL, 0, 0, (void *) CHR('U','D','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
671 : { (unichar_t *) N_("Ukrainian"), NULL, 0, 0, (void *) CHR('U','K','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
672 : { (unichar_t *) N_("Urdu"), NULL, 0, 0, (void *) CHR('U','R','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
673 : { (unichar_t *) N_("Upper Sorbian"), NULL, 0, 0, (void *) CHR('U','S','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
674 : { (unichar_t *) N_("Uyghur"), NULL, 0, 0, (void *) CHR('U','Y','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
675 : { (unichar_t *) N_("Uzbek"), NULL, 0, 0, (void *) CHR('U','Z','B',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
676 : { (unichar_t *) N_("Venda"), NULL, 0, 0, (void *) CHR('V','E','N',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
677 : { (unichar_t *) N_("Vietnamese"), NULL, 0, 0, (void *) CHR('V','I','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
678 : { (unichar_t *) N_("Wa"), NULL, 0, 0, (void *) CHR('W','A',' ',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
679 : { (unichar_t *) N_("Wagdi"), NULL, 0, 0, (void *) CHR('W','A','G',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
680 : { (unichar_t *) N_("West-Cree"), NULL, 0, 0, (void *) CHR('W','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
681 : { (unichar_t *) N_("Welsh"), NULL, 0, 0, (void *) CHR('W','E','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
682 : { (unichar_t *) N_("Wolof"), NULL, 0, 0, (void *) CHR('W','L','F',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
683 : { (unichar_t *) N_("Tai Lue"), NULL, 0, 0, (void *) CHR('X','B','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
684 : { (unichar_t *) N_("Xhosa"), NULL, 0, 0, (void *) CHR('X','H','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
685 : { (unichar_t *) N_("Yakut"), NULL, 0, 0, (void *) CHR('Y','A','K',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
686 : { (unichar_t *) N_("Yoruba"), NULL, 0, 0, (void *) CHR('Y','B','A',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
687 : { (unichar_t *) N_("Y-Cree"), NULL, 0, 0, (void *) CHR('Y','C','R',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
688 : { (unichar_t *) N_("Yi Classic"), NULL, 0, 0, (void *) CHR('Y','I','C',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
689 : { (unichar_t *) N_("Yi Modern"), NULL, 0, 0, (void *) CHR('Y','I','M',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
690 : { (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'},
691 : { (unichar_t *) N_("Chinese Phonetic"), NULL, 0, 0, (void *) CHR('Z','H','P',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
692 : { (unichar_t *) N_("Chinese Simplified"), NULL, 0, 0, (void *) CHR('Z','H','S',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
693 : { (unichar_t *) N_("Chinese Traditional"), NULL, 0, 0, (void *) CHR('Z','H','T',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
694 : { (unichar_t *) N_("Zande"), NULL, 0, 0, (void *) CHR('Z','N','D',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
695 : { (unichar_t *) N_("Zulu"), NULL, 0, 0, (void *) CHR('Z','U','L',' '), NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
696 : GTEXTINFO_EMPTY
697 : };
698 :
699 : static char *LK_LangsDlg(GGadget *, int r, int c);
700 : static char *LK_ScriptsDlg(GGadget *, int r, int c);
701 : static struct col_init scriptci[] = {
702 : /* GT: English uses "script" to mean a general writing system (latin, greek, kanji) */
703 : /* GT: and the cursive handwriting style. Here we mean the general writing system. */
704 : { me_stringchoicetag , NULL, scripts, NULL, N_("writing system|Script") },
705 : { me_funcedit, LK_LangsDlg, NULL, NULL, N_("Language(s)") },
706 : COL_INIT_EMPTY
707 : };
708 : static struct col_init featureci[] = {
709 : { me_stringchoicetrans , NULL, NULL, NULL, N_("Feature") },
710 : { me_funcedit, LK_ScriptsDlg, NULL, NULL, N_("Script(s) & Language(s)") },
711 : COL_INIT_EMPTY
712 : };
713 :
714 1 : void LookupUIInit(void) {
715 : static int done = false;
716 : int i, j;
717 : static GTextInfo *needswork[] = {
718 : scripts, languages, gpos_lookuptypes, gsub_lookuptypes,
719 : NULL
720 : };
721 :
722 1 : if ( done )
723 1 : return;
724 1 : done = true;
725 5 : for ( j=0; needswork[j]!=NULL; ++j ) {
726 518 : for ( i=0; needswork[j][i].text!=NULL || needswork[j][i].line; ++i )
727 514 : if ( needswork[j][i].text!=NULL )
728 512 : needswork[j][i].text = (unichar_t *) S_((char *) needswork[j][i].text);
729 : }
730 1 : LookupInit();
731 :
732 1 : featureci[0].title = S_(featureci[0].title);
733 1 : featureci[1].title = S_(featureci[1].title);
734 1 : scriptci[0].title = S_(scriptci[0].title);
735 1 : scriptci[1].title = S_(scriptci[1].title);
736 : }
737 :
738 : #define CID_LookupType 1000
739 : #define CID_LookupFeatures 1001
740 : #define CID_Lookup_R2L 1002
741 : #define CID_Lookup_IgnBase 1003
742 : #define CID_Lookup_IgnLig 1004
743 : #define CID_Lookup_IgnMark 1005
744 : #define CID_Lookup_ProcessMark 1006
745 : #define CID_LookupName 1007
746 : #define CID_LookupAfm 1008
747 : #define CID_OK 1009
748 : #define CID_Cancel 1010
749 : #define CID_Lookup_ProcessSet 1011
750 :
751 : #define CID_FeatureScripts 1020
752 : #define CID_ShowAnchors 1021
753 :
754 : struct lookup_dlg {
755 : OTLookup *orig;
756 : SplineFont *sf;
757 : GWindow gw, scriptgw;
758 : int isgpos;
759 : int done, scriptdone, name_has_been_set;
760 : int ok;
761 : char *scriptret;
762 : };
763 :
764 0 : static int langs_e_h(GWindow gw, GEvent *event) {
765 0 : int *done = GDrawGetUserData(gw);
766 :
767 0 : if ( event->type==et_close ) {
768 0 : *done = true;
769 0 : } else if ( event->type==et_char ) {
770 0 : if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
771 0 : help("lookups.html#scripts-dlg");
772 0 : return( true );
773 : }
774 0 : return( false );
775 0 : } else if ( event->type==et_controlevent && event->u.control.subtype == et_buttonactivate ) {
776 0 : switch ( GGadgetGetCid(event->u.control.g)) {
777 : case CID_OK:
778 0 : *done = 2;
779 0 : break;
780 : case CID_Cancel:
781 0 : *done = true;
782 0 : break;
783 : }
784 : }
785 0 : return( true );
786 : }
787 :
788 0 : static char *LK_LangsDlg(GGadget *g, int r, int c) {
789 : int rows, i;
790 0 : struct matrix_data *strings = GMatrixEditGet(g, &rows);
791 0 : char *langstr = strings[2*r+c].u.md_str, *pt, *start;
792 : unsigned char tagstr[4], warnstr[8];
793 : uint32 tag;
794 0 : int warn_cnt = 0;
795 0 : int j,done=0;
796 : GWindowAttrs wattrs;
797 : GGadgetCreateData gcd[5], *varray[6], *harray[7], boxes[3];
798 : GTextInfo label[5];
799 : GRect pos;
800 : GWindow gw;
801 : int32 len;
802 : GTextInfo **ti;
803 : char *ret;
804 :
805 0 : for ( i=0; languages[i].text!=NULL; ++i )
806 0 : languages[i].selected = false;
807 :
808 0 : for ( start= langstr; *start; ) {
809 0 : memset(tagstr,' ',sizeof(tagstr));
810 0 : for ( pt=start, j=0; *pt!='\0' && *pt!=','; ++pt, ++j ) {
811 0 : if ( j<4 )
812 0 : tagstr[j] = *pt;
813 : }
814 0 : if ( *pt==',' ) ++pt;
815 0 : tag = (tagstr[0]<<24) | (tagstr[1]<<16) | (tagstr[2]<<8) | tagstr[3];
816 0 : for ( i=0; languages[i].text!=NULL; ++i )
817 0 : if ( languages[i].userdata == (void *) (intpt) tag ) {
818 0 : languages[i].selected = true;
819 0 : break;
820 : }
821 0 : if ( languages[i].text==NULL ) {
822 0 : ++warn_cnt;
823 0 : memcpy(warnstr,tagstr,4);
824 0 : warnstr[4] = '\0';
825 : }
826 0 : start = pt;
827 : }
828 0 : if ( warn_cnt!=0 ) {
829 0 : if ( warn_cnt==1 )
830 0 : ff_post_error(_("Unknown Language"),_("The language, '%s', is not in the list of known languages and will be omitted"), warnstr );
831 : else
832 0 : ff_post_error(_("Unknown Language"),_("Several language tags, including '%s', are not in the list of known languages and will be omitted"), warnstr );
833 : }
834 :
835 0 : memset(&wattrs,0,sizeof(wattrs));
836 0 : wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
837 0 : wattrs.event_masks = ~(1<<et_charup);
838 0 : wattrs.restrict_input_to_me = 1;
839 0 : wattrs.undercursor = 1;
840 0 : wattrs.cursor = ct_pointer;
841 0 : wattrs.utf8_window_title = _("Language List");
842 0 : wattrs.is_dlg = true;
843 0 : pos.x = pos.y = 0;
844 0 : pos.width = GGadgetScale(GDrawPointsToPixels(NULL,150));
845 0 : pos.height = GDrawPointsToPixels(NULL,193);
846 0 : gw = GDrawCreateTopWindow(NULL,&pos,langs_e_h,&done,&wattrs);
847 :
848 0 : memset(&gcd,0,sizeof(gcd));
849 0 : memset(&boxes,0,sizeof(boxes));
850 0 : memset(&label,0,sizeof(label));
851 :
852 0 : i = 0;
853 0 : gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = 5;
854 0 : gcd[i].gd.pos.height = 12*12+6;
855 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_list_alphabetic|gg_list_multiplesel|gg_utf8_popup;
856 0 : gcd[i].gd.u.list = languages;
857 0 : gcd[i].gd.cid = 0;
858 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
859 : "Select as many languages as needed\n"
860 : "Hold down the control key when clicking\n"
861 : "to make disjoint selections.");
862 0 : varray[0] = &gcd[i]; varray[1] = NULL;
863 0 : gcd[i++].creator = GListCreate;
864 :
865 0 : 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;
866 0 : gcd[i].gd.pos.width = -1; gcd[i].gd.pos.height = 0;
867 0 : gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
868 0 : label[i].text = (unichar_t *) _("_OK");
869 0 : label[i].text_is_1byte = true;
870 0 : label[i].text_in_resource = true;
871 0 : gcd[i].gd.mnemonic = 'O';
872 0 : gcd[i].gd.label = &label[i];
873 0 : gcd[i].gd.cid = CID_OK;
874 0 : harray[0] = GCD_Glue; harray[1] = &gcd[i]; harray[2] = GCD_Glue;
875 0 : gcd[i++].creator = GButtonCreate;
876 :
877 0 : gcd[i].gd.pos.x = -15; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+3;
878 0 : gcd[i].gd.pos.width = -1; gcd[i].gd.pos.height = 0;
879 0 : gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
880 0 : label[i].text = (unichar_t *) _("_Cancel");
881 0 : label[i].text_is_1byte = true;
882 0 : label[i].text_in_resource = true;
883 0 : gcd[i].gd.label = &label[i];
884 0 : gcd[i].gd.mnemonic = 'C';
885 0 : gcd[i].gd.cid = CID_Cancel;
886 0 : harray[3] = GCD_Glue; harray[4] = &gcd[i]; harray[5] = GCD_Glue; harray[6] = NULL;
887 0 : gcd[i++].creator = GButtonCreate;
888 :
889 0 : boxes[2].gd.flags = gg_enabled|gg_visible;
890 0 : boxes[2].gd.u.boxelements = harray;
891 0 : boxes[2].creator = GHBoxCreate;
892 0 : varray[2] = &boxes[2]; varray[3] = NULL; varray[4] = NULL;
893 :
894 0 : boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
895 0 : boxes[0].gd.flags = gg_enabled|gg_visible;
896 0 : boxes[0].gd.u.boxelements = varray;
897 0 : boxes[0].creator = GHVGroupCreate;
898 :
899 0 : GGadgetsCreate(gw,boxes);
900 0 : GHVBoxSetExpandableRow(boxes[0].ret,0);
901 0 : GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
902 0 : GHVBoxFitWindow(boxes[0].ret);
903 :
904 0 : GDrawSetVisible(gw,true);
905 : retry:
906 0 : while ( !done )
907 0 : GDrawProcessOneEvent(NULL);
908 0 : ret = NULL;
909 0 : ti = GGadgetGetList(gcd[0].ret,&len);
910 0 : if ( done==2 ) {
911 0 : int lcnt=0;
912 0 : for ( i=0; i<len; ++i ) {
913 0 : if ( ti[i]->selected )
914 0 : ++lcnt;
915 : }
916 0 : if ( lcnt==0 ) {
917 0 : ff_post_error(_("Language Missing"),_("You must select at least one language.\nUse the \"Default\" language if nothing else fits."));
918 0 : done = 0;
919 0 : goto retry;
920 : }
921 0 : ret = malloc(5*lcnt+1);
922 0 : *ret = '\0';
923 0 : pt = ret;
924 0 : for ( i=0; i<len; ++i ) {
925 0 : if ( done==2 && ti[i]->selected ) {
926 0 : uint32 tag = (uint32) (intpt) (ti[i]->userdata);
927 0 : *pt++ = tag>>24;
928 0 : *pt++ = tag>>16;
929 0 : *pt++ = tag>>8;
930 0 : *pt++ = tag&0xff;
931 0 : *pt++ = ',';
932 : }
933 : }
934 0 : if ( pt!=ret )
935 0 : pt[-1] = '\0';
936 : }
937 0 : GDrawDestroyWindow(gw);
938 0 : return( ret );
939 : }
940 :
941 0 : static void LK_NewScript(GGadget *g,int row) {
942 : int rows;
943 0 : struct matrix_data *strings = GMatrixEditGet(g, &rows);
944 : /* What's a good default lang list for a new script? */
945 : /* well it depends on what the script is, but we don't know that yet */
946 : /* dflt is safe */
947 :
948 0 : strings[2*row+1].u.md_str = copy("dflt");
949 0 : }
950 :
951 0 : static void ScriptMatrixInit(struct matrixinit *mi,char *scriptstr) {
952 : struct matrix_data *md;
953 : int k, cnt;
954 : char *start, *scriptend, *langsend;
955 :
956 0 : memset(mi,0,sizeof(*mi));
957 0 : mi->col_cnt = 2;
958 0 : mi->col_init = scriptci;
959 :
960 0 : md = NULL;
961 0 : for ( k=0; k<2; ++k ) {
962 0 : cnt = 0;
963 0 : for ( start = scriptstr; *start; ) {
964 0 : for ( scriptend=start; *scriptend!='\0' && *scriptend!='{'; ++scriptend );
965 0 : langsend = scriptend;
966 0 : if ( *scriptend=='{' )
967 0 : for ( langsend=scriptend+1; *langsend!='\0' && *langsend!='}'; ++langsend );
968 0 : if ( k ) {
969 0 : md[2*cnt+0].u.md_str = copyn(start,scriptend-start);
970 0 : if ( *scriptend=='{' )
971 0 : md[2*cnt+1].u.md_str = copyn(scriptend+1,langsend-(scriptend+1));
972 : else
973 0 : md[2*cnt+1].u.md_str = copy("");
974 : }
975 0 : ++cnt;
976 0 : if ( *langsend=='}' ) ++langsend;
977 0 : if ( *langsend==' ' ) ++langsend;
978 0 : start = langsend;
979 : }
980 0 : if ( md==NULL )
981 0 : md = calloc(2*(cnt+10),sizeof(struct matrix_data));
982 : }
983 0 : mi->matrix_data = md;
984 0 : mi->initial_row_cnt = cnt;
985 :
986 0 : mi->initrow = LK_NewScript;
987 0 : mi->finishedit = NULL;
988 0 : mi->candelete = NULL;
989 0 : mi->popupmenu = NULL;
990 0 : mi->handle_key = NULL;
991 0 : mi->bigedittitle = NULL;
992 0 : }
993 :
994 0 : static int Script_OK(GGadget *g, GEvent *e) {
995 :
996 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
997 0 : struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
998 : int rows, i, j, script_cnt, lang_cnt;
999 0 : struct matrix_data *strings = GMatrixEditGet(GWidgetGetControl(ld->scriptgw,CID_FeatureScripts), &rows);
1000 : char *pt, *start, *ret, *rpt;
1001 : char foo[4];
1002 :
1003 0 : if ( rows==0 ) {
1004 0 : ff_post_error(_("No scripts"),_("You must select at least one script if you provide a feature tag."));
1005 0 : return(true);
1006 : }
1007 0 : script_cnt = rows;
1008 0 : lang_cnt = 0;
1009 0 : for ( i=0; i<rows; ++i ) {
1010 0 : if ( strlen(strings[2*i+0].u.md_str)>4 ) {
1011 0 : ff_post_error(_("Bad script tag"),_("The script tag on line %d (%s) is too long. It may be at most 4 letters"),
1012 0 : i+1, strings[2*i+0].u.md_str);
1013 0 : return(true);
1014 : } else {
1015 0 : for ( pt=strings[2*i+0].u.md_str; *pt!='\0' ; ++pt )
1016 0 : if ( *pt>0x7e ) {
1017 0 : ff_post_error(_("Bad script tag"),_("The script tag on line %d (%s) should be in ASCII.\n"),
1018 0 : i+1, strings[2*i+0].u.md_str);
1019 0 : return( true );
1020 : }
1021 : }
1022 : /* Now check the languages */
1023 0 : if ( *strings[2*i+1].u.md_str=='\0' ) {
1024 0 : ff_post_error(_("No languages"),_("You must select at least one language for each script."));
1025 0 : return(true);
1026 : }
1027 0 : for ( start=strings[2*i+1].u.md_str; *start!='\0'; ) {
1028 0 : for ( pt=start; *pt!=',' && *pt!='\0'; ++pt ) {
1029 0 : if ( *pt>0x7e ) {
1030 0 : ff_post_error(_("Bad language tag"),_("A language tag on line %d (%s) should be in ASCII.\n"),
1031 0 : i+1, strings[2*i+1].u.md_str);
1032 0 : return( true );
1033 : }
1034 : }
1035 0 : if ( pt-start>4 ) {
1036 0 : ff_post_error(_("Bad language tag"),_("A language tag on line %d (%s) is too long. It may be at most 4 letters"),
1037 0 : i+1, strings[2*i+0].u.md_str);
1038 0 : return(true);
1039 : }
1040 0 : ++lang_cnt;
1041 0 : start = pt;
1042 0 : if ( *start==',' ) ++start;
1043 : }
1044 : }
1045 :
1046 : /* Ok, we validated the script lang list. Now parse it */
1047 0 : rpt = ret = malloc(6*script_cnt+5*lang_cnt+10);
1048 0 : for ( i=0; i<rows; ++i ) {
1049 0 : memset(foo,' ',sizeof(foo));
1050 0 : for ( j=0, pt = strings[2*i+0].u.md_str; j<4 && *pt; foo[j++] = *pt++ );
1051 0 : strncpy(rpt,foo,4);
1052 0 : rpt += 4;
1053 0 : *rpt++ = '{';
1054 : /* Now do the languages */
1055 0 : for ( start=strings[2*i+1].u.md_str; *start!='\0'; ) {
1056 0 : memset(foo,' ',sizeof(foo));
1057 0 : for ( j=0,pt=start; *pt!=',' && *pt!='\0'; ++pt )
1058 0 : foo[j++] = *pt;
1059 0 : strncpy(rpt,foo,4);
1060 0 : rpt += 4; *rpt++ = ',';
1061 0 : if ( *pt==',' ) ++pt;
1062 0 : start = pt;
1063 : }
1064 0 : if ( rpt>ret && rpt[-1]==',' )
1065 0 : rpt[-1] = '}';
1066 0 : *rpt++ = ' ';
1067 : }
1068 0 : if ( rpt>ret && rpt[-1]==' ' ) rpt[-1] = '\0';
1069 0 : else *rpt = '\0';
1070 0 : ld->scriptdone = true;
1071 0 : ld->scriptret = ret;
1072 : }
1073 0 : return( true );
1074 : }
1075 :
1076 0 : static int Script_Cancel(GGadget *g, GEvent *e) {
1077 :
1078 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1079 0 : struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1080 0 : ld->scriptdone = true;
1081 0 : ld->scriptret = NULL;
1082 : }
1083 0 : return( true );
1084 : }
1085 :
1086 0 : static int script_e_h(GWindow gw, GEvent *event) {
1087 :
1088 0 : if ( event->type==et_close ) {
1089 0 : struct lookup_dlg *ld = GDrawGetUserData(gw);
1090 0 : ld->scriptdone = true;
1091 0 : ld->scriptret = NULL;
1092 0 : } else if ( event->type == et_char ) {
1093 0 : if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
1094 0 : help("lookups.html#scripts-dlg");
1095 0 : return( true );
1096 : }
1097 0 : return( false );
1098 : }
1099 :
1100 0 : return( true );
1101 : }
1102 :
1103 0 : static char *LK_ScriptsDlg(GGadget *g, int r, int c) {
1104 : int rows, i, k, j;
1105 0 : struct matrix_data *strings = GMatrixEditGet(g, &rows);
1106 0 : char *scriptstr = strings[2*r+c].u.md_str;
1107 0 : struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1108 : GRect pos;
1109 : GWindow gw;
1110 : GWindowAttrs wattrs;
1111 : GGadgetCreateData gcd[4], boxes[3];
1112 : GGadgetCreateData *varray[6], *harray3[8];
1113 : GTextInfo label[4];
1114 : struct matrixinit mi;
1115 :
1116 0 : ld->scriptdone = 0;
1117 0 : ld->scriptret = NULL;
1118 :
1119 0 : memset(&wattrs,0,sizeof(wattrs));
1120 0 : wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
1121 0 : wattrs.event_masks = ~(1<<et_charup);
1122 0 : wattrs.is_dlg = true;
1123 0 : wattrs.restrict_input_to_me = 1;
1124 0 : wattrs.undercursor = 1;
1125 0 : wattrs.cursor = ct_pointer;
1126 0 : wattrs.utf8_window_title = _("Script(s)");
1127 0 : pos.x = pos.y = 0;
1128 0 : pos.width =GDrawPointsToPixels(NULL,GGadgetScale(268));
1129 0 : pos.height = GDrawPointsToPixels(NULL,375);
1130 0 : ld->scriptgw = gw = GDrawCreateTopWindow(NULL,&pos,script_e_h,ld,&wattrs);
1131 :
1132 0 : ScriptMatrixInit(&mi,scriptstr);
1133 :
1134 0 : memset(&gcd,0,sizeof(gcd));
1135 0 : memset(&boxes,0,sizeof(boxes));
1136 0 : memset(&label,0,sizeof(label));
1137 0 : k=j=0;
1138 0 : gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[1].gd.pos.y+14;
1139 0 : gcd[k].gd.pos.width = 300; gcd[k].gd.pos.height = 90;
1140 0 : gcd[k].gd.flags = gg_enabled | gg_visible | gg_utf8_popup;
1141 0 : gcd[k].gd.cid = CID_FeatureScripts;
1142 0 : gcd[k].gd.u.matrix = &mi;
1143 0 : gcd[k].gd.popup_msg = (unichar_t *) _(
1144 : "Each feature is active for a specific set of\n"
1145 : "scripts and languages.\n"
1146 : "Usually only one script is specified, but\n"
1147 : "occasionally more will be.\n"
1148 : "A script is a four letter OpenType script tag\n");
1149 0 : gcd[k].creator = GMatrixEditCreate;
1150 0 : varray[j++] = &gcd[k++]; varray[j++] = NULL;
1151 :
1152 0 : gcd[k].gd.pos.x = 30-3;
1153 0 : gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
1154 0 : gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
1155 0 : label[k].text = (unichar_t *) _("_OK");
1156 0 : label[k].text_is_1byte = true;
1157 0 : label[k].text_in_resource = true;
1158 0 : gcd[k].gd.label = &label[k];
1159 0 : gcd[k].gd.handle_controlevent = Script_OK;
1160 0 : gcd[k].gd.cid = CID_OK;
1161 0 : gcd[k++].creator = GButtonCreate;
1162 :
1163 0 : gcd[k].gd.pos.x = -30;
1164 0 : gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
1165 0 : gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
1166 0 : label[k].text = (unichar_t *) _("_Cancel");
1167 0 : label[k].text_is_1byte = true;
1168 0 : label[k].text_in_resource = true;
1169 0 : gcd[k].gd.label = &label[k];
1170 0 : gcd[k].gd.handle_controlevent = Script_Cancel;
1171 0 : gcd[k].gd.cid = CID_Cancel;
1172 0 : gcd[k++].creator = GButtonCreate;
1173 :
1174 0 : harray3[0] = harray3[2] = harray3[3] = harray3[4] = harray3[6] = GCD_Glue;
1175 0 : harray3[7] = NULL;
1176 0 : harray3[1] = &gcd[k-2]; harray3[5] = &gcd[k-1];
1177 :
1178 0 : boxes[0].gd.flags = gg_enabled|gg_visible;
1179 0 : boxes[0].gd.u.boxelements = harray3;
1180 0 : boxes[0].creator = GHBoxCreate;
1181 0 : varray[j++] = &boxes[0]; varray[j++] = NULL; varray[j] = NULL;
1182 :
1183 0 : boxes[1].gd.pos.x = boxes[1].gd.pos.y = 2;
1184 0 : boxes[1].gd.flags = gg_enabled|gg_visible;
1185 0 : boxes[1].gd.u.boxelements = varray;
1186 0 : boxes[1].creator = GHVGroupCreate;
1187 :
1188 0 : GGadgetsCreate(gw,boxes+1);
1189 :
1190 0 : for ( i=0; i<mi.initial_row_cnt; ++i ) {
1191 0 : free( mi.matrix_data[2*i+0].u.md_str );
1192 0 : free( mi.matrix_data[2*i+1].u.md_str );
1193 : }
1194 0 : free( mi.matrix_data );
1195 :
1196 0 : GMatrixEditSetNewText(gcd[0].ret,S_("OpenTypeFeature|New"));
1197 0 : GHVBoxSetExpandableRow(boxes[1].ret,0);
1198 0 : GHVBoxSetExpandableCol(boxes[0].ret,gb_expandgluesame);
1199 :
1200 0 : GHVBoxFitWindow(boxes[1].ret);
1201 :
1202 0 : GDrawSetVisible(gw,true);
1203 0 : while ( !ld->scriptdone )
1204 0 : GDrawProcessOneEvent(NULL);
1205 0 : GDrawDestroyWindow(gw);
1206 0 : return( ld->scriptret );
1207 : }
1208 :
1209 0 : static FeatureScriptLangList *LK_ParseFL(struct matrix_data *strings, int rows ) {
1210 : int i,j;
1211 : char *pt, *start;
1212 : FeatureScriptLangList *fl, *fhead, *flast;
1213 : struct scriptlanglist *sl, *slast;
1214 : unsigned char foo[4];
1215 0 : uint32 *langs=NULL;
1216 0 : int lmax=0, lcnt=0;
1217 : int feature, setting;
1218 :
1219 0 : fhead = flast = NULL;
1220 0 : for ( i=0; i<rows; ++i ) {
1221 0 : fl = chunkalloc(sizeof(FeatureScriptLangList));
1222 0 : if ( sscanf(strings[2*i+0].u.md_str,"<%d,%d>", &feature, &setting )== 2 ) {
1223 0 : fl->ismac = true;
1224 0 : fl->featuretag = (feature<<16)|setting;
1225 : } else {
1226 0 : memset(foo,' ',sizeof(foo));
1227 0 : for ( j=0, pt = strings[2*i+0].u.md_str; j<4 && *pt; foo[j++] = *pt++ );
1228 0 : fl->featuretag = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
1229 : }
1230 0 : if ( flast==NULL )
1231 0 : fhead = fl;
1232 : else
1233 0 : flast->next = fl;
1234 0 : flast = fl;
1235 : /* Now do the script langs */
1236 0 : slast = NULL;
1237 0 : for ( start=strings[2*i+1].u.md_str; *start!='\0'; ) {
1238 0 : memset(foo,' ',sizeof(foo));
1239 0 : for ( j=0,pt=start; *pt!='{' && *pt!='\0'; ++pt )
1240 0 : foo[j++] = *pt;
1241 0 : sl = chunkalloc(sizeof( struct scriptlanglist ));
1242 0 : sl->script = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
1243 0 : if ( slast==NULL )
1244 0 : fl->scripts = sl;
1245 : else
1246 0 : slast->next = sl;
1247 0 : slast = sl;
1248 0 : if ( *pt!='{' ) {
1249 0 : sl->lang_cnt = 1;
1250 0 : sl->langs[0] = DEFAULT_LANG;
1251 0 : start = pt;
1252 : } else {
1253 0 : lcnt=0;
1254 0 : for ( start=pt+1; *start!='}' && *start!='\0' ; ) {
1255 0 : memset(foo,' ',sizeof(foo));
1256 0 : for ( j=0,pt=start; *pt!='}' && *pt!=',' && *pt!='\0'; foo[j++] = *pt++ );
1257 0 : if ( lcnt>=lmax )
1258 0 : langs = realloc(langs,(lmax+=20)*sizeof(uint32));
1259 0 : langs[lcnt++] = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3];
1260 0 : start = ( *pt==',' ) ? pt+1 : pt;
1261 : }
1262 0 : if ( *start=='}' ) ++start;
1263 0 : for ( j=0; j<lcnt && j<MAX_LANG; ++j )
1264 0 : sl->langs[j] = langs[j];
1265 0 : if ( lcnt>MAX_LANG ) {
1266 0 : sl->morelangs = malloc((lcnt-MAX_LANG)*sizeof(uint32));
1267 0 : for ( ; j<lcnt; ++j )
1268 0 : sl->morelangs[j-MAX_LANG] = langs[j];
1269 : }
1270 0 : sl->lang_cnt = lcnt;
1271 : }
1272 0 : while ( *start==' ' ) ++start;
1273 : }
1274 : }
1275 0 : free(langs);
1276 0 : return( fhead );
1277 : }
1278 :
1279 0 : static void LK_FinishEdit(GGadget *g,int row, int col, int wasnew) {
1280 0 : struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1281 :
1282 0 : if ( col==0 ) {
1283 : int rows;
1284 0 : struct matrix_data *strings = GMatrixEditGet(g, &rows);
1285 :
1286 0 : if ( !ld->isgpos &&
1287 0 : (strcmp(strings[row].u.md_str,"liga")==0 ||
1288 0 : strcmp(strings[row].u.md_str,"rlig")==0 ))
1289 0 : GGadgetSetChecked( GWidgetGetControl(ld->gw,CID_LookupAfm ), true );
1290 : }
1291 0 : if ( row==0 && !ld->name_has_been_set && ld->orig->features==NULL ) {
1292 : int rows;
1293 0 : struct matrix_data *strings = GMatrixEditGet(g, &rows);
1294 0 : OTLookup *otl = ld->orig;
1295 0 : int old_type = otl->lookup_type;
1296 0 : FeatureScriptLangList *oldfl = otl->features;
1297 :
1298 0 : otl->lookup_type = (intpt) GGadgetGetListItemSelected(GWidgetGetControl(ld->gw,CID_LookupType))->userdata;
1299 0 : otl->features = LK_ParseFL(strings,rows);
1300 0 : NameOTLookup(otl,ld->sf);
1301 0 : GGadgetSetTitle8(GWidgetGetControl(ld->gw,CID_LookupName),otl->lookup_name);
1302 0 : free(otl->lookup_name); otl->lookup_name = NULL;
1303 0 : FeatureScriptLangListFree(otl->features); otl->features = oldfl;
1304 0 : otl->lookup_type = old_type;
1305 : }
1306 0 : }
1307 :
1308 0 : static void LK_NewFeature(GGadget *g,int row) {
1309 : int rows;
1310 0 : struct matrix_data *strings = GMatrixEditGet(g, &rows);
1311 0 : struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1312 0 : SplineFont *sf, *_sf = ld->sf;
1313 : SplineChar *sc;
1314 : /* What's a good default script / lang list for a new feature? */
1315 : /* well it depends on what the feature is, and on what's inside the lookup */
1316 : /* Neither of those has been set yet. */
1317 : /* So... give them everything we can think of. They can remove stuff */
1318 : /* which is inappropriate */
1319 : uint32 scripts[20], script, *langs;
1320 0 : int scnt = 0, i, l;
1321 : int gid, k;
1322 : char *buf;
1323 : int bmax, bpos;
1324 :
1325 0 : k=0;
1326 : do {
1327 0 : sf = k<_sf->subfontcnt ? _sf->subfonts[k] : _sf;
1328 0 : for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
1329 0 : script = SCScriptFromUnicode(sc);
1330 0 : for ( i=0; i<scnt; ++i )
1331 0 : if ( script == scripts[i] )
1332 0 : break;
1333 0 : if ( i==scnt ) {
1334 0 : scripts[scnt++] = script;
1335 0 : if ( scnt>=20 ) /* If they've got lots of scripts, enumerating them all won't be much use... */
1336 0 : break;
1337 : }
1338 : }
1339 0 : ++k;
1340 0 : } while ( k<_sf->subfontcnt && scnt<20 );
1341 :
1342 0 : if ( scnt==0 )
1343 0 : scripts[scnt++] = DEFAULT_SCRIPT;
1344 :
1345 0 : buf = malloc(bmax = 100);
1346 0 : bpos = 0;
1347 0 : for ( i=0; i<scnt; ++i ) {
1348 0 : langs = SFLangsInScript(sf,-1,scripts[i]);
1349 0 : for ( l=0; langs[l]!=0; ++l );
1350 0 : if ( bpos + 5+5*l+4 > bmax )
1351 0 : buf = realloc( buf, bmax += 5+5*l+100 );
1352 0 : sprintf( buf+bpos, "%c%c%c%c{", scripts[i]>>24, scripts[i]>>16, scripts[i]>>8, scripts[i] );
1353 0 : bpos+=5;
1354 0 : for ( l=0; langs[l]!=0; ++l ) {
1355 0 : sprintf( buf+bpos, "%c%c%c%c,", langs[l]>>24, langs[l]>>16, langs[l]>>8, langs[l] );
1356 0 : bpos += 5;
1357 : }
1358 0 : if ( l>0 )
1359 0 : --bpos;
1360 0 : strcpy(buf+bpos,"} ");
1361 0 : bpos += 2;
1362 0 : free(langs);
1363 : }
1364 0 : if ( bpos>0 )
1365 0 : buf[bpos-1] = '\0';
1366 :
1367 0 : strings[2*row+1].u.md_str = copy(buf);
1368 0 : free(buf);
1369 0 : }
1370 :
1371 0 : static void LKMatrixInit(struct matrixinit *mi,OTLookup *otl) {
1372 : struct matrix_data *md;
1373 : int k, cnt,j;
1374 : FeatureScriptLangList *fl;
1375 : struct scriptlanglist *sl;
1376 0 : char featbuf[32], *buf=NULL;
1377 0 : int blen=0, bpos=0;
1378 :
1379 0 : memset(mi,0,sizeof(*mi));
1380 0 : mi->col_cnt = 2;
1381 0 : mi->col_init = featureci;
1382 :
1383 0 : md = NULL;
1384 0 : for ( k=0; k<2; ++k ) {
1385 0 : cnt = 0;
1386 0 : for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
1387 0 : if ( k ) {
1388 0 : if ( fl->ismac )
1389 0 : sprintf( featbuf, "<%d,%d>", fl->featuretag>>16, fl->featuretag&0xffff );
1390 : else
1391 0 : sprintf( featbuf, "%c%c%c%c", fl->featuretag>>24, fl->featuretag>>16,
1392 0 : fl->featuretag>>8, fl->featuretag );
1393 0 : md[2*cnt+0].u.md_str = copy(featbuf);
1394 0 : bpos=0;
1395 0 : for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) {
1396 0 : if ( bpos+4/*script*/+1/*open brace*/+5*sl->lang_cnt+1+2 > blen )
1397 0 : buf = realloc(buf,blen+=5+5*sl->lang_cnt+3+200);
1398 0 : sprintf( buf+bpos, "%c%c%c%c{", sl->script>>24, sl->script>>16,
1399 0 : sl->script>>8, sl->script );
1400 0 : bpos+=5;
1401 0 : for ( j=0; j<sl->lang_cnt; ++j ) {
1402 0 : uint32 tag = j<MAX_LANG ? sl->langs[j] : sl->morelangs[j-MAX_LANG];
1403 0 : sprintf( buf+bpos, "%c%c%c%c,", tag>>24, tag>>16,
1404 : tag>>8, tag );
1405 0 : bpos+=5;
1406 : }
1407 0 : if ( sl->lang_cnt!=0 )
1408 0 : --bpos;
1409 0 : buf[bpos++] = '}';
1410 0 : buf[bpos++] = ' ';
1411 : }
1412 0 : if ( bpos>0 )
1413 0 : --bpos;
1414 0 : if (buf) {
1415 0 : buf[bpos] = '\0';
1416 0 : md[2*cnt+1].u.md_str = copy(buf);
1417 : }
1418 : else {
1419 0 : md[2*cnt+1].u.md_str = NULL;
1420 : }
1421 : }
1422 0 : ++cnt;
1423 : }
1424 0 : if ( md==NULL )
1425 0 : md = calloc(2*(cnt+10),sizeof(struct matrix_data));
1426 : }
1427 0 : mi->matrix_data = md;
1428 0 : mi->initial_row_cnt = cnt;
1429 :
1430 0 : mi->initrow = LK_NewFeature;
1431 0 : mi->finishedit = LK_FinishEdit;
1432 0 : mi->candelete = NULL;
1433 0 : mi->popupmenu = NULL;
1434 0 : mi->handle_key = NULL;
1435 0 : mi->bigedittitle = NULL;
1436 0 : free(buf);
1437 0 : }
1438 :
1439 0 : static GTextInfo *SFMarkClassList(SplineFont *sf,int class) {
1440 : int i;
1441 : GTextInfo *ti;
1442 :
1443 0 : if ( sf->cidmaster ) sf = sf->cidmaster;
1444 0 : else if ( sf->mm!=NULL ) sf=sf->mm->normal;
1445 :
1446 0 : i = sf->mark_class_cnt;
1447 0 : ti = calloc(i+4,sizeof( GTextInfo ));
1448 0 : ti[0].text = utf82u_copy( _("All"));
1449 0 : ti[0].selected = class==0;
1450 0 : for ( i=1; i<sf->mark_class_cnt; ++i ) {
1451 0 : ti[i].text = (unichar_t *) copy(sf->mark_class_names[i]);
1452 0 : ti[i].userdata = (void *) (intpt) i;
1453 0 : ti[i].text_is_1byte = true;
1454 0 : if ( i==class ) ti[i].selected = true;
1455 : }
1456 0 : return( ti );
1457 : }
1458 :
1459 0 : static GTextInfo *SFMarkSetList(SplineFont *sf,int set) {
1460 : int i;
1461 : GTextInfo *ti;
1462 :
1463 0 : if ( sf->cidmaster ) sf = sf->cidmaster;
1464 0 : else if ( sf->mm!=NULL ) sf=sf->mm->normal;
1465 :
1466 0 : i = sf->mark_set_cnt;
1467 0 : ti = calloc(i+4,sizeof( GTextInfo ));
1468 0 : ti[0].text = utf82u_copy( _("All"));
1469 0 : ti[0].userdata = (void *) (intpt) -1;
1470 0 : ti[0].selected = set==-1;
1471 0 : for ( i=0; i<sf->mark_set_cnt; ++i ) {
1472 0 : ti[i+1].text = (unichar_t *) copy(sf->mark_set_names[i]);
1473 0 : ti[i+1].userdata = (void *) (intpt) i;
1474 0 : ti[i+1].text_is_1byte = true;
1475 0 : if ( i==set ) ti[i+1].selected = true;
1476 : }
1477 0 : return( ti );
1478 : }
1479 :
1480 0 : static int MaskFromLookupType(int lookup_type ) {
1481 0 : switch ( lookup_type ) {
1482 : case gsub_single: case gsub_multiple: case gsub_alternate:
1483 : case gsub_ligature: case gsub_context: case gsub_contextchain:
1484 0 : return( 1<<(lookup_type-1));
1485 : case gsub_reversecchain:
1486 0 : return( gsub_reversecchain_mask );
1487 : case morx_indic: case morx_context: case morx_insert:
1488 0 : return( morx_indic_mask<<(lookup_type-morx_indic) );
1489 : case gpos_single: case gpos_pair: case gpos_cursive:
1490 : case gpos_mark2base: case gpos_mark2ligature: case gpos_mark2mark:
1491 : case gpos_context: case gpos_contextchain:
1492 0 : return( gpos_single_mask<<(lookup_type-gpos_single) );
1493 : case kern_statemachine:
1494 0 : return( kern_statemachine_mask );
1495 : default:
1496 0 : return( 0 );
1497 : }
1498 : }
1499 :
1500 0 : static GTextInfo *FeatureListFromLookupType(int lookup_type) {
1501 : int k, i, cnt, mask;
1502 : GTextInfo *ti;
1503 :
1504 0 : mask = MaskFromLookupType( lookup_type );
1505 0 : for ( k=0; k<2; ++k ) {
1506 0 : cnt = 0;
1507 0 : for ( i=0; friendlies[i].tag!=0; ++i ) if ( friendlies[i].masks&mask ) {
1508 0 : if ( k ) {
1509 : char buf[128];
1510 0 : snprintf(buf, sizeof buf, "%s %s", friendlies[i].tagstr, friendlies[i].friendlyname);
1511 0 : ti[cnt].text = (unichar_t *) copy( buf );
1512 0 : ti[cnt].text_is_1byte = true;
1513 0 : ti[cnt].userdata = friendlies[i].tagstr;
1514 : }
1515 0 : ++cnt;
1516 : }
1517 0 : if ( cnt==0 ) {
1518 0 : if ( k ) {
1519 0 : ti[cnt].text = (unichar_t *) copy( _("You must choose a lookup type") );
1520 0 : ti[cnt].text_is_1byte = true;
1521 0 : ti[cnt].userdata = "????";
1522 : }
1523 0 : ++cnt;
1524 : }
1525 0 : if ( !k )
1526 0 : ti = calloc(cnt+1,sizeof(GTextInfo));
1527 : }
1528 0 : return( ti );
1529 : }
1530 :
1531 0 : static int Lookup_NameChanged(GGadget *g, GEvent *e) {
1532 :
1533 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
1534 0 : struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1535 0 : if ( *_GGadgetGetTitle(g)!='\0' )
1536 0 : ld->name_has_been_set = true;
1537 : }
1538 0 : return( true );
1539 : }
1540 :
1541 0 : static int LK_TypeChanged(GGadget *g, GEvent *e) {
1542 :
1543 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
1544 0 : int lookup_type = (intpt) GGadgetGetListItemSelected(g)->userdata;
1545 0 : struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1546 0 : GTextInfo *ti = FeatureListFromLookupType( lookup_type );
1547 0 : GMatrixEditSetColumnChoices( GWidgetGetControl(ld->gw,CID_LookupFeatures), 0, ti );
1548 0 : GTextInfoListFree(ti);
1549 0 : if ( !ld->isgpos ) {
1550 0 : GGadgetSetEnabled( GWidgetGetControl(ld->gw,CID_LookupAfm ), lookup_type==gsub_ligature );
1551 0 : if ( lookup_type!=gsub_ligature )
1552 0 : GGadgetSetChecked( GWidgetGetControl(ld->gw,CID_LookupAfm ), false );
1553 : }
1554 : }
1555 0 : return( true );
1556 : }
1557 :
1558 0 : static int Lookup_OK(GGadget *g, GEvent *e) {
1559 :
1560 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1561 0 : struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1562 0 : int lookup_type = (intpt) GGadgetGetListItemSelected(GWidgetGetControl(ld->gw,CID_LookupType))->userdata;
1563 : int rows, i, isgpos;
1564 0 : struct matrix_data *strings = GMatrixEditGet(GWidgetGetControl(ld->gw,CID_LookupFeatures), &rows);
1565 : char *pt, *start, *name;
1566 0 : OTLookup *otl = ld->orig, *test;
1567 : int flags, afm;
1568 : FeatureScriptLangList *fhead;
1569 : int feat, set;
1570 :
1571 0 : if ( lookup_type==ot_undef ) {
1572 0 : ff_post_error(_("No Lookup Type Selected"),_("You must select a Lookup Type."));
1573 0 : return(true);
1574 : }
1575 0 : if ( *_GGadgetGetTitle(GWidgetGetControl(ld->gw,CID_LookupName))=='\0' ) {
1576 0 : ff_post_error(_("Unnamed lookup"),_("You must name the lookup."));
1577 0 : return(true);
1578 : }
1579 0 : for ( i=0; i<rows; ++i ) {
1580 0 : if ( sscanf(strings[2*i+0].u.md_str,"<%d,%d>", &feat, &set )== 2 )
1581 : /* It's a mac feature/setting */;
1582 0 : else if ( strlen(strings[2*i+0].u.md_str)>4 ) {
1583 0 : 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>)"),
1584 0 : i+1, strings[2*i+0].u.md_str);
1585 0 : return(true);
1586 : } else {
1587 0 : for ( pt=strings[2*i+0].u.md_str; *pt!='\0' ; ++pt )
1588 0 : if ( *pt>0x7e ) {
1589 0 : ff_post_error(_("Bad feature tag"),_("The feature tag on line %d (%s) should be in ASCII.\n"),
1590 0 : i+1, strings[2*i+0].u.md_str);
1591 0 : return( true );
1592 : }
1593 : }
1594 : /* Now check the script langs */
1595 0 : for ( start=strings[2*i+1].u.md_str; *start!='\0'; ) {
1596 0 : for ( pt=start; *pt!='{' && *pt!='\0'; ++pt ) {
1597 0 : if ( *pt>0x7e ) {
1598 0 : ff_post_error(_("Bad script tag"),_("A script tag on line %d (%s) should be in ASCII.\n"),
1599 0 : i+1, strings[2*i+1].u.md_str);
1600 0 : return( true );
1601 : }
1602 : }
1603 0 : if ( pt-start>4 ) {
1604 0 : ff_post_error(_("Bad script tag"),_("A script tag on line %d (%s) is too long. It may be at most 4 letters"),
1605 0 : i+1, strings[2*i+0].u.md_str);
1606 0 : return(true);
1607 : }
1608 0 : if ( *pt=='{' ) {
1609 0 : for ( start=pt+1; *start!='}' && *start!='\0' ; ) {
1610 0 : for ( pt=start; *pt!='}' && *pt!=',' && *pt!='\0'; ++pt ) {
1611 0 : if ( *pt>0x7e ) {
1612 0 : ff_post_error(_("Bad language tag"),_("A language tag on line %d (%s) should be in ASCII.\n"),
1613 0 : i+1, strings[2*i+1].u.md_str);
1614 0 : return( true );
1615 : }
1616 : }
1617 0 : if ( pt-start>4 ) {
1618 0 : ff_post_error(_("Bad language tag"),_("A language tag on line %d (%s) is too long. It may be at most 4 letters"),
1619 0 : i+1, strings[2*i+0].u.md_str);
1620 0 : return(true);
1621 : }
1622 0 : start = ( *pt==',' ) ? pt+1 : pt;
1623 : }
1624 0 : if ( *start=='}' ) ++start;
1625 : } else
1626 0 : start = pt;
1627 0 : while ( *start==' ' ) ++start;
1628 : }
1629 : }
1630 0 : name = GGadgetGetTitle8(GWidgetGetControl(ld->gw,CID_LookupName));
1631 0 : for ( isgpos=0; isgpos<2; ++isgpos ) {
1632 0 : for ( test = isgpos ? ld->sf->gpos_lookups : ld->sf->gsub_lookups; test!=NULL; test=test->next ) {
1633 0 : if ( test!=otl && strcmp(test->lookup_name,name)==0 ) {
1634 0 : ff_post_error(_("Lookup name already used"),_("This name has already been used for another lookup.\nLookup names must be unique.") );
1635 0 : free(name);
1636 0 : return(true);
1637 : }
1638 : }
1639 : }
1640 :
1641 0 : flags = 0;
1642 0 : if ( GGadgetIsChecked(GWidgetGetControl(ld->gw,CID_Lookup_R2L)) ) flags |= pst_r2l;
1643 0 : if ( GGadgetIsChecked(GWidgetGetControl(ld->gw,CID_Lookup_IgnBase)) ) flags |= pst_ignorebaseglyphs;
1644 0 : if ( GGadgetIsChecked(GWidgetGetControl(ld->gw,CID_Lookup_IgnLig)) ) flags |= pst_ignoreligatures;
1645 0 : if ( GGadgetIsChecked(GWidgetGetControl(ld->gw,CID_Lookup_IgnMark)) ) flags |= pst_ignorecombiningmarks;
1646 0 : flags |= ((intpt) GGadgetGetListItemSelected(GWidgetGetControl(ld->gw,CID_Lookup_ProcessMark))->userdata)<<8;
1647 0 : set = ((intpt) GGadgetGetListItemSelected(GWidgetGetControl(ld->gw,CID_Lookup_ProcessSet))->userdata);
1648 0 : if ( set!=-1 )
1649 0 : flags |= pst_usemarkfilteringset | (set<<16);
1650 :
1651 0 : if ( !ld->isgpos )
1652 0 : afm = GGadgetIsChecked( GWidgetGetControl(ld->gw,CID_LookupAfm ));
1653 : else
1654 0 : afm = false;
1655 :
1656 : /* Ok, we validated the feature script lang list. Now parse it */
1657 0 : fhead = LK_ParseFL(strings,rows);
1658 0 : free( otl->lookup_name );
1659 0 : FeatureScriptLangListFree( otl->features );
1660 0 : otl->lookup_name = name;
1661 0 : otl->lookup_type = lookup_type;
1662 0 : otl->lookup_flags = flags;
1663 0 : otl->features = FLOrder(fhead);
1664 0 : otl->store_in_afm = afm;
1665 0 : ld->done = true;
1666 0 : ld->ok = true;
1667 : }
1668 0 : return( true );
1669 : }
1670 :
1671 0 : static int Lookup_Cancel(GGadget *g, GEvent *e) {
1672 :
1673 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1674 0 : struct lookup_dlg *ld = GDrawGetUserData(GGadgetGetWindow(g));
1675 0 : ld->done = true;
1676 0 : ld->ok = false;
1677 : }
1678 0 : return( true );
1679 : }
1680 :
1681 0 : static int lookup_e_h(GWindow gw, GEvent *event) {
1682 :
1683 0 : if ( event->type==et_close ) {
1684 0 : struct lookup_dlg *ld = GDrawGetUserData(gw);
1685 0 : ld->done = true;
1686 0 : ld->ok = false;
1687 0 : } else if ( event->type == et_char ) {
1688 0 : if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
1689 0 : help("lookups.html#Add-Lookup");
1690 0 : return( true );
1691 : }
1692 0 : return( false );
1693 : }
1694 :
1695 0 : return( true );
1696 : }
1697 :
1698 0 : int EditLookup(OTLookup *otl,int isgpos,SplineFont *sf) {
1699 : /* Ok we must provide a lookup type (but only if otl->type==ot_undef) */
1700 : /* a set of lookup flags */
1701 : /* a name */
1702 : /* A potentially empty set of features, scripts and languages */
1703 : /* afm */
1704 : /* check to make sure the name isn't a duplicate */
1705 : GRect pos;
1706 : GWindow gw;
1707 : GWindowAttrs wattrs;
1708 : GGadgetCreateData gcd[16], boxes[7];
1709 : GGadgetCreateData *harray1[4], *varray[14], *flaghvarray[10], *flagarray[12],
1710 : *harray2[4], *harray3[8];
1711 : GTextInfo label[16];
1712 : struct lookup_dlg ld;
1713 : struct matrixinit mi;
1714 : int class, i, k, vpos;
1715 :
1716 0 : LookupUIInit();
1717 :
1718 0 : memset(&ld,0,sizeof(ld));
1719 0 : memset(&wattrs,0,sizeof(wattrs));
1720 0 : wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
1721 0 : wattrs.event_masks = ~(1<<et_charup);
1722 0 : wattrs.is_dlg = true;
1723 0 : wattrs.restrict_input_to_me = 1;
1724 0 : wattrs.undercursor = 1;
1725 0 : wattrs.cursor = ct_pointer;
1726 0 : wattrs.utf8_window_title = _("Lookup");
1727 0 : pos.x = pos.y = 0;
1728 0 : pos.width =GDrawPointsToPixels(NULL,GGadgetScale(268));
1729 0 : pos.height = GDrawPointsToPixels(NULL,375);
1730 0 : ld.gw = gw = GDrawCreateTopWindow(NULL,&pos,lookup_e_h,&ld,&wattrs);
1731 :
1732 0 : ld.orig = otl;
1733 0 : ld.sf = sf;
1734 0 : ld.isgpos = isgpos;
1735 :
1736 0 : memset(boxes,0,sizeof(boxes));
1737 0 : memset(gcd,0,sizeof(gcd));
1738 0 : memset(label,0,sizeof(label));
1739 :
1740 0 : label[0].text = (unichar_t *) _("Type:");
1741 0 : label[0].text_is_1byte = true;
1742 0 : label[0].text_in_resource = true;
1743 0 : gcd[0].gd.label = &label[0];
1744 0 : gcd[0].gd.pos.x = 12; gcd[0].gd.pos.y = 6+6;
1745 0 : gcd[0].gd.flags = gg_visible | gg_enabled;
1746 0 : gcd[0].creator = GLabelCreate;
1747 :
1748 0 : gcd[1].gd.pos.x = 100; gcd[1].gd.pos.y = 6; gcd[1].gd.pos.width = 200;
1749 0 : gcd[1].gd.flags = otl->lookup_type!=ot_undef && otl->subtables!=NULL?
1750 : (gg_visible| gg_utf8_popup) : (gg_visible | gg_enabled| gg_utf8_popup);
1751 0 : gcd[1].gd.cid = CID_LookupType;
1752 0 : gcd[1].gd.u.list = lookuptypes[isgpos];
1753 0 : gcd[1].gd.handle_controlevent = LK_TypeChanged;
1754 0 : gcd[1].gd.popup_msg = (unichar_t *) _(
1755 : "Each lookup may contain many transformations,\n"
1756 : "but each transformation must be of the same type.");
1757 0 : gcd[1].creator = GListButtonCreate;
1758 :
1759 0 : for ( i=0; lookuptypes[isgpos][i].text!=NULL || lookuptypes[isgpos][i].line; ++i ) {
1760 0 : if ( (void *) otl->lookup_type == lookuptypes[isgpos][i].userdata &&
1761 0 : !lookuptypes[isgpos][i].line) {
1762 0 : lookuptypes[isgpos][i].selected = true;
1763 0 : gcd[1].gd.label = &lookuptypes[isgpos][i];
1764 : } else
1765 0 : lookuptypes[isgpos][i].selected = false;
1766 : }
1767 0 : harray1[0] = &gcd[0]; harray1[1] = &gcd[1]; harray1[2] = GCD_Glue; harray1[3] = NULL;
1768 0 : boxes[0].gd.flags = gg_enabled|gg_visible;
1769 0 : boxes[0].gd.u.boxelements = harray1;
1770 0 : boxes[0].creator = GHBoxCreate;
1771 0 : varray[0] = &boxes[0]; varray[1] = NULL;
1772 :
1773 0 : LKMatrixInit(&mi,otl);
1774 0 : featureci[0].enum_vals = FeatureListFromLookupType(otl->lookup_type);
1775 :
1776 0 : gcd[2].gd.pos.x = 10; gcd[2].gd.pos.y = gcd[1].gd.pos.y+14;
1777 0 : gcd[2].gd.pos.width = 300; gcd[2].gd.pos.height = 90;
1778 0 : gcd[2].gd.flags = gg_enabled | gg_visible | gg_utf8_popup;
1779 0 : gcd[2].gd.cid = CID_LookupFeatures;
1780 0 : gcd[2].gd.u.matrix = &mi;
1781 0 : gcd[2].gd.popup_msg = (unichar_t *) _(
1782 : "Most lookups will be attached to a feature\n"
1783 : "active in a specific script for certain languages.\n"
1784 : "In some cases lookups will not be attached to any\n"
1785 : "feature, but will be invoked by another lookup,\n"
1786 : "a conditional one. In other cases a lookup might\n"
1787 : "be attached to several features.\n"
1788 : "A feature is either a four letter OpenType feature\n"
1789 : "tag, or a two number mac <feature,setting> combination.");
1790 0 : gcd[2].creator = GMatrixEditCreate;
1791 0 : varray[2] = &gcd[2]; varray[3] = NULL;
1792 :
1793 :
1794 0 : gcd[3].gd.pos.x = 5; gcd[3].gd.pos.y = 5;
1795 0 : gcd[3].gd.flags = gg_visible | gg_enabled | (otl->lookup_flags&pst_r2l?gg_cb_on:0);
1796 0 : label[3].text = (unichar_t *) _("Right To Left");
1797 0 : label[3].text_is_1byte = true;
1798 0 : gcd[3].gd.label = &label[3];
1799 0 : gcd[3].gd.cid = CID_Lookup_R2L;
1800 0 : gcd[3].creator = GCheckBoxCreate;
1801 0 : flagarray[0] = &gcd[3]; flagarray[1] = NULL;
1802 :
1803 0 : gcd[4].gd.pos.x = 5; gcd[4].gd.pos.y = gcd[3].gd.pos.y+15;
1804 0 : gcd[4].gd.flags = gg_visible | gg_enabled | (otl->lookup_flags&pst_ignorebaseglyphs?gg_cb_on:0);
1805 0 : label[4].text = (unichar_t *) _("Ignore Base Glyphs");
1806 0 : label[4].text_is_1byte = true;
1807 0 : gcd[4].gd.label = &label[4];
1808 0 : gcd[4].gd.cid = CID_Lookup_IgnBase;
1809 0 : gcd[4].creator = GCheckBoxCreate;
1810 0 : flagarray[2] = &gcd[4]; flagarray[3] = NULL;
1811 :
1812 0 : gcd[5].gd.pos.x = 5; gcd[5].gd.pos.y = gcd[4].gd.pos.y+15;
1813 0 : gcd[5].gd.flags = gg_visible | gg_enabled | (otl->lookup_flags&pst_ignoreligatures?gg_cb_on:0);
1814 0 : label[5].text = (unichar_t *) _("Ignore Ligatures");
1815 0 : label[5].text_is_1byte = true;
1816 0 : gcd[5].gd.label = &label[5];
1817 0 : gcd[5].gd.cid = CID_Lookup_IgnLig;
1818 0 : gcd[5].creator = GCheckBoxCreate;
1819 0 : flagarray[4] = &gcd[5]; flagarray[5] = NULL;
1820 :
1821 0 : gcd[6].gd.pos.x = 5; gcd[6].gd.pos.y = gcd[5].gd.pos.y+15;
1822 0 : gcd[6].gd.flags = gg_visible | gg_enabled | (otl->lookup_flags&pst_ignorecombiningmarks?gg_cb_on:0);
1823 0 : label[6].text = (unichar_t *) _("Ignore Combining Marks");
1824 0 : label[6].text_is_1byte = true;
1825 0 : gcd[6].gd.label = &label[6];
1826 0 : gcd[6].gd.cid = CID_Lookup_IgnMark;
1827 0 : gcd[6].creator = GCheckBoxCreate;
1828 0 : flagarray[6] = &gcd[6]; flagarray[7] = NULL;
1829 :
1830 : /* GT: Process is a verb here and Mark is a noun. */
1831 : /* GT: Marks of the given mark class are to be processed */
1832 0 : label[7].text = (unichar_t *) _("Mark Class:");
1833 0 : label[7].text_is_1byte = true;
1834 0 : gcd[7].gd.label = &label[7];
1835 0 : gcd[7].gd.pos.x = 5; gcd[7].gd.pos.y = gcd[6].gd.pos.y+16;
1836 0 : gcd[7].gd.flags = sf->mark_class_cnt<=1 ? gg_visible : (gg_enabled|gg_visible);
1837 0 : gcd[7].creator = GLabelCreate;
1838 0 : flaghvarray[0] = &gcd[7];
1839 :
1840 0 : gcd[8].gd.pos.x = 10; gcd[8].gd.pos.y = gcd[7].gd.pos.y;
1841 0 : gcd[8].gd.pos.width = 140;
1842 0 : gcd[8].gd.flags = gcd[7].gd.flags;
1843 0 : if ( (class = ((otl->lookup_flags&0xff00)>>8) )>= sf->mark_class_cnt )
1844 0 : class = 0;
1845 0 : gcd[8].gd.u.list = SFMarkClassList(sf,class);
1846 0 : gcd[8].gd.label = &gcd[8].gd.u.list[class];
1847 0 : gcd[8].gd.cid = CID_Lookup_ProcessMark;
1848 0 : gcd[8].creator = GListButtonCreate;
1849 0 : flaghvarray[1] = &gcd[8]; flaghvarray[2] = GCD_Glue; flaghvarray[3] = NULL;
1850 :
1851 : /* GT: Mark is a noun here and Set is also a noun. */
1852 0 : label[9].text = (unichar_t *) _("Mark Set:");
1853 0 : label[9].text_is_1byte = true;
1854 0 : gcd[9].gd.label = &label[9];
1855 0 : gcd[9].gd.flags = sf->mark_set_cnt<1 ? gg_visible : (gg_enabled|gg_visible);
1856 0 : gcd[9].creator = GLabelCreate;
1857 0 : flaghvarray[4] = &gcd[9];
1858 :
1859 0 : gcd[10].gd.pos.width = 140;
1860 0 : gcd[10].gd.flags = gcd[9].gd.flags;
1861 0 : class = (otl->lookup_flags>>16) & 0xffff;
1862 0 : if ( !(otl->lookup_flags&pst_usemarkfilteringset) || class >= sf->mark_set_cnt )
1863 0 : class = -1;
1864 0 : gcd[10].gd.u.list = SFMarkSetList(sf,class);
1865 0 : gcd[10].gd.label = &gcd[10].gd.u.list[class+1];
1866 0 : gcd[10].gd.cid = CID_Lookup_ProcessSet;
1867 0 : gcd[10].creator = GListButtonCreate;
1868 0 : flaghvarray[5] = &gcd[10]; flaghvarray[6] = GCD_Glue; flaghvarray[7] = NULL; flaghvarray[8] = NULL;
1869 :
1870 0 : boxes[1].gd.flags = gg_enabled|gg_visible;
1871 0 : boxes[1].gd.u.boxelements = flaghvarray;
1872 0 : boxes[1].creator = GHVBoxCreate;
1873 :
1874 0 : flagarray[8] = &boxes[1]; flagarray[9] = NULL; flagarray[10] = NULL;
1875 :
1876 0 : boxes[2].gd.pos.x = boxes[2].gd.pos.y = 2;
1877 0 : boxes[2].gd.flags = gg_enabled|gg_visible;
1878 0 : boxes[2].gd.u.boxelements = flagarray;
1879 0 : boxes[2].creator = GHVGroupCreate;
1880 0 : varray[4] = &boxes[2]; varray[5] = NULL;
1881 :
1882 0 : label[11].text = (unichar_t *) _("Lookup Name:");
1883 0 : label[11].text_is_1byte = true;
1884 0 : gcd[11].gd.label = &label[11];
1885 0 : gcd[11].gd.pos.x = 5; gcd[11].gd.pos.y = gcd[8].gd.pos.y+16;
1886 0 : gcd[11].gd.flags = gg_enabled|gg_visible;
1887 0 : gcd[11].creator = GLabelCreate;
1888 0 : harray2[0] = &gcd[11];
1889 :
1890 0 : label[12].text = (unichar_t *) otl->lookup_name;
1891 0 : label[12].text_is_1byte = true;
1892 0 : gcd[12].gd.pos.x = 10; gcd[12].gd.pos.y = gcd[11].gd.pos.y;
1893 0 : gcd[12].gd.pos.width = 140;
1894 0 : gcd[12].gd.flags = gcd[11].gd.flags|gg_text_xim;
1895 0 : gcd[12].gd.label = otl->lookup_name==NULL ? NULL : &label[12];
1896 0 : gcd[12].gd.cid = CID_LookupName;
1897 0 : gcd[12].gd.handle_controlevent = Lookup_NameChanged;
1898 0 : gcd[12].creator = GTextFieldCreate;
1899 0 : harray2[1] = &gcd[12]; harray2[2] = NULL;
1900 0 : if ( otl->lookup_name!=NULL && *otl->lookup_name!='\0' )
1901 0 : ld.name_has_been_set = true;
1902 :
1903 0 : boxes[3].gd.flags = gg_enabled|gg_visible;
1904 0 : boxes[3].gd.u.boxelements = harray2;
1905 0 : boxes[3].creator = GHBoxCreate;
1906 0 : varray[6] = &boxes[3]; varray[7] = NULL;
1907 :
1908 0 : k = 13; vpos = 8;
1909 0 : if ( !isgpos ) {
1910 0 : gcd[13].gd.pos.x = 5; gcd[13].gd.pos.y = gcd[5].gd.pos.y+15;
1911 0 : gcd[13].gd.flags = otl->lookup_type!=gsub_ligature ? gg_visible :
1912 0 : otl->store_in_afm ? (gg_visible | gg_enabled | gg_cb_on) :
1913 : (gg_visible | gg_enabled);
1914 0 : label[13].text = (unichar_t *) _("Store ligature data in AFM files");
1915 0 : label[13].text_is_1byte = true;
1916 0 : gcd[13].gd.label = &label[13];
1917 0 : gcd[13].gd.cid = CID_LookupAfm;
1918 0 : gcd[13].creator = GCheckBoxCreate;
1919 0 : varray[8] = &gcd[13]; varray[9] = NULL;
1920 0 : k = 14; vpos = 10;
1921 : }
1922 :
1923 0 : gcd[k].gd.pos.x = 30-3;
1924 0 : gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
1925 0 : gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
1926 0 : label[k].text = (unichar_t *) _("_OK");
1927 0 : label[k].text_is_1byte = true;
1928 0 : label[k].text_in_resource = true;
1929 0 : gcd[k].gd.label = &label[k];
1930 0 : gcd[k].gd.handle_controlevent = Lookup_OK;
1931 0 : gcd[k].gd.cid = CID_OK;
1932 0 : gcd[k].creator = GButtonCreate;
1933 :
1934 0 : gcd[k+1].gd.pos.x = -30;
1935 0 : gcd[k+1].gd.pos.width = -1; gcd[k+1].gd.pos.height = 0;
1936 0 : gcd[k+1].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
1937 0 : label[k+1].text = (unichar_t *) _("_Cancel");
1938 0 : label[k+1].text_is_1byte = true;
1939 0 : label[k+1].text_in_resource = true;
1940 0 : gcd[k+1].gd.label = &label[k+1];
1941 0 : gcd[k+1].gd.handle_controlevent = Lookup_Cancel;
1942 0 : gcd[k+1].gd.cid = CID_Cancel;
1943 0 : gcd[k+1].creator = GButtonCreate;
1944 :
1945 0 : harray3[0] = harray3[2] = harray3[3] = harray3[4] = harray3[6] = GCD_Glue;
1946 0 : harray3[7] = NULL;
1947 0 : harray3[1] = &gcd[k]; harray3[5] = &gcd[k+1];
1948 :
1949 0 : boxes[4].gd.flags = gg_enabled|gg_visible;
1950 0 : boxes[4].gd.u.boxelements = harray3;
1951 0 : boxes[4].creator = GHBoxCreate;
1952 0 : varray[vpos++] = &boxes[4]; varray[vpos++] = NULL; varray[vpos] = NULL;
1953 :
1954 0 : boxes[5].gd.pos.x = boxes[5].gd.pos.y = 2;
1955 0 : boxes[5].gd.flags = gg_enabled|gg_visible;
1956 0 : boxes[5].gd.u.boxelements = varray;
1957 0 : boxes[5].creator = GHVGroupCreate;
1958 :
1959 0 : GGadgetsCreate(gw,boxes+5);
1960 :
1961 0 : GTextInfoListFree(gcd[8].gd.u.list);
1962 0 : GTextInfoListFree(gcd[10].gd.u.list);
1963 :
1964 0 : for ( i=0; i<mi.initial_row_cnt; ++i ) {
1965 0 : free( mi.matrix_data[2*i+0].u.md_str );
1966 0 : free( mi.matrix_data[2*i+1].u.md_str );
1967 : }
1968 0 : free( mi.matrix_data );
1969 :
1970 0 : GMatrixEditSetNewText(gcd[2].ret,S_("OpenTypeFeature|New"));
1971 0 : GHVBoxSetExpandableRow(boxes[5].ret,1);
1972 0 : GHVBoxSetExpandableCol(boxes[4].ret,gb_expandgluesame);
1973 0 : GHVBoxSetExpandableCol(boxes[3].ret,1);
1974 0 : GHVBoxSetExpandableCol(boxes[1].ret,1);
1975 0 : GHVBoxSetExpandableCol(boxes[0].ret,gb_expandglue);
1976 :
1977 0 : GHVBoxFitWindow(boxes[5].ret);
1978 :
1979 0 : GDrawSetVisible(gw,true);
1980 0 : while ( !ld.done )
1981 0 : GDrawProcessOneEvent(NULL);
1982 0 : GDrawDestroyWindow(gw);
1983 :
1984 0 : return( ld.ok );
1985 : }
1986 :
1987 0 : static OTLookup *CreateAndSortNewLookupOfType(SplineFont *sf, int lookup_type) {
1988 : OTLookup *newotl;
1989 0 : int isgpos = lookup_type>=gpos_start;
1990 :
1991 0 : newotl = chunkalloc(sizeof(OTLookup));
1992 0 : newotl->lookup_type = lookup_type;
1993 0 : if ( !EditLookup(newotl,isgpos,sf)) {
1994 0 : chunkfree(newotl,sizeof(OTLookup));
1995 0 : return( NULL );
1996 : }
1997 0 : SortInsertLookup(sf, newotl);
1998 0 : return( newotl );
1999 : }
2000 :
2001 : /* ************************************************************************** */
2002 : /* ***************************** Anchor Subtable **************************** */
2003 : /* ************************************************************************** */
2004 : typedef struct anchorclassdlg {
2005 : SplineFont *sf;
2006 : int def_layer;
2007 : struct lookup_subtable *sub;
2008 : GWindow gw;
2009 : int mag, pixelsize;
2010 : int orig_pos, orig_value, down;
2011 : BDFFont *display;
2012 : int done;
2013 : int popup_r;
2014 : GCursor cursor_current;
2015 : /* Used during autokern */
2016 : int rows_at_start;
2017 : int rows,cols; /* Total rows, columns allocated */
2018 : int next_row;
2019 : struct matrix_data *psts;
2020 : } AnchorClassDlg, PSTKernDlg;
2021 : #define CID_Anchors 2001
2022 :
2023 : #define CID_PSTList 2001
2024 : #define CID_Alpha 2002
2025 : #define CID_Unicode 2003
2026 : #define CID_Scripts 2004
2027 : #define CID_BaseChar 2005
2028 : #define CID_Suffix 2006
2029 : #define CID_AllSame 2007
2030 : #define CID_Separation 2008
2031 : #define CID_MinKern 2009
2032 : #define CID_Touched 2010
2033 : #define CID_OnlyCloser 2011
2034 : #define CID_Autokern 2012
2035 :
2036 : #define CID_KernDisplay 2022
2037 : #define CID_PixelSize 2023
2038 : #define CID_Magnification 2024
2039 :
2040 : static GTextInfo magnifications[] = {
2041 : { (unichar_t *) "100%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 1, 0, 1, 0, 0, '\0'},
2042 : { (unichar_t *) "200%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
2043 : { (unichar_t *) "300%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
2044 : { (unichar_t *) "400%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
2045 : GTEXTINFO_EMPTY
2046 : };
2047 :
2048 0 : static AnchorClass *SFAddAnchorClass(SplineFont *sf,struct lookup_subtable *sub,
2049 : char *name) {
2050 : AnchorClass *ac;
2051 :
2052 0 : for ( ac=sf->anchor; ac!=NULL; ac=ac->next) {
2053 0 : if ( strcmp(ac->name,name)==0 && ac->subtable==NULL )
2054 0 : break;
2055 : }
2056 0 : if ( ac==NULL ) {
2057 0 : ac = chunkalloc(sizeof(AnchorClass));
2058 0 : ac->name = copy(name);
2059 0 : ac->next = sf->anchor;
2060 0 : sf->anchor = ac;
2061 : }
2062 0 : ac->type = sub->lookup->lookup_type == gpos_mark2base ? act_mark :
2063 0 : sub->lookup->lookup_type == gpos_mark2ligature ? act_mklg :
2064 0 : sub->lookup->lookup_type == gpos_cursive ? act_curs :
2065 : act_mkmk;
2066 0 : ac->subtable = sub;
2067 0 : return( ac );
2068 : }
2069 :
2070 0 : static int AnchorClassD_ShowAnchors(GGadget *g, GEvent *e) {
2071 : AnchorClassDlg *acd;
2072 :
2073 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2074 : struct matrix_data *classes;
2075 : int32 class_cnt;
2076 : int row;
2077 : AnchorClass *ac;
2078 :
2079 0 : acd = GDrawGetUserData(GGadgetGetWindow(g));
2080 0 : classes = GMatrixEditGet(GWidgetGetControl(acd->gw,CID_Anchors), &class_cnt);
2081 0 : row = GMatrixEditGetActiveRow(GWidgetGetControl(acd->gw,CID_Anchors));
2082 0 : if ( row==-1 )
2083 0 : return( true );
2084 0 : ac = classes[2*row+1].u.md_addr;
2085 0 : if ( ac==NULL || ac->subtable==NULL) {
2086 0 : ac = SFAddAnchorClass(acd->sf,acd->sub,classes[2*row+0].u.md_str);
2087 0 : } else if ( ac->subtable!=acd->sub ) {
2088 0 : ff_post_error(_("Name in use"),_("The name, %.80s, has already been used to identify an anchor class in a different lookup subtable (%.80s)"),
2089 0 : ac->name, ac->subtable->subtable_name );
2090 0 : return( true );
2091 : }
2092 0 : AnchorControlClass(acd->sf,ac,acd->def_layer);
2093 : }
2094 0 : return( true );
2095 : }
2096 :
2097 0 : static void AC_EnableOtherButtons(GGadget *g,int r, int c) {
2098 0 : GGadgetSetEnabled(GWidgetGetControl(GGadgetGetWindow(g),CID_ShowAnchors),
2099 : r!=-1 );
2100 0 : }
2101 :
2102 0 : static int AC_OK(GGadget *g, GEvent *e) {
2103 : AnchorClassDlg *acd;
2104 :
2105 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2106 : struct matrix_data *classes;
2107 : int32 class_cnt;
2108 : int i,j;
2109 : AnchorClass *ac, *acnext, *actest;
2110 :
2111 0 : acd = GDrawGetUserData(GGadgetGetWindow(g));
2112 0 : classes = GMatrixEditGet(GWidgetGetControl(acd->gw,CID_Anchors), &class_cnt);
2113 0 : acd->sub->anchor_classes = true /*class_cnt!=0*/;
2114 0 : for ( i=0; i<class_cnt; ++i ) {
2115 0 : if ( *classes[2*i+0].u.md_str=='\0' )
2116 0 : continue; /* Ignore blank lines. They pressed return once too often or something */
2117 0 : for ( j=i+1; j<class_cnt; ++j ) {
2118 0 : if ( strcmp(classes[2*i+0].u.md_str,classes[2*j+0].u.md_str)==0 ) {
2119 0 : ff_post_error(_("Name used twice"),_("The name, %.80s, appears twice in this list.\nEach anchor class must have a distinct name."),
2120 0 : classes[2*i+0].u.md_str );
2121 0 : return( true );
2122 : }
2123 : }
2124 0 : for ( actest = acd->sf->anchor; actest!=NULL; actest=actest->next )
2125 0 : if ( actest->subtable!=NULL && actest->subtable!=acd->sub &&
2126 0 : strcmp(actest->name,classes[2*i+0].u.md_str)==0 )
2127 0 : break;
2128 0 : if ( actest!=NULL ) {
2129 0 : ff_post_error(_("Name in use"),_("The name, %.80s, has already been used to identify an anchor class in a different lookup subtable (%.80s)"),
2130 0 : actest->name, actest->subtable->subtable_name );
2131 0 : return( true );
2132 : }
2133 : }
2134 :
2135 0 : for ( ac = acd->sf->anchor; ac!=NULL; ac=ac->next )
2136 0 : ac->processed = false;
2137 0 : for ( i=0; i<class_cnt; ++i ) {
2138 0 : ac = classes[2*i+1].u.md_addr;
2139 0 : if ( ac!=NULL )
2140 0 : ac->processed = true;
2141 : }
2142 0 : for ( ac = acd->sf->anchor; ac!=NULL; ac=ac->next ) {
2143 0 : if ( !ac->processed && ac->subtable == acd->sub ) {
2144 : char *buts[3];
2145 0 : int askresult = 0;
2146 : /* want to support keeping APs when class is removed from subtable */
2147 0 : buts[0] = _("_Remove"); buts[1] = _("_Cancel"); buts[2]=NULL;
2148 0 : 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."),
2149 : ac->name );
2150 0 : if ( askresult==1 )
2151 0 : return( true );
2152 : else
2153 0 : ac->ticked = askresult==2 ? 0 : 1;
2154 : }
2155 : }
2156 :
2157 0 : for ( i=0; i<class_cnt; ++i ) {
2158 0 : if ( *classes[2*i+0].u.md_str=='\0' )
2159 0 : continue; /* Ignore blank lines. They pressed return once too much or something */
2160 0 : ac = classes[2*i+1].u.md_addr;
2161 0 : if ( ac==NULL || ac->subtable==NULL ) {
2162 0 : ac = SFAddAnchorClass(acd->sf,acd->sub,classes[2*i+0].u.md_str);
2163 0 : ac->processed = true;
2164 : } else {
2165 0 : free(ac->name);
2166 0 : ac->name = copy(classes[2*i+0].u.md_str);
2167 0 : ac->processed = true;
2168 : }
2169 : }
2170 0 : for ( ac = acd->sf->anchor; ac!=NULL; ac=acnext ) {
2171 0 : acnext = ac->next;
2172 0 : if ( !ac->processed && ac->subtable == acd->sub ) {
2173 0 : if ( ac->ticked )
2174 0 : SFRemoveAnchorClass(acd->sf,ac);
2175 : else {
2176 0 : ac->ticked = 0;
2177 0 : ac->subtable = NULL;
2178 : }
2179 : }
2180 : }
2181 0 : acd->done = true;
2182 : }
2183 0 : return( true );
2184 : }
2185 :
2186 0 : static void AC_DoCancel(AnchorClassDlg *acd) {
2187 0 : acd->done = true;
2188 0 : }
2189 :
2190 0 : static int AC_Cancel(GGadget *g, GEvent *e) {
2191 : AnchorClassDlg *acd;
2192 :
2193 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2194 0 : acd = GDrawGetUserData(GGadgetGetWindow(g));
2195 0 : AC_DoCancel(acd);
2196 : }
2197 0 : return( true );
2198 : }
2199 :
2200 0 : static int acd_e_h(GWindow gw, GEvent *event) {
2201 0 : AnchorClassDlg *acd = GDrawGetUserData(gw);
2202 :
2203 0 : switch ( event->type ) {
2204 : case et_close:
2205 0 : AC_DoCancel(acd);
2206 0 : break;
2207 : case et_char:
2208 0 : if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
2209 0 : help("lookups.html#Anchor");
2210 0 : return( true );
2211 : }
2212 0 : return( false );
2213 : break;
2214 : case et_destroy:
2215 0 : break;
2216 : case et_mouseup: case et_mousemove: case et_mousedown:
2217 0 : break;
2218 : case et_expose:
2219 0 : break;
2220 : case et_resize:
2221 0 : break;
2222 : }
2223 0 : return( true );
2224 : }
2225 :
2226 0 : static void ACDMatrixInit(struct matrixinit *mi,SplineFont *sf, struct lookup_subtable *sub) {
2227 : int cnt;
2228 : AnchorClass *ac;
2229 : struct matrix_data *md;
2230 : static struct col_init ci[] = {
2231 : { me_string , NULL, NULL, NULL, N_("Anchor Class Name") },
2232 : { me_addr , NULL, NULL, NULL, "Anchor Class Pointer, hidden" },
2233 : COL_INIT_EMPTY
2234 : };
2235 : static int initted = false;
2236 :
2237 0 : if ( !initted ) {
2238 0 : initted = true;
2239 0 : ci[0].title = S_(ci[0].title);
2240 : }
2241 :
2242 0 : memset(mi,0,sizeof(*mi));
2243 0 : mi->col_cnt = 2;
2244 0 : mi->col_init = ci;
2245 :
2246 0 : for ( ac=sf->anchor, cnt=0; ac!=NULL; ac=ac->next )
2247 0 : if ( ac->subtable == sub )
2248 0 : ++cnt;
2249 0 : if ( cnt==0 ) {
2250 0 : md = calloc(1,sizeof(struct matrix_data));
2251 0 : mi->initial_row_cnt = 0;
2252 : } else {
2253 0 : md = calloc(2*cnt,sizeof(struct matrix_data));
2254 0 : for ( ac=sf->anchor, cnt=0; ac!=NULL; ac=ac->next )
2255 0 : if ( ac->subtable == sub ) {
2256 0 : md[2*cnt +0].u.md_str = ac->name;
2257 0 : md[2*cnt++ +1].u.md_addr = ac;
2258 : }
2259 0 : mi->initial_row_cnt = cnt;
2260 : }
2261 0 : mi->matrix_data = md;
2262 0 : }
2263 :
2264 : /* Anchor list, [new], [delete], [edit], [show first mark/entry], [show first base/exit] */
2265 : /* [ok], [cancel] */
2266 0 : static void AnchorClassD(SplineFont *sf, struct lookup_subtable *sub, int def_layer) {
2267 : GRect pos;
2268 : GWindowAttrs wattrs;
2269 : AnchorClassDlg acd;
2270 : GWindow gw;
2271 : char buffer[200];
2272 : GGadgetCreateData gcd[6], buttonbox, mainbox[2];
2273 : GGadgetCreateData *buttonarray[8], *varray[7];
2274 : GTextInfo label[6];
2275 : int i;
2276 : struct matrixinit mi;
2277 :
2278 0 : memset(&acd,0,sizeof(acd));
2279 0 : acd.sf = sf;
2280 0 : acd.def_layer = def_layer;
2281 0 : acd.sub = sub;
2282 :
2283 0 : memset(&wattrs,0,sizeof(wattrs));
2284 0 : memset(&gcd,0,sizeof(gcd));
2285 0 : memset(&buttonbox,0,sizeof(buttonbox));
2286 0 : memset(&mainbox,0,sizeof(mainbox));
2287 0 : memset(&label,0,sizeof(label));
2288 :
2289 0 : wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
2290 0 : wattrs.event_masks = ~(1<<et_charup);
2291 0 : wattrs.restrict_input_to_me = true;
2292 0 : wattrs.undercursor = 1;
2293 0 : wattrs.cursor = ct_pointer;
2294 0 : snprintf(buffer,sizeof(buffer),_("Anchor classes in subtable %.80s"),
2295 : sub->subtable_name );
2296 0 : wattrs.utf8_window_title = buffer;
2297 0 : wattrs.is_dlg = true;
2298 0 : pos.x = pos.y = 0;
2299 0 : pos.width = GGadgetScale(GDrawPointsToPixels(NULL,325));
2300 0 : pos.height = GDrawPointsToPixels(NULL,250);
2301 0 : acd.gw = gw = GDrawCreateTopWindow(NULL,&pos,acd_e_h,&acd,&wattrs);
2302 :
2303 0 : i = 0;
2304 :
2305 0 : ACDMatrixInit(&mi,sf,sub);
2306 0 : gcd[i].gd.pos.width = 300; gcd[i].gd.pos.height = 200;
2307 0 : gcd[i].gd.flags = gg_enabled | gg_visible | gg_utf8_popup;
2308 0 : gcd[i].gd.cid = CID_Anchors;
2309 0 : gcd[i].gd.u.matrix = &mi;
2310 0 : gcd[i].data = ∿
2311 0 : gcd[i++].creator = GMatrixEditCreate;
2312 0 : varray[0] = &gcd[i-1]; varray[1] = NULL;
2313 :
2314 0 : gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+24+3;
2315 0 : gcd[i].gd.pos.width = -1;
2316 0 : gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
2317 0 : label[i].text = (unichar_t *) _("_OK");
2318 0 : label[i].text_is_1byte = true;
2319 0 : label[i].text_in_resource = true;
2320 0 : gcd[i].gd.label = &label[i];
2321 0 : gcd[i].gd.handle_controlevent = AC_OK;
2322 0 : gcd[i].gd.cid = CID_OK;
2323 0 : gcd[i++].creator = GButtonCreate;
2324 :
2325 0 : gcd[i].gd.pos.x = -10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+3;
2326 0 : gcd[i].gd.pos.width = -1;
2327 0 : gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
2328 0 : label[i].text = (unichar_t *) _("_Cancel");
2329 0 : label[i].text_is_1byte = true;
2330 0 : label[i].text_in_resource = true;
2331 0 : gcd[i].gd.label = &label[i];
2332 0 : gcd[i].gd.handle_controlevent = AC_Cancel;
2333 0 : gcd[i].gd.cid = CID_Cancel;
2334 0 : gcd[i++].creator = GButtonCreate;
2335 :
2336 0 : buttonarray[0] = GCD_Glue; buttonarray[1] = &gcd[i-2]; buttonarray[2] = GCD_Glue;
2337 0 : buttonarray[3] = GCD_Glue; buttonarray[4] = &gcd[i-1]; buttonarray[5] = GCD_Glue;
2338 0 : buttonarray[6] = NULL;
2339 0 : buttonbox.gd.flags = gg_enabled|gg_visible;
2340 0 : buttonbox.gd.u.boxelements = buttonarray;
2341 0 : buttonbox.creator = GHBoxCreate;
2342 0 : varray[2] = &buttonbox; varray[3] = NULL; varray[4] = NULL;
2343 :
2344 0 : mainbox[0].gd.pos.x = mainbox[0].gd.pos.y = 2;
2345 0 : mainbox[0].gd.flags = gg_enabled|gg_visible;
2346 0 : mainbox[0].gd.u.boxelements = varray;
2347 0 : mainbox[0].creator = GHVGroupCreate;
2348 :
2349 0 : GGadgetsCreate(acd.gw,mainbox);
2350 0 : GHVBoxSetExpandableRow(mainbox[0].ret,0);
2351 0 : GHVBoxSetExpandableCol(buttonbox.ret,gb_expandgluesame);
2352 0 : GMatrixEditShowColumn(gcd[0].ret,1,false);
2353 :
2354 0 : gcd[i].gd.flags = gg_visible | gg_enabled;
2355 0 : label[i].text = (unichar_t *) S_("Anchor Control...");
2356 0 : label[i].text_is_1byte = true;
2357 0 : label[i].text_in_resource = true;
2358 0 : gcd[i].gd.label = &label[i];
2359 0 : gcd[i].gd.cid = CID_ShowAnchors;
2360 0 : gcd[i].gd.handle_controlevent = AnchorClassD_ShowAnchors;
2361 0 : gcd[i].creator = GButtonCreate;
2362 :
2363 0 : GMatrixEditAddButtons(gcd[0].ret,gcd+i);
2364 0 : GMatrixEditSetOtherButtonEnable(gcd[0].ret,AC_EnableOtherButtons);
2365 0 : GMatrixEditSetNewText(gcd[0].ret,S_("New Anchor Class"));
2366 :
2367 0 : GDrawSetVisible(acd.gw,true);
2368 0 : while ( !acd.done )
2369 0 : GDrawProcessOneEvent(NULL);
2370 0 : GDrawDestroyWindow(acd.gw);
2371 0 : }
2372 :
2373 : /* ************************************************************************** */
2374 : /* ********************** Single/Double Glyph Subtables ********************* */
2375 : /* ************************************************************************** */
2376 : static int isalphabetic = true, byscripts = true, stemming=true;
2377 : int lookup_hideunused = true;
2378 : static int ispair;
2379 :
2380 : struct sortinfo {
2381 : char *glyphname; /* We replace the glyph name with a pointer to*/
2382 : /* this structure, but must restore it when */
2383 : /* finished sorting */
2384 : SplineChar *sc; /* The glyph itself */
2385 : SplineChar *base; /* The base of Agrave would be A */
2386 : uint32 script;
2387 : };
2388 :
2389 0 : static void SortPrep(PSTKernDlg *pstkd, struct matrix_data *md, struct sortinfo *si) {
2390 :
2391 0 : si->glyphname = md->u.md_str;
2392 0 : md->u.md_ival = (intpt) si;
2393 :
2394 0 : si->sc = SFGetChar(pstkd->sf,-1,si->glyphname);
2395 0 : if ( si->sc==NULL )
2396 0 : return;
2397 0 : if ( byscripts )
2398 0 : si->script = SCScriptFromUnicode(si->sc);
2399 0 : if ( stemming ) {
2400 0 : const unichar_t *alt=NULL;
2401 0 : int uni = si->sc->unicodeenc;
2402 : char *pt;
2403 :
2404 0 : if ( uni!=-1 && uni<0x10000 &&
2405 0 : isdecompositionnormative(uni) &&
2406 0 : unicode_alternates[uni>>8]!=NULL &&
2407 0 : (alt = unicode_alternates[uni>>8][uni&0xff])!=NULL )
2408 0 : si->base = SFGetChar(pstkd->sf,alt[0],NULL);
2409 0 : if ( si->base==NULL &&
2410 0 : ((pt=strchr(si->glyphname,'.'))!=NULL ||
2411 0 : (pt=strchr(si->glyphname,'_'))!=NULL )) {
2412 0 : int ch = *pt;
2413 0 : *pt = '\0';
2414 0 : si->base = SFGetChar(pstkd->sf,-1,si->glyphname);
2415 0 : *pt = ch;
2416 : }
2417 0 : if ( si->base==NULL )
2418 0 : si->base = si->sc;
2419 : }
2420 : }
2421 :
2422 0 : static void SortUnPrep(struct matrix_data *md) {
2423 0 : struct sortinfo *si = (struct sortinfo *) (md->u.md_ival);
2424 :
2425 0 : md->u.md_str = si->glyphname;
2426 0 : }
2427 :
2428 0 : static int _md_cmp(const struct sortinfo *md1, const struct sortinfo *md2) {
2429 :
2430 0 : if ( md1->sc==NULL || md2->sc==NULL ) {
2431 0 : if ( md1->sc!=NULL )
2432 0 : return( 1 );
2433 0 : else if ( md2->sc!=NULL )
2434 0 : return( -1 );
2435 : else
2436 0 : return( strcmp( md1->glyphname,md2->glyphname ));
2437 : }
2438 :
2439 0 : if ( byscripts && md1->script!=md2->script ) {
2440 0 : if ( md1->script==DEFAULT_SCRIPT )
2441 0 : return( 1 );
2442 0 : else if ( md2->script==DEFAULT_SCRIPT )
2443 0 : return( -1 );
2444 0 : if ( md1->script>md2->script )
2445 0 : return( 1 );
2446 : else
2447 0 : return( -1 );
2448 : }
2449 :
2450 0 : if ( !isalphabetic ) {
2451 : int uni1;
2452 : int uni2;
2453 :
2454 0 : if ( stemming ) {
2455 : /* First ignore case */
2456 0 : uni1 = (md1->base->unicodeenc!=-1)? md1->base->unicodeenc : 0xffffff;
2457 0 : uni2 = (md2->base->unicodeenc!=-1)? md2->base->unicodeenc : 0xffffff;
2458 0 : if ( uni1<0x10000 && islower(uni1)) uni1 = toupper(uni1);
2459 0 : if ( uni2<0x10000 && islower(uni2)) uni2 = toupper(uni2);
2460 :
2461 0 : if ( uni1>uni2 )
2462 0 : return( 1 );
2463 0 : else if ( uni1<uni2 )
2464 0 : return( -1 );
2465 :
2466 0 : uni1 = (md1->base->unicodeenc!=-1)? md1->base->unicodeenc : 0xffffff;
2467 0 : uni2 = (md2->base->unicodeenc!=-1)? md2->base->unicodeenc : 0xffffff;
2468 0 : if ( uni1>uni2 )
2469 0 : return( 1 );
2470 0 : else if ( uni1<uni2 )
2471 0 : return( -1 );
2472 : }
2473 :
2474 0 : uni1 = (md1->sc->unicodeenc!=-1)? md1->sc->unicodeenc : 0xffffff;
2475 0 : uni2 = (md2->sc->unicodeenc!=-1)? md2->sc->unicodeenc : 0xffffff;
2476 0 : if ( uni1>uni2 )
2477 0 : return( 1 );
2478 0 : else if ( uni1<uni2 )
2479 0 : return( -1 );
2480 : } else {
2481 0 : if ( stemming ) {
2482 : int ret;
2483 0 : ret = strcasecmp(md1->base->name,md2->base->name);
2484 0 : if ( ret!=0 )
2485 0 : return( ret );
2486 0 : ret = strcmp(md1->base->name,md2->base->name);
2487 0 : if ( ret!=0 )
2488 0 : return( ret );
2489 : }
2490 : }
2491 0 : return( strcmp(md1->glyphname,md2->glyphname));
2492 : }
2493 :
2494 0 : static int md_cmp(const void *_md1, const void *_md2) {
2495 0 : const struct matrix_data *md1 = _md1, *md2 = _md2;
2496 0 : int ret = _md_cmp((struct sortinfo *) md1->u.md_ival,(struct sortinfo *) md2->u.md_ival);
2497 :
2498 0 : if ( ret==0 && ispair )
2499 0 : ret = _md_cmp((struct sortinfo *) md1[1].u.md_ival,(struct sortinfo *) md2[1].u.md_ival);
2500 0 : return( ret );
2501 : }
2502 :
2503 0 : static void PSTKD_DoSort(PSTKernDlg *pstkd,struct matrix_data *psts,int rows,int cols) {
2504 0 : struct sortinfo *primary, *secondary=NULL;
2505 : int i;
2506 :
2507 0 : if ( pstkd->gw!=NULL && GWidgetGetControl(pstkd->gw,CID_Alpha)!=NULL ) {
2508 0 : isalphabetic = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Alpha));
2509 0 : byscripts = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Scripts));
2510 0 : stemming = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_BaseChar));
2511 : }
2512 0 : primary = calloc(rows,sizeof(struct sortinfo));
2513 0 : ispair = pstkd->sub->lookup->lookup_type == gpos_pair;
2514 0 : if ( ispair )
2515 0 : secondary = calloc(rows,sizeof(struct sortinfo));
2516 0 : for ( i=0; i<rows; ++i ) {
2517 0 : SortPrep(pstkd,&psts[i*cols+0],&primary[i]);
2518 0 : if ( ispair )
2519 0 : SortPrep(pstkd,&psts[i*cols+1],&secondary[i]);
2520 : }
2521 0 : qsort( psts, rows, cols*sizeof(struct matrix_data), md_cmp);
2522 0 : for ( i=0; i<rows; ++i ) {
2523 0 : SortUnPrep(&psts[i*cols+0]);
2524 0 : if ( ispair )
2525 0 : SortUnPrep(&psts[i*cols+1]);
2526 : }
2527 0 : free(primary);
2528 0 : free(secondary);
2529 0 : }
2530 :
2531 0 : static void PST_FreeImage(const void *_pstkd, GImage *img) {
2532 0 : GImageDestroy(img);
2533 0 : }
2534 :
2535 0 : static GImage *_PST_GetImage(const void *_pstkd) {
2536 0 : PSTKernDlg *pstkd = (PSTKernDlg *) _pstkd;
2537 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
2538 0 : int rows, cols = GMatrixEditGetColCnt(pstk);
2539 0 : struct matrix_data *old = GMatrixEditGet(pstk,&rows);
2540 0 : SplineChar *sc = SFGetChar(pstkd->sf,-1, old[cols*pstkd->popup_r].u.md_str);
2541 :
2542 0 : return( PST_GetImage(pstk,pstkd->sf,pstkd->def_layer,pstkd->sub,pstkd->popup_r,sc) );
2543 : }
2544 :
2545 0 : static void PST_PopupPrepare(GGadget *g, int r, int c) {
2546 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
2547 0 : int rows, cols = GMatrixEditGetColCnt(g);
2548 0 : struct matrix_data *old = GMatrixEditGet(g,&rows);
2549 0 : if ( c!=0 && pstkd->sub->lookup->lookup_type == gpos_single )
2550 0 : return;
2551 0 : if ( c<0 || c>=cols || r<0 || r>=rows || old[cols*r].u.md_str==NULL ||
2552 0 : SFGetChar(pstkd->sf,-1, old[cols*r].u.md_str)==NULL )
2553 0 : return;
2554 0 : pstkd->popup_r = r;
2555 0 : GGadgetPreparePopupImage(GGadgetGetWindow(g),NULL,pstkd,_PST_GetImage,PST_FreeImage);
2556 : }
2557 :
2558 : static void PSTKD_FinishSuffixedEdit(GGadget *g, int row, int col, int wasnew);
2559 : static void PSTKD_FinishBoundsEdit(GGadget *g, int row, int col, int wasnew);
2560 : static void PSTKD_FinishKernEdit(GGadget *g, int row, int col, int wasnew);
2561 : static void PSTKD_InitSameAsRow(GGadget *g, int row);
2562 :
2563 0 : static int is_boundsFeat( struct lookup_subtable *sub) {
2564 0 : FeatureScriptLangList *features = sub->lookup->features, *testf;
2565 :
2566 0 : if ( sub->lookup->lookup_type == gpos_single ) {
2567 0 : for ( testf=features; testf!=NULL; testf=testf->next ) {
2568 0 : if ( testf->featuretag==CHR('l','f','b','d'))
2569 0 : return( 1 );
2570 0 : else if ( testf->featuretag==CHR('r','t','b','d'))
2571 0 : return( -1 );
2572 : }
2573 : }
2574 0 : return( 0 );
2575 : }
2576 :
2577 0 : static void PSTMatrixInit(struct matrixinit *mi,SplineFont *_sf, struct lookup_subtable *sub, PSTKernDlg *pstkd) {
2578 : int cnt;
2579 : struct matrix_data *md;
2580 : static struct col_init simplesubsci[] = {
2581 : { me_string , NULL, NULL, NULL, N_("Base Glyph Name") },
2582 : { me_string, NULL, NULL, NULL, N_("Replacement Glyph Name") },
2583 : COL_INIT_EMPTY
2584 : };
2585 : static struct col_init ligatureci[] = {
2586 : { me_string , NULL, NULL, NULL, N_("Ligature Glyph Name") },
2587 : { me_string, NULL, NULL, NULL, N_("Source Glyph Names") },
2588 : COL_INIT_EMPTY
2589 : };
2590 : static struct col_init altmultsubsci[] = {
2591 : { me_string , NULL, NULL, NULL, N_("Base Glyph Name") },
2592 : { me_string, NULL, NULL, NULL, N_("Replacement Glyph Names") },
2593 : COL_INIT_EMPTY
2594 : };
2595 : #define SIM_DX 1
2596 : #define SIM_DY 3
2597 : #define SIM_DX_ADV 5
2598 : #define SIM_DY_ADV 7
2599 : #define PAIR_DX1 2
2600 : #define PAIR_DY1 4
2601 : #define PAIR_DX_ADV1 6
2602 : #define PAIR_DY_ADV1 8
2603 : #define PAIR_DX2 10
2604 : #define PAIR_DY2 12
2605 : #define PAIR_DX_ADV2 14
2606 : #define PAIR_DY_ADV2 16
2607 : static struct col_init simpleposci[] = {
2608 : { me_string , NULL, NULL, NULL, N_("Base Glyph Name") },
2609 : { me_int, NULL, NULL, NULL, NU_("∆x") }, /* delta-x */
2610 : /* GT: "Adjust" here means Device Table based pixel adjustments, an OpenType */
2611 : /* GT: concept which allows small corrections for small pixel sizes where */
2612 : /* GT: rounding errors (in kerning for example) may smush too glyphs together */
2613 : /* GT: or space them too far apart. Generally not a problem for big pixelsizes*/
2614 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2615 : { me_int, NULL, NULL, NULL, NU_("∆y") }, /* delta-y */
2616 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2617 : { me_int, NULL, NULL, NULL, NU_("∆x_adv") }, /* delta-x-adv */
2618 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2619 : { me_int, NULL, NULL, NULL, NU_("∆y_adv") }, /* delta-y-adv */
2620 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2621 : COL_INIT_EMPTY
2622 : };
2623 : static struct col_init pairposci[] = {
2624 : { me_string , NULL, NULL, NULL, N_("First Glyph Name") },
2625 : { me_string , NULL, NULL, NULL, N_("Second Glyph Name") },
2626 : { me_int, NULL, NULL, NULL, NU_("∆x #1") }, /* delta-x */
2627 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2628 : { me_int, NULL, NULL, NULL, NU_("∆y #1") }, /* delta-y */
2629 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2630 : { me_int, NULL, NULL, NULL, NU_("∆x_adv #1") }, /* delta-x-adv */
2631 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2632 : { me_int, NULL, NULL, NULL, NU_("∆y_adv #1") }, /* delta-y-adv */
2633 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2634 : { me_int, NULL, NULL, NULL, NU_("∆x #2") }, /* delta-x */
2635 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2636 : { me_int, NULL, NULL, NULL, NU_("∆y #2") }, /* delta-y */
2637 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2638 : { me_int, NULL, NULL, NULL, NU_("∆x_adv #2") }, /* delta-x-adv */
2639 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2640 : { me_int, NULL, NULL, NULL, NU_("∆y_adv #2") }, /* delta-y-adv */
2641 : { me_funcedit, DevTab_Dlg, NULL, NULL, N_("Adjust") },
2642 : COL_INIT_EMPTY
2643 : };
2644 : static struct { int ltype; int cnt; struct col_init *ci; } fuf[] = {
2645 : { gsub_single, sizeof(simplesubsci)/sizeof(struct col_init)-1, simplesubsci },
2646 : { gsub_multiple, sizeof(altmultsubsci)/sizeof(struct col_init)-1, altmultsubsci },
2647 : { gsub_alternate, sizeof(altmultsubsci)/sizeof(struct col_init)-1, altmultsubsci },
2648 : { gsub_ligature, sizeof(ligatureci)/sizeof(struct col_init)-1, ligatureci },
2649 : { gpos_single, sizeof(simpleposci)/sizeof(struct col_init)-1, simpleposci },
2650 : { gpos_pair, sizeof(pairposci)/sizeof(struct col_init)-1, pairposci },
2651 : { 0, 0, NULL }
2652 : };
2653 : static int initted = false;
2654 0 : int lookup_type = sub->lookup->lookup_type;
2655 0 : int isv, r2l = sub->lookup->lookup_flags&pst_r2l;
2656 : int i,j, gid,k;
2657 : SplineFont *sf;
2658 : SplineChar *sc;
2659 : PST *pst;
2660 : KernPair *kp;
2661 :
2662 0 : if ( !initted ) {
2663 0 : initted = true;
2664 0 : for ( i=0; fuf[i].ltype!=0; ++i ) if ( fuf[i].ltype!=gsub_alternate ) {
2665 0 : for ( j=0; j<fuf[i].cnt; ++j )
2666 0 : fuf[i].ci[j].title = S_(fuf[i].ci[j].title);
2667 : }
2668 : }
2669 :
2670 0 : memset(mi,0,sizeof(*mi));
2671 0 : for ( i=0; fuf[i].ltype!=0 && fuf[i].ltype!=lookup_type; ++i );
2672 0 : if ( fuf[i].ltype==0 ) {
2673 0 : IError( "Unknown lookup type in PSTMatrixInit");
2674 0 : i -= 2;
2675 : }
2676 0 : mi->col_cnt = fuf[i].cnt;
2677 0 : mi->col_init = fuf[i].ci;
2678 :
2679 0 : for ( j=0; j<2; ++j ) {
2680 0 : cnt = 0;
2681 0 : k = 0;
2682 : do {
2683 0 : sf = _sf->subfontcnt==0 ? _sf : _sf->subfonts[k];
2684 0 : for ( gid = 0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
2685 0 : for ( pst = sc->possub; pst!=NULL; pst=pst->next ) {
2686 0 : if ( pst->subtable == sub ) {
2687 0 : if ( j ) {
2688 0 : md[cnt*mi->col_cnt].u.md_str = SCNameUniStr( sc );
2689 0 : switch ( lookup_type ) {
2690 : case gsub_single:
2691 0 : md[cnt*mi->col_cnt+1].u.md_str = SFNameList2NameUni(sf,pst->u.subs.variant);
2692 0 : break;
2693 : case gsub_multiple:
2694 : case gsub_alternate:
2695 0 : md[cnt*mi->col_cnt+1].u.md_str = SFNameList2NameUni(sf,pst->u.mult.components);
2696 0 : break;
2697 : case gsub_ligature:
2698 0 : md[cnt*mi->col_cnt+1].u.md_str = SFNameList2NameUni(sf,pst->u.lig.components);
2699 0 : break;
2700 : case gpos_single:
2701 0 : md[cnt*mi->col_cnt+SIM_DX].u.md_ival = pst->u.pos.xoff;
2702 0 : md[cnt*mi->col_cnt+SIM_DY].u.md_ival = pst->u.pos.yoff;
2703 0 : md[cnt*mi->col_cnt+SIM_DX_ADV].u.md_ival = pst->u.pos.h_adv_off;
2704 0 : md[cnt*mi->col_cnt+SIM_DY_ADV].u.md_ival = pst->u.pos.v_adv_off;
2705 0 : ValDevTabToStrings(md,cnt*mi->col_cnt+SIM_DX+1,pst->u.pos.adjust);
2706 0 : break;
2707 : case gpos_pair:
2708 0 : md[cnt*mi->col_cnt+1].u.md_str = SFNameList2NameUni( sf,pst->u.pair.paired );
2709 0 : md[cnt*mi->col_cnt+PAIR_DX1].u.md_ival = pst->u.pair.vr[0].xoff;
2710 0 : md[cnt*mi->col_cnt+PAIR_DY1].u.md_ival = pst->u.pair.vr[0].yoff;
2711 0 : md[cnt*mi->col_cnt+PAIR_DX_ADV1].u.md_ival = pst->u.pair.vr[0].h_adv_off;
2712 0 : md[cnt*mi->col_cnt+PAIR_DY_ADV1].u.md_ival = pst->u.pair.vr[0].v_adv_off;
2713 0 : md[cnt*mi->col_cnt+PAIR_DX2].u.md_ival = pst->u.pair.vr[1].xoff;
2714 0 : md[cnt*mi->col_cnt+PAIR_DY2].u.md_ival = pst->u.pair.vr[1].yoff;
2715 0 : md[cnt*mi->col_cnt+PAIR_DX_ADV2].u.md_ival = pst->u.pair.vr[1].h_adv_off;
2716 0 : md[cnt*mi->col_cnt+PAIR_DY_ADV2].u.md_ival = pst->u.pair.vr[1].v_adv_off;
2717 0 : ValDevTabToStrings(md,cnt*mi->col_cnt+PAIR_DX1+1,pst->u.pair.vr[0].adjust);
2718 0 : ValDevTabToStrings(md,cnt*mi->col_cnt+PAIR_DX2+1,pst->u.pair.vr[1].adjust);
2719 0 : break;
2720 : }
2721 : }
2722 0 : ++cnt;
2723 : }
2724 : }
2725 0 : if ( lookup_type==gpos_pair ) {
2726 0 : for ( isv=0; isv<2; ++isv ) {
2727 0 : for ( kp = isv ? sc->vkerns : sc->kerns ; kp!=NULL; kp=kp->next ) {
2728 0 : if ( kp->subtable == sub ) {
2729 0 : if ( j ) {
2730 0 : md[cnt*mi->col_cnt+0].u.md_str = SCNameUniStr( sc );
2731 0 : md[cnt*mi->col_cnt+1].u.md_str = SCNameUniStr( kp->sc );
2732 0 : if ( isv ) {
2733 0 : md[cnt*mi->col_cnt+PAIR_DY_ADV1].u.md_ival = kp->off;
2734 0 : DevTabToString(&md[cnt*mi->col_cnt+PAIR_DY_ADV1+1].u.md_str,kp->adjust);
2735 0 : } else if ( r2l ) {
2736 0 : md[cnt*mi->col_cnt+PAIR_DX_ADV1].u.md_ival = kp->off;
2737 0 : DevTabToString(&md[cnt*mi->col_cnt+PAIR_DX_ADV1+1].u.md_str,kp->adjust);
2738 0 : md[cnt*mi->col_cnt+PAIR_DX1].u.md_ival = kp->off;
2739 0 : DevTabToString(&md[cnt*mi->col_cnt+PAIR_DX1+1].u.md_str,kp->adjust);
2740 : } else {
2741 0 : md[cnt*mi->col_cnt+PAIR_DX_ADV1].u.md_ival = kp->off;
2742 0 : DevTabToString(&md[cnt*mi->col_cnt+PAIR_DX_ADV1+1].u.md_str,kp->adjust);
2743 : }
2744 : }
2745 0 : ++cnt;
2746 : }
2747 : }
2748 : }
2749 : }
2750 : }
2751 0 : ++k;
2752 0 : } while ( k<_sf->subfontcnt );
2753 0 : if ( !j ) {
2754 0 : mi->initial_row_cnt = cnt;
2755 0 : if ( cnt==0 ) {
2756 0 : md = calloc(mi->col_cnt,sizeof(struct matrix_data));
2757 0 : break;
2758 : } else {
2759 0 : md = calloc(mi->col_cnt*cnt,sizeof(struct matrix_data));
2760 : }
2761 : }
2762 : }
2763 0 : PSTKD_DoSort(pstkd,md,cnt,mi->col_cnt);
2764 0 : mi->matrix_data = md;
2765 0 : if ( lookup_type==gsub_single )
2766 0 : mi->finishedit = PSTKD_FinishSuffixedEdit;
2767 0 : else if ( lookup_type==gpos_single ) {
2768 0 : mi->initrow = PSTKD_InitSameAsRow;
2769 0 : if ( is_boundsFeat(sub))
2770 0 : mi->finishedit = PSTKD_FinishBoundsEdit;
2771 0 : } else if ( lookup_type==gpos_pair && !sub->vertical_kerning )
2772 0 : mi->finishedit = PSTKD_FinishKernEdit;
2773 0 : }
2774 :
2775 0 : static void PSTKD_FinishSuffixedEdit(GGadget *g, int row, int col, int wasnew) {
2776 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
2777 0 : int rows, cols = GMatrixEditGetColCnt(g);
2778 0 : struct matrix_data *psts = _GMatrixEditGet(g,&rows);
2779 0 : char *suffix = GGadgetGetTitle8(GWidgetGetControl(pstkd->gw,CID_Suffix));
2780 : SplineChar *alt, *sc;
2781 :
2782 0 : if ( col!=0 || !wasnew || psts[row*cols+0].u.md_str==NULL )
2783 0 : return;
2784 0 : if ( *suffix=='\0' || ( suffix[0]=='.' && suffix[1]=='\0' ))
2785 0 : return;
2786 0 : sc = SFGetChar(pstkd->sf,-1,psts[row*cols+0].u.md_str);
2787 0 : if ( sc==NULL )
2788 0 : return;
2789 0 : alt = SuffixCheck(sc,suffix);
2790 0 : if ( alt!=NULL )
2791 0 : psts[row*cols+1].u.md_str = copy(alt->name);
2792 : }
2793 :
2794 0 : static void PSTKD_FinishBoundsEdit(GGadget *g, int row, int col, int wasnew) {
2795 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
2796 0 : int rows, cols = GMatrixEditGetColCnt(g);
2797 0 : struct matrix_data *psts = _GMatrixEditGet(g,&rows);
2798 0 : int is_bounds = is_boundsFeat(pstkd->sub);
2799 : SplineChar *sc;
2800 : real loff, roff;
2801 :
2802 0 : if ( col!=0 || !wasnew || psts[row*cols+0].u.md_str==NULL )
2803 0 : return;
2804 0 : if ( is_bounds==0 )
2805 0 : return;
2806 0 : sc = SFGetChar(pstkd->sf,-1,psts[row*cols+0].u.md_str);
2807 0 : if ( sc==NULL )
2808 0 : return;
2809 :
2810 0 : GuessOpticalOffset(sc,pstkd->def_layer,&loff,&roff,0);
2811 0 : if ( is_bounds>0 ) {
2812 0 : psts[row*cols+SIM_DX].u.md_ival = -loff;
2813 0 : psts[row*cols+SIM_DX_ADV].u.md_ival = -loff;
2814 : } else
2815 0 : psts[row*cols+SIM_DX_ADV].u.md_ival = -roff;
2816 : }
2817 :
2818 0 : static void PSTKD_AddKP(void *data,SplineChar *left, SplineChar *right, int off) {
2819 0 : PSTKernDlg *pstkd = data;
2820 0 : struct matrix_data *psts = pstkd->psts;
2821 0 : int cols = pstkd->cols;
2822 : int i;
2823 0 : SplineChar *first=left, *second=right;
2824 :
2825 0 : if ( pstkd->sub->lookup->lookup_flags & pst_r2l ) {
2826 0 : first = right;
2827 0 : second = left;
2828 : }
2829 :
2830 0 : for ( i=0; i<pstkd->rows_at_start; ++i ) {
2831 : /* If the user has already got this combination in the lookup */
2832 : /* then don't add a new guess, and don't override the old */
2833 0 : if ( psts[i*cols+0].u.md_str!=NULL && psts[i*cols+1].u.md_str!=NULL &&
2834 0 : strcmp(psts[i*cols+0].u.md_str,first->name)==0 &&
2835 0 : strcmp(psts[i*cols+1].u.md_str,second->name)==0 )
2836 0 : return;
2837 : }
2838 :
2839 0 : i = pstkd->next_row++;
2840 0 : if ( i >= pstkd->rows )
2841 0 : pstkd->psts = psts = realloc(psts,(pstkd->rows += 100)*cols*sizeof(struct matrix_data));
2842 0 : memset(psts+i*cols,0,cols*sizeof(struct matrix_data));
2843 0 : psts[i*cols+0].u.md_str = copy(first->name);
2844 0 : psts[i*cols+1].u.md_str = copy(second->name);
2845 0 : if ( pstkd->sub->lookup->lookup_flags & pst_r2l )
2846 0 : psts[i*cols+PAIR_DX_ADV1].u.md_ival = psts[i*cols+PAIR_DX1].u.md_ival = off;
2847 : /* else if ( pstkd->sub->vertical_kerning ) */ /* We don't do vertical stuff */
2848 : else
2849 0 : psts[i*cols+PAIR_DX_ADV1].u.md_ival = off;
2850 0 : if ( strcmp(psts[0+0].u.md_str,"T")!=0 )
2851 0 : second = left;
2852 : }
2853 :
2854 0 : static void PSTKD_FinishKernEdit(GGadget *g, int row, int col, int wasnew) {
2855 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
2856 0 : int rows, cols = GMatrixEditGetColCnt(g);
2857 0 : struct matrix_data *psts = _GMatrixEditGet(g,&rows);
2858 : SplineChar *sc1, *sc2;
2859 : SplineChar *lefts[2], *rights[2];
2860 : int err, touch, separation;
2861 :
2862 0 : if ( col>1 || psts[row*cols+0].u.md_str==NULL ||
2863 0 : psts[row*cols+1].u.md_str==NULL ||
2864 0 : psts[row*cols+PAIR_DX1].u.md_ival!=0 ||
2865 0 : psts[row*cols+PAIR_DX_ADV1].u.md_ival!=0 ||
2866 0 : psts[row*cols+PAIR_DY_ADV1].u.md_ival!=0 ||
2867 0 : psts[row*cols+PAIR_DX_ADV2].u.md_ival!=0 )
2868 0 : return;
2869 0 : sc1 = SFGetChar(pstkd->sf,-1,psts[row*cols+0].u.md_str);
2870 0 : sc2 = SFGetChar(pstkd->sf,-1,psts[row*cols+1].u.md_str);
2871 0 : if ( sc1==NULL || sc2==NULL )
2872 0 : return;
2873 0 : lefts[1] = rights[1] = NULL;
2874 0 : if ( pstkd->sub->lookup->lookup_flags & pst_r2l ) {
2875 0 : lefts[0] = sc2;
2876 0 : rights[0] = sc1;
2877 : } else {
2878 0 : lefts[0] = sc1;
2879 0 : rights[0] = sc2;
2880 : }
2881 :
2882 0 : err = false;
2883 0 : touch = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Touched));
2884 0 : separation = GetInt8(pstkd->gw,CID_Separation,_("Separation"),&err);
2885 0 : if ( err )
2886 0 : return;
2887 :
2888 0 : pstkd->cols = GMatrixEditGetColCnt(g);
2889 0 : pstkd->psts = psts;
2890 0 : pstkd->rows_at_start = 0;
2891 0 : pstkd->next_row = row;
2892 0 : pstkd->rows = rows;
2893 :
2894 0 : AutoKern2(pstkd->sf,pstkd->def_layer,lefts,rights,pstkd->sub,
2895 : separation,0,touch,0,0, /* Don't bother with minkern,onlyCloser they asked for this, they get it, whatever it may be */
2896 : PSTKD_AddKP,pstkd);
2897 0 : if ( pstkd->psts != psts )
2898 0 : IError("AutoKern added too many pairs, was only supposed to add one");
2899 : else
2900 0 : GGadgetRedraw(g);
2901 : }
2902 :
2903 0 : static void PSTKD_InitSameAsRow(GGadget *g, int row) {
2904 0 : GWidget gw = GGadgetGetWindow(g);
2905 0 : int rows, cols = GMatrixEditGetColCnt(g);
2906 0 : struct matrix_data *psts = GMatrixEditGet(g,&rows);
2907 :
2908 0 : if ( row==0 )
2909 0 : return;
2910 0 : if ( !GGadgetIsChecked(GWidgetGetControl(gw,CID_AllSame)))
2911 0 : return;
2912 0 : psts[row*cols+SIM_DX].u.md_ival = psts[0+SIM_DX].u.md_ival;
2913 0 : psts[row*cols+SIM_DY].u.md_ival = psts[0+SIM_DY].u.md_ival;
2914 0 : psts[row*cols+SIM_DX_ADV].u.md_ival = psts[0+SIM_DX_ADV].u.md_ival;
2915 0 : psts[row*cols+SIM_DY_ADV].u.md_ival = psts[0+SIM_DY_ADV].u.md_ival;
2916 : }
2917 :
2918 0 : static int PSTKD_Sort(GGadget *g, GEvent *e) {
2919 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
2920 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
2921 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
2922 0 : int rows, cols = GMatrixEditGetColCnt(pstk);
2923 0 : struct matrix_data *old = GMatrixEditGet(pstk,&rows);
2924 0 : PSTKD_DoSort(pstkd,old,rows,cols);
2925 0 : GGadgetRedraw(pstk);
2926 : }
2927 0 : return( true );
2928 : }
2929 :
2930 0 : static void PSTKD_DoHideUnused(PSTKernDlg *pstkd) {
2931 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
2932 0 : int rows, cols = GMatrixEditGetColCnt(pstk);
2933 0 : struct matrix_data *old = GMatrixEditGet(pstk,&rows);
2934 : uint8 cols_used[20];
2935 : int r, col, startc, tot;
2936 :
2937 0 : startc = ( pstkd->sub->lookup->lookup_type == gpos_single ) ? 1 : 2;
2938 0 : if ( lookup_hideunused ) {
2939 0 : memset(cols_used,0,sizeof(cols_used));
2940 0 : for ( r=0; r<rows; ++r ) {
2941 0 : for ( col=startc; col<cols; col+=2 ) {
2942 0 : if ( old[cols*r+col].u.md_ival!=0 )
2943 0 : cols_used[col] = true;
2944 0 : if ( old[cols*r+col+1].u.md_str!=NULL && *old[cols*r+col+1].u.md_str!='\0' )
2945 0 : cols_used[col+1] = true;
2946 : }
2947 : }
2948 : /* If no columns used (no info yet, all info is to preempt a kernclass and sets to 0) */
2949 : /* then show what we expect to be the default column for this kerning mode*/
2950 0 : for ( col=startc, tot=0; col<cols; ++col )
2951 0 : tot += cols_used[col];
2952 0 : if ( tot==0 ) {
2953 0 : if ( startc==1 ) {
2954 0 : cols_used[SIM_DX] = cols_used[SIM_DY] =
2955 0 : cols_used[SIM_DX_ADV] = cols_used[SIM_DY_ADV] = true;
2956 : } else {
2957 0 : if ( pstkd->sub->vertical_kerning )
2958 0 : cols_used[PAIR_DY_ADV1] = true;
2959 0 : else if ( pstkd->sub->lookup->lookup_flags&pst_r2l )
2960 0 : cols_used[PAIR_DX_ADV1] = cols_used[PAIR_DX1] = true;
2961 : else
2962 0 : cols_used[PAIR_DX_ADV1] = true;
2963 : }
2964 : }
2965 0 : for ( col=startc; col<cols; ++col )
2966 0 : GMatrixEditShowColumn(pstk,col,cols_used[col]);
2967 : } else {
2968 0 : for ( col=startc; col<cols; ++col )
2969 0 : GMatrixEditShowColumn(pstk,col,true);
2970 : }
2971 0 : GWidgetToDesiredSize(pstkd->gw);
2972 :
2973 0 : GGadgetRedraw(pstk);
2974 0 : }
2975 :
2976 0 : static int PSTKD_HideUnused(GGadget *g, GEvent *e) {
2977 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
2978 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
2979 0 : lookup_hideunused = GGadgetIsChecked(g);
2980 0 : PSTKD_DoHideUnused(pstkd);
2981 0 : GGadgetRedraw(GWidgetGetControl(pstkd->gw,CID_PSTList));
2982 : }
2983 0 : return( true );
2984 : }
2985 :
2986 0 : static int PSTKD_MagnificationChanged(GGadget *g, GEvent *e) {
2987 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
2988 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
2989 0 : int mag = GGadgetGetFirstListSelectedItem(g);
2990 :
2991 0 : if ( mag!=-1 && mag!=pstkd->mag-1 ) {
2992 0 : pstkd->mag = mag+1;
2993 0 : GGadgetRedraw(GWidgetGetControl(pstkd->gw,CID_KernDisplay));
2994 : }
2995 : }
2996 0 : return( true );
2997 : }
2998 :
2999 0 : static int PSTKD_DisplaySizeChanged(GGadget *g, GEvent *e) {
3000 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
3001 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3002 0 : const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(pstkd->gw,CID_PixelSize));
3003 : unichar_t *end;
3004 0 : int pixelsize = u_strtol(ret,&end,10);
3005 :
3006 0 : while ( *end==' ' ) ++end;
3007 0 : if ( pixelsize>4 && pixelsize<400 && *end=='\0' ) {
3008 0 : pstkd->pixelsize = pixelsize;
3009 0 : if ( pstkd->display!=NULL ) {
3010 0 : BDFFontFree(pstkd->display);
3011 0 : pstkd->display = NULL;
3012 : }
3013 0 : GGadgetRedraw(GWidgetGetControl(pstkd->gw,CID_KernDisplay));
3014 : }
3015 : }
3016 0 : return( true );
3017 : }
3018 :
3019 0 : static void PSTKD_METextChanged(GGadget *g, int r, int c, GGadget *text) {
3020 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3021 0 : GGadgetRedraw(GWidgetGetControl(pstkd->gw,CID_KernDisplay));
3022 0 : }
3023 :
3024 0 : static int FigureValue(struct matrix_data *old,int rcol,int c, int startc,
3025 : GGadget *tf,double scale, int pixelsize) {
3026 : int val;
3027 0 : char *str, *freeme=NULL;
3028 : DeviceTable dt;
3029 :
3030 0 : if ( c==startc && tf!=NULL )
3031 0 : val = u_strtol(_GGadgetGetTitle(tf),NULL,10);
3032 : else
3033 0 : val = old[rcol+startc].u.md_ival;
3034 0 : val = rint(val*scale);
3035 0 : if ( c==startc+1 && tf!=NULL )
3036 0 : str = freeme = GGadgetGetTitle8(tf);
3037 : else
3038 0 : str = old[rcol+startc+1].u.md_str;
3039 0 : memset(&dt,0,sizeof(dt));
3040 0 : DeviceTableParse(&dt,str);
3041 0 : if ( pixelsize>=dt.first_pixel_size && pixelsize<=dt.last_pixel_size && dt.corrections!=NULL )
3042 0 : val += dt.corrections[pixelsize-dt.first_pixel_size];
3043 0 : free(dt.corrections);
3044 0 : free(freeme);
3045 0 : return( val );
3046 : }
3047 :
3048 0 : static int ParsePSTKVR(PSTKernDlg *pstkd,GGadget *pstk,int startc,struct vr *vr) {
3049 0 : int rows, cols = GMatrixEditGetColCnt(pstk);
3050 0 : struct matrix_data *old = _GMatrixEditGet(pstk,&rows);
3051 0 : GGadget *tf = _GMatrixEditGetActiveTextField(pstk);
3052 0 : int r = GMatrixEditGetActiveRow(pstk);
3053 0 : int c = GMatrixEditGetActiveCol(pstk);
3054 0 : double scale = pstkd->pixelsize/(double) (pstkd->sf->ascent+pstkd->sf->descent);
3055 :
3056 0 : vr->xoff = FigureValue(old,r*cols,c,startc,tf,scale,pstkd->pixelsize);
3057 0 : vr->yoff = FigureValue(old,r*cols,c,startc+2,tf,scale,pstkd->pixelsize);
3058 0 : vr->h_adv_off = FigureValue(old,r*cols,c,startc+4,tf,scale,pstkd->pixelsize);
3059 0 : vr->v_adv_off = FigureValue(old,r*cols,c,startc+6,tf,scale,pstkd->pixelsize);
3060 0 : return( true );
3061 : }
3062 :
3063 0 : static void PSTKern_DrawGlyph(GWindow pixmap,int x,int y, BDFChar *bc, int mag) {
3064 : /* x,y is the location of the origin of the glyph, they need to */
3065 : /* be adjusted by the images xmin, ymax, etc. */
3066 : struct _GImage base;
3067 : GImage gi;
3068 : GClut clut;
3069 : int scale, l;
3070 : Color fg, bg;
3071 :
3072 0 : scale = bc->depth == 8 ? 8 : 4;
3073 :
3074 0 : memset(&gi,'\0',sizeof(gi));
3075 0 : memset(&base,'\0',sizeof(base));
3076 0 : memset(&clut,'\0',sizeof(clut));
3077 0 : gi.u.image = &base;
3078 0 : base.clut = &clut;
3079 0 : base.data = bc->bitmap;
3080 0 : base.bytes_per_line = bc->bytes_per_line;
3081 0 : base.width = bc->xmax-bc->xmin+1;
3082 0 : base.height = bc->ymax-bc->ymin+1;
3083 0 : base.image_type = it_index;
3084 0 : clut.clut_len = 1<<scale;
3085 0 : bg = GDrawGetDefaultBackground(NULL);
3086 0 : fg = GDrawGetDefaultForeground(NULL);
3087 0 : for ( l=0; l<(1<<scale); ++l )
3088 0 : clut.clut[l] =
3089 0 : COLOR_CREATE(
3090 : COLOR_RED(bg) + (l*(COLOR_RED(fg)-COLOR_RED(bg)))/((1<<scale)-1),
3091 : COLOR_GREEN(bg) + (l*(COLOR_GREEN(fg)-COLOR_GREEN(bg)))/((1<<scale)-1),
3092 : COLOR_BLUE(bg) + (l*(COLOR_BLUE(fg)-COLOR_BLUE(bg)))/((1<<scale)-1) );
3093 :
3094 0 : x += bc->xmin;
3095 0 : y -= bc->ymax;
3096 :
3097 0 : if ( mag==1 )
3098 0 : GDrawDrawImage(pixmap,&gi,NULL,x,y);
3099 : else
3100 0 : GDrawDrawImageMagnified(pixmap, &gi, NULL,
3101 : x*mag,y*mag,
3102 0 : base.width*mag,base.height*mag);
3103 0 : }
3104 :
3105 0 : static void PSTKern_Expose(GWindow pixmap, PSTKernDlg *pstkd) {
3106 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3107 : int r;
3108 0 : int rows, cols = GMatrixEditGetColCnt(pstk);
3109 0 : struct matrix_data *old = _GMatrixEditGet(pstk,&rows);
3110 : SplineChar *sc1, *sc2;
3111 : BDFChar *bc1, *bc2;
3112 : struct vr vr1, vr2;
3113 : int xorig, yorig;
3114 : GRect size;
3115 0 : int mag = pstkd->mag;
3116 :
3117 0 : if ( (r = GMatrixEditGetActiveRow(pstk))==-1 )
3118 0 : return; /* No kerning pair is active */
3119 0 : if ( old[r*cols+0].u.md_str==NULL || old[r*cols+1].u.md_str==NULL )
3120 0 : return; /* No glyphs specified to kern */
3121 0 : sc1 = SFGetChar(pstkd->sf,-1,old[r*cols+0].u.md_str);
3122 0 : sc2 = SFGetChar(pstkd->sf,-1,old[r*cols+1].u.md_str);
3123 0 : if ( sc1==NULL || sc2==NULL )
3124 0 : return; /* The names specified weren't in the font */
3125 :
3126 0 : if ( !ParsePSTKVR(pstkd,pstk,PAIR_DX1,&vr1) ||
3127 0 : !ParsePSTKVR(pstkd,pstk,PAIR_DX2,&vr2) )
3128 0 : return; /* Couldn't parse the numeric kerning info */
3129 :
3130 0 : if ( pstkd->display==NULL )
3131 0 : pstkd->display = SplineFontPieceMeal(pstkd->sf,pstkd->def_layer,pstkd->pixelsize,72,pf_antialias,NULL);
3132 0 : bc1 = BDFPieceMealCheck(pstkd->display,sc1->orig_pos);
3133 0 : bc2 = BDFPieceMealCheck(pstkd->display,sc2->orig_pos);
3134 :
3135 0 : GDrawGetSize(GDrawableGetWindow(GWidgetGetControl(pstkd->gw,CID_KernDisplay)),&size);
3136 0 : if ( pstkd->sub->vertical_kerning ) {
3137 0 : double scale = pstkd->pixelsize/(double) (pstkd->sf->ascent+pstkd->sf->descent);
3138 0 : int vwidth1 = rint( sc1->vwidth*scale ), vwidth2 = rint( sc2->vwidth*scale );
3139 0 : xorig = size.width/10;
3140 0 : yorig = size.height/20;
3141 0 : xorig /= mag; yorig /= mag;
3142 0 : PSTKern_DrawGlyph(pixmap,xorig+vr1.xoff,yorig+vwidth1-vr1.yoff, bc1, mag);
3143 0 : PSTKern_DrawGlyph(pixmap,xorig+vr2.xoff,yorig+vwidth1+vwidth2+vr1.v_adv_off-vr2.yoff, bc2, mag);
3144 0 : } else if ( pstkd->sub->lookup->lookup_flags&pst_r2l ) {
3145 0 : xorig = 9*size.width/10;
3146 0 : yorig = pstkd->sf->ascent*size.height/(pstkd->sf->ascent+pstkd->sf->descent);
3147 0 : GDrawDrawLine(pixmap,xorig+vr1.h_adv_off-vr1.xoff,0,xorig+vr1.h_adv_off-vr1.xoff,size.height, 0x808080);
3148 0 : GDrawDrawLine(pixmap,0,yorig,size.width,yorig, 0x808080);
3149 0 : xorig /= mag; yorig /= mag;
3150 0 : PSTKern_DrawGlyph(pixmap,xorig-bc1->width,yorig-vr1.yoff, bc1, mag);
3151 0 : PSTKern_DrawGlyph(pixmap,xorig-bc1->width-bc2->width-vr2.h_adv_off-vr2.xoff-vr1.xoff,yorig-vr2.yoff, bc2, mag);
3152 : } else {
3153 0 : xorig = size.width/10;
3154 0 : yorig = pstkd->sf->ascent*size.height/(pstkd->sf->ascent+pstkd->sf->descent);
3155 0 : GDrawDrawLine(pixmap,xorig,0,xorig,size.height, 0x808080);
3156 0 : GDrawDrawLine(pixmap,0,yorig,size.width,yorig, 0x808080);
3157 0 : xorig /= mag; yorig /= mag;
3158 0 : PSTKern_DrawGlyph(pixmap,xorig+vr1.xoff,yorig-vr1.yoff, bc1, mag);
3159 0 : PSTKern_DrawGlyph(pixmap,xorig+bc1->width+vr1.h_adv_off+vr2.xoff,yorig-vr2.yoff, bc2, mag);
3160 : }
3161 : }
3162 :
3163 0 : static void PSTKern_Mouse(PSTKernDlg *pstkd,GEvent *event) {
3164 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3165 : int r;
3166 0 : int rows, cols = GMatrixEditGetColCnt(pstk);
3167 0 : struct matrix_data *old = _GMatrixEditGet(pstk,&rows);
3168 0 : int c = GMatrixEditGetActiveCol(pstk);
3169 0 : GGadget *tf = _GMatrixEditGetActiveTextField(pstk);
3170 0 : double scale = pstkd->pixelsize/(double) (pstkd->sf->ascent+pstkd->sf->descent);
3171 : int diff, col, col2;
3172 0 : int r2l = pstkd->sub->lookup->lookup_flags&pst_r2l;
3173 : char buffer[20];
3174 0 : GCursor ct = ct_mypointer;
3175 : GRect size;
3176 :
3177 0 : if ( (r = GMatrixEditGetActiveRow(pstk))==-1 )
3178 0 : return; /* No kerning pair is active */
3179 :
3180 0 : if ( pstkd->sub->vertical_kerning ) {
3181 0 : diff = event->u.mouse.y - pstkd->orig_pos;
3182 0 : col = PAIR_DY_ADV1;
3183 0 : } else if ( r2l ) {
3184 0 : diff = pstkd->orig_pos - event->u.mouse.x ;
3185 0 : col = PAIR_DX_ADV1;
3186 0 : col2 = PAIR_DX1;
3187 : } else {
3188 0 : diff = event->u.mouse.x - pstkd->orig_pos;
3189 0 : col = PAIR_DX_ADV1;
3190 : }
3191 :
3192 0 : if ( event->type == et_mousedown ) {
3193 0 : pstkd->down = true;
3194 0 : pstkd->orig_pos = pstkd->sub->vertical_kerning ? event->u.mouse.y : event->u.mouse.x;
3195 0 : pstkd->orig_value = FigureValue(old,r*cols,c,col,tf,1.0,-1);
3196 0 : } else if ( pstkd->down ) {
3197 0 : diff = rint(diff/scale);
3198 0 : if ( col==c && tf!=NULL ) {
3199 0 : sprintf( buffer, "%d", pstkd->orig_value + diff);
3200 0 : GGadgetSetTitle8(tf,buffer);
3201 0 : GGadgetRedraw(tf);
3202 : } else {
3203 0 : old[r*cols+col].u.md_ival = pstkd->orig_value + diff;
3204 0 : GGadgetRedraw(pstk);
3205 : }
3206 0 : if ( r2l ) {
3207 0 : if ( col2==c && tf!=NULL ) {
3208 0 : sprintf( buffer, "%d", pstkd->orig_value + diff);
3209 0 : GGadgetSetTitle8(tf,buffer);
3210 0 : GGadgetRedraw(tf);
3211 : } else {
3212 0 : old[r*cols+col2].u.md_ival = pstkd->orig_value + diff;
3213 0 : GGadgetRedraw(pstk);
3214 : }
3215 : }
3216 0 : GGadgetRedraw(GWidgetGetControl(pstkd->gw,CID_KernDisplay));
3217 0 : if ( event->type == et_mouseup )
3218 0 : pstkd->down = false;
3219 0 : } else if ( event->type == et_mousemove ) {
3220 0 : SplineChar *sc1 = SFGetChar(pstkd->sf,-1,old[r*cols+0].u.md_str);
3221 0 : if (sc1!=NULL ) {
3222 0 : GDrawGetSize(event->w,&size);
3223 0 : if ( r2l ) {
3224 0 : if ( event->u.mouse.x < 9*size.width/10-sc1->width*scale )
3225 0 : ct = ct_kerning;
3226 0 : } else if ( col==PAIR_DX_ADV1 && event->u.mouse.x-size.width/10 > sc1->width*scale ) {
3227 0 : ct = ct_kerning;
3228 : }
3229 : }
3230 : }
3231 0 : if ( ct!=pstkd->cursor_current ) {
3232 0 : GDrawSetCursor(event->w,ct);
3233 0 : pstkd->cursor_current = ct;
3234 : }
3235 : }
3236 :
3237 0 : static int pstkern_e_h(GWindow gw, GEvent *event) {
3238 0 : switch ( event->type ) {
3239 : case et_char:
3240 0 : if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
3241 0 : help("lookups.html#Pair");
3242 0 : return( true );
3243 : }
3244 0 : return( false );
3245 : case et_expose:
3246 0 : PSTKern_Expose(gw,GDrawGetUserData(gw));
3247 0 : return( true );
3248 : case et_mousedown: case et_mouseup: case et_mousemove:
3249 0 : PSTKern_Mouse(GDrawGetUserData(gw),event);
3250 0 : return( true );
3251 : }
3252 0 : return( true );
3253 : }
3254 :
3255 0 : static int SCReasonable(SplineChar *sc) {
3256 0 : if ( sc==NULL )
3257 0 : return( false );
3258 0 : if ( strcmp(sc->name,".notdef")==0 ||
3259 0 : strcmp(sc->name,".null")==0 ||
3260 0 : strcmp(sc->name,"nonmarkingreturn")==0 )
3261 0 : return( false );
3262 :
3263 0 : return( true );
3264 : }
3265 :
3266 0 : static struct matrix_data *MDCopy(struct matrix_data *old,int rows,int cols) {
3267 0 : struct matrix_data *md = malloc(rows*cols*sizeof(struct matrix_data));
3268 : int r;
3269 :
3270 0 : memcpy(md,old,rows*cols*sizeof(struct matrix_data));
3271 0 : for ( r=0; r<rows; ++r ) {
3272 0 : md[r*cols+0].u.md_str = copy(md[r*cols+0].u.md_str);
3273 0 : if ( cols==2 /* subs, lig, alt, etc. */ || cols>=10 /* kerning */ )
3274 0 : md[r*cols+1].u.md_str = copy(md[r*cols+1].u.md_str);
3275 : }
3276 0 : return( md );
3277 : }
3278 :
3279 0 : static int SCNameUnused(char *name,struct matrix_data *old,int rows,int cols) {
3280 : int r;
3281 :
3282 0 : for ( r=0; r<rows; ++r ) {
3283 0 : if ( old[r*cols+0].u.md_str!=NULL && strcmp(old[r*cols+0].u.md_str,name)==0 ) {
3284 0 : if (( cols==2 && (old[r*cols+1].u.md_str == NULL || *old[r*cols+1].u.md_str=='\0')) ||
3285 0 : (cols==5 && old[r*cols+1].u.md_ival==0 &&
3286 0 : old[r*cols+2].u.md_ival==0 &&
3287 0 : old[r*cols+3].u.md_ival==0 &&
3288 0 : old[r*cols+4].u.md_ival==0 ))
3289 0 : return( r ); /* There's an entry, but it's blank, fill it if we can */
3290 : else
3291 0 : return( -1 );
3292 : }
3293 : }
3294 0 : return( r ); /* Off end of list */
3295 : }
3296 :
3297 0 : static int SCIsLigature(SplineChar *sc) {
3298 : int len;
3299 0 : const unichar_t *alt=NULL;
3300 :
3301 0 : if ( strchr(sc->name,'_')!=NULL )
3302 0 : return( true );
3303 0 : len = strlen( sc->name );
3304 0 : if ( strncmp(sc->name,"uni",3)==0 && (len-3)%4==0 && len>7 )
3305 0 : return( true );
3306 :
3307 0 : if ( sc->unicodeenc==-1 || sc->unicodeenc>=0x10000 )
3308 0 : return( false );
3309 0 : else if ( isdecompositionnormative(sc->unicodeenc) &&
3310 0 : unicode_alternates[sc->unicodeenc>>8]!=NULL &&
3311 0 : (alt = unicode_alternates[sc->unicodeenc>>8][sc->unicodeenc&0xff])!=NULL ) {
3312 0 : if ( alt[1]=='\0' )
3313 0 : return( false ); /* Single replacements aren't ligatures */
3314 0 : else if ( iscombining(alt[1]) && ( alt[2]=='\0' || iscombining(alt[2])))
3315 0 : return( false ); /* Nor am I interested in accented letters */
3316 : else
3317 0 : return( true );
3318 : } else
3319 0 : return( false );
3320 : }
3321 :
3322 : enum pop_type { pt_all, pt_suffixed, pt_selected };
3323 :
3324 0 : static void PSTKD_DoPopulate(PSTKernDlg *pstkd,char *suffix, enum pop_type pt) {
3325 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3326 0 : int rows, row_max, old_rows, cols = GMatrixEditGetColCnt(pstk);
3327 0 : struct matrix_data *old = GMatrixEditGet(pstk,&rows), *psts;
3328 : int pos;
3329 : int gid,k;
3330 : SplineChar *sc, *alt;
3331 : int enc;
3332 0 : SplineFont *sf = pstkd->sf;
3333 0 : FontView *fv = (FontView *) sf->fv;
3334 0 : EncMap *map = fv->b.map;
3335 0 : GGadget *gallsame = GWidgetGetControl(pstkd->gw,CID_AllSame);
3336 0 : int allsame = false;
3337 0 : FeatureScriptLangList *features = pstkd->sub->lookup->features;
3338 :
3339 0 : if ( gallsame!=NULL )
3340 0 : allsame = GGadgetIsChecked(gallsame);
3341 :
3342 0 : psts = MDCopy(old,rows,cols);
3343 0 : old_rows = row_max = rows;
3344 0 : k = 0;
3345 : do {
3346 0 : sf = pstkd->sf->subfontcnt==0 ? pstkd->sf : pstkd->sf->subfonts[k];
3347 0 : for ( gid=0; gid<sf->glyphcnt; ++gid ) {
3348 0 : if ( SCReasonable(sc = sf->glyphs[gid]) &&
3349 0 : (pt==pt_selected || ScriptInFeatureScriptList(SCScriptFromUnicode(sc),
3350 0 : features)) &&
3351 0 : (pt!=pt_selected || (gid<fv->b.sf->glyphcnt &&
3352 0 : (enc = map->backmap[gid])!=-1 &&
3353 0 : fv->b.selected[enc])) &&
3354 0 : (pos = SCNameUnused(sc->name,old,old_rows,cols))!=-1 &&
3355 0 : (pstkd->sub->lookup->lookup_type!=gsub_ligature ||
3356 0 : SCIsLigature(sc)) ) {
3357 0 : alt = NULL;
3358 0 : if ( suffix!=NULL ) {
3359 0 : alt = SuffixCheck(sc,suffix);
3360 0 : if ( pt==pt_suffixed && alt==NULL )
3361 0 : continue;
3362 : }
3363 0 : if ( pos==old_rows ) {
3364 0 : pos = rows;
3365 0 : if ( rows>=row_max ) {
3366 0 : if ( row_max< sf->glyphcnt-10 )
3367 0 : row_max = sf->glyphcnt;
3368 : else
3369 0 : row_max += 15;
3370 0 : psts = realloc(psts,row_max*cols*sizeof(struct matrix_data));
3371 : }
3372 0 : memset(psts+rows*cols,0,cols*sizeof(struct matrix_data));
3373 0 : psts[rows*cols+0].u.md_str = copy(sc->name);
3374 0 : ++rows;
3375 : }
3376 0 : if ( alt!=NULL )
3377 0 : psts[pos*cols+1].u.md_str = copy(alt->name);
3378 0 : else if ( allsame && pos!=0 ) {
3379 0 : psts[pos*cols+SIM_DX].u.md_ival = psts[0+SIM_DX].u.md_ival;
3380 0 : psts[pos*cols+SIM_DY].u.md_ival = psts[0+SIM_DY].u.md_ival;
3381 0 : psts[pos*cols+SIM_DX_ADV].u.md_ival = psts[0+SIM_DX_ADV].u.md_ival;
3382 0 : psts[pos*cols+SIM_DY_ADV].u.md_ival = psts[0+SIM_DY_ADV].u.md_ival;
3383 0 : } else if ( pstkd->sub->lookup->lookup_type!=gpos_pair )
3384 0 : SCSubtableDefaultSubsCheck(sc,pstkd->sub,psts,cols,pos,pstkd->def_layer);
3385 : }
3386 : }
3387 0 : ++k;
3388 0 : } while ( k<pstkd->sf->subfontcnt );
3389 0 : if ( rows<row_max )
3390 0 : psts = realloc(psts,rows*cols*sizeof(struct matrix_data));
3391 0 : PSTKD_DoSort(pstkd,psts,rows,cols);
3392 0 : GMatrixEditSet(pstk,psts,rows,false);
3393 0 : GGadgetRedraw(pstk);
3394 0 : }
3395 :
3396 0 : static void PSTKD_SetSuffix(PSTKernDlg *pstkd) {
3397 : char *suffix;
3398 :
3399 0 : if ( pstkd->sub->lookup->lookup_type!=gsub_single )
3400 0 : return; /* Not applicable */
3401 :
3402 0 : suffix = GGadgetGetTitle8(GWidgetGetControl(pstkd->gw,CID_Suffix));
3403 0 : if ( *suffix!='\0' && ( suffix[0]!='.' || suffix[1]!='\0' )) {
3404 0 : free(pstkd->sub->suffix);
3405 0 : pstkd->sub->suffix = ( *suffix=='.' ) ? copy(suffix+1): copy(suffix);
3406 0 : free(suffix);
3407 : }
3408 : }
3409 :
3410 0 : static int PSTKD_PopulateWithSuffix(GGadget *g, GEvent *e) {
3411 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3412 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3413 0 : char *suffix = GGadgetGetTitle8(GWidgetGetControl(pstkd->gw,CID_Suffix));
3414 0 : if ( *suffix!='\0' && ( suffix[0]!='.' || suffix[1]!='\0' )) {
3415 0 : PSTKD_DoPopulate(pstkd,suffix, pt_suffixed);
3416 0 : PSTKD_SetSuffix(pstkd);
3417 : }
3418 0 : free(suffix);
3419 : }
3420 0 : return( true );
3421 : }
3422 :
3423 0 : static int PSTKD_Populate(GGadget *g, GEvent *e) {
3424 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3425 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3426 0 : GGadget *gsuffix = GWidgetGetControl(pstkd->gw,CID_Suffix);
3427 0 : char *suffix = NULL;
3428 0 : if ( gsuffix != NULL ) {
3429 0 : suffix = GGadgetGetTitle8(gsuffix);
3430 0 : if ( *suffix=='\0' || ( suffix[0]=='.' && suffix[1]=='\0' )) {
3431 0 : free(suffix);
3432 0 : suffix = NULL;
3433 : }
3434 : }
3435 0 : PSTKD_SetSuffix(pstkd);
3436 0 : PSTKD_DoPopulate(pstkd,suffix,pt_all);
3437 0 : free(suffix);
3438 : }
3439 0 : return( true );
3440 : }
3441 :
3442 0 : static int PSTKD_PopulateSelected(GGadget *g, GEvent *e) {
3443 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3444 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3445 0 : GGadget *gsuffix = GWidgetGetControl(pstkd->gw,CID_Suffix);
3446 0 : char *suffix = NULL;
3447 0 : if ( gsuffix != NULL ) {
3448 0 : suffix = GGadgetGetTitle8(gsuffix);
3449 0 : if ( *suffix=='\0' || ( suffix[0]=='.' && suffix[1]=='\0' )) {
3450 0 : free(suffix);
3451 0 : suffix = NULL;
3452 : }
3453 : }
3454 0 : PSTKD_SetSuffix(pstkd);
3455 0 : PSTKD_DoPopulate(pstkd,suffix,pt_selected);
3456 0 : free(suffix);
3457 : }
3458 0 : return( true );
3459 : }
3460 :
3461 0 : static int PSTKD_DoAutoKern(PSTKernDlg *pstkd,SplineChar **glyphlist) {
3462 : int err, touch, separation, minkern, onlyCloser;
3463 :
3464 0 : if ( !GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Autokern)) )
3465 0 : return( false );
3466 :
3467 0 : err = false;
3468 0 : touch = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Touched));
3469 0 : separation = GetInt8(pstkd->gw,CID_Separation,_("Separation"),&err);
3470 0 : minkern = GetInt8(pstkd->gw,CID_MinKern,_("Min Kern"),&err);
3471 0 : onlyCloser = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_OnlyCloser));
3472 0 : if ( err )
3473 0 : return( false );
3474 0 : AutoKern2(pstkd->sf,pstkd->def_layer,glyphlist,glyphlist,pstkd->sub,
3475 : separation,minkern,touch,onlyCloser,0,
3476 : PSTKD_AddKP,pstkd);
3477 0 : return( true );
3478 : }
3479 :
3480 0 : static int PSTKD_AutoKern(GGadget *g, GEvent *e) {
3481 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3482 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3483 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3484 0 : int rows, cols = GMatrixEditGetColCnt(pstk);
3485 0 : struct matrix_data *old = GMatrixEditGet(pstk,&rows);
3486 0 : SplineFont *sf = pstkd->sf;
3487 : int i,gid,cnt;
3488 : SplineChar **list, *sc;
3489 0 : FeatureScriptLangList *features = pstkd->sub->lookup->features, *testf;
3490 : struct scriptlanglist *scripts;
3491 : uint32 *scripttags;
3492 :
3493 0 : pstkd->cols = GMatrixEditGetColCnt(pstk);
3494 0 : pstkd->psts = MDCopy(old,rows,cols);
3495 0 : pstkd->rows_at_start = pstkd->rows = pstkd->next_row = rows;
3496 :
3497 0 : for ( testf=features,cnt=0; testf!=NULL; testf=testf->next ) {
3498 0 : for ( scripts=testf->scripts; scripts!=NULL; scripts=scripts->next )
3499 0 : ++cnt;
3500 : }
3501 0 : if ( cnt==0 ) {
3502 0 : ff_post_error(_("No scripts"),_("There are no scripts bound to features bound to this lookup. So nothing happens." ));
3503 0 : return(true);
3504 : }
3505 0 : scripttags = malloc((cnt+1)*sizeof(uint32));
3506 0 : for ( testf=features,cnt=0; testf!=NULL; testf=testf->next ) {
3507 0 : for ( scripts=testf->scripts; scripts!=NULL; scripts=scripts->next ) {
3508 0 : for ( i=0; i<cnt; ++i )
3509 0 : if ( scripttags[i]==scripts->script )
3510 0 : break;
3511 0 : if ( i==cnt )
3512 0 : scripttags[cnt++] = scripts->script;
3513 : }
3514 : }
3515 0 : scripttags[cnt] = 0;
3516 :
3517 0 : list = malloc((sf->glyphcnt+1)*sizeof(SplineChar *));
3518 0 : for ( i=0; scripttags[i]!=0; ++i ) {
3519 0 : uint32 script = scripttags[i];
3520 :
3521 0 : for ( cnt=gid=0; gid<sf->glyphcnt; ++gid ) {
3522 0 : if ( (sc = sf->glyphs[gid])!=NULL &&
3523 0 : SCWorthOutputting(sc) &&
3524 0 : SCScriptFromUnicode(sc)==script )
3525 0 : list[cnt++] = sc;
3526 : }
3527 0 : list[cnt] = NULL;
3528 0 : PSTKD_DoAutoKern(pstkd,list);
3529 : }
3530 0 : free(list);
3531 0 : free(scripttags);
3532 0 : if ( pstkd->next_row<pstkd->rows )
3533 0 : pstkd->psts = realloc(pstkd->psts,pstkd->rows*cols*sizeof(struct matrix_data));
3534 0 : PSTKD_DoSort(pstkd,pstkd->psts,pstkd->next_row,cols);
3535 0 : GMatrixEditSet(pstk,pstkd->psts,pstkd->next_row,false);
3536 0 : GGadgetRedraw(pstk);
3537 : }
3538 0 : return( true );
3539 : }
3540 :
3541 0 : static int PSTKD_AutoKernSelected(GGadget *g, GEvent *e) {
3542 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3543 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3544 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3545 0 : int rows, cols = GMatrixEditGetColCnt(pstk);
3546 0 : struct matrix_data *old = GMatrixEditGet(pstk,&rows);
3547 0 : SplineFont *sf = pstkd->sf;
3548 0 : FontViewBase *fv = sf->fv;
3549 : int enc,gid,cnt;
3550 : SplineChar **list, *sc;
3551 :
3552 0 : pstkd->cols = GMatrixEditGetColCnt(pstk);
3553 0 : pstkd->psts = MDCopy(old,rows,cols);
3554 0 : pstkd->rows_at_start = pstkd->rows = pstkd->next_row = rows;
3555 :
3556 0 : for ( enc=0,cnt=0; enc<fv->map->enccount; ++enc ) {
3557 0 : if ( fv->selected[enc] && (gid=fv->map->map[enc])!=-1 &&
3558 0 : SCWorthOutputting(sc = sf->glyphs[gid]))
3559 0 : ++cnt;
3560 : }
3561 0 : list = malloc((cnt+1)*sizeof(SplineChar *));
3562 0 : for ( enc=0,cnt=0; enc<fv->map->enccount; ++enc ) {
3563 0 : if ( fv->selected[enc] && (gid=fv->map->map[enc])!=-1 &&
3564 0 : SCWorthOutputting(sc = sf->glyphs[gid]))
3565 0 : list[cnt++] = sc;
3566 : }
3567 0 : list[cnt] = NULL;
3568 0 : PSTKD_DoAutoKern(pstkd,list);
3569 0 : free(list);
3570 0 : if ( pstkd->next_row<pstkd->rows )
3571 0 : pstkd->psts = realloc(pstkd->psts,pstkd->rows*cols*sizeof(struct matrix_data));
3572 0 : PSTKD_DoSort(pstkd,pstkd->psts,pstkd->next_row,cols);
3573 0 : GMatrixEditSet(pstk,pstkd->psts,pstkd->next_row,false);
3574 0 : GGadgetRedraw(pstk);
3575 : }
3576 0 : return( true );
3577 : }
3578 :
3579 0 : static int PSTKD_RemoveAll(GGadget *g, GEvent *e) {
3580 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3581 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3582 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3583 0 : int cols = GMatrixEditGetColCnt(pstk);
3584 0 : struct matrix_data *psts=NULL;
3585 :
3586 0 : psts = calloc(cols,sizeof(struct matrix_data));
3587 0 : GMatrixEditSet(pstk,psts,0,false);
3588 : }
3589 0 : return( true );
3590 : }
3591 :
3592 0 : static int PSTKD_RemoveEmpty(GGadget *g, GEvent *e) {
3593 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3594 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3595 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3596 0 : int rows, cols = GMatrixEditGetColCnt(pstk);
3597 0 : struct matrix_data *old = GMatrixEditGet(pstk,&rows), *psts=NULL;
3598 : int r, empty, rm_cnt, j;
3599 :
3600 0 : for ( r=rows-1, rm_cnt=0; r>=0; --r ) {
3601 0 : if ( pstkd->sub->lookup->lookup_type==gpos_single )
3602 0 : empty = old[r*cols+SIM_DX].u.md_ival==0 &&
3603 0 : old[r*cols+SIM_DY].u.md_ival==0 &&
3604 0 : old[r*cols+SIM_DX_ADV].u.md_ival==0 &&
3605 0 : old[r*cols+SIM_DY_ADV].u.md_ival==0;
3606 : else
3607 0 : empty = old[r*cols+1].u.md_str == NULL || *old[r*cols+1].u.md_str=='\0';
3608 0 : if ( empty ) {
3609 0 : if ( psts==NULL )
3610 0 : psts = MDCopy(old,rows,cols);
3611 0 : free(psts[r*cols+0].u.md_str);
3612 0 : if ( cols!=5 )
3613 0 : free(psts[r*cols+1].u.md_str);
3614 0 : for ( j=r+1; j<rows-rm_cnt; ++j )
3615 0 : memcpy(psts+(j-1)*cols,psts+j*cols,
3616 : cols*sizeof(struct matrix_data));
3617 0 : ++rm_cnt;
3618 : }
3619 : }
3620 0 : if ( rm_cnt!=0 ) {
3621 : /* Some reallocs explode if given a size of 0 */
3622 0 : psts = realloc(psts,(rows-rm_cnt+1)*cols*sizeof(struct matrix_data));
3623 0 : GMatrixEditSet(pstk,psts,rows-rm_cnt,false);
3624 : }
3625 : }
3626 0 : return( true );
3627 : }
3628 :
3629 0 : void SFUntickAllPSTandKern(SplineFont *sf)
3630 : {
3631 : SplineChar *sc;
3632 : PST *pst;
3633 : KernPair *kp;
3634 : int gid, isv;
3635 0 : for ( gid=0; gid<sf->glyphcnt; ++gid ) {
3636 0 : if ( (sc = sf->glyphs[gid])!=NULL ) {
3637 0 : for ( pst = sc->possub; pst!=NULL; pst=pst->next )
3638 0 : pst->ticked = false;
3639 0 : for ( isv=0; isv<2; ++isv )
3640 0 : for ( kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kp->next )
3641 0 : kp->kcid = 0;
3642 : }
3643 : }
3644 0 : }
3645 :
3646 :
3647 :
3648 : /**
3649 : * Return true of the splinechar has any horizontal or vert kerns.
3650 : */
3651 0 : static int SCHasAnyKerns(SplineChar *sc) {
3652 : KernPair *kp;
3653 : int v;
3654 0 : int ret = 0;
3655 0 : for ( v=0; v<2; ++v ) {
3656 0 : kp = v ? sc->vkerns : sc->kerns;
3657 0 : if ( kp!=NULL ) {
3658 0 : ret = 1;
3659 : }
3660 : }
3661 0 : return ret;
3662 : }
3663 :
3664 :
3665 0 : int kernsLength( KernPair *kp ) {
3666 0 : if( !kp )
3667 0 : return 0;
3668 0 : int i = 0;
3669 0 : for ( ; kp; kp=kp->next ) {
3670 0 : ++i;
3671 : }
3672 0 : return i;
3673 : }
3674 :
3675 0 : static int kerncomp(const void *_r1, const void *_r2) {
3676 0 : const KernPair *kp1 = *(KernPair * const *)_r1;
3677 0 : const KernPair *kp2 = *(KernPair * const *)_r2;
3678 0 : if( kp1->sc->orig_pos == kp2->sc->orig_pos )
3679 0 : return 0;
3680 0 : if( kp1->sc->orig_pos < kp2->sc->orig_pos )
3681 0 : return -1;
3682 0 : return 1;
3683 : }
3684 :
3685 : /**
3686 : * This will sort both the kerns and vkerns for the splinefont so that
3687 : * the list is in the order of "orig_pos" for each kernpair.
3688 : *
3689 : * Sometimes FontForge had two KernPair lists which were identical but
3690 : * where not sorted the same way, using this function will force a
3691 : * specific ordering on the KernPair list to make performing a lexical
3692 : * "diff" between two serialized KernPair lists work as expected.
3693 : */
3694 0 : static void SCKernsSort( SplineChar *sc ) {
3695 : KernPair *kp;
3696 : int v;
3697 : KernPair *cur;
3698 0 : KernPair *newlist = 0;
3699 0 : for ( v=0; v<2; ++v ) {
3700 0 : kp = v ? sc->vkerns : sc->kerns;
3701 0 : if ( kp!=NULL ) {
3702 0 : int idx = 0;
3703 0 : int length = kernsLength(kp);
3704 0 : if( length == 0 ) {
3705 : // nothing is always sorted already!
3706 0 : continue;
3707 : }
3708 :
3709 0 : KernPair** ordered = malloc( sizeof(KernPair*) * length+1 );
3710 : // copy to temp array
3711 0 : idx = 0;
3712 0 : for ( cur = kp; cur; cur=cur->next ) {
3713 0 : ordered[idx] = cur;
3714 0 : idx++;
3715 : }
3716 : // sort array using easy swap()
3717 0 : qsort(ordered,length,sizeof(KernPair*),kerncomp);
3718 :
3719 : // and back to a linked list again
3720 0 : newlist = ordered[0];
3721 0 : ordered[length-1]->next = 0;
3722 0 : for( idx = 0; idx < length-1; idx++ ) {
3723 0 : ordered[idx]->next = ordered[idx+1];
3724 : }
3725 0 : free( ordered );
3726 :
3727 : // now change the right kerns member of sc
3728 0 : if( v ) sc->vkerns = newlist;
3729 0 : else sc->kerns = newlist;
3730 : }
3731 : }
3732 0 : }
3733 :
3734 : extern char* SFDCreateUndoForLookup( SplineFont *sf, int lookup_type );
3735 :
3736 : /**
3737 : * Create a fragment of SFD file which represents the state of the
3738 : * lookup_type for the given splinefont.
3739 : *
3740 : * Note that the kernpair lists may be modified by this function in
3741 : * that those lists might be sorted on return.
3742 : *
3743 : * The caller needs to free() the returned string value.
3744 : */
3745 0 : char* SFDCreateUndoForLookup( SplineFont *sf, int lookup_type )
3746 : {
3747 0 : int gid = 0;
3748 0 : SplineChar *sc = 0;
3749 0 : PST *pst = 0;
3750 0 : FILE* sfd = MakeTemporaryFile();
3751 0 : SFD_DumpLookup( sfd, sf );
3752 0 : for ( gid=0; gid<sf->glyphcnt; ++gid )
3753 : {
3754 0 : if ( (sc = sf->glyphs[gid])!=NULL )
3755 : {
3756 0 : int haveStartMarker = 0;
3757 0 : if(lookup_type==gpos_pair)
3758 : {
3759 0 : haveStartMarker = 1;
3760 0 : SFDDumpCharStartingMarker( sfd, sc );
3761 :
3762 0 : if( SCHasAnyKerns(sc) )
3763 : {
3764 0 : SCKernsSort( sc );
3765 0 : SFD_DumpKerns( sfd, sc, 0 );
3766 : }
3767 : }
3768 : else
3769 : {
3770 0 : for ( pst = sc->possub; pst; pst=pst->next )
3771 : {
3772 0 : if( !haveStartMarker )
3773 : {
3774 0 : haveStartMarker = 1;
3775 0 : SFDDumpCharStartingMarker( sfd, sc );
3776 : }
3777 0 : SFD_DumpPST( sfd, sc );
3778 : }
3779 : }
3780 0 : if( haveStartMarker )
3781 : {
3782 0 : fprintf(sfd,"EndChar\n" );
3783 : }
3784 : }
3785 : }
3786 :
3787 0 : char* str = FileToAllocatedString( sfd );
3788 0 : fclose(sfd);
3789 0 : return(str);
3790 : }
3791 :
3792 0 : static SplineChar* SCFindByGlyphName( SplineFont *sf, char* n ) {
3793 0 : int i=0;
3794 0 : for ( i=0; i<sf->glyphcnt; ++i ) {
3795 0 : if ( sf->glyphs[i]!=NULL ) {
3796 0 : SplineChar *sc = sf->glyphs[i];
3797 0 : if( !strcmp( sc->name, n )) {
3798 0 : return sc;
3799 : }
3800 : }
3801 : }
3802 0 : return 0;
3803 : }
3804 :
3805 :
3806 0 : static void SFDTrimUndoOldToNew_Output( FILE* retf, char* glyph, char* line ) {
3807 0 : fwrite( "StartChar:", strlen("StartChar:"), 1, retf );
3808 0 : fwrite( glyph, strlen(glyph), 1, retf );
3809 0 : fwrite( "\n", 1, 1, retf );
3810 0 : fwrite( line, strlen(line), 1, retf );
3811 0 : fwrite( "\n", 1, 1, retf );
3812 0 : fwrite( "EndChar\n", strlen("EndChar\n"), 1, retf );
3813 0 : }
3814 :
3815 : /**
3816 : * Given two SFD fragments, oldstr and newstr, compare them and remove
3817 : * the SFD for glyphs which have not changed state significantly
3818 : * between old and new.
3819 : *
3820 : * This can be very handy for fonts like DroidSans which have kerning
3821 : * table which is about 2mb in SFD format. If you only change the
3822 : * kerning for a 10 glyphs then the resulting SFD from this function
3823 : * might be only in the 10-50kb size instead of megabytes.
3824 : *
3825 : * The caller needs to free the return value.
3826 : */
3827 0 : static char* SFDTrimUndoOldToNew( SplineFont *sf, char* oldstr, char* newstr ) {
3828 :
3829 0 : if( !oldstr || !newstr ) {
3830 0 : return 0;
3831 : }
3832 :
3833 : /* open temporary files */
3834 : FILE *of, *nf, *retf;
3835 0 : if ( !(retf=MakeTemporaryFile()) )
3836 0 : goto error0SFDTrimUndoOldToNew;
3837 0 : if ( !(of=MakeTemporaryFile()) )
3838 0 : goto error1SFDTrimUndoOldToNew;
3839 0 : if ( !(nf=MakeTemporaryFile()) )
3840 0 : goto error2SFDTrimUndoOldToNew;
3841 :
3842 0 : int glyphsWithUndoInfoCount = 0;
3843 0 : fwrite( oldstr, strlen(oldstr), 1, of );
3844 0 : fwrite( newstr, strlen(newstr), 1, nf );
3845 0 : fseek( of, 0, SEEK_SET );
3846 0 : fseek( nf, 0, SEEK_SET );
3847 :
3848 0 : char* oglyph = 0;
3849 0 : char* nglyph = 0;
3850 0 : char* oline = 0;
3851 0 : char* nline = 0;
3852 :
3853 : // copy the header from the old SFD fragment
3854 0 : while((oline = getquotedeol(of)) && !feof(of)) {
3855 0 : if( !strnmatch( oline, "StartChar:", strlen( "StartChar:" ))) {
3856 0 : int len = strlen("StartChar:");
3857 0 : while( oline[len] && oline[len] == ' ' )
3858 0 : len++;
3859 0 : oglyph = copy( oline+len );
3860 0 : break;
3861 : }
3862 0 : fwrite( oline, strlen(oline), 1, retf );
3863 0 : fwrite( "\n", 1, 1, retf );
3864 0 : free(oline);
3865 : }
3866 :
3867 0 : while (oglyph) {
3868 0 : oline = getquotedeol(of);
3869 0 : SplineChar* oldsc = SCFindByGlyphName( sf, oglyph );
3870 0 : int newGlyphsSeen = 0;
3871 :
3872 0 : while ((nglyph = SFDMoveToNextStartChar(nf))) {
3873 0 : newGlyphsSeen++;
3874 0 : SplineChar* newsc = SCFindByGlyphName( sf, nglyph );
3875 0 : if( !newsc || !oldsc ) {
3876 : goto error3SFDTrimUndoOldToNew;
3877 : }
3878 : /**
3879 : * If the user has deleted a whole glyph from the SFD
3880 : * fragment then the nglyph in the new list will be
3881 : * *after* oglyph in the ordering. If that's the case then
3882 : * we have to output oglyph and read another glyph from
3883 : * the old list.
3884 : */
3885 0 : while( newsc->orig_pos > oldsc->orig_pos ) {
3886 0 : glyphsWithUndoInfoCount++;
3887 0 : SFDTrimUndoOldToNew_Output( retf, oglyph, oline );
3888 0 : free(oline);
3889 0 : oglyph = SFDMoveToNextStartChar(of);
3890 0 : oline = getquotedeol(of);
3891 0 : oldsc = SCFindByGlyphName( sf, oglyph );
3892 : }
3893 :
3894 0 : nline = getquotedeol(nf);
3895 0 : if( !oline || !nline ) {
3896 0 : fprintf(stderr,"failed to read new or old files during SFD diff. Returning entire new data as diff!\n");
3897 0 : goto error3SFDTrimUndoOldToNew;
3898 : }
3899 0 : if( strcmp( oglyph, nglyph )) {
3900 : // fprintf(stderr,"mismatch between old and new SFD fragments. Skipping new glyph that is not in old...\n");
3901 0 : glyphsWithUndoInfoCount++;
3902 0 : SFDTrimUndoOldToNew_Output( retf, nglyph, "Kerns2: " );
3903 0 : free(nline);
3904 0 : continue;
3905 : }
3906 :
3907 0 : if( !strcmp( oline, nline )) {
3908 : // printf("old and new have the same data, skipping glyph:%s\n", oglyph );
3909 0 : break;
3910 : }
3911 :
3912 0 : glyphsWithUndoInfoCount++;
3913 0 : SFDTrimUndoOldToNew_Output( retf, oglyph, oline );
3914 0 : free(nline);
3915 0 : break;
3916 : }
3917 :
3918 : /**
3919 : * There is one or more old glyphs which do not have a new glyph
3920 : * in the SFD fragments, preserve those old SFD fragments.
3921 : */
3922 0 : if( !newGlyphsSeen ) {
3923 0 : glyphsWithUndoInfoCount++;
3924 0 : SFDTrimUndoOldToNew_Output( retf, oglyph, oline );
3925 : }
3926 :
3927 0 : free(oline);
3928 0 : oglyph = SFDMoveToNextStartChar(of);
3929 : }
3930 0 : fclose(of);
3931 :
3932 : /* Trailing new glyph data that has nothing in the old file.
3933 : *
3934 : * we don't care what the data is if its new,
3935 : * we only want to be able to revert to the old state (nothing)
3936 : */
3937 0 : while ((nglyph = SFDMoveToNextStartChar(nf))) {
3938 0 : SCFindByGlyphName( sf, nglyph );
3939 0 : nline = getquotedeol(nf);
3940 0 : glyphsWithUndoInfoCount++;
3941 0 : SFDTrimUndoOldToNew_Output( retf, nglyph, "Kerns2: " );
3942 : }
3943 0 : fclose(nf);
3944 :
3945 0 : if( !glyphsWithUndoInfoCount )
3946 0 : goto error1SFDTrimUndoOldToNew;
3947 :
3948 0 : char* ret = FileToAllocatedString( retf );
3949 0 : fclose(retf);
3950 0 : return ret;
3951 :
3952 0 : error3SFDTrimUndoOldToNew: fclose(of);
3953 0 : error2SFDTrimUndoOldToNew: fclose(nf);
3954 0 : error1SFDTrimUndoOldToNew: fclose(retf);
3955 : error0SFDTrimUndoOldToNew:
3956 0 : return 0;
3957 : }
3958 :
3959 :
3960 :
3961 0 : static int PSTKD_Ok(GGadget *g, GEvent *e) {
3962 :
3963 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3964 0 : PSTKernDlg *pstkd = GDrawGetUserData(GGadgetGetWindow(g));
3965 0 : GGadget *pstk = GWidgetGetControl(pstkd->gw,CID_PSTList);
3966 0 : int rows, cols = GMatrixEditGetColCnt(pstk);
3967 0 : struct matrix_data *psts = GMatrixEditGet(pstk,&rows);
3968 : int r, r1, k, gid, isv, ch;
3969 : char *pt, *start;
3970 0 : int lookup_type = pstkd->sub->lookup->lookup_type;
3971 0 : SplineFont *sf = NULL;
3972 : SplineChar *sc, *found;
3973 : char *buts[3];
3974 : KernPair *kp, *kpprev, *kpnext;
3975 : PST *pst, *pstprev, *pstnext;
3976 0 : int err, touch=0, separation=0, minkern=0, onlyCloser=0, autokern=0;
3977 0 : int _t = lookup_type == gpos_single ? pst_position
3978 0 : : lookup_type == gpos_pair ? pst_pair
3979 0 : : lookup_type == gsub_single ? pst_substitution
3980 0 : : lookup_type == gsub_alternate ? pst_alternate
3981 0 : : lookup_type == gsub_multiple ? pst_multiple
3982 0 : : pst_ligature;
3983 :
3984 : /* First check for errors */
3985 0 : if ( lookup_type==gpos_pair ) {
3986 : /* bad metadata */
3987 0 : err = false;
3988 0 : touch = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Touched));
3989 0 : separation = GetInt8(pstkd->gw,CID_Separation,_("Separation"),&err);
3990 0 : minkern = GetInt8(pstkd->gw,CID_MinKern,_("Min Kern"),&err);
3991 0 : onlyCloser = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_OnlyCloser));
3992 0 : autokern = GGadgetIsChecked(GWidgetGetControl(pstkd->gw,CID_Autokern));
3993 0 : if ( err )
3994 0 : return( true );
3995 : }
3996 :
3997 : /* Glyph names that aren't in the font */
3998 0 : for ( r=0; r<rows; ++r ) {
3999 0 : if ( SFGetChar(pstkd->sf,-1,psts[r*cols+0].u.md_str)==NULL ) {
4000 0 : ff_post_error( _("Missing glyph"),_("There is no glyph named %s in the font"),
4001 0 : psts[cols*r+0].u.md_str );
4002 0 : return( true );
4003 : }
4004 : }
4005 : /* Empty entries */
4006 0 : if ( cols==2 || cols==10 ) {
4007 0 : for ( r=0; r<rows; ++r ) {
4008 0 : start = psts[cols*r+1].u.md_str;
4009 0 : if ( start==NULL ) start="";
4010 0 : while ( *start== ' ' ) ++start;
4011 0 : if ( *start=='\0' ) {
4012 0 : ff_post_error( _("Missing glyph name"),_("You must specify a replacement glyph for %s"),
4013 0 : psts[cols*r+0].u.md_str );
4014 0 : return( true );
4015 : }
4016 : /* Replacements which aren't in the font */
4017 0 : while ( *start ) {
4018 0 : for ( pt=start; *pt!='\0' && *pt!=' ' && *pt!='('; ++pt );
4019 0 : ch = *pt; *pt='\0';
4020 0 : found = SFGetChar(pstkd->sf,-1,start);
4021 0 : if ( found==NULL ) {
4022 0 : buts[0] = _("_Yes");
4023 0 : buts[1] = _("_Cancel");
4024 0 : buts[2] = NULL;
4025 0 : 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?"),
4026 0 : psts[cols*r+0].u.md_str, start)==1 ) {
4027 0 : *pt = ch;
4028 0 : return( true );
4029 : }
4030 : }
4031 0 : *pt = ch;
4032 0 : if ( ch=='(' ) {
4033 0 : while ( *pt!=')' && *pt!='\0' ) ++pt;
4034 0 : if ( *pt==')' ) ++pt;
4035 : }
4036 0 : while ( *pt== ' ' ) ++pt;
4037 0 : start = pt;
4038 : }
4039 : }
4040 : }
4041 : /* Duplicate entries */
4042 0 : for ( r=0; r<rows; ++r ) {
4043 0 : for ( r1=r+1; r1<rows; ++r1 ) {
4044 0 : if ( strcmp(psts[r*cols+0].u.md_str,psts[r1*cols+0].u.md_str)==0 ) {
4045 0 : if ( lookup_type==gpos_pair || lookup_type==gsub_ligature ) {
4046 0 : if ( strcmp(psts[r*cols+1].u.md_str,psts[r1*cols+1].u.md_str)==0 ) {
4047 0 : ff_post_error( _("Duplicate data"),_("There are two entries for the same glyph set (%.80s and %.80s)"),
4048 0 : psts[cols*r+0].u.md_str, psts[cols*r+1].u.md_str );
4049 0 : return( true );
4050 : }
4051 : } else {
4052 0 : ff_post_error( _("Duplicate data"),_("There are two entries for the same glyph (%.80s)"),
4053 0 : psts[cols*r+0].u.md_str );
4054 0 : return( true );
4055 : }
4056 : }
4057 : }
4058 : }
4059 :
4060 : /* Check for badly specified device tables */
4061 0 : if ( _t==pst_position || _t==pst_pair ) {
4062 0 : int startc = _t==pst_position ? SIM_DX+1 : PAIR_DX1+1;
4063 : int low, high, c;
4064 0 : for ( r=0; r<rows; ++r ) {
4065 0 : for ( c=startc; c<cols; c+=2 ) {
4066 0 : if ( !DeviceTableOK(psts[r*cols+c].u.md_str,&low,&high) ) {
4067 0 : ff_post_error( _("Bad Device Table Adjustment"),_("A device table adjustment specified for %.80s is invalid"),
4068 0 : psts[cols*r+0].u.md_str );
4069 0 : return( true );
4070 : }
4071 : }
4072 : }
4073 : }
4074 :
4075 : /* Ok, if we get here then there should be no errors and we can parse */
4076 :
4077 : /* First grab a snapshot of the state of the system as it is
4078 : * now so that we can "undo" the lookup table edits as a
4079 : * single operation if desired */
4080 0 : char* oldsfd = 0;
4081 0 : if( !pstkd->sf->subfontcnt ) {
4082 0 : sf = pstkd->sf;
4083 0 : oldsfd = SFDCreateUndoForLookup( sf, lookup_type );
4084 :
4085 0 : if( DEBUG && oldsfd )
4086 0 : GFileWriteAll( "/tmp/old-lookup-table.sfd", oldsfd );
4087 : }
4088 :
4089 : /* Then mark all the current things as unused */
4090 0 : k=0;
4091 : do {
4092 0 : sf = pstkd->sf->subfontcnt==0 ? pstkd->sf : pstkd->sf->subfonts[k];
4093 0 : SFUntickAllPSTandKern( sf );
4094 0 : ++k;
4095 0 : } while ( k<pstkd->sf->subfontcnt );
4096 :
4097 : /* Write the changes to each SplineChar. For example, updating
4098 : * the ligaments will update the splinechar's
4099 : * pst->u.subs.variant and pst->u.lig.lig
4100 : */
4101 0 : if ( lookup_type!=gpos_pair ) {
4102 0 : for ( r=0; r<rows; ++r ) {
4103 :
4104 0 : sc = SFGetChar(pstkd->sf,-1,psts[cols*r+0].u.md_str);
4105 0 : for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
4106 0 : if ( pst->subtable == pstkd->sub && !pst->ticked )
4107 0 : break;
4108 : }
4109 0 : if ( pst==NULL ) {
4110 0 : pst = chunkalloc(sizeof(PST));
4111 0 : pst->type = _t;
4112 0 : pst->subtable = pstkd->sub;
4113 0 : pst->next = sc->possub;
4114 0 : sc->possub = pst;
4115 0 : } else if ( lookup_type!=gpos_single )
4116 0 : free( pst->u.subs.variant );
4117 0 : pst->ticked = true;
4118 0 : if ( lookup_type==gpos_single ) {
4119 0 : VRDevTabParse(&pst->u.pos,&psts[cols*r+SIM_DX+1]);
4120 0 : pst->u.pos.xoff = psts[cols*r+SIM_DX].u.md_ival;
4121 0 : pst->u.pos.yoff = psts[cols*r+SIM_DY].u.md_ival;
4122 0 : pst->u.pos.h_adv_off = psts[cols*r+SIM_DX_ADV].u.md_ival;
4123 0 : pst->u.pos.v_adv_off = psts[cols*r+SIM_DY_ADV].u.md_ival;
4124 : } else {
4125 0 : pst->u.subs.variant = GlyphNameListDeUnicode( psts[cols*r+1].u.md_str );
4126 0 : if ( lookup_type==gsub_ligature )
4127 0 : pst->u.lig.lig = sc;
4128 : }
4129 : }
4130 0 : } else if ( lookup_type==gpos_pair ) {
4131 0 : for ( r=0; r<rows; ++r ) {
4132 0 : sc = SFGetChar(pstkd->sf,-1,psts[cols*r+0].u.md_str);
4133 0 : KpMDParse(sc,pstkd->sub,psts,rows,cols,r);
4134 : }
4135 : }
4136 :
4137 : /* Now free anything with this subtable which did not get ticked
4138 : * during the above update */
4139 0 : k=0;
4140 : do {
4141 0 : sf = pstkd->sf->subfontcnt==0 ? pstkd->sf : pstkd->sf->subfonts[k];
4142 0 : for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
4143 0 : for ( pstprev=NULL, pst = sc->possub; pst!=NULL; pst=pstnext ) {
4144 0 : pstnext = pst->next;
4145 0 : if ( pst->ticked || pst->subtable!=pstkd->sub )
4146 0 : pstprev = pst;
4147 : else {
4148 0 : if ( pstprev==NULL )
4149 0 : sc->possub = pstnext;
4150 : else
4151 0 : pstprev->next = pstnext;
4152 0 : pst->next = NULL;
4153 0 : PSTFree(pst);
4154 : }
4155 : }
4156 0 : for ( isv=0; isv<2; ++isv ) {
4157 0 : for ( kpprev=NULL, kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kpnext ) {
4158 0 : kpnext = kp->next;
4159 0 : if ( kp->kcid!=0 || kp->subtable!=pstkd->sub )
4160 0 : kpprev = kp;
4161 : else {
4162 0 : if ( kpprev!=NULL )
4163 0 : kpprev->next = kpnext;
4164 0 : else if ( isv )
4165 0 : sc->vkerns = kpnext;
4166 : else
4167 0 : sc->kerns = kpnext;
4168 0 : kp->next = NULL;
4169 0 : KernPairsFree(kp);
4170 : }
4171 : }
4172 : }
4173 : }
4174 0 : ++k;
4175 0 : } while ( k<pstkd->sf->subfontcnt );
4176 :
4177 : /* The field we use to tick kern pairs must be reset to false */
4178 0 : k=0;
4179 : do {
4180 0 : for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc = sf->glyphs[gid])!=NULL ) {
4181 0 : for ( isv=0; isv<2; ++isv ) {
4182 0 : for ( kp = isv ? sc->vkerns : sc->kerns; kp!=NULL; kp=kp->next ) {
4183 0 : kp->kcid = false;
4184 : }
4185 : }
4186 : }
4187 0 : ++k;
4188 0 : } while ( k<pstkd->sf->subfontcnt );
4189 0 : PSTKD_SetSuffix(pstkd);
4190 0 : if ( lookup_type==gpos_pair ) {
4191 0 : pstkd->sub->separation = separation;
4192 0 : pstkd->sub->minkern = minkern;
4193 0 : pstkd->sub->kerning_by_touch = touch;
4194 0 : pstkd->sub->onlyCloser = onlyCloser;
4195 0 : pstkd->sub->dontautokern = !autokern;
4196 : }
4197 :
4198 : /* compare the updated data to the snapshot we took before and
4199 : * trim the undo operation of superfluious data
4200 : */
4201 0 : if( oldsfd ) {
4202 :
4203 0 : int shouldCreateUndoEntry = 1;
4204 0 : sf = pstkd->sf;
4205 0 : char* str = SFDCreateUndoForLookup( sf, lookup_type );
4206 :
4207 0 : if( !pstkd->sf->subfontcnt ) {
4208 0 : char* diffstr = SFDTrimUndoOldToNew( sf, oldsfd, str );
4209 0 : if( !diffstr ) {
4210 : // If nothing has changed after all,
4211 : // don't create an empty undo
4212 0 : shouldCreateUndoEntry = 0;
4213 : } else {
4214 0 : free(str);
4215 0 : str = diffstr;
4216 : }
4217 : }
4218 :
4219 0 : if( shouldCreateUndoEntry ) {
4220 :
4221 0 : dlist_trim_to_limit( (struct dlistnode **)&sf->undoes, fontlevel_undo_limit,
4222 : (dlist_visitor_func_type)SFUndoFreeAssociated );
4223 :
4224 0 : enum sfundotype t = sfut_lookups;
4225 0 : if( lookup_type == gpos_pair ) {
4226 0 : t = sfut_lookups_kerns;
4227 : }
4228 0 : SFUndoes* undo = SFUndoCreateSFD( t, _("Lookup Table Edit"), str );
4229 0 : dlist_pushfront( (struct dlistnode **)&sf->undoes, (struct dlistnode *)undo );
4230 : }
4231 : // printf("we now have %d splinefont level undoes\n", dlist_size((struct dlistnode **)&sf->undoes));
4232 : }
4233 :
4234 0 : pstkd->done = true;
4235 : }
4236 0 : return( true );
4237 : }
4238 :
4239 0 : static void PSTKD_DoCancel(PSTKernDlg *pstkd) {
4240 0 : pstkd->done = true;
4241 0 : }
4242 :
4243 0 : static int PSTKD_Cancel(GGadget *g, GEvent *e) {
4244 : PSTKernDlg *pstkd;
4245 :
4246 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
4247 0 : pstkd = GDrawGetUserData(GGadgetGetWindow(g));
4248 0 : PSTKD_DoCancel(pstkd);
4249 : }
4250 0 : return( true );
4251 : }
4252 :
4253 0 : char *GlyphNameListDeUnicode( char *str ) {
4254 : char *pt;
4255 : char *ret, *rpt;
4256 :
4257 0 : rpt = ret = malloc(strlen(str)+1);
4258 0 : while ( *str==' ' ) ++str;
4259 0 : for ( pt=str; *pt!='\0'; ) {
4260 0 : if ( *pt==' ' ) {
4261 0 : while ( *pt==' ' ) ++pt;
4262 0 : --pt;
4263 : }
4264 0 : if ( *pt=='(' ) {
4265 0 : while ( *pt!=')' && *pt!='\0' ) ++pt;
4266 0 : if ( *pt==')' ) ++pt;
4267 : } else
4268 0 : *rpt++ = *pt++;
4269 : }
4270 0 : *rpt = '\0';
4271 0 : return( ret );
4272 : }
4273 :
4274 0 : char *SFNameList2NameUni(SplineFont *sf, char *str) {
4275 : char *start, *pt, *ret, *rpt;
4276 : int cnt, ch;
4277 : SplineChar *sc;
4278 :
4279 0 : if ( str==NULL )
4280 0 : return( NULL );
4281 0 : if ( !add_char_to_name_list )
4282 0 : return( copy(str));
4283 :
4284 0 : cnt = 0;
4285 0 : for ( pt=str; *pt!='\0'; ++pt )
4286 0 : if ( *pt==' ' )
4287 0 : ++cnt;
4288 0 : rpt = ret = malloc(strlen(str) + (cnt+1)*7 + 1);
4289 0 : for ( start=str; *start!='\0'; ) {
4290 0 : while ( *start==' ' ) ++start;
4291 0 : if ( *start=='\0' )
4292 0 : break;
4293 0 : for ( pt=start; *pt!='\0' && *pt!=' ' && *pt!='('; ++pt );
4294 0 : ch = *pt; *pt='\0';
4295 0 : sc = SFGetChar(sf,-1,start);
4296 0 : strcpy(rpt,start);
4297 0 : rpt += strlen(rpt);
4298 0 : *pt = ch;
4299 : /* don't show control characters, or space or parens, or */
4300 : /* latin letters (their names are themselves, no need to duplicate) */
4301 : /* or things in the private use area */
4302 0 : if ( sc!=NULL && sc->unicodeenc>32 && sc->unicodeenc!=')' &&
4303 0 : !( sc->unicodeenc<0x7f && isalpha(sc->unicodeenc)) &&
4304 0 : !issurrogate(sc->unicodeenc) &&
4305 0 : !isprivateuse(sc->unicodeenc)) {
4306 0 : *rpt++ = '(';
4307 0 : rpt = utf8_idpb(rpt,sc->unicodeenc,0);
4308 0 : *rpt++ = ')';
4309 : }
4310 0 : *rpt++ = ' ';
4311 0 : if ( ch=='(' )
4312 0 : while ( *pt!=')' && *pt!='\0' ) ++pt;
4313 0 : while ( *pt==' ' ) ++pt;
4314 0 : start = pt;
4315 : }
4316 0 : if ( rpt>ret )
4317 0 : rpt[-1] = '\0';
4318 : else
4319 0 : ret[0] = '\0';
4320 0 : return( ret );
4321 : }
4322 :
4323 0 : char *SCNameUniStr(SplineChar *sc) {
4324 : char *temp, *pt;
4325 : int len;
4326 :
4327 0 : if ( sc==NULL )
4328 0 : return( NULL );
4329 0 : if ( !add_char_to_name_list )
4330 0 : return( copy(sc->name));
4331 :
4332 0 : len = strlen(sc->name);
4333 0 : temp = malloc(len + 8);
4334 0 : strcpy(temp,sc->name);
4335 0 : if ( sc->unicodeenc>32 && sc->unicodeenc!=')' && add_char_to_name_list &&
4336 0 : !( sc->unicodeenc<0x7f && isalpha(sc->unicodeenc)) &&
4337 0 : !issurrogate(sc->unicodeenc) &&
4338 0 : !isprivateuse(sc->unicodeenc)) {
4339 0 : pt = temp+len;
4340 0 : *pt++ = '(';
4341 0 : pt = utf8_idpb(pt,sc->unicodeenc,0);
4342 0 : *pt++ = ')';
4343 0 : *pt = '\0';
4344 : }
4345 0 : return( temp );
4346 : }
4347 :
4348 0 : unichar_t *uSCNameUniStr(SplineChar *sc) {
4349 : unichar_t *temp;
4350 : int len;
4351 :
4352 0 : if ( sc==NULL )
4353 0 : return( NULL );
4354 0 : temp = malloc((strlen(sc->name) + 5) * sizeof(unichar_t));
4355 0 : utf82u_strcpy(temp,sc->name);
4356 0 : if ( sc->unicodeenc>32 && sc->unicodeenc!=')' && add_char_to_name_list &&
4357 0 : !( sc->unicodeenc<0x7f && isalpha(sc->unicodeenc)) &&
4358 0 : !issurrogate(sc->unicodeenc) &&
4359 0 : !isprivateuse(sc->unicodeenc)) {
4360 0 : len = u_strlen(temp);
4361 0 : temp[len] = '(';
4362 0 : temp[len+1] = sc->unicodeenc;
4363 0 : temp[len+2] = ')';
4364 0 : temp[len+3] = '\0';
4365 : }
4366 0 : return( temp );
4367 : }
4368 :
4369 0 : unichar_t **SFGlyphNameCompletion(SplineFont *sf,GGadget *t,int from_tab,
4370 : int new_name_after_space) {
4371 : unichar_t *pt, *spt, *basept, *wild; unichar_t **ret;
4372 : int gid, cnt, doit, match_len;
4373 : SplineChar *sc;
4374 : int do_wildcards;
4375 :
4376 0 : pt = spt = basept = (unichar_t *) _GGadgetGetTitle(t);
4377 0 : if ( pt==NULL || *pt=='\0' )
4378 0 : return( NULL );
4379 0 : if ( new_name_after_space ) {
4380 0 : if (( spt = u_strrchr(spt,' '))== NULL )
4381 0 : spt = basept;
4382 : else {
4383 0 : pt = ++spt;
4384 0 : if ( *pt=='\0' )
4385 0 : return( NULL );
4386 : }
4387 : }
4388 0 : while ( *pt && *pt!='*' && *pt!='?' && *pt!='[' && *pt!='{' )
4389 0 : ++pt;
4390 0 : do_wildcards = *pt!='\0';
4391 :
4392 0 : if (( !do_wildcards && pt-spt==1 && ( *spt>=0x10000 || !isalpha(*spt))) ||
4393 0 : (!from_tab && do_wildcards && pt-spt==2 && spt[1]==' ')) {
4394 0 : sc = SFGetChar(sf,*spt,NULL);
4395 : /* One unicode character which isn't a glyph name (so not "A") and */
4396 : /* isn't a wildcard (so not "*") gets expanded to its glyph name. */
4397 : /* (so "," becomes "comma(,)" */
4398 : /* Or, a single wildcard followed by a space gets expanded to glyph name */
4399 0 : if ( sc!=NULL ) {
4400 0 : ret = malloc((2)*sizeof(unichar_t *));
4401 0 : ret[0] = uSCNameUniStr(sc);
4402 0 : ret[1] = NULL;
4403 0 : return( ret );
4404 : }
4405 : }
4406 0 : if ( do_wildcards && !from_tab )
4407 0 : return( NULL );
4408 :
4409 0 : wild = NULL;
4410 0 : if ( do_wildcards ) {
4411 0 : pt = spt;
4412 0 : wild = malloc((u_strlen(spt)+2)*sizeof(unichar_t));
4413 0 : u_strcpy(wild,pt);
4414 0 : uc_strcat(wild,"*");
4415 : }
4416 :
4417 0 : match_len = u_strlen(spt);
4418 0 : ret = NULL;
4419 0 : for ( doit=0; doit<2; ++doit ) {
4420 0 : cnt=0;
4421 0 : for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
4422 : int matched;
4423 0 : if ( do_wildcards ) {
4424 0 : unichar_t *temp = utf82u_copy(sc->name);
4425 0 : matched = GGadgetWildMatch((unichar_t *) wild,temp,false);
4426 0 : free(temp);
4427 : } else
4428 0 : matched = uc_strncmp(spt,sc->name,match_len)==0;
4429 0 : if ( matched ) {
4430 0 : if ( doit ) {
4431 0 : if ( spt==basept ) {
4432 0 : ret[cnt] = uSCNameUniStr(sc);
4433 : } else {
4434 0 : unichar_t *temp = malloc((spt-basept+strlen(sc->name)+4)*sizeof(unichar_t));
4435 : int len;
4436 0 : u_strncpy(temp,basept,spt-basept);
4437 0 : utf82u_strcpy(temp+(spt-basept),sc->name);
4438 0 : len = u_strlen(temp);
4439 0 : if ( sc->unicodeenc>32 && add_char_to_name_list &&
4440 0 : !( sc->unicodeenc<0x7f && isalpha(sc->unicodeenc)) &&
4441 0 : !issurrogate(sc->unicodeenc) &&
4442 0 : !isprivateuse(sc->unicodeenc)) {
4443 0 : temp[len] = '(';
4444 0 : temp[len+1] = sc->unicodeenc;
4445 0 : temp[len+2] = ')';
4446 0 : temp[len+3] = '\0';
4447 : }
4448 0 : ret[cnt] = temp;
4449 : }
4450 : }
4451 0 : ++cnt;
4452 : }
4453 : }
4454 0 : if ( doit )
4455 0 : ret[cnt] = NULL;
4456 0 : else if ( cnt==0 )
4457 0 : break;
4458 : else
4459 0 : ret = malloc((cnt+1)*sizeof(unichar_t *));
4460 : }
4461 0 : free(wild);
4462 0 : return( ret );
4463 : }
4464 :
4465 0 : static unichar_t **PSTKD_GlyphNameCompletion(GGadget *t,int from_tab) {
4466 0 : PSTKernDlg *pstkd = GDrawGetUserData(GDrawGetParentWindow(GGadgetGetWindow(t)));
4467 0 : SplineFont *sf = pstkd->sf;
4468 :
4469 0 : return( SFGlyphNameCompletion(sf,t,from_tab,false));
4470 : }
4471 :
4472 0 : static unichar_t **PSTKD_GlyphListCompletion(GGadget *t,int from_tab) {
4473 0 : PSTKernDlg *pstkd = GDrawGetUserData(GDrawGetParentWindow(GGadgetGetWindow(t)));
4474 0 : SplineFont *sf = pstkd->sf;
4475 :
4476 0 : return( SFGlyphNameCompletion(sf,t,from_tab,true));
4477 : }
4478 :
4479 0 : static int pstkd_e_h(GWindow gw, GEvent *event) {
4480 0 : PSTKernDlg *pstkd = GDrawGetUserData(gw);
4481 :
4482 0 : switch ( event->type ) {
4483 : case et_close:
4484 0 : PSTKD_DoCancel(pstkd);
4485 0 : break;
4486 : case et_char:
4487 0 : if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
4488 0 : int lookup_type = pstkd->sub->lookup->lookup_type;
4489 0 : if ( lookup_type==gpos_single )
4490 0 : help("lookups.html#Single-pos");
4491 0 : else if ( lookup_type==gpos_pair )
4492 0 : help("lookups.html#Pair");
4493 : else
4494 0 : help("lookups.html#basic-subs");
4495 0 : return( true );
4496 : }
4497 0 : return( false );
4498 : break;
4499 : case et_destroy:
4500 0 : break;
4501 : case et_mouseup: case et_mousemove: case et_mousedown:
4502 0 : break;
4503 : case et_expose:
4504 0 : break;
4505 : case et_resize:
4506 0 : break;
4507 : }
4508 0 : return( true );
4509 : }
4510 :
4511 0 : static void PSTKernD(SplineFont *sf, struct lookup_subtable *sub, int def_layer) {
4512 : PSTKernDlg pstkd;
4513 : GRect pos;
4514 : GWindowAttrs wattrs;
4515 : char title[300];
4516 : struct matrixinit mi;
4517 : GGadgetCreateData gcd[23], buttongcd[6], box[6], hbox;
4518 : GGadgetCreateData *h1array[8], *h2array[7], *h3array[7], *varray[20], *h4array[8], *h5array[4];
4519 : GTextInfo label[23], buttonlabel[6];
4520 : int i,k,mi_pos, mi_k;
4521 0 : enum otlookup_type lookup_type = sub->lookup->lookup_type;
4522 : char sepbuf[40], mkbuf[40];
4523 :
4524 0 : if ( sub->separation==0 && !sub->kerning_by_touch ) {
4525 0 : sub->separation = sf->width_separation;
4526 0 : if ( sf->width_separation==0 )
4527 0 : sub->separation = 15*(sf->ascent+sf->descent)/100;
4528 0 : sub->minkern = sub->separation/10;
4529 : }
4530 :
4531 0 : memset(&pstkd,0,sizeof(pstkd));
4532 0 : pstkd.sf = sf;
4533 0 : pstkd.def_layer = def_layer;
4534 0 : pstkd.sub = sub;
4535 :
4536 0 : memset(&wattrs,0,sizeof(wattrs));
4537 0 : wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
4538 0 : wattrs.event_masks = ~(1<<et_charup);
4539 0 : wattrs.restrict_input_to_me = true;
4540 0 : wattrs.undercursor = 1;
4541 0 : wattrs.cursor = ct_pointer;
4542 0 : snprintf(title,sizeof(title), _("Lookup Subtable, %s"), sub->subtable_name );
4543 0 : wattrs.utf8_window_title = title;
4544 0 : wattrs.is_dlg = true;
4545 0 : pos.x = pos.y = 0;
4546 0 : pos.width = GGadgetScale(GDrawPointsToPixels(NULL,300));
4547 0 : pos.height = GDrawPointsToPixels(NULL,400);
4548 0 : pstkd.gw = GDrawCreateTopWindow(NULL,&pos,pstkd_e_h,&pstkd,&wattrs);
4549 :
4550 0 : memset(&gcd,0,sizeof(gcd));
4551 0 : memset(&buttongcd,0,sizeof(buttongcd));
4552 0 : memset(&box,0,sizeof(box));
4553 0 : memset(&label,0,sizeof(label));
4554 0 : memset(&buttonlabel,0,sizeof(buttonlabel));
4555 :
4556 0 : i = k = 0;
4557 0 : label[i].text = (unichar_t *) _("_Alphabetic");
4558 0 : label[i].text_is_1byte = true;
4559 0 : label[i].text_in_resource = true;
4560 0 : gcd[i].gd.label = &label[i];
4561 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4562 0 : gcd[i].gd.flags = isalphabetic ? (gg_enabled|gg_visible|gg_cb_on|gg_utf8_popup) : (gg_enabled|gg_visible|gg_utf8_popup);
4563 0 : gcd[i].gd.popup_msg = (unichar_t *) _("Sort this display based on the alphabetic name of the glyph");
4564 0 : gcd[i].gd.handle_controlevent = PSTKD_Sort;
4565 0 : gcd[i].gd.cid = CID_Alpha;
4566 0 : gcd[i].creator = GRadioCreate;
4567 0 : h1array[0] = &gcd[i++];
4568 :
4569 0 : label[i].text = (unichar_t *) _("_Unicode");
4570 0 : label[i].text_is_1byte = true;
4571 0 : label[i].text_in_resource = true;
4572 0 : gcd[i].gd.label = &label[i];
4573 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4574 0 : gcd[i].gd.flags = !isalphabetic ? (gg_enabled|gg_visible|gg_cb_on|gg_utf8_popup) : (gg_enabled|gg_visible|gg_utf8_popup);
4575 0 : gcd[i].gd.popup_msg = (unichar_t *) _("Sort this display based on the unicode code of the glyph");
4576 0 : gcd[i].gd.handle_controlevent = PSTKD_Sort;
4577 0 : gcd[i].gd.cid = CID_Unicode;
4578 0 : gcd[i].creator = GRadioCreate;
4579 0 : h1array[1] = &gcd[i++]; h1array[2] = GCD_HPad10;
4580 :
4581 0 : label[i].text = (unichar_t *) _("_By Base Char");
4582 0 : label[i].text_is_1byte = true;
4583 0 : label[i].text_in_resource = true;
4584 0 : gcd[i].gd.label = &label[i];
4585 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4586 0 : gcd[i].gd.flags = stemming ? (gg_enabled|gg_visible|gg_cb_on|gg_utf8_popup) : (gg_enabled|gg_visible|gg_utf8_popup);
4587 0 : gcd[i].gd.popup_msg = (unichar_t *) _("Sort first using the base glyph (if any).\nThus Agrave would sort with A");
4588 0 : gcd[i].gd.handle_controlevent = PSTKD_Sort;
4589 0 : gcd[i].gd.cid = CID_BaseChar;
4590 0 : gcd[i].creator = GCheckBoxCreate;
4591 0 : h1array[3] = &gcd[i++]; h1array[4] = GCD_HPad10;
4592 :
4593 0 : label[i].text = (unichar_t *) _("By _Scripts");
4594 0 : label[i].text_is_1byte = true;
4595 0 : label[i].text_in_resource = true;
4596 0 : gcd[i].gd.label = &label[i];
4597 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4598 0 : gcd[i].gd.flags = byscripts ? (gg_enabled|gg_visible|gg_cb_on|gg_utf8_popup) : (gg_enabled|gg_visible|gg_utf8_popup);
4599 0 : gcd[i].gd.popup_msg = (unichar_t *) _("Sort first using the glyph's script.\nThus A and Z would sort together\nwhile Alpha would sort with Omega and not A");
4600 0 : if ( sub->lookup->features==NULL ||
4601 0 : (sub->lookup->features->next==NULL &&
4602 0 : (sub->lookup->features->scripts==NULL ||
4603 0 : sub->lookup->features->scripts->next==NULL)))
4604 0 : gcd[i].gd.flags = gg_visible|gg_cb_on; /* If there is only one script, we can't really sort by it */
4605 0 : gcd[i].gd.handle_controlevent = PSTKD_Sort;
4606 0 : gcd[i].gd.cid = CID_Scripts;
4607 0 : gcd[i].creator = GCheckBoxCreate;
4608 0 : h1array[5] = &gcd[i++]; h1array[6] = GCD_Glue; h1array[7] = NULL;
4609 :
4610 0 : box[0].gd.flags = gg_enabled|gg_visible;
4611 0 : box[0].gd.u.boxelements = h1array;
4612 0 : box[0].creator = GHBoxCreate;
4613 0 : varray[k++] = &box[0]; varray[k++] = NULL;
4614 :
4615 0 : if ( sub->lookup->lookup_type == gpos_pair || sub->lookup->lookup_type == gpos_single ) {
4616 0 : label[i].text = (unichar_t *) _("_Hide Unused Columns");
4617 0 : label[i].text_is_1byte = true;
4618 0 : label[i].text_in_resource = true;
4619 0 : gcd[i].gd.label = &label[i];
4620 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4621 0 : gcd[i].gd.flags = lookup_hideunused ? (gg_enabled|gg_visible|gg_cb_on|gg_utf8_popup) : (gg_enabled|gg_visible|gg_utf8_popup);
4622 0 : gcd[i].gd.popup_msg = (unichar_t *) _("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.");
4623 0 : gcd[i].gd.handle_controlevent = PSTKD_HideUnused;
4624 0 : gcd[i].creator = GCheckBoxCreate;
4625 0 : varray[k++] = &gcd[i++]; varray[k++] = NULL;
4626 : }
4627 :
4628 0 : PSTMatrixInit(&mi,sf,sub,&pstkd);
4629 0 : mi_pos = i;
4630 0 : gcd[i].gd.pos.height = 200;
4631 0 : gcd[i].gd.flags = gg_enabled | gg_visible | gg_utf8_popup;
4632 0 : gcd[i].gd.cid = CID_PSTList;
4633 0 : gcd[i].gd.u.matrix = &mi;
4634 0 : gcd[i].data = &pstkd;
4635 0 : gcd[i].creator = GMatrixEditCreate;
4636 0 : mi_k = k;
4637 0 : varray[k++] = &gcd[i++]; varray[k++] = NULL;
4638 :
4639 0 : buttonlabel[0].text = (unichar_t *) _("_Populate");
4640 0 : if ( lookup_type==gpos_pair )
4641 0 : buttonlabel[0].text = (unichar_t *) _("Auto_Kern");
4642 0 : buttonlabel[0].text_is_1byte = true;
4643 0 : buttonlabel[0].text_in_resource = true;
4644 0 : buttongcd[0].gd.label = &buttonlabel[0];
4645 0 : buttongcd[0].gd.pos.x = 5; buttongcd[0].gd.pos.y = 5+4;
4646 0 : buttongcd[0].gd.flags =
4647 0 : sub->lookup->features==NULL || sub->vertical_kerning ?
4648 : gg_visible|gg_utf8_popup :
4649 : gg_enabled|gg_visible|gg_utf8_popup;
4650 0 : if ( lookup_type==gpos_pair ) {
4651 0 : buttongcd[0].gd.popup_msg = (unichar_t *) _("For each script to which this lookup applies, look at all pairs of\n"
4652 : "glyphs in that script and try to guess a reasonable kerning value\n"
4653 : "for that pair.");
4654 0 : buttongcd[0].gd.handle_controlevent = PSTKD_AutoKern;
4655 : } else {
4656 0 : buttongcd[0].gd.popup_msg = (unichar_t *) _("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.");
4657 0 : buttongcd[0].gd.handle_controlevent = PSTKD_Populate;
4658 : }
4659 0 : buttongcd[0].creator = GButtonCreate;
4660 :
4661 0 : buttonlabel[1].text = (unichar_t *) _("_Add Selected");
4662 0 : if ( lookup_type==gpos_pair )
4663 0 : buttonlabel[1].text = (unichar_t *) _("_AutoKern Selected");
4664 0 : buttonlabel[1].text_is_1byte = true;
4665 0 : buttonlabel[1].text_in_resource = true;
4666 0 : buttongcd[1].gd.label = &buttonlabel[1];
4667 0 : buttongcd[1].gd.pos.x = 5; buttongcd[1].gd.pos.y = 5+4;
4668 0 : buttongcd[1].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4669 0 : if ( lookup_type==gpos_pair ) {
4670 0 : if ( sub->vertical_kerning )
4671 0 : buttongcd[1].gd.flags = gg_visible|gg_utf8_popup;
4672 0 : buttongcd[1].gd.popup_msg = (unichar_t *) _("Add kerning info between all pairs of selected glyphs" );
4673 0 : buttongcd[1].gd.handle_controlevent = PSTKD_AutoKernSelected;
4674 : } else {
4675 0 : buttongcd[1].gd.popup_msg = (unichar_t *) _("Add entries for all selected glyphs.");
4676 0 : buttongcd[1].gd.handle_controlevent = PSTKD_PopulateSelected;
4677 : }
4678 0 : buttongcd[1].creator = GButtonCreate;
4679 :
4680 0 : buttonlabel[2].text = (unichar_t *) _("_Remove Empty");
4681 0 : buttonlabel[2].text_is_1byte = true;
4682 0 : buttonlabel[2].text_in_resource = true;
4683 0 : buttongcd[2].gd.label = &buttonlabel[2];
4684 0 : buttongcd[2].gd.pos.x = 5; buttongcd[2].gd.pos.y = 5+4;
4685 0 : buttongcd[2].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4686 0 : buttongcd[2].gd.popup_msg = (unichar_t *)
4687 0 : (sub->lookup->lookup_type == gpos_single ? _("Remove all \"empty\" entries -- those where all fields are 0") :
4688 0 : sub->lookup->lookup_type == gpos_pair ? _("Remove all \"empty\" entries -- entries with no second glyph") :
4689 0 : sub->lookup->lookup_type == gsub_ligature ? _("Remove all \"empty\" entries -- those with no source glyphs") :
4690 : _("Remove all \"empty\" entries -- those with no replacement glyphs"));
4691 0 : buttongcd[2].gd.handle_controlevent = PSTKD_RemoveEmpty;
4692 0 : buttongcd[2].creator = GButtonCreate;
4693 :
4694 0 : buttonlabel[3].text = (unichar_t *) _("Remove All");
4695 0 : buttonlabel[3].text_is_1byte = true;
4696 0 : buttonlabel[3].text_in_resource = true;
4697 0 : buttongcd[3].gd.label = &buttonlabel[3];
4698 0 : buttongcd[3].gd.pos.x = 5; buttongcd[3].gd.pos.y = 5+4;
4699 0 : buttongcd[3].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4700 0 : buttongcd[3].gd.popup_msg = (unichar_t *) _("Remove all entries.");
4701 0 : buttongcd[3].gd.handle_controlevent = PSTKD_RemoveAll;
4702 0 : buttongcd[3].creator = GButtonCreate;
4703 :
4704 0 : if ( sub->lookup->lookup_type == gsub_single ) {
4705 0 : label[i].text = (unichar_t *) _("_Default Using Suffix:");
4706 0 : label[i].text_is_1byte = true;
4707 0 : label[i].text_in_resource = true;
4708 0 : gcd[i].gd.label = &label[i];
4709 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4710 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4711 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
4712 : "Add entries to the lookup based on the following suffix.\n"
4713 : "So if the suffix is set to \"superior\" and the font\n"
4714 : "contains glyphs named \"A\" and \"A.superior\" (and the\n"
4715 : "lookup applies to the latin script), then FontForge will\n"
4716 : "add an entry mapping \"A\" -> \"A.superior\"." );
4717 0 : gcd[i].gd.handle_controlevent = PSTKD_PopulateWithSuffix;
4718 0 : gcd[i].creator = GButtonCreate;
4719 0 : h2array[0] = &gcd[i++];
4720 :
4721 0 : label[i].text = (unichar_t *) sub->suffix;
4722 0 : label[i].text_is_1byte = true;
4723 0 : label[i].text_in_resource = true;
4724 0 : gcd[i].gd.label = sub->suffix==NULL ? NULL : &label[i];
4725 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4726 0 : gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
4727 0 : gcd[i].gd.cid = CID_Suffix;
4728 0 : gcd[i].creator = GTextFieldCreate;
4729 0 : h2array[1] = &gcd[i++]; h2array[2] = GCD_Glue; h2array[3] = NULL;
4730 :
4731 0 : box[1].gd.flags = gg_enabled|gg_visible;
4732 0 : box[1].gd.u.boxelements = h2array;
4733 0 : box[1].creator = GHBoxCreate;
4734 0 : varray[k++] = &box[1]; varray[k++] = NULL;
4735 0 : } else if ( sub->lookup->lookup_type == gpos_single ) {
4736 0 : label[i].text = (unichar_t *) _("_Default New Entries to First");
4737 0 : label[i].text_is_1byte = true;
4738 0 : label[i].text_in_resource = true;
4739 0 : gcd[i].gd.label = &label[i];
4740 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4741 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on|gg_utf8_popup;
4742 0 : if ( is_boundsFeat(sub)!=0 )
4743 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4744 0 : gcd[i].gd.popup_msg = (unichar_t *) _("When adding new entries, give them the same\ndelta values as those on the first line.");
4745 0 : gcd[i].gd.cid = CID_AllSame;
4746 0 : gcd[i].creator = GCheckBoxCreate;
4747 0 : varray[k++] = &gcd[i++]; varray[k++] = NULL;
4748 :
4749 0 : } else if ( sub->lookup->lookup_type == gpos_pair ) {
4750 0 : label[i].text = (unichar_t *) _("_Default Separation:");
4751 0 : label[i].text_is_1byte = true;
4752 0 : label[i].text_in_resource = true;
4753 0 : gcd[i].gd.label = &label[i];
4754 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4755 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4756 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
4757 : "Add entries to the lookup trying to make the optical\n"
4758 : "separation between all pairs of glyphs equal to this\n"
4759 : "value." );
4760 0 : gcd[i].creator = GLabelCreate;
4761 0 : h4array[0] = &gcd[i++];
4762 :
4763 0 : sprintf( sepbuf, "%d", sub->separation );
4764 0 : label[i].text = (unichar_t *) sepbuf;
4765 0 : label[i].text_is_1byte = true;
4766 0 : label[i].text_in_resource = true;
4767 0 : gcd[i].gd.label = &label[i];
4768 0 : gcd[i].gd.pos.width = 50;
4769 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4770 0 : gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
4771 0 : gcd[i].gd.cid = CID_Separation;
4772 0 : gcd[i].creator = GTextFieldCreate;
4773 0 : h4array[1] = &gcd[i++];
4774 :
4775 0 : label[i].text = (unichar_t *) _("_Min Kern:");
4776 0 : label[i].text_is_1byte = true;
4777 0 : label[i].text_in_resource = true;
4778 0 : gcd[i].gd.label = &label[i];
4779 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4780 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4781 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
4782 : "Any computed kerning change whose absolute value is less\n"
4783 : "that this will be ignored.\n" );
4784 0 : gcd[i].creator = GLabelCreate;
4785 0 : h4array[2] = &gcd[i++];
4786 :
4787 0 : sprintf( mkbuf, "%d", sub->minkern );
4788 0 : label[i].text = (unichar_t *) mkbuf;
4789 0 : label[i].text_is_1byte = true;
4790 0 : label[i].text_in_resource = true;
4791 0 : gcd[i].gd.label = &label[i];
4792 0 : gcd[i].gd.pos.width = 50;
4793 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4794 0 : gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
4795 0 : gcd[i].gd.cid = CID_MinKern;
4796 0 : gcd[i].creator = GTextFieldCreate;
4797 0 : h4array[3] = &gcd[i++];
4798 :
4799 0 : label[i].text = (unichar_t *) _("_Touching");
4800 0 : label[i].text_is_1byte = true;
4801 0 : label[i].text_in_resource = true;
4802 0 : gcd[i].gd.label = &label[i];
4803 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
4804 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4805 0 : if ( sub->kerning_by_touch )
4806 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup|gg_cb_on;
4807 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
4808 : "Normally kerning is based on achieving a constant (optical)\n"
4809 : "separation between glyphs, but occasionally it is desirable\n"
4810 : "to have a kerning table where the kerning is based on the\n"
4811 : "closest approach between two glyphs (So if the desired separ-\n"
4812 : "ation is 0 then the glyphs will actually be touching.");
4813 0 : gcd[i].gd.cid = CID_Touched;
4814 0 : gcd[i].creator = GCheckBoxCreate;
4815 0 : h4array[4] = &gcd[i++];
4816 :
4817 0 : h4array[5] = GCD_Glue; h4array[6] = NULL;
4818 :
4819 0 : box[1].gd.flags = gg_enabled|gg_visible;
4820 0 : box[1].gd.u.boxelements = h4array;
4821 0 : box[1].creator = GHBoxCreate;
4822 0 : varray[k++] = &box[1]; varray[k++] = NULL;
4823 :
4824 0 : label[i].text = (unichar_t *) _("Only kern glyphs closer");
4825 0 : label[i].text_is_1byte = true;
4826 0 : label[i].text_in_resource = true;
4827 0 : gcd[i].gd.label = &label[i];
4828 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4829 0 : if ( sub->onlyCloser )
4830 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup|gg_cb_on;
4831 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
4832 : "When doing autokerning, only move glyphs closer together,\n"
4833 : "so the kerning offset will be negative.");
4834 0 : gcd[i].gd.cid = CID_OnlyCloser;
4835 0 : gcd[i].creator = GCheckBoxCreate;
4836 0 : h5array[0] = &gcd[i++];
4837 :
4838 0 : label[i].text = (unichar_t *) _("Autokern new entries");
4839 0 : label[i].text_is_1byte = true;
4840 0 : label[i].text_in_resource = true;
4841 0 : gcd[i].gd.label = &label[i];
4842 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
4843 0 : if ( !sub->dontautokern )
4844 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup|gg_cb_on;
4845 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
4846 : "When adding new entries provide default kerning values.");
4847 0 : gcd[i].gd.cid = CID_Autokern;
4848 0 : gcd[i].creator = GCheckBoxCreate;
4849 0 : h5array[1] = &gcd[i++]; h5array[2] = NULL;
4850 :
4851 0 : memset(&hbox,0,sizeof(hbox));
4852 0 : hbox.gd.flags = gg_enabled|gg_visible;
4853 0 : hbox.gd.u.boxelements = h5array;
4854 0 : hbox.creator = GHBoxCreate;
4855 0 : varray[k++] = &hbox; varray[k++] = NULL;
4856 :
4857 0 : label[i].text = (unichar_t *) _("Size:");
4858 0 : label[i].text_is_1byte = true;
4859 0 : gcd[i].gd.label = &label[i];
4860 0 : gcd[i].gd.pos.x = 30; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+30;
4861 0 : gcd[i].gd.flags = gg_visible|gg_enabled ;
4862 0 : gcd[i++].creator = GLabelCreate;
4863 0 : h2array[0] = &gcd[i-1];
4864 :
4865 0 : pstkd.pixelsize = 150;
4866 0 : label[i].text = (unichar_t *) "150";
4867 0 : label[i].text_is_1byte = true;
4868 0 : gcd[i].gd.label = &label[i];
4869 0 : gcd[i].gd.pos.x = 92; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y-4;
4870 0 : gcd[i].gd.pos.width = 80;
4871 0 : gcd[i].gd.flags = gg_visible|gg_enabled ;
4872 0 : gcd[i].gd.cid = CID_PixelSize;
4873 0 : gcd[i].gd.handle_controlevent = PSTKD_DisplaySizeChanged;
4874 0 : gcd[i++].creator = GTextFieldCreate;
4875 0 : h2array[1] = &gcd[i-1]; h2array[2] = GCD_HPad10;
4876 :
4877 : /* GT: Short for "Magnification" */
4878 0 : label[i].text = (unichar_t *) _("Mag:");
4879 0 : label[i].text_is_1byte = true;
4880 0 : gcd[i].gd.label = &label[i];
4881 0 : gcd[i].gd.pos.x = 185; gcd[i].gd.pos.y = gcd[i-2].gd.pos.y;
4882 0 : gcd[i].gd.flags = gg_visible|gg_enabled ;
4883 0 : gcd[i++].creator = GLabelCreate;
4884 0 : h2array[3] = &gcd[i-1];
4885 :
4886 0 : pstkd.mag = 1;
4887 0 : gcd[i].gd.flags = gg_visible|gg_enabled ;
4888 0 : gcd[i].gd.cid = CID_Magnification;
4889 0 : gcd[i].gd.u.list = magnifications;
4890 0 : gcd[i].gd.handle_controlevent = PSTKD_MagnificationChanged;
4891 0 : gcd[i++].creator = GListButtonCreate;
4892 0 : h2array[4] = &gcd[i-1]; h2array[5] = GCD_Glue; h2array[6] = NULL;
4893 :
4894 0 : box[2].gd.flags = gg_enabled|gg_visible;
4895 0 : box[2].gd.u.boxelements = h2array;
4896 0 : box[2].creator = GHBoxCreate;
4897 0 : varray[k++] = &box[2]; varray[k++] = NULL;
4898 :
4899 0 : gcd[i].gd.pos.width = 200;
4900 0 : gcd[i].gd.pos.height = 200;
4901 0 : gcd[i].gd.flags = gg_visible | gg_enabled;
4902 0 : gcd[i].gd.u.drawable_e_h = pstkern_e_h;
4903 0 : gcd[i].gd.cid = CID_KernDisplay;
4904 0 : gcd[i].creator = GDrawableCreate;
4905 0 : varray[k++] = &gcd[i++]; varray[k++] = NULL;
4906 : }
4907 :
4908 0 : gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+24+3;
4909 0 : gcd[i].gd.pos.width = -1;
4910 0 : gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
4911 0 : label[i].text = (unichar_t *) _("_OK");
4912 0 : label[i].text_is_1byte = true;
4913 0 : label[i].text_in_resource = true;
4914 0 : gcd[i].gd.label = &label[i];
4915 0 : gcd[i].gd.handle_controlevent = PSTKD_Ok;
4916 0 : gcd[i++].creator = GButtonCreate;
4917 :
4918 0 : gcd[i].gd.pos.x = -10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+3;
4919 0 : gcd[i].gd.pos.width = -1;
4920 0 : gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
4921 0 : label[i].text = (unichar_t *) _("_Cancel");
4922 0 : label[i].text_is_1byte = true;
4923 0 : label[i].text_in_resource = true;
4924 0 : gcd[i].gd.label = &label[i];
4925 0 : gcd[i].gd.handle_controlevent = PSTKD_Cancel;
4926 0 : gcd[i].gd.cid = CID_Cancel;
4927 0 : gcd[i++].creator = GButtonCreate;
4928 :
4929 0 : h3array[0] = GCD_Glue; h3array[1] = &gcd[i-2]; h3array[2] = GCD_Glue;
4930 0 : h3array[3] = GCD_Glue; h3array[4] = &gcd[i-1]; h3array[5] = GCD_Glue;
4931 0 : h3array[6] = NULL;
4932 :
4933 0 : box[3].gd.flags = gg_enabled|gg_visible;
4934 0 : box[3].gd.u.boxelements = h3array;
4935 0 : box[3].creator = GHBoxCreate;
4936 0 : varray[k++] = &box[3]; varray[k++] = NULL; varray[k++] = NULL;
4937 :
4938 0 : box[4].gd.pos.x = box[4].gd.pos.y = 2;
4939 0 : box[4].gd.flags = gg_enabled|gg_visible;
4940 0 : box[4].gd.u.boxelements = varray;
4941 0 : box[4].creator = GHVGroupCreate;
4942 :
4943 0 : GGadgetsCreate(pstkd.gw,box+4);
4944 0 : GHVBoxSetExpandableRow(box[4].ret,mi_k/2);
4945 0 : GHVBoxSetExpandableCol(box[3].ret,gb_expandgluesame);
4946 0 : if ( sub->lookup->lookup_type == gsub_single )
4947 0 : GHVBoxSetExpandableCol(box[1].ret,gb_expandglue);
4948 0 : else if ( sub->lookup->lookup_type==gpos_pair ) {
4949 0 : GHVBoxSetExpandableCol(box[1].ret,gb_expandglue);
4950 0 : GHVBoxSetExpandableCol(box[2].ret,gb_expandglue);
4951 : }
4952 0 : GHVBoxSetExpandableCol(box[0].ret,gb_expandglue);
4953 0 : GMatrixEditAddButtons(gcd[mi_pos].ret,buttongcd);
4954 0 : GMatrixEditSetColumnCompletion(gcd[mi_pos].ret,0,PSTKD_GlyphNameCompletion);
4955 0 : if ( sub->lookup->lookup_type == gsub_single || sub->lookup->lookup_type==gpos_pair )
4956 0 : GMatrixEditSetColumnCompletion(gcd[mi_pos].ret,1,PSTKD_GlyphNameCompletion);
4957 0 : else if ( sub->lookup->lookup_type == gsub_multiple ||
4958 0 : sub->lookup->lookup_type==gsub_alternate ||
4959 0 : sub->lookup->lookup_type==gsub_ligature )
4960 0 : GMatrixEditSetColumnCompletion(gcd[mi_pos].ret,1,PSTKD_GlyphListCompletion);
4961 :
4962 0 : if ( sub->lookup->lookup_type == gpos_pair )
4963 0 : GMatrixEditSetTextChangeReporter(gcd[mi_pos].ret,PSTKD_METextChanged);
4964 : else
4965 0 : GMatrixEditSetMouseMoveReporter(gcd[mi_pos].ret,PST_PopupPrepare);
4966 0 : if ( sub->lookup->lookup_type == gpos_pair || sub->lookup->lookup_type == gpos_single )
4967 0 : PSTKD_DoHideUnused(&pstkd);
4968 : else
4969 0 : GHVBoxFitWindow(box[4].ret);
4970 :
4971 0 : GDrawSetVisible(pstkd.gw,true);
4972 :
4973 0 : while ( !pstkd.done )
4974 0 : GDrawProcessOneEvent(NULL);
4975 0 : GDrawDestroyWindow(pstkd.gw);
4976 0 : if ( pstkd.display!=NULL ) {
4977 0 : BDFFontFree(pstkd.display);
4978 0 : pstkd.display = NULL;
4979 : }
4980 0 : }
4981 : /* ************************************************************************** */
4982 : /* *************************** Subtable Selection *************************** */
4983 : /* ************************************************************************** */
4984 :
4985 0 : static int SubtableNameInUse(char *subname, SplineFont *sf, struct lookup_subtable *exclude) {
4986 : int isgpos, i, j;
4987 : OTLookup *otl;
4988 : struct lookup_subtable *sub;
4989 :
4990 0 : if ( sf->fontinfo!=NULL ) {
4991 0 : for ( isgpos=0; isgpos<2; ++isgpos ) {
4992 0 : struct lkdata *lk = &sf->fontinfo->tables[isgpos];
4993 0 : for ( i=0; i<lk->cnt; ++i ) {
4994 0 : if ( lk->all[i].deleted )
4995 0 : continue;
4996 0 : for ( j=0; j<lk->all[i].subtable_cnt; ++j ) {
4997 0 : if ( lk->all[i].subtables[j].deleted || lk->all[i].subtables[j].subtable==exclude )
4998 0 : continue;
4999 0 : if ( strcmp(lk->all[i].subtables[j].subtable->subtable_name,subname)==0 )
5000 0 : return( true );
5001 : }
5002 : }
5003 : }
5004 : } else {
5005 0 : for ( isgpos=0; isgpos<2; ++isgpos ) {
5006 0 : for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
5007 0 : for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
5008 0 : if ( sub==exclude )
5009 0 : continue;
5010 0 : if ( strcmp(sub->subtable_name,subname)==0 )
5011 0 : return( true );
5012 : }
5013 : }
5014 : }
5015 : }
5016 0 : return( false );
5017 : }
5018 :
5019 0 : int EditSubtable(struct lookup_subtable *sub,int isgpos,SplineFont *sf,
5020 : struct subtable_data *sd, int def_layer) {
5021 0 : char *def = sub->subtable_name;
5022 0 : int new = def==NULL;
5023 0 : char *freeme = NULL;
5024 : int name_search;
5025 :
5026 0 : if ( new ) {
5027 0 : def = freeme = malloc(strlen(sub->lookup->lookup_name)+10);
5028 0 : name_search = 1;
5029 : do {
5030 0 : sprintf( def, "%s-%d", sub->lookup->lookup_name, name_search++ );
5031 0 : } while ( SubtableNameInUse(def,sf,sub));
5032 : }
5033 : for (;;) {
5034 0 : def = gwwv_ask_string(_("Please name this subtable"),def,_("Please name this subtable"));
5035 0 : free(freeme);
5036 0 : if ( def==NULL )
5037 0 : return( false );
5038 0 : freeme = def;
5039 0 : if ( SubtableNameInUse(def,sf,sub) )
5040 0 : ff_post_notice(_("Duplicate name"),_("There is already a subtable with that name, please pick another."));
5041 : else
5042 0 : break;
5043 0 : }
5044 0 : free(sub->subtable_name);
5045 0 : sub->subtable_name = def;
5046 0 : if ( new && sub->lookup->lookup_type == gsub_single )
5047 0 : sub->suffix = SuffixFromTags(sub->lookup->features);
5048 0 : if ( new && (sd==NULL || !(sd->flags&sdf_dontedit)) )
5049 0 : _LookupSubtableContents(sf, sub, sd, def_layer);
5050 0 : return( true );
5051 : }
5052 :
5053 0 : static struct lookup_subtable *NewSubtable(OTLookup *otl,int isgpos,SplineFont *sf, struct subtable_data *sd,int def_layer) {
5054 : struct lookup_subtable *sub, *last;
5055 : int i,j;
5056 :
5057 0 : sub = chunkalloc(sizeof(struct lookup_subtable));
5058 0 : sub->lookup = otl;
5059 0 : sub->separation = 15*(sf->ascent+sf->descent)/100;
5060 0 : sub->minkern = sub->separation/10;
5061 0 : if ( !EditSubtable(sub,isgpos,sf,sd,def_layer)) {
5062 0 : chunkfree(sub,sizeof(struct lookup_subtable));
5063 0 : return( NULL );
5064 : }
5065 0 : if ( otl->subtables==NULL )
5066 0 : otl->subtables = sub;
5067 : else {
5068 0 : for ( last=otl->subtables; last->next!=NULL; last=last->next );
5069 0 : last->next = sub;
5070 : }
5071 0 : if ( sf->fontinfo!=NULL ) {
5072 0 : struct lkdata *lk = &sf->fontinfo->tables[isgpos];
5073 0 : for ( i=0; i<lk->cnt && lk->all[i].lookup!=otl; ++i );
5074 0 : if ( i==lk->cnt ) {
5075 0 : IError( "Lookup missing from FontInfo lookup list");
5076 : } else {
5077 0 : if ( lk->all[i].subtable_cnt>=lk->all[i].subtable_max )
5078 0 : lk->all[i].subtables = realloc(lk->all[i].subtables,(lk->all[i].subtable_max+=10)*sizeof(struct lksubinfo));
5079 0 : j = lk->all[i].subtable_cnt++;
5080 0 : memset(&lk->all[i].subtables[j],0,sizeof(struct lksubinfo));
5081 0 : lk->all[i].subtables[j].subtable = sub;
5082 0 : GFI_LookupScrollbars(sf->fontinfo,isgpos, true);
5083 0 : GFI_LookupEnableButtons(sf->fontinfo,isgpos);
5084 : }
5085 : }
5086 0 : return( sub );
5087 : }
5088 :
5089 0 : GTextInfo **SFSubtablesOfType(SplineFont *sf, int lookup_type, int kernclass,
5090 : int add_none) {
5091 0 : int isgpos = (lookup_type>=gpos_start);
5092 : int k, cnt, lcnt, pos;
5093 : OTLookup *otl;
5094 : struct lookup_subtable *sub;
5095 : GTextInfo **ti;
5096 :
5097 0 : if ( sf->cidmaster != NULL ) sf=sf->cidmaster;
5098 0 : else if ( sf->mm != NULL ) sf = sf->mm->normal;
5099 :
5100 0 : for ( k=0; k<2; ++k ) {
5101 0 : cnt = lcnt = pos = 0;
5102 0 : if ( k && add_none ) {
5103 0 : ti[pos] = calloc(1,sizeof(GTextInfo));
5104 0 : ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5105 0 : ti[pos]->userdata = (void *) -1;
5106 0 : ti[pos++]->text = utf82u_copy(_("No Subtable"));
5107 0 : ti[pos] = calloc(1,sizeof(GTextInfo));
5108 0 : ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5109 0 : ti[pos++]->line = true;
5110 : }
5111 0 : for ( otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
5112 0 : if ( otl->lookup_type==lookup_type && otl->subtables!=NULL ) {
5113 0 : if ( k ) {
5114 0 : ti[pos] = calloc(1,sizeof(GTextInfo));
5115 0 : ti[pos]->text = malloc((utf82u_strlen(otl->lookup_name)+2)*sizeof(unichar_t));
5116 0 : ti[pos]->text[0] = ' ';
5117 0 : utf82u_strcpy(ti[pos]->text+1,otl->lookup_name);
5118 0 : ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5119 0 : ti[pos++]->disabled = true;
5120 : }
5121 0 : ++lcnt;
5122 0 : for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
5123 0 : if ( lookup_type!=gpos_pair || kernclass==-1 ||
5124 0 : (kernclass && sub->kc!=NULL) ||
5125 0 : (!kernclass && sub->per_glyph_pst_or_kern)) {
5126 0 : if ( k ) {
5127 0 : ti[pos] = calloc(1,sizeof(GTextInfo));
5128 0 : ti[pos]->text = utf82u_copy(sub->subtable_name);
5129 0 : ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5130 0 : ti[pos++]->userdata = sub;
5131 : }
5132 0 : ++cnt;
5133 : }
5134 : }
5135 : }
5136 : }
5137 0 : if ( !k ) {
5138 0 : ti = calloc(cnt+lcnt+3+2*add_none,sizeof(GTextInfo*));
5139 : } else {
5140 0 : ti[pos] = calloc(1,sizeof(GTextInfo));
5141 0 : ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5142 0 : ti[pos++]->line = true;
5143 0 : ti[pos] = calloc(1,sizeof(GTextInfo));
5144 0 : ti[pos]->fg = ti[pos]->bg = COLOR_DEFAULT;
5145 0 : ti[pos++]->text = utf82u_copy(_("New Lookup Subtable..."));
5146 0 : ti[pos] = calloc(1,sizeof(GTextInfo));
5147 0 : return( ti );
5148 : }
5149 : }
5150 : /* We'll never get here */
5151 0 : return( NULL );
5152 : }
5153 :
5154 0 : GTextInfo *SFSubtableListOfType(SplineFont *sf, int lookup_type, int kernclass,int add_none) {
5155 : GTextInfo **temp, *ti;
5156 : int cnt;
5157 :
5158 0 : temp = SFSubtablesOfType(sf,lookup_type,kernclass,add_none);
5159 0 : if ( temp==NULL )
5160 0 : return( NULL );
5161 0 : for ( cnt=0; temp[cnt]->text!=NULL || temp[cnt]->line; ++cnt );
5162 0 : ti = calloc(cnt+1,sizeof(GTextInfo));
5163 0 : for ( cnt=0; temp[cnt]->text!=NULL || temp[cnt]->line; ++cnt ) {
5164 0 : ti[cnt] = *temp[cnt];
5165 0 : free(temp[cnt]);
5166 : }
5167 0 : free(temp);
5168 0 : return( ti );
5169 : }
5170 :
5171 0 : struct lookup_subtable *SFNewLookupSubtableOfType(SplineFont *sf, int lookup_type, struct subtable_data *sd, int def_layer ) {
5172 0 : int isgpos = (lookup_type>=gpos_start);
5173 0 : OTLookup *otl, *found=NULL;
5174 : int cnt, ans;
5175 : struct lookup_subtable *sub;
5176 : char **choices;
5177 :
5178 0 : if ( sf->cidmaster ) sf=sf->cidmaster;
5179 :
5180 0 : cnt = 0;
5181 0 : for ( otl=isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next )
5182 0 : if ( otl->lookup_type==lookup_type )
5183 0 : ++cnt;
5184 0 : if ( cnt==0 ) {
5185 : /* There are no lookups of this type, so there is nothing for them to */
5186 : /* pick from. So we must create a new lookup for them, and then add */
5187 : /* a subtable to it */
5188 0 : found = CreateAndSortNewLookupOfType(sf,lookup_type);
5189 0 : if ( found==NULL )
5190 0 : return( NULL );
5191 0 : sub = NewSubtable(found,isgpos,sf,sd,def_layer);
5192 : /* even if they canceled the subtable creation they are now stuck */
5193 : /* with the lookup */
5194 0 : return( sub );
5195 : }
5196 :
5197 : /* I thought briefly that if cnt were 1 I might want to automagically */
5198 : /* create a subtable in that lookup... but no. Still give them the */
5199 : /* option of creating a new lookup */
5200 :
5201 0 : choices = malloc((cnt+2)*sizeof(char *));
5202 0 : for ( cnt=0, otl=isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next )
5203 0 : if ( otl->lookup_type==lookup_type )
5204 0 : choices[cnt++] = otl->lookup_name;
5205 0 : choices[cnt++] = _("Create a new lookup");
5206 0 : choices[cnt] = NULL;
5207 0 : ans = gwwv_choose(_("Add a subtable to which lookup?"),(const char **) choices,cnt,cnt-1,
5208 0 : _("Add a subtable to which lookup?"));
5209 0 : if ( ans==-1 )
5210 0 : found = NULL;
5211 0 : else if ( ans==cnt )
5212 0 : found = CreateAndSortNewLookupOfType(sf,lookup_type);
5213 : else {
5214 0 : found = NULL;
5215 0 : for ( cnt=0, otl=isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
5216 0 : if ( otl->lookup_type==lookup_type ) {
5217 0 : if ( cnt==ans ) {
5218 0 : found = otl;
5219 0 : break;
5220 : } else
5221 0 : ++cnt;
5222 : }
5223 : }
5224 : }
5225 0 : free(choices);
5226 0 : if ( found==NULL )
5227 0 : return( NULL );
5228 :
5229 0 : return( NewSubtable(found,isgpos,sf,sd,def_layer));
5230 : }
5231 :
5232 0 : static void kf_activateMe(struct fvcontainer *fvc,FontViewBase *fvb) {
5233 0 : struct kf_dlg *kf = (struct kf_dlg *) fvc;
5234 0 : FontView *fv = (FontView *) fvb;
5235 :
5236 0 : if ( !fv->notactive )
5237 0 : return;
5238 :
5239 0 : kf->first_fv->notactive = true;
5240 0 : kf->second_fv->notactive = true;
5241 0 : fv->notactive = false;
5242 0 : kf->active = fv;
5243 0 : GDrawSetUserData(kf->dw,fv);
5244 0 : GDrawRequestExpose(kf->dw,NULL,false);
5245 : }
5246 :
5247 0 : static void kf_charEvent(struct fvcontainer *fvc,void *event) {
5248 0 : struct kf_dlg *kf = (struct kf_dlg *) fvc;
5249 0 : FVChar(kf->active,event);
5250 0 : }
5251 :
5252 0 : static void kf_doClose(struct fvcontainer *fvc) {
5253 0 : struct kf_dlg *kf = (struct kf_dlg *) fvc;
5254 0 : kf->done = true;
5255 0 : }
5256 :
5257 0 : static void kf_doResize(struct fvcontainer *fvc,FontViewBase *fvb,
5258 : int width, int height) {
5259 0 : struct kf_dlg *kf = (struct kf_dlg *) fvc;
5260 0 : FontView *fv = (FontView *) fvb;
5261 0 : FontView *otherfv = fv==kf->first_fv ? kf->second_fv : kf->first_fv;
5262 : static int nested=0;
5263 : GRect size;
5264 :
5265 0 : if ( fv->filled==NULL || otherfv->filled == NULL )
5266 0 : return; /* Not initialized yet */
5267 0 : if ( nested )
5268 0 : return;
5269 0 : nested = 1;
5270 0 : FVSetUIToMatch(otherfv,fv);
5271 0 : nested = 0;
5272 :
5273 0 : memset(&size,0,sizeof(size));
5274 0 : size.height = fv->mbh + kf->infoh + kf->fh+4 + 2*(height-fv->mbh) + kf->fh + 2;
5275 0 : size.width = width;
5276 0 : GGadgetSetDesiredSize(kf->guts,NULL, &size);
5277 0 : GHVBoxFitWindow(kf->topbox);
5278 : }
5279 :
5280 : static struct fvcontainer_funcs kernformat_funcs = {
5281 : fvc_kernformat,
5282 : true, /* Modal dialog. No charviews, etc. */
5283 : kf_activateMe,
5284 : kf_charEvent,
5285 : kf_doClose,
5286 : kf_doResize
5287 : };
5288 :
5289 0 : static void kf_FVSetSize(struct kf_dlg *kf,FontView *fv,int width, int height,
5290 : int y, int sbsize) {
5291 : int cc, rc, topchar;
5292 : GRect subsize;
5293 :
5294 0 : topchar = fv->rowoff*fv->colcnt;
5295 0 : cc = (width-1) / fv->cbw;
5296 0 : if ( cc<1 ) cc=1;
5297 0 : rc = (height-1)/ fv->cbh;
5298 0 : if ( rc<1 ) rc = 1;
5299 0 : subsize.x = 0; subsize.y = 0;
5300 0 : subsize.width = cc*fv->cbw + 1;
5301 0 : subsize.height = rc*fv->cbh + 1;
5302 0 : GDrawResize(fv->v,subsize.width,subsize.height);
5303 0 : GDrawMove(fv->v,0,y);
5304 0 : GGadgetMove(fv->vsb,subsize.width,y);
5305 0 : GGadgetResize(fv->vsb,sbsize,subsize.height);
5306 :
5307 0 : fv->colcnt = cc; fv->rowcnt = rc;
5308 0 : fv->width = subsize.width; fv->height = subsize.height;
5309 0 : fv->rowltot = (fv->b.map->enccount+fv->colcnt-1)/fv->colcnt;
5310 0 : GScrollBarSetBounds(fv->vsb,0,fv->rowltot,fv->rowcnt);
5311 0 : fv->rowoff = topchar/fv->colcnt;
5312 0 : if ( fv->rowoff>=fv->rowltot-fv->rowcnt )
5313 0 : fv->rowoff = fv->rowltot-fv->rowcnt;
5314 0 : if ( fv->rowoff<0 ) fv->rowoff =0;
5315 0 : GScrollBarSetPos(fv->vsb,fv->rowoff);
5316 :
5317 0 : GDrawRequestExpose(fv->v,NULL,true);
5318 0 : }
5319 :
5320 0 : static void kf_sizeSet(struct kf_dlg *kf,GWindow dw) {
5321 : GRect size, gsize;
5322 : int width, height, y;
5323 :
5324 0 : GDrawGetSize(dw,&size);
5325 0 : GGadgetGetSize(kf->first_fv->vsb,&gsize);
5326 0 : width = size.width - gsize.width;
5327 0 : height = size.height - kf->mbh - kf->first_fv->infoh - 2*(kf->fh+4);
5328 0 : height /= 2;
5329 :
5330 0 : y = kf->mbh + kf->first_fv->infoh + (kf->fh + 4);
5331 0 : kf_FVSetSize(kf,kf->first_fv,width,height,y,gsize.width);
5332 :
5333 0 : kf->label2_y = y + height+2;
5334 0 : y = kf->label2_y + kf->fh + 2;
5335 0 : kf_FVSetSize(kf,kf->second_fv,width,height,y,gsize.width);
5336 0 : }
5337 :
5338 0 : static int kf_sub_e_h(GWindow pixmap, GEvent *event) {
5339 : FontView *active_fv;
5340 : struct kf_dlg *kf;
5341 :
5342 0 : if ( event->type==et_destroy )
5343 0 : return( true );
5344 :
5345 0 : active_fv = (FontView *) GDrawGetUserData(pixmap);
5346 0 : kf = (struct kf_dlg *) (active_fv->b.container);
5347 :
5348 0 : if (( event->type==et_mouseup || event->type==et_mousedown ) &&
5349 0 : (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
5350 0 : return( GGadgetDispatchEvent(active_fv->vsb,event));
5351 : }
5352 :
5353 0 : switch ( event->type ) {
5354 : case et_expose:
5355 0 : FVDrawInfo(active_fv,pixmap,event);
5356 0 : GDrawSetFont(pixmap, kf->first_fv->notactive? kf->plain : kf->bold );
5357 0 : GDrawDrawText8(pixmap,10,kf->mbh+kf->first_fv->infoh+kf->as,
5358 0 : _("Select glyphs for the first part of the kern pair"),-1,0x000000);
5359 0 : GDrawSetFont(pixmap, kf->second_fv->notactive? kf->plain : kf->bold );
5360 0 : GDrawDrawText8(pixmap,10,kf->label2_y+kf->as,
5361 0 : _("Select glyphs for the second part of the kern pair"),-1,0x000000);
5362 0 : break;
5363 : case et_char:
5364 0 : kf_charEvent(&kf->base,event);
5365 0 : break;
5366 : case et_mousedown:
5367 0 : if ( event->u.mouse.y<kf->mbh )
5368 0 : return(false);
5369 0 : kf_activateMe(&kf->base,(struct fontviewbase *)
5370 0 : (( event->u.mouse.y > kf->label2_y ) ?
5371 : kf->second_fv : kf->first_fv) );
5372 0 : return(false);
5373 : break;
5374 : case et_mouseup: case et_mousemove:
5375 0 : return(false);
5376 : case et_resize:
5377 0 : kf_sizeSet(kf,pixmap);
5378 0 : break;
5379 : }
5380 0 : return( true );
5381 : }
5382 :
5383 : #undef CID_Separation
5384 : #undef CID_MinKern
5385 : #undef CID_Touched
5386 : #undef CID_OnlyCloser
5387 : #undef CID_Autokern
5388 : #define CID_KPairs 1000
5389 : #define CID_KClasses 1001
5390 : #define CID_KCBuild 1002
5391 : #define CID_Separation 1003
5392 : #define CID_MinKern 1004
5393 : #define CID_Touched 1005
5394 : #define CID_ClassDistance 1006
5395 : #define CID_Guts 1008
5396 : #define CID_OnlyCloser 1009
5397 : #define CID_Autokern 1010
5398 :
5399 : struct kf_results {
5400 : int asked;
5401 : int autokern;
5402 : int autobuild;
5403 : real good_enough;
5404 : SplineChar **firstglyphs;
5405 : SplineChar **secondglyphs;
5406 : };
5407 :
5408 0 : static int KF_FormatChange(GGadget *g, GEvent *e) {
5409 : struct kf_dlg *kf;
5410 : char mkbuf[10];
5411 :
5412 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
5413 0 : kf = GDrawGetUserData(GGadgetGetWindow(g));
5414 0 : if ( GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_KPairs)) ) {
5415 0 : GGadgetSetEnabled(GWidgetGetControl(kf->gw,CID_KCBuild),0);
5416 0 : sprintf(mkbuf,"%d",15*(kf->sf->ascent+kf->sf->descent)/1000 );
5417 0 : GGadgetSetTitle8(GWidgetGetControl(kf->gw,CID_MinKern),mkbuf);
5418 : } else {
5419 0 : GGadgetSetEnabled(GWidgetGetControl(kf->gw,CID_KCBuild),1);
5420 0 : GGadgetSetTitle8(GWidgetGetControl(kf->gw,CID_MinKern),"0");
5421 : }
5422 : }
5423 0 : return( true );
5424 : }
5425 :
5426 0 : static SplineChar **SelectedGlyphs(FontView *_fv) {
5427 0 : FontViewBase *fv = (FontViewBase *) _fv;
5428 : SplineFont *sf;
5429 : EncMap *map;
5430 : int selcnt;
5431 : int enc,gid;
5432 : SplineChar **glyphlist, *sc;
5433 :
5434 0 : map = fv->map;
5435 0 : sf = fv->sf;
5436 0 : selcnt=0;
5437 0 : for ( enc=0; enc<map->enccount; ++enc ) {
5438 0 : if ( fv->selected[enc] && (gid=map->map[enc])!=-1 &&
5439 0 : SCWorthOutputting(sf->glyphs[gid]))
5440 0 : ++selcnt;
5441 : }
5442 0 : if ( selcnt<1 ) {
5443 0 : 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."));
5444 0 : return(NULL);
5445 : }
5446 :
5447 0 : glyphlist = malloc((selcnt+1)*sizeof(SplineChar *));
5448 0 : selcnt=0;
5449 0 : for ( enc=0; enc<map->enccount; ++enc ) {
5450 0 : if ( fv->selected[enc] && (gid=map->map[enc])!=-1 &&
5451 0 : SCWorthOutputting(sc = sf->glyphs[gid]))
5452 0 : glyphlist[selcnt++] = sc;
5453 : }
5454 0 : glyphlist[selcnt] = NULL;
5455 0 : return( glyphlist );
5456 : }
5457 :
5458 0 : static int KF_OK(GGadget *g, GEvent *e) {
5459 :
5460 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
5461 0 : struct kf_dlg *kf = GDrawGetUserData(GGadgetGetWindow(g));
5462 : int touch, separation, minkern, err, onlyCloser, autokern;
5463 0 : real good_enough=0;
5464 0 : int isclass, autobuild=0;
5465 0 : struct kf_results *results = kf->results;
5466 :
5467 0 : err = false;
5468 0 : touch = GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_Touched));
5469 0 : separation = GetInt8(kf->gw,CID_Separation,_("Separation"),&err);
5470 0 : minkern = GetInt8(kf->gw,CID_MinKern,_("Min Kern"),&err);
5471 0 : onlyCloser = GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_OnlyCloser));
5472 0 : autokern = GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_Autokern));
5473 0 : if ( err )
5474 0 : return( true );
5475 :
5476 0 : isclass = GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_KClasses));
5477 0 : if ( isclass ) {
5478 0 : autobuild = GGadgetIsChecked(GWidgetGetControl(kf->gw,CID_KCBuild));
5479 0 : good_enough = GetReal8(kf->gw,CID_ClassDistance,_("Intra Class Distance"),&err);
5480 0 : if ( err )
5481 0 : return( true );
5482 : }
5483 0 : if ( autobuild || autokern ) {
5484 0 : results->firstglyphs = SelectedGlyphs(kf->first_fv);
5485 0 : if ( results->firstglyphs == NULL )
5486 0 : return( true );
5487 0 : results->secondglyphs = SelectedGlyphs(kf->second_fv);
5488 0 : if ( results->secondglyphs == NULL ) {
5489 0 : free(results->firstglyphs); results->firstglyphs=NULL;
5490 0 : return( true );
5491 : }
5492 : }
5493 0 : kf->sub->separation = separation;
5494 0 : kf->sub->minkern = minkern;
5495 0 : kf->sub->kerning_by_touch = touch;
5496 0 : kf->sub->onlyCloser = onlyCloser;
5497 0 : kf->sub->dontautokern = !autokern;
5498 0 : results->good_enough = good_enough;
5499 0 : if ( !isclass )
5500 0 : results->asked = 0;
5501 : else
5502 0 : results->asked = 1;
5503 0 : results->autobuild = autobuild;
5504 0 : results->autokern = autokern;
5505 0 : kf->done = true;
5506 : }
5507 0 : return( true );
5508 : }
5509 :
5510 0 : static int KF_Cancel(GGadget *g, GEvent *e) {
5511 : struct kf_dlg *kf;
5512 :
5513 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
5514 0 : kf = GDrawGetUserData(GGadgetGetWindow(g));
5515 0 : kf->done = true;
5516 : }
5517 0 : return( true );
5518 : }
5519 :
5520 0 : static int kf_e_h(GWindow gw, GEvent *event) {
5521 0 : struct kf_dlg *kf = GDrawGetUserData(gw);
5522 :
5523 0 : switch ( event->type ) {
5524 : case et_close:
5525 0 : kf->done = true;
5526 0 : break;
5527 : case et_char:
5528 0 : return( false );
5529 : break;
5530 : }
5531 0 : return( true );
5532 : }
5533 :
5534 0 : static int kern_format_dlg( SplineFont *sf, int def_layer,
5535 : struct lookup_subtable *sub, struct kf_results *results ) {
5536 : GRect pos;
5537 : GWindowAttrs wattrs;
5538 : GGadgetCreateData gcd[16], boxes[7], hbox;
5539 : GGadgetCreateData *varray[21], *h2array[6], *h3array[6], *h4array[8], *buttonarray[8], *h5array[4];
5540 : GTextInfo label[15];
5541 : char sepbuf[40], mkbuf[40], distancebuf[40];
5542 : struct kf_dlg kf;
5543 : int i,j, guts_row;
5544 : /* Returns are 0=>Pairs, 1=>Classes, 2=>Cancel */
5545 : FontRequest rq;
5546 : int as, ds, ld;
5547 : static GFont *plainfont = NULL, *boldfont=NULL;
5548 :
5549 0 : if ( sub->separation==0 && !sub->kerning_by_touch ) {
5550 0 : sub->separation = sf->width_separation;
5551 0 : if ( sf->width_separation==0 )
5552 0 : sub->separation = 15*(sf->ascent+sf->descent)/100;
5553 0 : sub->minkern = sub->separation/10;
5554 : }
5555 :
5556 0 : memset(&wattrs,0,sizeof(wattrs));
5557 0 : memset(&gcd,0,sizeof(gcd));
5558 0 : memset(&boxes,0,sizeof(boxes));
5559 0 : memset(&label,0,sizeof(label));
5560 0 : memset(&kf,0,sizeof(kf));
5561 :
5562 0 : kf.base.funcs = &kernformat_funcs;
5563 0 : kf.sub = sub;
5564 0 : kf.results = results;
5565 0 : kf.sf = sf;
5566 0 : kf.def_layer = def_layer;
5567 0 : results->asked = 2; /* Cancel */
5568 :
5569 0 : wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
5570 0 : wattrs.event_masks = ~(1<<et_charup);
5571 0 : wattrs.restrict_input_to_me = false;
5572 0 : wattrs.undercursor = 1;
5573 0 : wattrs.cursor = ct_pointer;
5574 0 : wattrs.utf8_window_title = _("Kerning format") ;
5575 0 : wattrs.is_dlg = true;
5576 0 : pos.x = pos.y = 0;
5577 0 : pos.width = 100;
5578 0 : pos.height = 100;
5579 0 : kf.gw = GDrawCreateTopWindow(NULL,&pos,kf_e_h,&kf,&wattrs);
5580 :
5581 0 : if ( plainfont==NULL ) {
5582 0 : memset(&rq,0,sizeof(rq));
5583 0 : rq.utf8_family_name = SANS_UI_FAMILIES;
5584 0 : rq.point_size = 12;
5585 0 : rq.weight = 400;
5586 0 : plainfont = GDrawInstanciateFont(NULL,&rq);
5587 0 : plainfont = GResourceFindFont("KernFormat.Font",plainfont);
5588 0 : GDrawDecomposeFont(plainfont, &rq);
5589 0 : rq.weight = 700;
5590 0 : boldfont = GDrawInstanciateFont(NULL,&rq);
5591 0 : boldfont = GResourceFindFont("KernFormat.BoldFont",boldfont);
5592 : }
5593 0 : kf.plain = plainfont; kf.bold = boldfont;
5594 0 : GDrawWindowFontMetrics(kf.gw,kf.plain,&as,&ds,&ld);
5595 0 : kf.fh = as+ds; kf.as = as;
5596 :
5597 0 : i = j = 0;
5598 :
5599 0 : label[i].text = (unichar_t *) _("Use individual kerning pairs");
5600 0 : label[i].text_is_1byte = true;
5601 0 : label[i].text_in_resource = true;
5602 0 : gcd[i].gd.label = &label[i];
5603 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup|gg_cb_on;
5604 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
5605 : "In this format you specify every kerning pair in which\n"
5606 : "you are interested in." );
5607 0 : gcd[i].gd.cid = CID_KPairs;
5608 0 : gcd[i].gd.handle_controlevent = KF_FormatChange;
5609 0 : gcd[i].creator = GRadioCreate;
5610 0 : varray[j++] = &gcd[i++]; varray[j++] = NULL;
5611 :
5612 0 : label[i].text = (unichar_t *) _("Use a matrix of kerning classes");
5613 0 : label[i].text_is_1byte = true;
5614 0 : label[i].text_in_resource = true;
5615 0 : gcd[i].gd.label = &label[i];
5616 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup|gg_rad_continueold;
5617 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
5618 : "In this format you define a series of glyph classes and\n"
5619 : "specify a matrix showing how each class interacts with all\n"
5620 : "the others.");
5621 0 : gcd[i].gd.cid = CID_KClasses;
5622 0 : gcd[i].gd.handle_controlevent = KF_FormatChange;
5623 0 : gcd[i].creator = GRadioCreate;
5624 0 : varray[j++] = &gcd[i++]; varray[j++] = NULL;
5625 :
5626 0 : label[i].text = (unichar_t *) _("FontForge will guess kerning classes for selected glyphs");
5627 0 : label[i].text_is_1byte = true;
5628 0 : label[i].text_in_resource = true;
5629 0 : gcd[i].gd.label = &label[i];
5630 0 : gcd[i].gd.flags = gg_visible|gg_utf8_popup|gg_cb_on;
5631 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
5632 : "FontForge will look at the glyphs selected in the font view\n"
5633 : "and will try to find groups of glyphs which are most alike\n"
5634 : "and generate kerning classes based on that information." );
5635 0 : gcd[i].gd.cid = CID_KCBuild;
5636 0 : gcd[i].creator = GCheckBoxCreate;
5637 0 : h2array[0] = GCD_HPad10; h2array[1] = &gcd[i++]; h2array[2] = GCD_Glue; h2array[3] = NULL;
5638 0 : boxes[3].gd.flags = gg_enabled|gg_visible;
5639 0 : boxes[3].gd.u.boxelements = h2array;
5640 0 : boxes[3].creator = GHBoxCreate;
5641 0 : varray[j++] = &boxes[3]; varray[j++] = NULL;
5642 :
5643 0 : label[i].text = (unichar_t *) _("Intra Class Distance:");
5644 0 : label[i].text_is_1byte = true;
5645 0 : label[i].text_in_resource = true;
5646 0 : gcd[i].gd.label = &label[i];
5647 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5648 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
5649 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
5650 : "This is roughly (very roughly) the number off em-units\n"
5651 : "of error that two glyphs may have to belong in the same\n"
5652 : "class. This error is taken by comparing the two glyphs\n"
5653 : "to all other glyphs and summing the differences.\n"
5654 : "A small number here (like 2) means lots of small classes,\n"
5655 : "while a larger number (like 20) will mean fewer classes,\n"
5656 : "each with more glyphs." );
5657 0 : gcd[i].creator = GLabelCreate;
5658 0 : h3array[0] = GCD_HPad10; h3array[1] = &gcd[i++];
5659 :
5660 0 : sprintf( distancebuf, "%g", (sf->ascent+sf->descent)/100. );
5661 0 : label[i].text = (unichar_t *) distancebuf;
5662 0 : label[i].text_is_1byte = true;
5663 0 : label[i].text_in_resource = true;
5664 0 : gcd[i].gd.label = &label[i];
5665 0 : gcd[i].gd.pos.width = 50;
5666 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
5667 0 : gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
5668 0 : gcd[i].gd.cid = CID_ClassDistance;
5669 0 : gcd[i].creator = GTextFieldCreate;
5670 0 : h3array[2] = &gcd[i++]; h3array[3] = GCD_Glue; h3array[4] = NULL;
5671 :
5672 0 : boxes[5].gd.flags = gg_enabled|gg_visible;
5673 0 : boxes[5].gd.u.boxelements = h3array;
5674 0 : boxes[5].creator = GHBoxCreate;
5675 0 : varray[j++] = &boxes[5]; varray[j++] = NULL;
5676 0 : varray[j++] = GCD_Glue; varray[j++] = NULL;
5677 :
5678 0 : label[i].text = (unichar_t *) _("_Default Separation:");
5679 0 : label[i].text_is_1byte = true;
5680 0 : label[i].text_in_resource = true;
5681 0 : gcd[i].gd.label = &label[i];
5682 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5683 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
5684 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
5685 : "Add entries to the lookup trying to make the optical\n"
5686 : "separation between all pairs of glyphs equal to this\n"
5687 : "value." );
5688 0 : gcd[i].creator = GLabelCreate;
5689 0 : h4array[0] = &gcd[i++];
5690 :
5691 0 : sprintf( sepbuf, "%d", sub->separation );
5692 0 : label[i].text = (unichar_t *) sepbuf;
5693 0 : label[i].text_is_1byte = true;
5694 0 : label[i].text_in_resource = true;
5695 0 : gcd[i].gd.label = &label[i];
5696 0 : gcd[i].gd.pos.width = 50;
5697 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
5698 0 : gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
5699 0 : gcd[i].gd.cid = CID_Separation;
5700 0 : gcd[i].creator = GTextFieldCreate;
5701 0 : h4array[1] = &gcd[i++]; h4array[2] = GCD_Glue;
5702 :
5703 0 : label[i].text = (unichar_t *) _("_Min Kern:");
5704 0 : label[i].text_is_1byte = true;
5705 0 : label[i].text_in_resource = true;
5706 0 : gcd[i].gd.label = &label[i];
5707 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5708 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
5709 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
5710 : "Any computed kerning change whose absolute value is less\n"
5711 : "that this will be ignored.\n" );
5712 0 : gcd[i].creator = GLabelCreate;
5713 0 : h4array[3] = &gcd[i++];
5714 :
5715 0 : sprintf( mkbuf, "%d", sub->minkern );
5716 0 : label[i].text = (unichar_t *) mkbuf;
5717 0 : label[i].text_is_1byte = true;
5718 0 : label[i].text_in_resource = true;
5719 0 : gcd[i].gd.label = &label[i];
5720 0 : gcd[i].gd.pos.width = 50;
5721 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
5722 0 : gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
5723 0 : gcd[i].gd.cid = CID_MinKern;
5724 0 : gcd[i].creator = GTextFieldCreate;
5725 0 : h4array[4] = &gcd[i++];
5726 :
5727 0 : label[i].text = (unichar_t *) _("_Touching");
5728 0 : label[i].text_is_1byte = true;
5729 0 : label[i].text_in_resource = true;
5730 0 : gcd[i].gd.label = &label[i];
5731 0 : gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
5732 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
5733 0 : if ( sub->kerning_by_touch )
5734 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup|gg_cb_on;
5735 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
5736 : "Normally kerning is based on achieving a constant (optical)\n"
5737 : "separation between glyphs, but occasionally it is desirable\n"
5738 : "to have a kerning table where the kerning is based on the\n"
5739 : "closest approach between two glyphs (So if the desired separ-\n"
5740 : "ation is 0 then the glyphs will actually be touching.");
5741 0 : gcd[i].gd.cid = CID_Touched;
5742 0 : gcd[i].creator = GCheckBoxCreate;
5743 0 : h4array[5] = &gcd[i++];
5744 :
5745 0 : h4array[6] = GCD_Glue; h4array[7] = NULL;
5746 :
5747 0 : boxes[4].gd.flags = gg_enabled|gg_visible;
5748 0 : boxes[4].gd.u.boxelements = h4array;
5749 0 : boxes[4].creator = GHBoxCreate;
5750 0 : varray[j++] = &boxes[4]; varray[j++] = NULL;
5751 :
5752 0 : label[i].text = (unichar_t *) _("Only kern glyphs closer");
5753 0 : label[i].text_is_1byte = true;
5754 0 : label[i].text_in_resource = true;
5755 0 : gcd[i].gd.label = &label[i];
5756 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup|gg_cb_on;
5757 0 : if ( sub->onlyCloser )
5758 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup|gg_cb_on;
5759 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
5760 : "When doing autokerning, only move glyphs closer together,\n"
5761 : "so the kerning offset will be negative.");
5762 0 : gcd[i].gd.cid = CID_OnlyCloser;
5763 0 : gcd[i].creator = GCheckBoxCreate;
5764 0 : h5array[0] = &gcd[i++];
5765 :
5766 0 : label[i].text = (unichar_t *) _("Autokern new entries");
5767 0 : label[i].text_is_1byte = true;
5768 0 : label[i].text_in_resource = true;
5769 0 : gcd[i].gd.label = &label[i];
5770 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup;
5771 0 : if ( !sub->dontautokern )
5772 0 : gcd[i].gd.flags = gg_enabled|gg_visible|gg_utf8_popup|gg_cb_on;
5773 0 : gcd[i].gd.popup_msg = (unichar_t *) _(
5774 : "When adding new entries provide default kerning values.");
5775 0 : gcd[i].gd.cid = CID_Autokern;
5776 0 : gcd[i].creator = GCheckBoxCreate;
5777 0 : h5array[1] = &gcd[i++]; h5array[2] = NULL;
5778 :
5779 0 : memset(&hbox,0,sizeof(hbox));
5780 0 : hbox.gd.flags = gg_enabled|gg_visible;
5781 0 : hbox.gd.u.boxelements = h5array;
5782 0 : hbox.creator = GHBoxCreate;
5783 0 : varray[j++] = &hbox; varray[j++] = NULL;
5784 :
5785 0 : guts_row = j/2;
5786 0 : gcd[i].gd.flags = gg_enabled|gg_visible;
5787 0 : gcd[i].gd.cid = CID_Guts;
5788 0 : gcd[i].gd.u.drawable_e_h = kf_sub_e_h;
5789 0 : gcd[i].creator = GDrawableCreate;
5790 0 : varray[j++] = &gcd[i++]; varray[j++] = NULL;
5791 :
5792 0 : gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
5793 0 : label[i].text = (unichar_t *) _("_OK");
5794 0 : label[i].text_is_1byte = true;
5795 0 : label[i].text_in_resource = true;
5796 0 : gcd[i].gd.label = &label[i];
5797 0 : gcd[i].gd.handle_controlevent = KF_OK;
5798 0 : gcd[i++].creator = GButtonCreate;
5799 :
5800 0 : gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
5801 0 : label[i].text = (unichar_t *) _("_Cancel");
5802 0 : label[i].text_is_1byte = true;
5803 0 : label[i].text_in_resource = true;
5804 0 : gcd[i].gd.label = &label[i];
5805 0 : gcd[i].gd.handle_controlevent = KF_Cancel;
5806 0 : gcd[i++].creator = GButtonCreate;
5807 :
5808 0 : buttonarray[0] = GCD_Glue; buttonarray[1] = &gcd[i-2]; buttonarray[2] = GCD_Glue;
5809 0 : buttonarray[3] = GCD_Glue; buttonarray[4] = &gcd[i-1]; buttonarray[5] = GCD_Glue;
5810 0 : buttonarray[6] = NULL;
5811 0 : boxes[6].gd.flags = gg_enabled|gg_visible;
5812 0 : boxes[6].gd.u.boxelements = buttonarray;
5813 0 : boxes[6].creator = GHBoxCreate;
5814 0 : varray[j++] = &boxes[6]; varray[j++] = NULL; varray[j++] = NULL;
5815 :
5816 0 : boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
5817 0 : boxes[0].gd.flags = gg_enabled|gg_visible;
5818 0 : boxes[0].gd.u.boxelements = varray;
5819 0 : boxes[0].creator = GHVGroupCreate;
5820 :
5821 0 : GGadgetsCreate(kf.gw,boxes);
5822 :
5823 0 : GHVBoxSetExpandableRow(boxes[0].ret,guts_row);
5824 0 : GHVBoxSetExpandableCol(boxes[3].ret,gb_expandglue);
5825 0 : GHVBoxSetExpandableCol(boxes[4].ret,gb_expandglue);
5826 0 : GHVBoxSetExpandableCol(boxes[5].ret,gb_expandglue);
5827 0 : GHVBoxSetExpandableCol(boxes[6].ret,gb_expandgluesame);
5828 :
5829 0 : kf.topbox = boxes[0].ret;
5830 0 : KFFontViewInits(&kf, GWidgetGetControl(kf.gw,CID_Guts));
5831 0 : kf_activateMe((struct fvcontainer *) &kf,(struct fontviewbase *) kf.first_fv);
5832 :
5833 0 : GHVBoxFitWindow(boxes[0].ret);
5834 0 : GDrawSetVisible(kf.gw,true);
5835 0 : while ( !kf.done )
5836 0 : GDrawProcessOneEvent(NULL);
5837 0 : FontViewFree(&kf.second_fv->b);
5838 0 : FontViewFree(&kf.first_fv->b);
5839 0 : GDrawSetUserData(kf.gw,NULL);
5840 0 : GDrawDestroyWindow(kf.gw);
5841 0 : return( results->asked );
5842 : }
5843 :
5844 0 : void _LookupSubtableContents(SplineFont *sf, struct lookup_subtable *sub,
5845 : struct subtable_data *sd,int def_layer) {
5846 0 : int lookup_type = sub->lookup->lookup_type;
5847 : static int nested=0;
5848 : extern int default_autokern_dlg;
5849 :
5850 0 : if ( (lookup_type == gsub_context || lookup_type == gsub_contextchain ||
5851 0 : lookup_type == gsub_reversecchain ||
5852 0 : lookup_type == gpos_context || lookup_type == gpos_contextchain) &&
5853 0 : sub->fpst==NULL ) {
5854 0 : sub->fpst = chunkalloc(sizeof(FPST));
5855 0 : sub->fpst->type = lookup_type == gsub_context ? pst_contextsub :
5856 : lookup_type == gsub_contextchain ? pst_chainsub :
5857 : lookup_type == gsub_reversecchain ? pst_reversesub :
5858 : lookup_type == gpos_context ? pst_contextpos :
5859 : pst_chainpos;
5860 0 : if ( lookup_type == gsub_reversecchain )
5861 0 : sub->fpst->format = pst_reversecoverage;
5862 0 : sub->fpst->subtable = sub;
5863 0 : sub->fpst->next = sf->possub;
5864 0 : sf->possub = sub->fpst;
5865 0 : } else if ( (lookup_type == morx_indic ||
5866 0 : lookup_type == morx_context ||
5867 0 : lookup_type == morx_insert ||
5868 0 : lookup_type == kern_statemachine) &&
5869 0 : sub->sm==NULL ) {
5870 0 : sub->sm = chunkalloc(sizeof(ASM));
5871 0 : sub->sm->type = lookup_type == morx_indic ? asm_indic :
5872 : lookup_type == morx_context ? asm_context :
5873 : lookup_type == morx_insert ? asm_insert :
5874 : asm_kern;
5875 0 : sub->sm->subtable = sub;
5876 0 : sub->sm->next = sf->sm;
5877 0 : sf->sm = sub->sm;
5878 0 : } else if ( lookup_type==gpos_pair &&
5879 0 : sub->kc==NULL &&
5880 0 : !sub->per_glyph_pst_or_kern ) {
5881 : char *buts[5];
5882 : struct kf_results results;
5883 :
5884 0 : memset(&results,0,sizeof(results));
5885 0 : if ( sd!=NULL && sd->flags&sdf_verticalkern )
5886 0 : sub->vertical_kerning = true;
5887 0 : else if ( sd!=NULL && sd->flags&sdf_horizontalkern )
5888 0 : sub->vertical_kerning = false;
5889 : else
5890 0 : sub->vertical_kerning = VerticalKernFeature(sf,sub->lookup,true);
5891 :
5892 0 : if ( sd!=NULL && (sd->flags&sdf_kernclass) )
5893 0 : results.asked = 1;
5894 0 : else if ( sd!=NULL && (sd->flags&sdf_kernpair) )
5895 0 : results.asked = 0;
5896 : else {
5897 0 : if ( sub->vertical_kerning || nested || !default_autokern_dlg ) {
5898 0 : buts[0] = _("_Pairs"); buts[1] = _("C_lasses");
5899 0 : buts[2] = _("_Cancel"); buts[3]=NULL;
5900 0 : 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?") );
5901 : } else {
5902 0 : nested = 1;
5903 0 : kern_format_dlg(sf,def_layer,sub,&results);
5904 0 : nested = 0;
5905 : }
5906 0 : if ( results.asked==2 )
5907 0 : return;
5908 : }
5909 0 : if ( results.asked==0 ) {
5910 0 : sub->per_glyph_pst_or_kern = true;
5911 0 : if ( results.autokern ) {
5912 : SplineChar **lefts, **rights;
5913 0 : if ( sub->lookup->lookup_flags & pst_r2l ) {
5914 0 : lefts = results.secondglyphs;
5915 0 : rights = results.firstglyphs;
5916 : } else {
5917 0 : lefts = results.firstglyphs;
5918 0 : rights = results.secondglyphs;
5919 : }
5920 0 : AutoKern2(sf, def_layer,lefts,rights,
5921 : sub,
5922 : /* If separation==0 and !touch then use default values */
5923 : 0,0,0,0, 0, NULL, NULL);
5924 : }
5925 : } else {
5926 0 : sub->kc = chunkalloc(sizeof(KernClass));
5927 0 : if ( sub->vertical_kerning ) {
5928 0 : sub->kc->next = sf->vkerns;
5929 0 : sf->vkerns = sub->kc;
5930 : } else {
5931 0 : sub->kc->next = sf->kerns;
5932 0 : sf->kerns = sub->kc;
5933 : }
5934 0 : sub->kc->subtable = sub;
5935 0 : sub->kc->first_cnt = sub->kc->second_cnt = 1;
5936 0 : sub->kc->firsts = calloc(1,sizeof(char *));
5937 0 : sub->kc->seconds = calloc(1,sizeof(char *));
5938 0 : sub->kc->offsets = calloc(1,sizeof(int16));
5939 0 : sub->kc->adjusts = calloc(1,sizeof(DeviceTable));
5940 : /* Need to fix for Hebrew !!!! */
5941 0 : if ( results.autobuild )
5942 : /* Specifying separation==0 and !touching means use default values */
5943 0 : AutoKern2BuildClasses(sf,def_layer,results.firstglyphs,
5944 : results.secondglyphs,sub,0,0,0,0,0,results.good_enough);
5945 : }
5946 0 : free(results.firstglyphs);
5947 0 : free(results.secondglyphs);
5948 : }
5949 :
5950 0 : if ( sub->fpst && sf->fontinfo!=NULL ) {
5951 0 : ContextChainEdit(sf,sub->fpst,sf->fontinfo,NULL,def_layer);
5952 0 : } else if ( sub->sm && sf->fontinfo!=NULL ) {
5953 0 : StateMachineEdit(sf,sub->sm,sf->fontinfo);
5954 0 : } else if ( sub->kc!=NULL ) {
5955 0 : KernClassD(sub->kc,sf,def_layer,sub->vertical_kerning);
5956 0 : } else if ( sub->lookup->lookup_type>=gpos_cursive &&
5957 0 : sub->lookup->lookup_type<=gpos_mark2mark )
5958 0 : AnchorClassD(sf,sub,def_layer);
5959 : else
5960 0 : PSTKernD(sf,sub,def_layer);
5961 : }
5962 : /******************************************************************************/
5963 : /*************************** Add/Remove Language **************************/
5964 : /******************************************************************************/
5965 0 : static uint32 StrNextLang(char **_pt) {
5966 0 : unsigned char *pt = (unsigned char *) *_pt;
5967 : unsigned char tag[4];
5968 : int i;
5969 :
5970 0 : memset(tag,' ',4);
5971 0 : while ( *pt==' ' || *pt==',' ) ++pt;
5972 0 : if ( *pt=='\0' )
5973 0 : return( 0 );
5974 :
5975 0 : for ( i=0; i<4 && *pt!='\0' && *pt!=','; ++i )
5976 0 : tag[i] = *pt++;
5977 0 : while ( *pt==' ' ) ++pt;
5978 0 : if ( *pt!='\0' && *pt!=',' )
5979 0 : return( 0xffffffff );
5980 0 : *_pt = (char *) pt;
5981 0 : return( (tag[0]<<24) | (tag[1]<<16) | (tag[2]<<8) | tag[3] );
5982 : }
5983 :
5984 0 : static void lk_AddRm(struct lkdata *lk,int add_lang,uint32 script, char *langs) {
5985 : int i,l;
5986 : OTLookup *otl;
5987 : FeatureScriptLangList *fl;
5988 : struct scriptlanglist *sl;
5989 : uint32 lang;
5990 : char *pt;
5991 :
5992 0 : for ( i=0; i<lk->cnt; ++i ) {
5993 0 : if ( lk->all[i].deleted || !lk->all[i].selected )
5994 0 : continue;
5995 0 : otl = lk->all[i].lookup;
5996 0 : for ( fl= otl->features; fl!=NULL; fl=fl->next ) {
5997 0 : for ( sl=fl->scripts; sl!=NULL; sl=sl->next ) if ( sl->script==script ) {
5998 0 : pt = langs;
5999 0 : while ( (lang = StrNextLang(&pt))!=0 ) {
6000 0 : for ( l=sl->lang_cnt-1; l>=0; --l ) {
6001 0 : if ( ((l>=MAX_LANG) ? sl->morelangs[l-MAX_LANG] : sl->langs[l]) == lang )
6002 0 : break;
6003 : }
6004 0 : if ( add_lang && l<0 ) {
6005 0 : if ( sl->lang_cnt<MAX_LANG ) {
6006 0 : sl->langs[sl->lang_cnt++] = lang;
6007 : } else {
6008 0 : sl->morelangs = realloc(sl->morelangs,(++sl->lang_cnt-MAX_LANG)*sizeof(uint32));
6009 0 : sl->morelangs[sl->lang_cnt-MAX_LANG-1] = lang;
6010 : }
6011 0 : } else if ( !add_lang && l>=0 ) {
6012 0 : --sl->lang_cnt;
6013 0 : while ( l<sl->lang_cnt ) {
6014 0 : uint32 nlang = l+1>=MAX_LANG ? sl->morelangs[l+1-MAX_LANG] : sl->langs[l+1];
6015 0 : if ( l>=MAX_LANG )
6016 0 : sl->morelangs[l-MAX_LANG] = nlang;
6017 : else
6018 0 : sl->langs[l] = nlang;
6019 0 : ++l;
6020 : }
6021 0 : if ( sl->lang_cnt==0 ) {
6022 0 : sl->langs[0] = DEFAULT_LANG;
6023 0 : sl->lang_cnt = 1;
6024 : }
6025 : }
6026 : }
6027 : }
6028 : }
6029 : }
6030 0 : }
6031 :
6032 : #define CID_ScriptTag 1001
6033 : #define CID_Langs 1002
6034 :
6035 : struct addrmlang {
6036 : GWindow gw;
6037 : int done;
6038 : int add_lang;
6039 : struct lkdata *lk;
6040 : };
6041 :
6042 0 : static int ARL_OK(GGadget *g, GEvent *e) {
6043 :
6044 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
6045 0 : struct addrmlang *arl = GDrawGetUserData(GGadgetGetWindow(g));
6046 : const unichar_t *spt;
6047 : char *langs, *pt;
6048 : int i;
6049 : unsigned char tag[4];
6050 : uint32 script_tag, lang;
6051 :
6052 0 : memset(tag,' ',4);
6053 0 : spt = _GGadgetGetTitle(GWidgetGetControl(arl->gw,CID_ScriptTag));
6054 0 : while ( *spt==' ' ) ++spt;
6055 0 : if ( *spt=='\0' ) {
6056 0 : ff_post_error(_("No Script Tag"), _("Please specify a 4 letter opentype script tag"));
6057 0 : return( true );
6058 : }
6059 0 : for ( i=0; i<4 && *spt!='\0'; ++i )
6060 0 : tag[i] = *spt++;
6061 0 : while ( *spt==' ' ) ++spt;
6062 0 : if ( *spt!='\0' ) {
6063 0 : ff_post_error(_("Script Tag too long"), _("Please specify a 4 letter opentype script tag"));
6064 0 : return( true );
6065 : }
6066 0 : script_tag = (tag[0]<<24) | (tag[1]<<16) | (tag[2]<<8) | tag[3];
6067 :
6068 0 : pt = langs = GGadgetGetTitle8(GWidgetGetControl(arl->gw,CID_Langs));
6069 0 : while ( (lang = StrNextLang(&pt))!=0 ) {
6070 0 : if ( lang==0xffffffff ) {
6071 0 : ff_post_error(_("Invalid language"), _("Please specify a comma separated list of 4 letter opentype language tags"));
6072 0 : return( true );
6073 : }
6074 : }
6075 :
6076 0 : lk_AddRm(arl->lk,arl->add_lang,script_tag, langs);
6077 0 : free(langs);
6078 0 : arl->done = true;
6079 : }
6080 0 : return( true );
6081 : }
6082 :
6083 0 : static int ARL_Cancel(GGadget *g, GEvent *e) {
6084 :
6085 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
6086 0 : struct addrmlang *arl = GDrawGetUserData(GGadgetGetWindow(g));
6087 0 : arl->done = true;
6088 : }
6089 0 : return( true );
6090 : }
6091 :
6092 0 : static int ARL_TagChanged(GGadget *g, GEvent *e) {
6093 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged &&
6094 0 : e->u.control.u.tf_changed.from_pulldown != -1 ) {
6095 0 : int which = e->u.control.u.tf_changed.from_pulldown;
6096 : int len;
6097 0 : GTextInfo **ti = GGadgetGetList(g,&len);
6098 : char tag[8];
6099 0 : uint32 tagval = (intpt) (ti[which]->userdata);
6100 :
6101 0 : tag[0] = tagval>>24;
6102 0 : tag[1] = tagval>>16;
6103 0 : tag[2] = tagval>>8;
6104 0 : tag[3] = tagval;
6105 0 : tag[4] = 0;
6106 0 : GGadgetSetTitle8(g,tag);
6107 : }
6108 0 : return( true );
6109 : }
6110 :
6111 0 : static int arl_e_h(GWindow gw, GEvent *event) {
6112 0 : struct addrmlang *arl = GDrawGetUserData(gw);
6113 :
6114 0 : switch ( event->type ) {
6115 : case et_char:
6116 0 : return( false );
6117 : case et_close:
6118 0 : arl->done = true;
6119 0 : break;
6120 : }
6121 0 : return( true );
6122 : }
6123 :
6124 0 : static GTextInfo *ScriptListOfFont(SplineFont *sf) {
6125 0 : uint32 *ourscripts = SFScriptsInLookups(sf,-1);
6126 : int i,j;
6127 : GTextInfo *ti;
6128 : char tag[8];
6129 :
6130 0 : if ( ourscripts==NULL || ourscripts[0]==0 ) {
6131 0 : free(ourscripts);
6132 0 : ourscripts = malloc(2*sizeof(uint32));
6133 0 : ourscripts[0] = DEFAULT_SCRIPT;
6134 0 : ourscripts[1] = 0;
6135 : }
6136 0 : for ( i=0; ourscripts[i]!=0; ++i );
6137 0 : ti = calloc(i+1,sizeof(GTextInfo));
6138 0 : for ( i=0; ourscripts[i]!=0; ++i ) {
6139 0 : ti[i].userdata = (void *) (intpt) ourscripts[i];
6140 0 : for ( j=0; scripts[j].text!=NULL; ++j) {
6141 0 : if ( scripts[j].userdata == (void *) (intpt) ourscripts[i])
6142 0 : break;
6143 : }
6144 0 : if ( scripts[j].text!=NULL )
6145 0 : ti[i].text = (unichar_t *) copy( (char *) scripts[j].text );
6146 : else {
6147 0 : tag[0] = ourscripts[i]>>24;
6148 0 : tag[1] = ourscripts[i]>>16;
6149 0 : tag[2] = ourscripts[i]>>8;
6150 0 : tag[3] = ourscripts[i]&0xff;
6151 0 : tag[4] = 0;
6152 0 : ti[i].text = (unichar_t *) copy( (char *) tag );
6153 : }
6154 0 : ti[i].text_is_1byte = true;
6155 : }
6156 0 : ti[0].selected = true;
6157 0 : free(ourscripts);
6158 0 : return( ti );
6159 : }
6160 :
6161 0 : void AddRmLang(SplineFont *sf, struct lkdata *lk,int add_lang) {
6162 : GRect pos;
6163 : GWindow gw;
6164 : GWindowAttrs wattrs;
6165 : struct addrmlang arl;
6166 : GGadgetCreateData gcd[14], *hvarray[4][3], *barray[8], boxes[3];
6167 : GTextInfo label[14];
6168 : int k;
6169 : GEvent dummy;
6170 :
6171 0 : LookupUIInit();
6172 :
6173 0 : memset(&arl,0,sizeof(arl));
6174 0 : arl.lk = lk;
6175 0 : arl.add_lang = add_lang;
6176 :
6177 0 : memset(&wattrs,0,sizeof(wattrs));
6178 0 : memset(&gcd,0,sizeof(gcd));
6179 0 : memset(&boxes,0,sizeof(boxes));
6180 0 : memset(&label,0,sizeof(label));
6181 :
6182 0 : wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
6183 0 : wattrs.event_masks = ~(1<<et_charup);
6184 0 : wattrs.restrict_input_to_me = false;
6185 0 : wattrs.undercursor = 1;
6186 0 : wattrs.cursor = ct_pointer;
6187 0 : wattrs.utf8_window_title = add_lang ? _("Add Language(s) to Script") : _("Remove Language(s) from Script");
6188 0 : wattrs.is_dlg = false;
6189 0 : pos.x = pos.y = 0;
6190 0 : pos.width = 100;
6191 0 : pos.height = 100;
6192 0 : arl.gw = gw = GDrawCreateTopWindow(NULL,&pos,arl_e_h,&arl,&wattrs);
6193 :
6194 0 : k = 0;
6195 :
6196 0 : label[k].text = (unichar_t *) _("Script Tag:");
6197 0 : label[k].text_is_1byte = true;
6198 0 : gcd[k].gd.label = &label[k];
6199 0 : gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6200 0 : gcd[k].gd.flags = gg_visible | gg_enabled;
6201 0 : gcd[k++].creator = GLabelCreate;
6202 0 : hvarray[0][0] = &gcd[k-1];
6203 :
6204 0 : gcd[k].gd.u.list = ScriptListOfFont(sf);
6205 0 : gcd[k].gd.flags = gg_visible | gg_enabled | gg_list_alphabetic;
6206 0 : gcd[k].gd.cid = CID_ScriptTag;
6207 0 : gcd[k].gd.handle_controlevent = ARL_TagChanged;
6208 0 : gcd[k++].creator = GListFieldCreate;
6209 0 : hvarray[0][1] = &gcd[k-1]; hvarray[0][2] = NULL;
6210 :
6211 0 : label[k].text = (unichar_t *) _("Language Tag:");
6212 0 : label[k].text_is_1byte = true;
6213 0 : gcd[k].gd.label = &label[k];
6214 0 : gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6215 0 : gcd[k].gd.flags = gg_visible | gg_enabled;
6216 0 : gcd[k++].creator = GLabelCreate;
6217 0 : hvarray[1][0] = &gcd[k-1];
6218 :
6219 0 : gcd[k].gd.u.list = languages;
6220 0 : gcd[k].gd.cid = CID_Langs;
6221 0 : gcd[k].gd.flags = gg_visible | gg_enabled | gg_list_alphabetic;
6222 0 : gcd[k].gd.handle_controlevent = ARL_TagChanged;
6223 0 : gcd[k++].creator = GListFieldCreate;
6224 0 : hvarray[1][1] = &gcd[k-1]; hvarray[1][2] = NULL;
6225 :
6226 :
6227 0 : label[k].text = (unichar_t *) _("_OK");
6228 0 : label[k].text_is_1byte = true;
6229 0 : label[k].text_in_resource = true;
6230 0 : gcd[k].gd.label = &label[k];
6231 0 : gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_default;
6232 0 : gcd[k].gd.handle_controlevent = ARL_OK;
6233 0 : gcd[k++].creator = GButtonCreate;
6234 :
6235 0 : label[k].text = (unichar_t *) _("_Cancel");
6236 0 : label[k].text_is_1byte = true;
6237 0 : label[k].text_in_resource = true;
6238 0 : gcd[k].gd.label = &label[k];
6239 0 : gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_cancel;
6240 0 : gcd[k].gd.handle_controlevent = ARL_Cancel;
6241 0 : gcd[k++].creator = GButtonCreate;
6242 :
6243 0 : barray[0] = barray[2] = barray[3] = barray[4] = barray[6] = GCD_Glue; barray[7] = NULL;
6244 0 : barray[1] = &gcd[k-2]; barray[5] = &gcd[k-1];
6245 :
6246 0 : boxes[2].gd.flags = gg_enabled|gg_visible;
6247 0 : boxes[2].gd.u.boxelements = barray;
6248 0 : boxes[2].creator = GHBoxCreate;
6249 :
6250 0 : hvarray[2][0] = &boxes[2]; hvarray[2][1] = GCD_ColSpan; hvarray[2][2] = NULL;
6251 0 : hvarray[3][0] = NULL;
6252 :
6253 0 : boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
6254 0 : boxes[0].gd.flags = gg_enabled|gg_visible;
6255 0 : boxes[0].gd.u.boxelements = hvarray[0];
6256 0 : boxes[0].creator = GHVGroupCreate;
6257 :
6258 0 : GGadgetsCreate(gw,boxes);
6259 :
6260 0 : memset(&dummy,0,sizeof(dummy));
6261 0 : dummy.type = et_controlevent;
6262 0 : dummy.u.control.subtype = et_textchanged;
6263 0 : dummy.u.control.u.tf_changed.from_pulldown = GGadgetGetFirstListSelectedItem(gcd[1].ret);
6264 0 : ARL_TagChanged(gcd[1].ret,&dummy);
6265 :
6266 0 : GTextInfoListFree(gcd[1].gd.u.list);
6267 :
6268 0 : GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
6269 :
6270 0 : GHVBoxFitWindow(boxes[0].ret);
6271 :
6272 0 : GDrawSetVisible(gw,true);
6273 0 : while ( !arl.done )
6274 0 : GDrawProcessOneEvent(NULL);
6275 :
6276 0 : GDrawDestroyWindow(gw);
6277 0 : }
6278 :
6279 : /******************************************************************************/
6280 : /**************************** Mass Glyph Rename ***************************/
6281 : /******************************************************************************/
6282 : typedef struct massrenamedlg {
6283 : GWindow gw;
6284 : int done;
6285 : FontView *fv;
6286 : } MassRenameDlg;
6287 :
6288 : #undef CID_Suffix
6289 : #define CID_SubTable 1001
6290 : #define CID_Suffix 1002
6291 : #define CID_StartName 1003
6292 : #define CID_ReplaceSuffix 1004
6293 : #define CID_Themselves 1005
6294 :
6295 0 : static int MRD_OK(GGadget *g, GEvent *e) {
6296 :
6297 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
6298 0 : MassRenameDlg *mrd = GDrawGetUserData(GGadgetGetWindow(g));
6299 0 : int sel_cnt, enc, enc_max = mrd->fv->b.map->enccount;
6300 : char *start_name, *suffix, *pt;
6301 : int enc_start;
6302 : SplineChar *sc, *sourcesc;
6303 : GTextInfo *subti;
6304 : struct lookup_subtable *sub;
6305 : PST *pst;
6306 0 : int themselves = GGadgetIsChecked(GWidgetGetControl(mrd->gw,CID_Themselves));
6307 0 : int rplsuffix = GGadgetIsChecked(GWidgetGetControl(mrd->gw,CID_ReplaceSuffix));
6308 :
6309 0 : for ( enc=sel_cnt=0; enc<enc_max; ++enc ) if ( mrd->fv->b.selected[enc] )
6310 0 : ++sel_cnt;
6311 0 : if ( !themselves ) {
6312 0 : char *freeme = GGadgetGetTitle8(GWidgetGetControl(mrd->gw,CID_StartName));
6313 0 : start_name = GlyphNameListDeUnicode(freeme);
6314 0 : free(freeme);
6315 0 : enc_start = SFFindSlot(mrd->fv->b.sf,mrd->fv->b.map,-1,start_name);
6316 0 : if ( enc_start==-1 ) {
6317 0 : ff_post_error(_("No Start Glyph"), _("The encoding does not contain something named %.40s"), start_name );
6318 0 : free(start_name);
6319 0 : return( true );
6320 : }
6321 0 : free( start_name );
6322 0 : if ( enc_start+sel_cnt>=enc_max ) {
6323 0 : ff_post_error(_("Not enough glyphs"), _("There aren't enough glyphs in the encoding to name all the selected characters"));
6324 0 : return( true );
6325 : }
6326 0 : for ( enc=enc_start; enc<enc_start+sel_cnt; ++enc ) if ( mrd->fv->b.selected[enc]) {
6327 0 : ff_post_error(_("Bad selection"), _("You may not rename any of the base glyphs, but your selection overlaps the set of base glyphs."));
6328 0 : return( true );
6329 : }
6330 : } else
6331 0 : enc_start = 0;
6332 :
6333 0 : sub = NULL;
6334 0 : subti = GGadgetGetListItemSelected(GWidgetGetControl(mrd->gw,CID_SubTable));
6335 0 : if ( subti!=NULL )
6336 0 : sub = subti->userdata;
6337 0 : if ( sub==(struct lookup_subtable *)-1 )
6338 0 : sub = NULL;
6339 0 : if ( sub!=NULL && themselves ) {
6340 0 : 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."));
6341 0 : return( true );
6342 : }
6343 :
6344 0 : suffix = GGadgetGetTitle8(GWidgetGetControl(mrd->gw,CID_Suffix));
6345 0 : if ( *suffix=='\0' || (*suffix=='.' && suffix[1]=='\0')) {
6346 0 : ff_post_error(_("Missing suffix"), _("If you don't specify a suffix, the glyphs don't get renamed."));
6347 0 : free(suffix);
6348 0 : return( true );
6349 : }
6350 0 : if ( *suffix!='.' ) {
6351 0 : char *old = suffix;
6352 0 : suffix = strconcat(".",suffix);
6353 0 : free(old);
6354 : }
6355 :
6356 0 : for ( enc=sel_cnt=0; enc<enc_max; ++enc ) if ( mrd->fv->b.selected[enc] ) {
6357 : char *oldname;
6358 0 : sourcesc = sc = SFMakeChar(mrd->fv->b.sf,mrd->fv->b.map,enc);
6359 0 : if ( !themselves )
6360 0 : sourcesc = SFMakeChar(mrd->fv->b.sf,mrd->fv->b.map,enc_start+sel_cnt);
6361 0 : oldname = sc->name;
6362 0 : if ( rplsuffix && (pt=strchr(sourcesc->name,'.'))!=NULL ) {
6363 0 : char *name = malloc(pt-sourcesc->name+strlen(suffix)+2);
6364 0 : strcpy(name,sourcesc->name);
6365 0 : strcpy(name+(pt-sourcesc->name),suffix);
6366 0 : sc->name = name;
6367 : } else
6368 0 : sc->name = strconcat(sourcesc->name,suffix);
6369 0 : free(oldname);
6370 0 : sc->unicodeenc = -1;
6371 0 : if ( sub!=NULL ) {
6372 : /* There can only be one single subs with this subtable */
6373 : /* attached to the source glyph */
6374 0 : for ( pst=sourcesc->possub; pst!=NULL && pst->subtable!=sub; pst=pst->next );
6375 0 : if ( pst==NULL ) {
6376 0 : pst = chunkalloc(sizeof(PST));
6377 0 : pst->next = sourcesc->possub;
6378 0 : sourcesc->possub = pst;
6379 0 : pst->subtable = sub;
6380 0 : pst->type = pst_substitution;
6381 : }
6382 0 : free(pst->u.subs.variant);
6383 0 : pst->u.subs.variant = copy(sc->name);
6384 : }
6385 0 : ++sel_cnt;
6386 : }
6387 0 : free(suffix);
6388 0 : mrd->done = true;
6389 : }
6390 0 : return( true );
6391 : }
6392 :
6393 0 : static int MRD_Cancel(GGadget *g, GEvent *e) {
6394 :
6395 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
6396 0 : MassRenameDlg *mrd = GDrawGetUserData(GGadgetGetWindow(g));
6397 0 : mrd->done = true;
6398 : }
6399 0 : return( true );
6400 : }
6401 :
6402 0 : static int MRD_SuffixChange(GGadget *g, GEvent *e) {
6403 :
6404 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
6405 0 : MassRenameDlg *mrd = GDrawGetUserData(GGadgetGetWindow(g));
6406 0 : char *suffix = GGadgetGetTitle8(g);
6407 : int32 len;
6408 : int i;
6409 0 : GTextInfo **ti = GGadgetGetList(GWidgetGetControl(mrd->gw,CID_SubTable),&len);
6410 : struct lookup_subtable *sub;
6411 :
6412 0 : for ( i=0; i<len; ++i ) {
6413 0 : sub = ti[i]->userdata;
6414 0 : if ( sub==NULL || sub==(struct lookup_subtable *) -1 )
6415 0 : continue;
6416 0 : if ( sub->suffix==NULL )
6417 0 : continue;
6418 0 : if ( strcmp(suffix,sub->suffix)==0 ) {
6419 0 : GGadgetSelectOneListItem(GWidgetGetControl(mrd->gw,CID_SubTable),i);
6420 0 : return( true );
6421 : }
6422 : }
6423 : }
6424 0 : return( true );
6425 : }
6426 :
6427 0 : static void MRD_SelectSubtable(MassRenameDlg *mrd,struct lookup_subtable *sub) {
6428 : int32 len;
6429 0 : GTextInfo **ti = GGadgetGetList(GWidgetGetControl(mrd->gw,CID_SubTable),&len);
6430 0 : int i, no_pos = -1;
6431 :
6432 0 : for ( i=0; i<len; ++i ) if ( !ti[i]->line ) {
6433 0 : if ( ti[i]->userdata == sub )
6434 0 : break;
6435 0 : else if ( ti[i]->userdata == (void *) -1 )
6436 0 : no_pos = i;
6437 : }
6438 0 : if ( i==len )
6439 0 : i = no_pos;
6440 0 : if ( i!=-1 )
6441 0 : GGadgetSelectOneListItem(GWidgetGetControl(mrd->gw,CID_SubTable),i);
6442 0 : }
6443 :
6444 0 : static int MRD_Subtable(GGadget *g, GEvent *e) {
6445 0 : MassRenameDlg *mrd = GDrawGetUserData(GGadgetGetWindow(g));
6446 : GTextInfo *ti;
6447 : struct lookup_subtable *sub;
6448 :
6449 0 : if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
6450 0 : ti = GGadgetGetListItemSelected(g);
6451 0 : if ( ti!=NULL ) {
6452 0 : if ( ti->userdata==NULL ) {
6453 0 : sub = SFNewLookupSubtableOfType(mrd->fv->b.sf,gsub_single,NULL,mrd->fv->b.active_layer);
6454 0 : if ( sub!=NULL )
6455 0 : GGadgetSetList(g,SFSubtablesOfType(mrd->fv->b.sf,gsub_single,false,true),false);
6456 0 : MRD_SelectSubtable(mrd,sub);
6457 0 : } else if ( (sub = ti->userdata) != (struct lookup_subtable *) -1 &&
6458 0 : sub->suffix != NULL )
6459 0 : GGadgetSetTitle8(GWidgetGetControl(mrd->gw,CID_Suffix),sub->suffix);
6460 : }
6461 : }
6462 0 : return( true );
6463 : }
6464 :
6465 0 : static unichar_t **MRD_GlyphNameCompletion(GGadget *t,int from_tab) {
6466 0 : MassRenameDlg *mrd = GDrawGetUserData(GGadgetGetWindow(t));
6467 0 : SplineFont *sf = mrd->fv->b.sf;
6468 :
6469 0 : return( SFGlyphNameCompletion(sf,t,from_tab,false));
6470 : }
6471 :
6472 0 : static int mrd_e_h(GWindow gw, GEvent *event) {
6473 0 : MassRenameDlg *mrd = GDrawGetUserData(gw);
6474 :
6475 0 : switch ( event->type ) {
6476 : case et_char:
6477 0 : return( false );
6478 : case et_close:
6479 0 : mrd->done = true;
6480 0 : break;
6481 : }
6482 0 : return( true );
6483 : }
6484 :
6485 0 : void FVMassGlyphRename(FontView *fv) {
6486 : GRect pos;
6487 : GWindow gw;
6488 : GWindowAttrs wattrs;
6489 : MassRenameDlg mrd;
6490 : GGadgetCreateData gcd[14], *hvarray[11][3], *barray[8], boxes[3];
6491 : GTextInfo label[14];
6492 : int i,k,subtablek, startnamek;
6493 :
6494 0 : memset(&mrd,0,sizeof(mrd));
6495 0 : mrd.fv = fv;
6496 :
6497 0 : memset(&wattrs,0,sizeof(wattrs));
6498 0 : memset(&gcd,0,sizeof(gcd));
6499 0 : memset(&label,0,sizeof(label));
6500 0 : memset(&boxes,0,sizeof(boxes));
6501 :
6502 0 : wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
6503 0 : wattrs.event_masks = ~(1<<et_charup);
6504 0 : wattrs.restrict_input_to_me = false;
6505 0 : wattrs.undercursor = 1;
6506 0 : wattrs.cursor = ct_pointer;
6507 0 : wattrs.utf8_window_title = _("Mass Glyph Rename");
6508 0 : wattrs.is_dlg = false;
6509 0 : pos.x = pos.y = 0;
6510 0 : pos.width = 100;
6511 0 : pos.height = 100;
6512 0 : mrd.gw = gw = GDrawCreateTopWindow(NULL,&pos,mrd_e_h,&mrd,&wattrs);
6513 :
6514 0 : k = i = 0;
6515 :
6516 0 : label[k].text = (unichar_t *) _("Rename all glyphs in the selection");
6517 0 : label[k].text_is_1byte = true;
6518 0 : gcd[k].gd.label = &label[k];
6519 0 : gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6520 0 : gcd[k].gd.flags = gg_visible | gg_enabled;
6521 0 : gcd[k++].creator = GLabelCreate;
6522 0 : hvarray[i][0] = &gcd[k-1]; hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
6523 :
6524 0 : label[k].text = (unichar_t *) _("By appending the suffix:");
6525 0 : label[k].text_is_1byte = true;
6526 0 : gcd[k].gd.label = &label[k];
6527 0 : gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6528 0 : gcd[k].gd.flags = gg_visible | gg_enabled;
6529 0 : gcd[k++].creator = GLabelCreate;
6530 0 : hvarray[i][0] = &gcd[k-1];
6531 :
6532 0 : gcd[k].gd.flags = gg_visible | gg_enabled;
6533 0 : gcd[k].gd.cid = CID_Suffix;
6534 0 : gcd[k].gd.handle_controlevent = MRD_SuffixChange;
6535 0 : gcd[k++].creator = GTextFieldCreate;
6536 0 : hvarray[i][1] = &gcd[k-1]; hvarray[i++][2] = NULL;
6537 :
6538 0 : label[k].text = (unichar_t *) _("To their own names");
6539 0 : label[k].text_is_1byte = true;
6540 0 : gcd[k].gd.label = &label[k];
6541 0 : gcd[k].gd.cid = CID_Themselves;
6542 0 : gcd[k].gd.flags = gg_visible | gg_enabled | gg_cb_on;
6543 0 : gcd[k++].creator = GRadioCreate;
6544 0 : hvarray[i][0] = &gcd[k-1];
6545 0 : hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
6546 :
6547 0 : label[k].text = (unichar_t *) _("To the glyph names starting at:");
6548 0 : label[k].text_is_1byte = true;
6549 0 : gcd[k].gd.label = &label[k];
6550 0 : gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6551 0 : gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup;
6552 0 : gcd[k].gd.popup_msg = (unichar_t *) _("So if you type \"A\" here the first selected glyph would be named \"A.suffix\".\nThe second \"B.suffix\", and so on.");
6553 0 : gcd[k++].creator = GRadioCreate;
6554 0 : hvarray[i][0] = &gcd[k-1];
6555 :
6556 0 : startnamek = k;
6557 0 : gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup;
6558 0 : gcd[k].gd.cid = CID_StartName;
6559 0 : gcd[k].gd.popup_msg = (unichar_t *) _("So if you type \"A\" here the first selected glyph would be named \"A.suffix\".\nThe second \"B.suffix\", and so on.");
6560 0 : gcd[k++].creator = GTextCompletionCreate;
6561 0 : hvarray[i][1] = &gcd[k-1]; hvarray[i++][2] = NULL;
6562 :
6563 0 : label[k].text = (unichar_t *) _("If one of those glyphs already has a suffix");
6564 0 : label[k].text_is_1byte = true;
6565 0 : gcd[k].gd.label = &label[k];
6566 0 : gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6567 0 : gcd[k].gd.flags = gg_visible | gg_enabled;
6568 0 : gcd[k++].creator = GLabelCreate;
6569 0 : hvarray[i][0] = &gcd[k-1]; hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
6570 :
6571 0 : label[k].text = (unichar_t *) _("Append to it");
6572 0 : label[k].text_is_1byte = true;
6573 0 : gcd[k].gd.label = &label[k];
6574 0 : gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6575 0 : gcd[k].gd.flags = gg_visible | gg_enabled;
6576 0 : gcd[k++].creator = GRadioCreate;
6577 0 : hvarray[i][0] = &gcd[k-1];
6578 :
6579 0 : label[k].text = (unichar_t *) _("Replace it");
6580 0 : label[k].text_is_1byte = true;
6581 0 : gcd[k].gd.label = &label[k];
6582 0 : gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6583 0 : gcd[k].gd.flags = gg_visible | gg_enabled | gg_cb_on;
6584 0 : gcd[k].gd.cid = CID_ReplaceSuffix;
6585 0 : gcd[k++].creator = GRadioCreate;
6586 0 : hvarray[i][1] = &gcd[k-1]; hvarray[i++][2] = NULL;
6587 :
6588 0 : label[k].text = (unichar_t *) _("Optionally, add this mapping to the lookup subtable:");
6589 0 : label[k].text_is_1byte = true;
6590 0 : gcd[k].gd.label = &label[k];
6591 0 : gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
6592 0 : gcd[k].gd.flags = gg_visible | gg_enabled;
6593 0 : gcd[k++].creator = GLabelCreate;
6594 0 : hvarray[i][0] = &gcd[k-1]; hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
6595 :
6596 0 : subtablek = k;
6597 0 : gcd[k].gd.flags = gg_enabled|gg_visible;
6598 0 : gcd[k].gd.cid = CID_SubTable;
6599 0 : gcd[k].gd.handle_controlevent = MRD_Subtable;
6600 0 : gcd[k++].creator = GListButtonCreate;
6601 0 : hvarray[i][0] = GCD_Glue; hvarray[i][1] = &gcd[k-1]; hvarray[i++][2] = NULL;
6602 0 : hvarray[i][0] = hvarray[i][1] = GCD_Glue; hvarray[i++][2] = NULL;
6603 :
6604 0 : label[k].text = (unichar_t *) _("_OK");
6605 0 : label[k].text_is_1byte = true;
6606 0 : label[k].text_in_resource = true;
6607 0 : gcd[k].gd.label = &label[k];
6608 0 : gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_default;
6609 0 : gcd[k].gd.handle_controlevent = MRD_OK;
6610 0 : gcd[k++].creator = GButtonCreate;
6611 :
6612 0 : label[k].text = (unichar_t *) _("_Cancel");
6613 0 : label[k].text_is_1byte = true;
6614 0 : label[k].text_in_resource = true;
6615 0 : gcd[k].gd.label = &label[k];
6616 0 : gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_cancel;
6617 0 : gcd[k].gd.handle_controlevent = MRD_Cancel;
6618 0 : gcd[k++].creator = GButtonCreate;
6619 :
6620 0 : barray[0] = barray[2] = barray[3] = barray[4] = barray[6] = GCD_Glue; barray[7] = NULL;
6621 0 : barray[1] = &gcd[k-2]; barray[5] = &gcd[k-1];
6622 0 : hvarray[i][0] = &boxes[2]; hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
6623 0 : hvarray[i][0] = NULL;
6624 :
6625 0 : boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
6626 0 : boxes[0].gd.flags = gg_enabled|gg_visible;
6627 0 : boxes[0].gd.u.boxelements = hvarray[0];
6628 0 : boxes[0].creator = GHVGroupCreate;
6629 :
6630 0 : boxes[2].gd.flags = gg_enabled|gg_visible;
6631 0 : boxes[2].gd.u.boxelements = barray;
6632 0 : boxes[2].creator = GHBoxCreate;
6633 :
6634 0 : GGadgetsCreate(gw,boxes);
6635 0 : GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
6636 0 : GHVBoxSetExpandableRow(boxes[0].ret,gb_expandglue);
6637 0 : GHVBoxSetExpandableCol(boxes[0].ret,1);
6638 :
6639 0 : GGadgetSetList(gcd[subtablek].ret,SFSubtablesOfType(fv->b.sf,gsub_single,false,true),false);
6640 0 : GGadgetSelectOneListItem(gcd[subtablek].ret,0);
6641 0 : GCompletionFieldSetCompletion(gcd[startnamek].ret,MRD_GlyphNameCompletion);
6642 0 : GWidgetIndicateFocusGadget(GWidgetGetControl(gw,CID_Suffix));
6643 :
6644 0 : GHVBoxFitWindow(boxes[0].ret);
6645 :
6646 0 : GDrawSetVisible(gw,true);
6647 0 : while ( !mrd.done )
6648 0 : GDrawProcessOneEvent(NULL);
6649 :
6650 0 : GDrawDestroyWindow(gw);
6651 0 : }
6652 :
6653 : /* ************************************************************************** */
6654 : /* ******************** Interface to FontInfo Lookup list ******************* */
6655 : /* ************************************************************************** */
6656 :
6657 0 : static void FI_SortInsertLookup(SplineFont *sf, OTLookup *newotl) {
6658 0 : int isgpos = newotl->lookup_type>=gpos_start;
6659 : int pos, i, k;
6660 :
6661 0 : if ( sf->fontinfo ) {
6662 0 : struct lkdata *lk = &sf->fontinfo->tables[isgpos];
6663 0 : pos = FeatureOrderId(isgpos,newotl->features);
6664 0 : if ( lk->cnt>=lk->max )
6665 0 : lk->all = realloc(lk->all,(lk->max+=10)*sizeof(struct lkinfo));
6666 0 : for ( i=0; i<lk->cnt && FeatureOrderId(isgpos,lk->all[i].lookup->features)<pos; ++i );
6667 0 : for ( k=lk->cnt; k>i+1; --k )
6668 0 : lk->all[k] = lk->all[k-1];
6669 0 : memset(&lk->all[k],0,sizeof(struct lkinfo));
6670 0 : lk->all[k].lookup = newotl;
6671 0 : ++lk->cnt;
6672 0 : GFI_LookupScrollbars(sf->fontinfo,isgpos, true);
6673 0 : GFI_LookupEnableButtons(sf->fontinfo,isgpos);
6674 : }
6675 0 : }
6676 :
6677 : /* Before may be:
6678 : * A lookup in into_sf, in which case insert new lookup before it
6679 : * NULL , in which case insert new lookup at end
6680 : * -1 , in which case insert new lookup at start
6681 : * -2 , try to guess a good position
6682 : */
6683 0 : static void FI_OrderNewLookup(SplineFont *into_sf,OTLookup *otl,OTLookup *before) {
6684 0 : int isgpos = otl->lookup_type>=gpos_start;
6685 0 : OTLookup **head = isgpos ? &into_sf->gpos_lookups : &into_sf->gsub_lookups;
6686 : int i, k;
6687 :
6688 0 : if ( into_sf->fontinfo ) {
6689 0 : struct lkdata *lk = &into_sf->fontinfo->tables[isgpos];
6690 :
6691 0 : if ( lk->cnt>=lk->max )
6692 0 : lk->all = realloc(lk->all,(lk->max+=10)*sizeof(struct lkinfo));
6693 :
6694 0 : if ( before == (OTLookup *) -2 )
6695 0 : FI_SortInsertLookup(into_sf,otl);
6696 : else {
6697 0 : if ( before == (OTLookup *) -1 || *head==NULL || *head==before ) {
6698 0 : i = 0;
6699 : } else {
6700 0 : for ( i=0; i<lk->cnt && lk->all[i].lookup!=before; ++i );
6701 : }
6702 0 : for ( k=lk->cnt; k>i; --k )
6703 0 : lk->all[k] = lk->all[k-1];
6704 0 : memset(&lk->all[i],0,sizeof(lk->all[i]));
6705 0 : lk->all[i].lookup = otl;
6706 0 : ++lk->cnt;
6707 0 : GFI_LookupScrollbars(into_sf->fontinfo,isgpos, true);
6708 0 : GFI_LookupEnableButtons(into_sf->fontinfo,isgpos);
6709 : }
6710 : }
6711 0 : }
6712 :
6713 0 : static void FI_OTLookupCopyInto(SplineFont *into_sf,SplineFont *from_sf,
6714 : OTLookup *from_otl, OTLookup *to_otl, int scnt, OTLookup *before) {
6715 0 : if ( into_sf->fontinfo ) {
6716 0 : int isgpos = from_otl->lookup_type>=gpos_start;
6717 0 : struct lkdata *lk = &into_sf->fontinfo->tables[isgpos];
6718 : struct lookup_subtable *sub;
6719 : int i;
6720 0 : for ( i=0; i<lk->cnt; ++i )
6721 0 : if ( lk->all[i].lookup==to_otl )
6722 0 : break;
6723 0 : if ( i==lk->cnt ) {
6724 0 : FI_OrderNewLookup(into_sf,to_otl,before);
6725 0 : for ( i=0; i<lk->cnt; ++i )
6726 0 : if ( lk->all[i].lookup==to_otl )
6727 0 : break;
6728 : }
6729 0 : lk->all[i].subtable_cnt = lk->all[i].subtable_max = scnt;
6730 0 : lk->all[i].subtables = calloc(scnt,sizeof(struct lksubinfo));
6731 0 : if ( scnt>0 )
6732 0 : for ( sub=to_otl->subtables, scnt=0; sub!=NULL; sub=sub->next, ++scnt )
6733 0 : lk->all[i].subtables[scnt].subtable = sub;
6734 : }
6735 0 : }
6736 :
6737 : struct fi_interface gdraw_fi_interface = {
6738 : FI_SortInsertLookup,
6739 : FI_OTLookupCopyInto,
6740 : FontInfoDestroy
6741 : };
|