LCOV - code coverage report
Current view: top level - fontforgeexe - lookupui.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 13 4060 0.3 %
Date: 2017-08-04 Functions: 1 137 0.7 %

          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 = &acd;
    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             : };

Generated by: LCOV version 1.10