LCOV - code coverage report
Current view: top level - fontforgeexe - showatt.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 0 2303 0.0 %
Date: 2017-08-04 Functions: 0 75 0.0 %

          Line data    Source code
       1             : /* -*- coding: utf-8 -*- */
       2             : /* Copyright (C) 2003-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 "fontforgeui.h"
      29             : #include "fvfonts.h"
      30             : #include "glyphcomp.h"
      31             : #include "lookups.h"
      32             : #include "splinefill.h"
      33             : #include "splinesaveafm.h"
      34             : #include "splineutil.h"
      35             : #include "tottfaat.h"
      36             : #include "tottfgpos.h"
      37             : #include <utype.h>
      38             : #include <ustring.h>
      39             : #include <gkeysym.h>
      40             : 
      41             : #include "ttf.h"
      42             : 
      43             : extern int _GScrollBar_Width;
      44             : 
      45             : /* This file contains routines to build a dialog showing GPOS/GSUB/morx */
      46             : /*  tables and their contents */
      47             : 
      48             : struct att_dlg;
      49             : struct node {
      50             :     unsigned int open: 1;
      51             :     unsigned int children_checked: 1;
      52             :     unsigned int used: 1;
      53             :     unsigned int macfeat: 1;
      54             :     unsigned int monospace: 1;
      55             :     unsigned int horizontal: 1;
      56             :     uint16 cnt;
      57             :     struct node *children, *parent;
      58             :     void (*build)(struct node *,struct att_dlg *);
      59             :     char *label;                /* utf8 */
      60             :     uint32 tag;
      61             :     union sak {
      62             :         SplineChar *sc;
      63             :         int index;
      64             :         OTLookup *otl;
      65             :         struct lookup_subtable *sub;
      66             :         struct baselangextent *langs;
      67             :         Justify *jscript;
      68             :         struct jstf_lang *jlang;
      69             :         OTLookup **otll;
      70             :     } u;
      71             :     int lpos;
      72             : };
      73             : 
      74             : enum dlg_type { dt_show_att, dt_font_comp };
      75             : 
      76             : struct att_dlg {
      77             :     unsigned int done: 1;
      78             :     struct node *tables;
      79             :     int open_cnt, lines_page, off_top, off_left, page_width, bmargin;
      80             :     int maxl;
      81             :     SplineFont *sf;
      82             :     int def_layer;
      83             :     GWindow gw,v;
      84             :     GGadget *vsb, *hsb, *cancel;
      85             :     int fh, as;
      86             :     GFont *font, *monofont;
      87             :     struct node *current;
      88             :     enum dlg_type dlg_type;
      89             :     FontView *fv1, *fv2;
      90             :     struct node *popup_node;
      91             : };
      92             : 
      93             : static void BuildGSUBlookups(struct node *node,struct att_dlg *att);
      94             : 
      95           0 : static void nodesfree(struct node *node) {
      96             :     int i;
      97             : 
      98           0 :     if ( node==NULL )
      99           0 : return;
     100           0 :     for ( i=0; node[i].label!=NULL; ++i ) {
     101           0 :         nodesfree(node[i].children);
     102           0 :         free(node[i].label);
     103             :     }
     104           0 :     free(node);
     105             : }
     106             : 
     107           0 : static int node_alphabetize(const void *_n1, const void *_n2) {
     108           0 :     const struct node *n1 = _n1, *n2 = _n2;
     109             :     int ret;
     110             : 
     111           0 :     ret = strcasecmp(n1->label,n2->label);
     112           0 :     if ( ret!=0 )
     113           0 : return( ret );
     114             : 
     115           0 : return( strcmp(n1->label,n2->label));
     116             : }
     117             : 
     118           0 : static void BuildMarkedLigatures(struct node *node,struct att_dlg *att) {
     119           0 :     SplineChar *sc = node->u.sc;
     120           0 :     struct lookup_subtable *sub = node->parent->parent->u.sub;
     121             :     AnchorClass *ac;
     122             :     int classcnt, j, max, k;
     123             :     AnchorPoint *ap;
     124             :     char buf[90];
     125           0 :     SplineFont *sf = att->sf;
     126             : 
     127           0 :     if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
     128             : 
     129           0 :     classcnt = 0;
     130           0 :     for ( ap=sc->anchor; ap!=NULL; ap=ap->next )
     131           0 :         if ( ap->anchor->subtable == sub )
     132           0 :             ++classcnt;
     133           0 :     max=0;
     134           0 :     for ( ap=sc->anchor; ap!=NULL ; ap=ap->next )
     135           0 :         if ( ap->lig_index>max )
     136           0 :             max = ap->lig_index;
     137           0 :     node->children = calloc(classcnt+1,sizeof(struct node));
     138           0 :     for ( k=j=0; k<=max; ++k ) {
     139           0 :         for ( ac=sf->anchor; ac!=NULL; ac=ac->next ) if ( ac->subtable==sub ) {
     140           0 :             for ( ap=sc->anchor; ap!=NULL && (ap->type!=at_baselig || ap->anchor!=ac || ap->lig_index!=k); ap=ap->next );
     141           0 :             if ( ap!=NULL ) {
     142           0 :                 sprintf(buf,_("Component %d %.30s (%d,%d)"),
     143           0 :                         k, ac->name, (int) ap->me.x, (int) ap->me.y );
     144           0 :                 node->children[j].label = copy(buf);
     145           0 :                 node->children[j++].parent = node;
     146             :             }
     147             :         }
     148             :     }
     149           0 :     node->cnt = j;
     150           0 : }
     151             : 
     152           0 : static void BuildMarkedChars(struct node *node,struct att_dlg *att) {
     153           0 :     SplineChar *sc = node->u.sc;
     154           0 :     struct lookup_subtable *sub = node->parent->parent->u.sub;
     155             :     AnchorClass *ac;
     156             :     int classcnt, j;
     157             :     AnchorPoint *ap;
     158             :     char buf[80];
     159           0 :     SplineFont *sf = att->sf;
     160             : 
     161           0 :     if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
     162             : 
     163           0 :     classcnt = 0;
     164           0 :     for ( ap=sc->anchor; ap!=NULL; ap=ap->next )
     165           0 :         if ( ap->anchor->subtable == sub )
     166           0 :             ++classcnt;
     167           0 :     node->children = calloc(classcnt+1,sizeof(struct node));
     168           0 :     for ( j=0, ac=sf->anchor; ac!=NULL; ac=ac->next ) if ( ac->subtable==sub ) {
     169           0 :         for ( ap=sc->anchor; ap!=NULL && (!(ap->type==at_basechar || ap->type==at_basemark) || ap->anchor!=ac); ap=ap->next );
     170           0 :         if ( ap!=NULL ) {
     171           0 :             sprintf(buf,"%.30s (%d,%d)", ac->name,
     172           0 :                     (int) ap->me.x, (int) ap->me.y );
     173           0 :             node->children[j].label = copy(buf);
     174           0 :             node->children[j++].parent = node;
     175             :         }
     176             :     }
     177           0 :     qsort(node->children,j,sizeof(struct node), node_alphabetize);
     178           0 :     node->cnt = j;
     179           0 : }
     180             : 
     181           0 : static void BuildBase(struct node *node,SplineChar **bases,enum anchor_type at, struct node *parent) {
     182             :     int i;
     183             : 
     184           0 :     node->parent = parent;
     185           0 :     node->label = copy(at==at_basechar?_("Base Glyphs"):
     186             :                         at==at_baselig?_("Base Ligatures"):
     187             :                         _("Base Marks"));
     188           0 :     for ( i=0; bases[i]!=NULL; ++i );
     189           0 :     if ( i==0 ) {
     190           0 :         node->cnt = 1;
     191           0 :         node->children = calloc(2,sizeof(struct node));
     192           0 :         node->children[0].label = copy(_("Empty"));
     193           0 :         node->children[0].parent = node;
     194             :     } else {
     195           0 :         node->cnt = i;
     196           0 :         node->children = calloc(i+1,sizeof(struct node));
     197           0 :         for ( i=0; bases[i]!=NULL; ++i ) {
     198           0 :             node->children[i].label = copy(bases[i]->name);
     199           0 :             node->children[i].parent = node;
     200           0 :             node->children[i].u.sc = bases[i];
     201           0 :             node->children[i].build = at==at_baselig?BuildMarkedLigatures:BuildMarkedChars;
     202             :         }
     203           0 :         qsort(node->children,node->cnt,sizeof(struct node), node_alphabetize);
     204             :     }
     205           0 : }
     206             : 
     207           0 : static void BuildMark(struct node *node,SplineChar **marks,AnchorClass *ac, struct node *parent) {
     208             :     int i;
     209             :     char buf[80];
     210             :     AnchorPoint *ap;
     211             : 
     212           0 :     node->parent = parent;
     213           0 :     sprintf(buf,_("Mark Class %.20s"),ac->name);
     214           0 :     node->label = copy(buf);
     215           0 :     for ( i=0; marks[i]!=NULL; ++i );
     216           0 :     if ( i==0 ) {
     217           0 :         node->cnt = 1;
     218           0 :         node->children = calloc(2,sizeof(struct node));
     219           0 :         node->children[0].label = copy(_("Empty"));
     220           0 :         node->children[0].parent = node;
     221             :     } else {
     222           0 :         node->cnt = i;
     223           0 :         node->children = calloc(i+1,sizeof(struct node));
     224           0 :         for ( i=0; marks[i]!=NULL; ++i ) {
     225           0 :             for ( ap=marks[i]->anchor; ap!=NULL && (ap->type!=at_mark || ap->anchor!=ac); ap=ap->next );
     226           0 :             sprintf(buf,_("%.30s (%d,%d)"), marks[i]->name,
     227           0 :                     (int) ap->me.x, (int) ap->me.y );
     228           0 :             node->children[i].label = copy(buf);
     229           0 :             node->children[i].parent = node;
     230             :         }
     231           0 :         qsort(node->children,node->cnt,sizeof(struct node), node_alphabetize);
     232             :     }
     233           0 : }
     234             : 
     235           0 : static void BuildAnchorLists(struct node *node,struct att_dlg *att) {
     236           0 :     struct lookup_subtable *sub = node->u.sub;
     237             :     AnchorClass *ac, *ac2;
     238             :     int cnt, i, j, classcnt;
     239             :     AnchorPoint *ap, *ent, *ext;
     240             :     SplineChar **base, **lig, **mkmk, **entryexit;
     241             :     SplineChar ***marks;
     242             :     int *subcnts;
     243           0 :     SplineFont *sf = att->sf;
     244             :     char buf[80];
     245             : 
     246           0 :     if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
     247             : 
     248           0 :     if ( sub->lookup->lookup_type==gpos_cursive ) {
     249           0 :         for ( ac = sf->anchor; ac!=NULL && ac->subtable!=sub; ac = ac->next );
     250           0 :         entryexit = ac==NULL ? NULL : EntryExitDecompose(sf,ac,NULL);
     251           0 :         if ( entryexit==NULL ) {
     252           0 :             node->children = calloc(2,sizeof(struct node));
     253           0 :             node->cnt = 1;
     254           0 :             node->children[0].label = copy(_("Empty"));
     255           0 :             node->children[0].parent = node;
     256             :         } else {
     257           0 :             for ( cnt=0; entryexit[cnt]!=NULL; ++cnt );
     258           0 :             node->children = calloc(cnt+1,sizeof(struct node));
     259           0 :             node->cnt = cnt;
     260           0 :             for ( cnt=0; entryexit[cnt]!=NULL; ++cnt ) {
     261           0 :                 node->children[cnt].u.sc = entryexit[cnt];
     262           0 :                 node->children[cnt].label = copy(entryexit[cnt]->name);
     263           0 :                 node->children[cnt].parent = node;
     264             :             }
     265           0 :             qsort(node->children,node->cnt,sizeof(struct node), node_alphabetize);
     266           0 :             for ( cnt=0; entryexit[cnt]!=NULL; ++cnt ) {
     267           0 :                 for ( ent=ext=NULL, ap = node->children[cnt].u.sc->anchor; ap!=NULL; ap=ap->next ) {
     268           0 :                     if ( ap->anchor==ac ) {
     269           0 :                         if ( ap->type == at_centry )
     270           0 :                             ent = ap;
     271           0 :                         else if ( ap->type == at_cexit )
     272           0 :                             ent = ap;
     273             :                     }
     274             :                 }
     275           0 :                 node->children[cnt].cnt = (ent!=NULL)+(ext!=NULL);
     276           0 :                 node->children[cnt].children = calloc((1+(ent!=NULL)+(ext!=NULL)),sizeof(struct node));
     277           0 :                 i = 0;
     278           0 :                 if ( ent!=NULL ) {
     279           0 :                     snprintf(buf,sizeof(buf), _("Entry (%d,%d)"),
     280           0 :                             (int) ent->me.x, (int) ent->me.y);
     281           0 :                     node->children[cnt].children[i].label = copy(buf);
     282           0 :                     node->children[cnt].children[i].parent = &node->children[cnt];
     283           0 :                     ++i;
     284             :                 }
     285           0 :                 if ( ext!=NULL ) {
     286           0 :                     snprintf(buf,sizeof(buf), _("Exit (%d,%d)"),
     287           0 :                             (int) ext->me.x, (int) ext->me.y);
     288           0 :                     node->children[cnt].children[i].label = copy(buf);
     289           0 :                     node->children[cnt].children[i].parent = &node->children[cnt];
     290           0 :                     ++i;
     291             :                 }
     292             :             }
     293             :         }
     294           0 :         free(entryexit);
     295             :     } else {
     296           0 :         classcnt = 0;
     297           0 :         ac = NULL;
     298           0 :         for ( ac2=sf->anchor; ac2!=NULL; ac2=ac2->next ) {
     299           0 :             if ( ac2->subtable == sub ) {
     300           0 :                 if ( ac==NULL ) ac=ac2;
     301           0 :                 ac2->matches = true;
     302           0 :                 ++classcnt;
     303             :             } else
     304           0 :                 ac2->matches = false;
     305             :         }
     306           0 :         if ( classcnt==0 )
     307           0 : return;
     308             : 
     309           0 :         marks = malloc(classcnt*sizeof(SplineChar **));
     310           0 :         subcnts = malloc(classcnt*sizeof(int));
     311           0 :         AnchorClassDecompose(sf,ac,classcnt,subcnts,marks,&base,&lig,&mkmk,NULL);
     312           0 :         node->cnt = classcnt+(base!=NULL)+(lig!=NULL)+(mkmk!=NULL);
     313           0 :         node->children = calloc(node->cnt+1,sizeof(struct node));
     314           0 :         i=0;
     315           0 :         if ( base!=NULL )
     316           0 :             BuildBase(&node->children[i++],base,at_basechar,node);
     317           0 :         if ( lig!=NULL )
     318           0 :             BuildBase(&node->children[i++],lig,at_baselig,node);
     319           0 :         if ( mkmk!=NULL )
     320           0 :             BuildBase(&node->children[i++],mkmk,at_basemark,node);
     321           0 :         for ( j=0, ac2=ac; j<classcnt; ac2=ac2->next ) if ( ac2->matches ) {
     322           0 :             if ( marks[j]!=NULL )
     323           0 :                 BuildMark(&node->children[i++],marks[j],ac2,node);
     324           0 :             ++j;
     325             :         }
     326           0 :         node->cnt = i;
     327           0 :         for ( i=0; i<classcnt; ++i )
     328           0 :             free(marks[i]);
     329           0 :         free(marks);
     330           0 :         free(subcnts);
     331           0 :         free(base);
     332           0 :         free(lig);
     333           0 :         free(mkmk);
     334             :     }
     335             : }
     336             : 
     337           0 : static void BuildKC2(struct node *node,struct att_dlg *att) {
     338           0 :     KernClass *kc = node->parent->u.sub->kc;
     339             :     struct node *seconds;
     340           0 :     int index=node->u.index,i,cnt, len;
     341             :     char buf[32];
     342             :     char *name;
     343             : 
     344           0 :     for ( i=1,cnt=0; i<kc->second_cnt; ++i )
     345           0 :         if ( kc->offsets[index*kc->second_cnt+i]!=0 && strlen(kc->seconds[i])!=0 )
     346           0 :             ++cnt;
     347             : 
     348           0 :     node->children = seconds = calloc(cnt+1,sizeof(struct node));
     349           0 :     node->cnt = cnt;
     350           0 :     cnt = 0;
     351           0 :     for ( i=1; i<kc->second_cnt; ++i ) if ( kc->offsets[index*kc->second_cnt+i]!=0 && strlen(kc->seconds[i])!=0 ) {
     352           0 :         sprintf( buf, "%d ", kc->offsets[index*kc->second_cnt+i]);
     353           0 :         len = strlen(buf)+strlen(kc->seconds[i])+1;
     354           0 :         name = malloc(len);
     355           0 :         strcpy(name,buf);
     356           0 :         strcat(name,kc->seconds[i]);
     357           0 :         seconds[cnt].label = name;
     358           0 :         seconds[cnt].parent = node;
     359           0 :         seconds[cnt].build = NULL;
     360           0 :         seconds[cnt++].u.index = i;
     361             :     }
     362           0 : }
     363             : 
     364           0 : static void BuildKC(struct node *node,struct att_dlg *att) {
     365           0 :     KernClass *kc = node->u.sub->kc;
     366             :     struct node *firsts;
     367             :     int i,j,cnt,cnt2;
     368             : 
     369           0 :     for ( i=1,cnt=0; i<kc->first_cnt; ++i ) {
     370           0 :         for ( j=1,cnt2=0 ; j<kc->second_cnt; ++j ) {
     371           0 :             if ( kc->offsets[i*kc->second_cnt+j]!=0 )
     372           0 :                 ++cnt2;
     373             :         }
     374           0 :         if ( cnt2 && strlen(kc->firsts[i])>0 )
     375           0 :             ++cnt;
     376             :     }
     377             : 
     378           0 :     node->children = firsts = calloc(cnt+1,sizeof(struct node));
     379           0 :     node->cnt = cnt;
     380           0 :     for ( i=1,cnt=0; i<kc->first_cnt; ++i ) {
     381           0 :         for ( j=1,cnt2=0 ; j<kc->second_cnt; ++j ) {
     382           0 :             if ( kc->offsets[i*kc->second_cnt+j]!=0 )
     383           0 :                 ++cnt2;
     384             :         }
     385           0 :         if ( cnt2==0 || strlen(kc->firsts[i])==0 )
     386           0 :     continue;
     387           0 :         firsts[cnt].label = copy(kc->firsts[i]);
     388           0 :         firsts[cnt].parent = node;
     389           0 :         firsts[cnt].build = BuildKC2;
     390           0 :         firsts[cnt++].u.index = i;
     391             :     }
     392           0 : }
     393             : 
     394           0 : static int PSTAllComponentsExist(SplineFont *sf,char *glyphnames ) {
     395             :     char *start, *end, ch;
     396             :     int ret;
     397             : 
     398           0 :     if ( glyphnames==NULL )
     399           0 : return( false );
     400           0 :     for ( start=glyphnames; *start; start = end ) {
     401           0 :         while ( *start==' ' ) ++start;
     402           0 :         for ( end = start; *end!=' ' && *end!='\0'; ++end );
     403           0 :         if ( end==start )
     404           0 :     break;
     405           0 :         ch = *end; *end = '\0';
     406           0 :         ret = SCWorthOutputting(SFGetChar(sf,-1,start));
     407           0 :         *end = ch;
     408           0 :         if ( !ret )
     409           0 : return( false );
     410             :     }
     411           0 : return( true );
     412             : }
     413             : 
     414           0 : static void BuildFPSTRule(struct node *node,struct att_dlg *att) {
     415           0 :     FPST *fpst = node->parent->u.sub->fpst;
     416           0 :     int index = node->u.index;
     417           0 :     struct fpst_rule *r = &fpst->rules[index];
     418             :     int len, i, j;
     419             :     struct node *lines;
     420             :     char buf[200], *pt, *start, *spt;
     421             :     char *upt;
     422             :     GrowBuf gb;
     423             : 
     424           0 :     memset(&gb,0,sizeof(gb));
     425             : 
     426           0 :     for ( i=0; i<2; ++i ) {
     427           0 :         len = 0;
     428             : 
     429           0 :         switch ( fpst->format ) {
     430             :           case pst_glyphs:
     431           0 :             if ( r->u.glyph.back!=NULL && *r->u.glyph.back!='\0' ) {
     432           0 :                 if ( i ) {
     433           0 :                     strcpy(buf, _("Backtrack Match: ") );
     434           0 :                     lines[len].label = malloc((strlen(buf)+strlen(r->u.glyph.back)+1));
     435           0 :                     strcpy(lines[len].label,buf);
     436           0 :                     upt = lines[len].label+strlen(lines[len].label);
     437           0 :                     for ( pt=r->u.glyph.back+strlen(r->u.glyph.back); pt>r->u.glyph.back; pt=start ) {
     438           0 :                         for ( start = pt-1; start>=r->u.glyph.back&& *start!=' '; --start );
     439           0 :                         for ( spt=start+1; spt<pt; )
     440           0 :                             *upt++ = *spt++;
     441           0 :                         *upt++ = ' ';
     442             :                     }
     443           0 :                     upt[-1] = '\0';
     444           0 :                     lines[len].parent = node;
     445             :                 }
     446           0 :                 ++len;
     447             :             }
     448           0 :             if ( i ) {
     449           0 :                 strcpy(buf, _("Match: ") );
     450           0 :                 lines[len].label = malloc((strlen(buf)+strlen(r->u.glyph.names)+1));
     451           0 :                 strcpy(lines[len].label,buf);
     452           0 :                 strcat(lines[len].label,r->u.glyph.names);
     453           0 :                 lines[len].parent = node;
     454             :             }
     455           0 :             ++len;
     456           0 :             if ( r->u.glyph.fore!=NULL && *r->u.glyph.fore!='\0' ) {
     457           0 :                 if ( i ) {
     458           0 :                     strcpy(buf, _("Lookahead Match: ") );
     459           0 :                     lines[len].label = malloc((strlen(buf)+strlen(r->u.glyph.fore)+1));
     460           0 :                     strcpy(lines[len].label,buf);
     461           0 :                     strcat(lines[len].label,r->u.glyph.fore);
     462           0 :                     lines[len].parent = node;
     463             :                 }
     464           0 :                 ++len;
     465             :             }
     466           0 :           break;
     467             :           case pst_class:
     468           0 :             if ( r->u.class.bcnt!=0 ) {
     469           0 :                 if ( i ) {
     470           0 :                     gb.pt = gb.base;
     471           0 :                     GrowBufferAddStr(&gb,P_("Backtrack class: ","Backtrack classes: ",r->u.class.bcnt));
     472           0 :                     for ( j=r->u.class.bcnt-1; j>=0; --j ) {
     473           0 :                         if ( fpst->bclassnames==NULL || fpst->bclassnames[r->u.class.bclasses[j]]==NULL ) {
     474           0 :                             sprintf( buf, "%d ", r->u.class.bclasses[j] );
     475           0 :                             GrowBufferAddStr(&gb,buf);
     476             :                         } else
     477           0 :                             GrowBufferAddStr(&gb,fpst->bclassnames[r->u.class.bclasses[j]]);
     478             :                     }
     479           0 :                     lines[len].label = copy(gb.base);
     480           0 :                     lines[len].parent = node;
     481             :                 }
     482           0 :                 ++len;
     483             :             }
     484           0 :             if ( i ) {
     485           0 :                 gb.pt = gb.base;
     486           0 :                 GrowBufferAddStr(&gb, P_("Class","Classes",r->u.class.ncnt));
     487           0 :                 for ( j=0; j<r->u.class.ncnt; ++j ) {
     488           0 :                     if ( fpst->nclassnames==NULL || fpst->nclassnames[r->u.class.nclasses[j]]==NULL ) {
     489           0 :                         sprintf( buf, "%d ", r->u.class.nclasses[j] );
     490           0 :                         GrowBufferAddStr(&gb,buf);
     491             :                     } else
     492           0 :                         GrowBufferAddStr(&gb,fpst->nclassnames[r->u.class.nclasses[j]]);
     493             :                 }
     494           0 :                 lines[len].label = copy(gb.base);
     495           0 :                 lines[len].parent = node;
     496             :             }
     497           0 :             ++len;
     498           0 :             if ( r->u.class.fcnt!=0 ) {
     499           0 :                 if ( i ) {
     500           0 :                     gb.pt = gb.base;
     501           0 :                     GrowBufferAddStr(&gb, P_("Lookahead Class","Lookahead Classes",r->u.class.fcnt));
     502           0 :                     for ( j=0; j<r->u.class.fcnt; ++j ) {
     503           0 :                         if ( fpst->fclassnames==NULL || fpst->fclassnames[r->u.class.fclasses[j]]==NULL ) {
     504           0 :                             sprintf( buf, "%d ", r->u.class.fclasses[j] );
     505           0 :                             GrowBufferAddStr(&gb,buf);
     506             :                         } else
     507           0 :                             GrowBufferAddStr(&gb,fpst->fclassnames[r->u.class.fclasses[j]]);
     508             :                     }
     509           0 :                     lines[len].label = copy(gb.base);
     510           0 :                     lines[len].parent = node;
     511             :                 }
     512           0 :                 ++len;
     513             :             }
     514           0 :           break;
     515             :           case pst_coverage:
     516             :           case pst_reversecoverage:
     517           0 :             for ( j=r->u.coverage.bcnt-1; j>=0; --j ) {
     518           0 :                 if ( i ) {
     519           0 :                     sprintf(buf, _("Back coverage %d: "), -j-1);
     520           0 :                     lines[len].label = malloc((strlen(buf)+strlen(r->u.coverage.bcovers[j])+1));
     521           0 :                     strcpy(lines[len].label,buf);
     522           0 :                     strcat(lines[len].label,r->u.coverage.bcovers[j]);
     523           0 :                     lines[len].parent = node;
     524             :                 }
     525           0 :                 ++len;
     526             :             }
     527           0 :             for ( j=0; j<r->u.coverage.ncnt; ++j ) {
     528           0 :                 if ( i ) {
     529           0 :                     sprintf(buf, _("Coverage %d: "), j);
     530           0 :                     lines[len].label = malloc((strlen(buf)+strlen(r->u.coverage.ncovers[j])+1));
     531           0 :                     strcpy(lines[len].label,buf);
     532           0 :                     strcat(lines[len].label,r->u.coverage.ncovers[j]);
     533           0 :                     lines[len].parent = node;
     534             :                 }
     535           0 :                 ++len;
     536             :             }
     537           0 :             for ( j=0; j<r->u.coverage.fcnt; ++j ) {
     538           0 :                 if ( i ) {
     539           0 :                     sprintf(buf, _("Lookahead coverage %d: "), j+r->u.coverage.ncnt);
     540           0 :                     lines[len].label = malloc((strlen(buf)+strlen(r->u.coverage.fcovers[j])+1));
     541           0 :                     strcpy(lines[len].label,buf);
     542           0 :                     strcat(lines[len].label,r->u.coverage.fcovers[j]);
     543           0 :                     lines[len].parent = node;
     544             :                 }
     545           0 :                 ++len;
     546             :             }
     547           0 :           break;
     548             :         }
     549           0 :         switch ( fpst->format ) {
     550             :           case pst_glyphs:
     551             :           case pst_class:
     552             :           case pst_coverage:
     553           0 :             for ( j=0; j<r->lookup_cnt; ++j ) {
     554           0 :                 if ( i ) {
     555           0 :                     sprintf(buf, _("Apply at %d %.80s"), r->lookups[j].seq,
     556           0 :                             r->lookups[j].lookup->lookup_name );
     557           0 :                     lines[len].label = copy(buf);
     558           0 :                     lines[len].parent = node;
     559           0 :                     lines[len].u.otl = r->lookups[j].lookup;
     560           0 :                     lines[len].build = BuildGSUBlookups;
     561             :                 }
     562           0 :                 ++len;
     563             :             }
     564           0 :           break;
     565             :           case pst_reversecoverage:
     566           0 :             if ( i ) {
     567           0 :                 strcpy(buf, _("Replacement: ") );
     568           0 :                 lines[len].label = malloc((strlen(buf)+strlen(r->u.rcoverage.replacements)+1));
     569           0 :                 strcpy(lines[len].label,buf);
     570           0 :                 strcat(lines[len].label,r->u.rcoverage.replacements);
     571           0 :                 lines[len].parent = node;
     572             :             }
     573           0 :             ++len;
     574           0 :           break;
     575             :         }
     576           0 :         if ( i==0 ) {
     577           0 :             node->children = lines = calloc(len+1,sizeof(struct node));
     578           0 :             node->cnt = len;
     579             :         }
     580             :     }
     581           0 :     free(gb.base);
     582           0 : }
     583             : 
     584           0 : static void BuildFPST(struct node *node,struct att_dlg *att) {
     585           0 :     FPST *fpst = node->u.sub->fpst;
     586             :     int len, i, j;
     587             :     struct node *lines;
     588             :     char buf[200];
     589             :     static char *type[] = { N_("Contextual Positioning"), N_("Contextual Substitution"),
     590             :             N_("Chaining Positioning"), N_("Chaining Substitution"),
     591             :             N_("Reverse Chaining Subs") };
     592             :     static char *format[] = { N_("glyphs"), N_("classes"), N_("coverage"), N_("coverage") };
     593             : 
     594           0 :     lines = NULL;
     595           0 :     for ( i=0; i<2; ++i ) {
     596           0 :         len = 0;
     597             : 
     598           0 :         if ( i ) {
     599             : /* GT: There are various broad classes of lookups here and the first string */
     600             : /* GT: describes those: "Contextual Positioning", Contextual Substitution", etc. */
     601             : /* GT: Each of those may be formated in 3 different ways: by (or perhaps using */
     602             : /* GT: would be a better word) glyphs, classes or coverage tables. */
     603             : /* GT: So this might look like: */
     604             : /* GT:  Contextual Positioning by classes */
     605           0 :             sprintf(buf, _("%s by %s"), _(type[fpst->type-pst_contextpos]),
     606           0 :                     _(format[fpst->format]));
     607           0 :             lines[len].label = copy(buf);
     608           0 :             lines[len].parent = node;
     609             :         }
     610           0 :         ++len;
     611           0 :         if ( fpst->format==pst_class ) {
     612           0 :             for ( j=1; j<fpst->bccnt ; ++j ) {
     613           0 :                 if ( i ) {
     614           0 :                     sprintf(buf, _("Backtrack class %d: "), j);
     615           0 :                     lines[len].label = malloc((strlen(buf)+strlen(fpst->bclass[j])+1));
     616           0 :                     strcpy(lines[len].label,buf);
     617           0 :                     strcat(lines[len].label,fpst->bclass[j]);
     618           0 :                     lines[len].parent = node;
     619             :                 }
     620           0 :                 ++len;
     621             :             }
     622           0 :             for ( j=1; j<fpst->nccnt ; ++j ) {
     623           0 :                 if ( i ) {
     624           0 :                     sprintf(buf, _("Class %d: "), j);
     625           0 :                     lines[len].label = malloc((strlen(buf)+strlen(fpst->nclass[j])+1));
     626           0 :                     strcpy(lines[len].label,buf);
     627           0 :                     strcat(lines[len].label,fpst->nclass[j]);
     628           0 :                     lines[len].parent = node;
     629             :                 }
     630           0 :                 ++len;
     631             :             }
     632           0 :             for ( j=1; j<fpst->fccnt ; ++j ) {
     633           0 :                 if ( i ) {
     634           0 :                     sprintf(buf, _("Lookahead class %d: "), j);
     635           0 :                     lines[len].label = malloc((strlen(buf)+strlen(fpst->fclass[j])+1));
     636           0 :                     strcpy(lines[len].label,buf);
     637           0 :                     strcat(lines[len].label,fpst->fclass[j]);
     638           0 :                     lines[len].parent = node;
     639             :                 }
     640           0 :                 ++len;
     641             :             }
     642             :         }
     643           0 :         for ( j=0; j<fpst->rule_cnt; ++j ) {
     644           0 :             if ( i ) {
     645           0 :                 sprintf(buf, _("Rule %d"), j);
     646           0 :                 lines[len].label = copy(buf);
     647           0 :                 lines[len].parent = node;
     648           0 :                 lines[len].u.index = j;
     649           0 :                 lines[len].build = BuildFPSTRule;
     650             :             }
     651           0 :             ++len;
     652             :         }
     653           0 :         if ( i==0 ) {
     654           0 :             node->children = lines = calloc(len+1,sizeof(struct node));
     655           0 :             node->cnt = len;
     656             :         }
     657             :     }
     658           0 : }
     659             : 
     660           0 : static void BuildASM(struct node *node,struct att_dlg *att) {
     661           0 :     ASM *sm = node->u.sub->sm;
     662           0 :     int len, i, j, k, scnt = 0;
     663             :     struct node *lines;
     664             :     char buf[200], *space;
     665             :     static char *type[] = { N_("Indic Reordering"), N_("Contextual Substitution"),
     666             :             N_("Ligatures"), N_("<undefined>"), N_("Simple Substitution"),
     667             :             N_("Glyph Insertion"), N_("<undefined>"),  N_("<undefined>"), N_("<undefined>"),
     668             :             N_("<undefined>"), N_("<undefined>"), N_("<undefined>"),N_("<undefined>"),
     669             :             N_("<undefined>"), N_("<undefined>"), N_("<undefined>"), N_("<undefined>"),
     670             :             N_("Kern by State") };
     671             :     OTLookup **used;
     672             : 
     673           0 :     if ( sm->type == asm_context ) {
     674           0 :         used = malloc(sm->class_cnt*sm->state_cnt*2*sizeof(OTLookup *));
     675           0 :         for ( i=scnt=0; i<sm->class_cnt*sm->state_cnt; ++i ) {
     676             :             OTLookup *otl;
     677           0 :             otl = sm->state[i].u.context.mark_lookup;
     678           0 :             if ( otl!=NULL ) {
     679           0 :                 for ( k=0; k<scnt && used[k]!=otl; ++k );
     680           0 :                 if ( k==scnt ) used[scnt++] = otl;
     681             :             }
     682           0 :             otl = sm->state[i].u.context.cur_lookup;
     683           0 :             if ( otl!=NULL ) {
     684           0 :                 for ( k=0; k<scnt && used[k]!=otl; ++k );
     685           0 :                 if ( k==scnt ) used[scnt++] = otl;
     686             :             }
     687             :         }
     688             :     }
     689             : 
     690           0 :     lines = NULL;
     691           0 :     space = malloc( 81*sm->class_cnt+40 );
     692           0 :     for ( i=0; i<2; ++i ) {
     693           0 :         len = 0;
     694             : 
     695           0 :         if ( i ) {
     696           0 :             lines[len].label = copy(_(type[sm->type]));
     697           0 :             lines[len].parent = node;
     698             :         }
     699           0 :         ++len;
     700           0 :         for ( j=4; j<sm->class_cnt ; ++j ) {
     701           0 :             if ( i ) {
     702           0 :                 sprintf(buf, _("Class %d: "), j);
     703           0 :                 lines[len].label = malloc((strlen(buf)+strlen(sm->classes[j])+1));
     704           0 :                 strcpy(lines[len].label,buf);
     705           0 :                 strcat(lines[len].label,sm->classes[j]);
     706           0 :                 lines[len].parent = node;
     707             :             }
     708           0 :             ++len;
     709             :         }
     710           0 :         for ( j=0; j<sm->state_cnt; ++j ) {
     711           0 :             if ( i ) {
     712             : /* GT: You're in a state machine, and this is describing the %4d'th state of */
     713             : /* GT: that machine. From the state the next state will be a list of */
     714             : /* GT: state-numbers which are appended to this string. */
     715           0 :                 sprintf(space, _("State %4d Next: "), j );
     716           0 :                 for ( k=0; k<sm->class_cnt; ++k )
     717           0 :                     sprintf( space+strlen(space), "%5d", sm->state[j*sm->class_cnt+k].next_state );
     718           0 :                 lines[len].label = copy(space);
     719           0 :                 lines[len].parent = node;
     720           0 :                 lines[len].monospace = true;
     721             :             }
     722           0 :             ++len;
     723           0 :             if ( i ) {
     724           0 :                 sprintf(space, _("State %4d Flags:"), j );
     725           0 :                 for ( k=0; k<sm->class_cnt; ++k )
     726           0 :                     sprintf( space+strlen(space), " %04x", sm->state[j*sm->class_cnt+k].flags );
     727           0 :                 lines[len].label = copy(space);
     728           0 :                 lines[len].parent = node;
     729           0 :                 lines[len].monospace = true;
     730             :             }
     731           0 :             ++len;
     732           0 :             if ( sm->type==asm_context ) {
     733           0 :                 if ( i ) {
     734           0 :                     sprintf(space, _("State %4d Mark: "), j );
     735           0 :                     for ( k=0; k<sm->class_cnt; ++k )
     736           0 :                         if ( sm->state[j*sm->class_cnt+k].u.context.mark_lookup==NULL )
     737           0 :                             strcat(space,"     ");
     738             :                         else
     739           0 :                             sprintf( space+strlen(space), " %.80s", sm->state[j*sm->class_cnt+k].u.context.mark_lookup->lookup_name );
     740           0 :                     lines[len].label = copy(space);
     741           0 :                     lines[len].parent = node;
     742           0 :                     lines[len].monospace = true;
     743             :                 }
     744           0 :                 ++len;
     745           0 :                 if ( i ) {
     746           0 :                     sprintf(space, _("State %4d Cur:  "), j );
     747           0 :                     for ( k=0; k<sm->class_cnt; ++k )
     748           0 :                         if ( sm->state[j*sm->class_cnt+k].u.context.cur_lookup==NULL )
     749           0 :                             strcat(space,"     ");
     750             :                         else
     751           0 :                             sprintf( space+strlen(space), " %.80s", sm->state[j*sm->class_cnt+k].u.context.cur_lookup->lookup_name );
     752           0 :                     lines[len].label = copy(space);
     753           0 :                     lines[len].parent = node;
     754           0 :                     lines[len].monospace = true;
     755             :                 }
     756           0 :                 ++len;
     757             :             }
     758             :         }
     759           0 :         for ( j=0; j<scnt; ++j ) {
     760           0 :             if ( i ) {
     761           0 :                 sprintf(buf, _("Nested Substitution %.80s"), used[j]->lookup_name );
     762           0 :                 lines[len].label = copy(buf);
     763           0 :                 lines[len].parent = node;
     764           0 :                 lines[len].u.otl = used[j];
     765           0 :                 lines[len].build = BuildGSUBlookups;
     766             :             }
     767           0 :             ++len;
     768             :         }
     769           0 :         if ( i==0 ) {
     770           0 :             node->children = lines = calloc(len+1,sizeof(struct node));
     771           0 :             node->cnt = len;
     772             :         }
     773             :     }
     774           0 :     free(space);
     775           0 :     free(used);
     776           0 : }
     777             : 
     778           0 : static void BuildKern2(struct node *node,struct att_dlg *att) {
     779           0 :     struct lookup_subtable *sub = node->parent->u.sub;
     780           0 :     SplineChar *base = node->u.sc;
     781           0 :     SplineFont *_sf = att->sf;
     782             :     int doit, cnt;
     783             :     int isv;
     784             :     PST *pst;
     785             :     KernPair *kp;
     786             :     char buffer[200];
     787             :     struct node *lines;
     788             : 
     789           0 :     for ( doit = 0; doit<2; ++doit ) {
     790           0 :         cnt = 0;
     791           0 :         for ( pst=base->possub; pst!=NULL; pst = pst->next ) {
     792           0 :             if ( pst->subtable==sub && pst->type==pst_pair &&
     793           0 :                     PSTAllComponentsExist(_sf,pst->u.pair.paired)) {
     794           0 :                 if ( doit ) {
     795           0 :                     sprintf(buffer, "%.80s ", pst->u.pair.paired );
     796           0 :                     if ( pst->u.pair.vr[0].xoff!=0 ||
     797             :                             /* If everything is 0, we'll want to display something */
     798             :                             /* might as well be this */
     799           0 :                             ( pst->u.pair.vr[0].yoff == 0 &&
     800           0 :                               pst->u.pair.vr[0].h_adv_off ==0 &&
     801           0 :                               pst->u.pair.vr[0].v_adv_off ==0 &&
     802           0 :                               pst->u.pair.vr[1].xoff ==0 &&
     803           0 :                               pst->u.pair.vr[1].yoff ==0 &&
     804           0 :                               pst->u.pair.vr[1].h_adv_off ==0 &&
     805           0 :                               pst->u.pair.vr[1].v_adv_off == 0 ))
     806           0 :                         sprintf( buffer+strlen(buffer), " ∆x¹=%d", pst->u.pair.vr[0].xoff );
     807           0 :                     if ( pst->u.pair.vr[0].yoff!=0 )
     808           0 :                         sprintf( buffer+strlen(buffer), " ∆y¹=%d", pst->u.pair.vr[0].yoff );
     809           0 :                     if ( pst->u.pair.vr[0].h_adv_off!=0 )
     810           0 :                         sprintf( buffer+strlen(buffer), " ∆x_adv¹=%d", pst->u.pair.vr[0].h_adv_off );
     811           0 :                     if ( pst->u.pair.vr[0].v_adv_off!=0 )
     812           0 :                         sprintf( buffer+strlen(buffer), " ∆y_adv¹=%d", pst->u.pair.vr[0].v_adv_off );
     813           0 :                     if ( pst->u.pair.vr[1].xoff!=0 )
     814           0 :                         sprintf( buffer+strlen(buffer), " ∆x²=%d", pst->u.pair.vr[1].xoff );
     815           0 :                     if ( pst->u.pair.vr[1].yoff!=0 )
     816           0 :                         sprintf( buffer+strlen(buffer), " ∆y²=%d", pst->u.pair.vr[1].yoff );
     817           0 :                     if ( pst->u.pair.vr[1].h_adv_off!=0 )
     818           0 :                         sprintf( buffer+strlen(buffer), " ∆x_adv²=%d", pst->u.pair.vr[1].h_adv_off );
     819           0 :                     if ( pst->u.pair.vr[1].v_adv_off!=0 )
     820           0 :                         sprintf( buffer+strlen(buffer), " ∆y_adv²=%d", pst->u.pair.vr[1].v_adv_off );
     821           0 :                     lines[cnt].label = copy(buffer);
     822           0 :                     lines[cnt].parent = node;
     823             :                 }
     824           0 :                 ++cnt;
     825             :             }
     826             :         }
     827           0 :         for ( isv=0; isv<2 ; ++isv ) {
     828           0 :             for ( kp= isv ? base->vkerns : base->kerns ; kp!=NULL; kp=kp->next ) {
     829           0 :                 if ( kp->subtable==sub ) {
     830           0 :                     if ( doit ) {
     831           0 :                         if ( isv )
     832           0 :                             sprintf( buffer, "%.80s  ∆y_adv¹=%d", kp->sc->name, kp->off );
     833           0 :                         else if ( sub->lookup->lookup_flags&pst_r2l )
     834           0 :                             sprintf( buffer, "%.80s  ∆x_adv²=%d", kp->sc->name, kp->off );
     835             :                         else
     836           0 :                             sprintf( buffer, "%.80s  ∆x_adv¹=%d", kp->sc->name, kp->off );
     837           0 :                         lines[cnt].label = copy(buffer);
     838           0 :                         lines[cnt].parent = node;
     839             :                     }
     840           0 :                     ++cnt;
     841             :                 }
     842             :             }
     843             :         }
     844           0 :         if ( !doit ) {
     845           0 :             node->children = lines = calloc(cnt+1,sizeof(struct node));
     846           0 :             node->cnt = cnt;
     847             :         } else
     848           0 :             qsort(lines,cnt,sizeof(struct node), node_alphabetize);
     849             :     }
     850           0 : }
     851             : 
     852           0 : static void BuildKern(struct node *node,struct att_dlg *att) {
     853           0 :     struct lookup_subtable *sub = node->u.sub;
     854           0 :     SplineFont *_sf = att->sf, *sf;
     855             :     int k, gid, doit, cnt, maxc, isv;
     856             :     SplineChar *sc;
     857             :     PST *pst;
     858             :     KernPair *kp;
     859             :     struct node *lines;
     860             : 
     861           0 :     if ( _sf->cidmaster!=NULL ) _sf = _sf->cidmaster;
     862             : 
     863           0 :     k=maxc=0;
     864             :     do {
     865           0 :         sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[k];
     866           0 :         if ( sf->glyphcnt>maxc ) maxc = sf->glyphcnt;
     867           0 :         ++k;
     868           0 :     } while ( k<_sf->subfontcnt );
     869             : 
     870           0 :     for ( doit = 0; doit<2; ++doit ) {
     871           0 :         cnt = 0;
     872           0 :         for ( gid=0; gid<maxc; ++gid ) {
     873           0 :             k=0;
     874           0 :             sc = NULL;
     875             :             do {
     876           0 :                 sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[k];
     877           0 :                 if ( gid<sf->glyphcnt && sf->glyphs[gid]!=NULL ) {
     878           0 :                     sc = sf->glyphs[gid];
     879           0 :             break;
     880             :                 }
     881           0 :                 ++k;
     882           0 :             } while ( k<_sf->subfontcnt );
     883           0 :             if ( sc!=NULL ) {
     884           0 :                 int found = false;
     885           0 :                 for ( pst=sc->possub; pst!=NULL; pst = pst->next ) {
     886           0 :                     if ( pst->subtable==sub ) {
     887           0 :                         found = true;
     888           0 :                 break;
     889             :                     }
     890             :                 }
     891           0 :                 for ( isv=0; isv<2 && !found; ++isv ) {
     892           0 :                     for ( kp= isv ? sc->vkerns : sc->kerns ; kp!=NULL; kp=kp->next ) {
     893           0 :                         if ( kp->subtable==sub ) {
     894           0 :                             found = true;
     895           0 :                     break;
     896             :                         }
     897             :                     }
     898             :                 }
     899           0 :                 if ( found ) {
     900           0 :                     if ( doit ) {
     901           0 :                         lines[cnt].label = copy(sc->name);
     902           0 :                         lines[cnt].parent = node;
     903           0 :                         lines[cnt].build = BuildKern2;
     904           0 :                         lines[cnt].u.sc = sc;
     905             :                     }
     906           0 :                     ++cnt;
     907             :                 }
     908             :             }
     909             :         }
     910           0 :         if ( !doit ) {
     911           0 :             node->children = lines = calloc(cnt+1,sizeof(struct node));
     912           0 :             node->cnt = cnt;
     913             :         } else
     914           0 :             qsort(lines,cnt,sizeof(struct node), node_alphabetize);
     915             :     }
     916           0 : }
     917             : 
     918           0 : static void BuildPST(struct node *node,struct att_dlg *att) {
     919           0 :     struct lookup_subtable *sub = node->u.sub;
     920           0 :     SplineFont *_sf = att->sf, *sf;
     921             :     int k, gid, doit, cnt, maxl, len, maxc;
     922             :     SplineChar *sc;
     923             :     PST *pst;
     924             :     struct node *lines;
     925           0 :     char *lbuf=NULL;
     926             : 
     927           0 :     if ( _sf->cidmaster!=NULL ) _sf = _sf->cidmaster;
     928             : 
     929           0 :     k=maxc=0;
     930             :     do {
     931           0 :         sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[k];
     932           0 :         if ( sf->glyphcnt>maxc ) maxc = sf->glyphcnt;
     933           0 :         ++k;
     934           0 :     } while ( k<_sf->subfontcnt );
     935             : 
     936           0 :     for ( doit = 0; doit<2; ++doit ) {
     937           0 :         cnt = maxl = 0;
     938           0 :         for ( gid=0; gid<maxc; ++gid ) {
     939           0 :             k=0;
     940           0 :             sc = NULL;
     941             :             do {
     942           0 :                 sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[k];
     943           0 :                 if ( gid<sf->glyphcnt && sf->glyphs[gid]!=NULL ) {
     944           0 :                     sc = sf->glyphs[gid];
     945           0 :             break;
     946             :                 }
     947           0 :                 ++k;
     948           0 :             } while ( k<_sf->subfontcnt );
     949           0 :             if ( sc!=NULL ) {
     950           0 :                 for ( pst=sc->possub; pst!=NULL; pst = pst->next ) {
     951           0 :                     if ( pst->subtable==sub ) {
     952           0 :                         if ( doit ) {
     953           0 :                             if ( pst->type==pst_position )
     954           0 :                                 sprintf(lbuf,"%s ∆x=%d ∆y=%d ∆x_adv=%d ∆y_adv=%d",
     955             :                                         sc->name,
     956           0 :                                         pst->u.pos.xoff, pst->u.pos.yoff,
     957           0 :                                         pst->u.pos.h_adv_off, pst->u.pos.v_adv_off );
     958             :                             else
     959           0 :                                 sprintf(lbuf, "%s %s %s", sc->name,
     960           0 :                                         pst->type==pst_ligature ? "<=" : "=>",
     961             :                                         pst->u.subs.variant );
     962           0 :                             lines[cnt].label = copy(lbuf);
     963           0 :                             lines[cnt].parent = node;
     964             :                         } else {
     965           0 :                             if ( pst->type==pst_position )
     966           0 :                                 len = strlen(sc->name)+40;
     967             :                             else
     968           0 :                                 len = strlen(sc->name)+strlen(pst->u.subs.variant)+8;
     969           0 :                             if ( len>maxl ) maxl = len;
     970             :                         }
     971           0 :                         ++cnt;
     972             :                     }
     973             :                 }
     974             :             }
     975             :         }
     976           0 :         if ( !doit ) {
     977           0 :             lbuf = malloc(maxl*sizeof(unichar_t));
     978           0 :             node->children = lines = calloc(cnt+1,sizeof(struct node));
     979           0 :             node->cnt = cnt;
     980             :         } else
     981           0 :             qsort(lines,cnt,sizeof(struct node), node_alphabetize);
     982             :     }
     983           0 :     free(lbuf);
     984           0 : }
     985             : 
     986           0 : static void BuildSubtableDispatch(struct node *node,struct att_dlg *att) {
     987           0 :     struct lookup_subtable *sub = node->u.sub;
     988           0 :     int lookup_type = sub->lookup->lookup_type;
     989             : 
     990           0 :     switch ( lookup_type ) {
     991             :       case gpos_context: case gpos_contextchain:
     992             :       case gsub_context: case gsub_contextchain: case gsub_reversecchain:
     993           0 :         BuildFPST(node,att);
     994           0 : return;
     995             :       case gpos_cursive: case gpos_mark2base: case gpos_mark2ligature: case gpos_mark2mark:
     996           0 :         BuildAnchorLists(node,att);
     997           0 : return;
     998             :       case gpos_pair:
     999           0 :         if ( sub->kc!=NULL )
    1000           0 :             BuildKC(node,att);
    1001             :         else
    1002           0 :             BuildKern(node,att);
    1003           0 : return;
    1004             :       case gpos_single:
    1005             :       case gsub_single: case gsub_multiple: case gsub_alternate: case gsub_ligature:
    1006           0 :         BuildPST(node,att);
    1007           0 : return;
    1008             :       case morx_indic: case morx_context: case morx_insert:
    1009             :       case kern_statemachine:
    1010           0 :         BuildASM(node,att);
    1011           0 : return;
    1012             :     }
    1013           0 :     IError( "Unknown lookup type in BuildDispatch");
    1014             : }
    1015             : 
    1016           0 : static void BuildGSUBlookups(struct node *node,struct att_dlg *att) {
    1017           0 :     OTLookup *otl = node->u.otl;
    1018             :     struct lookup_subtable *sub;
    1019             :     struct node *subslist;
    1020             :     int cnt;
    1021             : 
    1022           0 :     for ( sub = otl->subtables, cnt=0; sub!=NULL; sub=sub->next, ++cnt );
    1023           0 :     subslist = calloc(cnt+1,sizeof(struct node));
    1024           0 :     for ( sub = otl->subtables, cnt=0; sub!=NULL; sub=sub->next, ++cnt ) {
    1025           0 :         subslist[cnt].parent = node;
    1026           0 :         subslist[cnt].u.sub = sub;
    1027           0 :         subslist[cnt].build = BuildSubtableDispatch;
    1028           0 :         subslist[cnt].label = copy(sub->subtable_name);
    1029             :     }
    1030             : 
    1031           0 :     node->children = subslist;
    1032           0 :     node->cnt = cnt;
    1033           0 : }
    1034             : 
    1035           0 : static void BuildGSUBfeatures(struct node *node,struct att_dlg *att) {
    1036           0 :     int isgsub = node->parent->parent->parent->tag==CHR('G','S','U','B');
    1037           0 :     uint32 script = node->parent->parent->tag, lang = node->parent->tag, feat=node->tag;
    1038             :     OTLookup *otl;
    1039           0 :     SplineFont *sf = att->sf;
    1040             :     int match;
    1041             :     FeatureScriptLangList *fl;
    1042             :     struct scriptlanglist *sl;
    1043             :     int cnt, j,l;
    1044           0 :     struct node *lookups = NULL;
    1045             : 
    1046           0 :     for ( j=0; j<2; ++j ) {
    1047           0 :         cnt = 0;
    1048           0 :         for ( otl = isgsub ? sf->gsub_lookups : sf->gpos_lookups ; otl!=NULL ; otl=otl->next ) {
    1049           0 :             match = false;
    1050           0 :             for ( fl = otl->features; fl!=NULL && !match; fl=fl->next ) {
    1051           0 :                 if ( fl->featuretag == feat ) {
    1052           0 :                     for ( sl = fl->scripts; sl!=NULL && !match; sl=sl->next ) {
    1053           0 :                         if ( sl->script == script ) {
    1054           0 :                             for ( l=0; l<sl->lang_cnt; ++l ) {
    1055           0 :                                 uint32 _lang = l<MAX_LANG ? sl->langs[l] : sl->morelangs[l-MAX_LANG];
    1056           0 :                                 if ( _lang == lang ) {
    1057           0 :                                     match = true;
    1058           0 :                             break;
    1059             :                                 }
    1060             :                             }
    1061             :                         }
    1062             :                     }
    1063             :                 }
    1064             :             }
    1065           0 :             if ( match ) {
    1066           0 :                 if ( lookups ) {
    1067           0 :                     lookups[cnt].parent = node;
    1068           0 :                     lookups[cnt].build = BuildGSUBlookups;
    1069           0 :                     lookups[cnt].label = copy(otl->lookup_name);
    1070           0 :                     lookups[cnt].u.otl = otl;
    1071             :                 }
    1072           0 :                 ++cnt;
    1073             :             }
    1074             :         }
    1075           0 :         if ( lookups == NULL )
    1076           0 :             lookups = calloc(cnt+1,sizeof(struct node));
    1077             :     }
    1078             : 
    1079           0 :     node->children = lookups;
    1080           0 :     node->cnt = cnt;
    1081           0 : }
    1082             : 
    1083           0 : static void BuildGSUBlang(struct node *node,struct att_dlg *att) {
    1084           0 :     int isgsub = node->parent->parent->tag==CHR('G','S','U','B');
    1085           0 :     uint32 script = node->parent->tag, lang = node->tag;
    1086             :     int i,j;
    1087           0 :     SplineFont *_sf = att->sf;
    1088             :     struct node *featnodes;
    1089             :     uint32 *featlist;
    1090             : 
    1091             :     /* Build up the list of features in this lang entry of this script in GSUB/GPOS */
    1092             : 
    1093           0 :     featlist = SFFeaturesInScriptLang(_sf,!isgsub,script,lang);
    1094           0 :     for ( j=0; featlist[j]!=0; ++j );
    1095           0 :     featnodes = calloc(j+1,sizeof(struct node));
    1096           0 :     for ( i=0; featlist[i]!=0; ++i ) {
    1097           0 :         featnodes[i].tag = featlist[i];
    1098           0 :         featnodes[i].parent = node;
    1099           0 :         featnodes[i].build = BuildGSUBfeatures;
    1100           0 :         featnodes[i].label = TagFullName(_sf,featnodes[i].tag,false,false);
    1101             :     }
    1102           0 :     free( featlist );
    1103             : 
    1104           0 :     node->children = featnodes;
    1105           0 :     node->cnt = j;
    1106           0 : }
    1107             : 
    1108           0 : static void BuildGSUBscript(struct node *node,struct att_dlg *att) {
    1109           0 :     SplineFont *sf = att->sf;
    1110             :     int lang_max;
    1111             :     int i,j;
    1112             :     struct node *langnodes;
    1113             :     uint32 *langlist;
    1114             :     extern GTextInfo languages[];
    1115             :     char buf[100];
    1116           0 :     int isgpos = node->parent->tag == CHR('G','P','O','S');
    1117             : 
    1118             :     /* Build the list of languages that are used in this script */
    1119             :     /* Don't bother to check whether they actually get used */
    1120             : 
    1121           0 :     langlist = SFLangsInScript(sf,isgpos,node->tag);
    1122           0 :     for ( j=0; langlist[j]!=0; ++j );
    1123           0 :     lang_max = j;
    1124           0 :     langnodes = calloc(lang_max+1,sizeof(struct node));
    1125           0 :     for ( i=0; langlist[i]!=0; ++i )
    1126           0 :         langnodes[i].tag = langlist[i];
    1127           0 :     free( langlist );
    1128             : 
    1129           0 :     for ( i=0; i<lang_max; ++i ) {
    1130           0 :         for ( j=0; languages[j].text!=NULL && langnodes[i].tag!=(uint32) (intpt) languages[j].userdata; ++j );
    1131           0 :         buf[0] = '\'';
    1132           0 :         buf[1] = langnodes[i].tag>>24;
    1133           0 :         buf[2] = (langnodes[i].tag>>16)&0xff;
    1134           0 :         buf[3] = (langnodes[i].tag>>8)&0xff;
    1135           0 :         buf[4] = langnodes[i].tag&0xff;
    1136           0 :         buf[5] = '\'';
    1137           0 :         buf[6] = ' ';
    1138           0 :         if ( languages[j].text!=NULL ) {
    1139           0 :             strcpy(buf+7,S_((char *) languages[j].text));
    1140           0 :             strcat(buf," ");
    1141             :         } else
    1142           0 :             buf[7]='\0';
    1143           0 :         strcat(buf,_("Language"));
    1144           0 :         langnodes[i].label = copy(buf);
    1145           0 :         langnodes[i].build = BuildGSUBlang;
    1146           0 :         langnodes[i].parent = node;
    1147             :     }
    1148           0 :     node->children = langnodes;
    1149           0 :     node->cnt = i;
    1150           0 : }
    1151             : 
    1152           0 : static void BuildLookupList(struct node *node,struct att_dlg *att) {
    1153           0 :     OTLookup **otll = node->u.otll;
    1154             :     int i;
    1155             :     struct node *lookupnodes;
    1156             : 
    1157           0 :     for ( i=0; otll[i]!=NULL; ++i );
    1158           0 :     lookupnodes = calloc(i+1,sizeof(struct node));
    1159           0 :     for ( i=0; otll[i]!=NULL; ++i ) {
    1160           0 :         lookupnodes[i].label = copy(otll[i]->lookup_name);
    1161           0 :         lookupnodes[i].parent = node;
    1162           0 :         lookupnodes[i].build = BuildGSUBlookups;
    1163           0 :         lookupnodes[i].u.otl = otll[i];
    1164             :     }
    1165             : 
    1166           0 :     node->children = lookupnodes;
    1167           0 :     node->cnt = i;
    1168           0 : }
    1169             : 
    1170           0 : static void BuildJSTFPrio(struct node *node, struct node *parent, OTLookup **otll,
    1171             :         char *label_if_some, char *label_if_none ) {
    1172             : 
    1173           0 :     node->parent = parent;
    1174           0 :     if ( otll==NULL || otll[0]==NULL ) {
    1175           0 :         node->label = copy(label_if_none);
    1176           0 :         node->children_checked = true;
    1177           0 :         node->cnt = 0;
    1178             :     } else {
    1179           0 :         node->label = copy(label_if_some);
    1180           0 :         node->build = BuildLookupList;
    1181           0 :         node->u.otll = otll;
    1182             :     }
    1183           0 : }
    1184             : 
    1185           0 : static void BuildJSTFlang(struct node *node,struct att_dlg *att) {
    1186           0 :     struct jstf_lang *jlang = node->u.jlang;
    1187             :     int i;
    1188             :     struct node *prionodes, *kids;
    1189             :     char buf[120];
    1190             : 
    1191           0 :     prionodes = calloc(jlang->cnt+1,sizeof(struct node));
    1192           0 :     for ( i=0; i<jlang->cnt; ++i ) {
    1193           0 :         kids = calloc(7,sizeof(struct node));
    1194           0 :         BuildJSTFPrio(&kids[0],&prionodes[i],jlang->prios[i].enableExtend,_("Lookups Enabled for Expansion"), _("No Lookups Enabled for Expansion"));
    1195           0 :         BuildJSTFPrio(&kids[1],&prionodes[i],jlang->prios[i].disableExtend,_("Lookups Disabled for Expansion"), _("No Lookups Disabled for Expansion"));
    1196           0 :         BuildJSTFPrio(&kids[2],&prionodes[i],jlang->prios[i].maxExtend,_("Lookups Limiting Expansion"), _("No Lookups Limiting Expansion"));
    1197           0 :         BuildJSTFPrio(&kids[3],&prionodes[i],jlang->prios[i].enableShrink,_("Lookups Enabled for Shrinkage"), _("No Lookups Enabled for Shrinkage"));
    1198           0 :         BuildJSTFPrio(&kids[4],&prionodes[i],jlang->prios[i].disableShrink,_("Lookups Disabled for Shrinkage"), _("No Lookups Disabled for Shrinkage"));
    1199           0 :         BuildJSTFPrio(&kids[5],&prionodes[i],jlang->prios[i].maxShrink,_("Lookups Limiting Shrinkage"), _("No Lookups Limiting Shrinkage"));
    1200           0 :         sprintf( buf, _("Priority: %d"), i );
    1201           0 :         prionodes[i].label = copy(buf);
    1202           0 :         prionodes[i].parent = node;
    1203           0 :         prionodes[i].children_checked = true;
    1204           0 :         prionodes[i].children = kids;
    1205           0 :         prionodes[i].cnt = 6;
    1206             :     }
    1207             : 
    1208           0 :     node->children = prionodes;
    1209           0 :     node->cnt = i;
    1210           0 : }
    1211             : 
    1212           0 : static void BuildJSTFscript(struct node *node,struct att_dlg *att) {
    1213           0 :     SplineFont *sf = att->sf;
    1214           0 :     Justify *jscript = node->u.jscript;
    1215             :     int lang_max;
    1216             :     int i,j, ch;
    1217           0 :     struct node *langnodes, *extenders=NULL;
    1218             :     extern GTextInfo languages[];
    1219             :     char buf[100];
    1220             :     struct jstf_lang *jlang;
    1221             :     char *start, *end;
    1222             :     int gc;
    1223             :     SplineChar *sc;
    1224             : 
    1225           0 :     for ( jlang=jscript->langs, lang_max=0; jlang!=NULL; jlang=jlang->next, ++lang_max );
    1226           0 :     langnodes = calloc(lang_max+2,sizeof(struct node));
    1227           0 :     gc =0;
    1228           0 :     if ( jscript->extenders!=NULL ) {
    1229           0 :         for ( start= jscript->extenders; ; ) {
    1230           0 :             for ( ; *start==',' || *start==' '; ++start );
    1231           0 :             for ( end=start ; *end!='\0' && *end!=',' && *end!=' '; ++end );
    1232           0 :             if ( start==end )
    1233           0 :         break;
    1234           0 :             ++gc;
    1235           0 :             if ( *end=='\0' )
    1236           0 :         break;
    1237           0 :             start = end;
    1238           0 :         }
    1239           0 :         extenders = calloc(gc+1,sizeof(struct node));
    1240           0 :         gc=0;
    1241           0 :         for ( start= jscript->extenders; ; ) {
    1242           0 :             for ( ; *start==',' || *start==' '; ++start );
    1243           0 :             for ( end=start ; *end!='\0' && *end!=',' && *end!=' '; ++end );
    1244           0 :             if ( start==end )
    1245           0 :         break;
    1246           0 :             ch = *end; *end='\0';
    1247           0 :             sc = SFGetChar(sf,-1,start);
    1248           0 :             *end = ch;
    1249           0 :             if ( sc!=NULL ) {
    1250           0 :                 extenders[gc].label = copy(sc->name);
    1251           0 :                 extenders[gc].parent = &langnodes[0];
    1252           0 :                 extenders[gc].children_checked = true;
    1253           0 :                 extenders[gc].u.sc = sc;
    1254           0 :                 ++gc;
    1255             :             }
    1256           0 :             if ( *end=='\0' )
    1257           0 :         break;
    1258           0 :             start = end;
    1259           0 :         }
    1260             :     }
    1261           0 :     if ( gc==0 ) {
    1262           0 :         free(extenders);
    1263           0 :         extenders=NULL;
    1264           0 :         langnodes[0].label = copy(_("No Extender Glyphs"));
    1265           0 :         langnodes[0].parent = node;
    1266           0 :         langnodes[0].children_checked = true;
    1267             :     } else {
    1268           0 :         langnodes[0].label = copy(_("Extender Glyphs"));
    1269           0 :         langnodes[0].parent = node;
    1270           0 :         langnodes[0].children_checked = true;
    1271           0 :         langnodes[0].children = extenders;
    1272           0 :         langnodes[0].cnt = gc;
    1273             :     }
    1274             :             
    1275           0 :     for ( jlang=jscript->langs, i=1; jlang!=NULL; jlang=jlang->next, ++i ) {
    1276           0 :         langnodes[i].tag = jlang->lang;
    1277           0 :         for ( j=0; languages[j].text!=NULL && langnodes[i].tag!=(uint32) (intpt) languages[j].userdata; ++j );
    1278           0 :         buf[0] = '\'';
    1279           0 :         buf[1] = langnodes[i].tag>>24;
    1280           0 :         buf[2] = (langnodes[i].tag>>16)&0xff;
    1281           0 :         buf[3] = (langnodes[i].tag>>8)&0xff;
    1282           0 :         buf[4] = langnodes[i].tag&0xff;
    1283           0 :         buf[5] = '\'';
    1284           0 :         buf[6] = ' ';
    1285           0 :         if ( languages[j].text!=NULL ) {
    1286           0 :             strcpy(buf+7,S_((char *) languages[j].text));
    1287           0 :             strcat(buf," ");
    1288             :         } else
    1289           0 :             buf[7]='\0';
    1290           0 :         strcat(buf,_("Language"));
    1291           0 :         langnodes[i].label = copy(buf);
    1292           0 :         langnodes[i].build = BuildJSTFlang;
    1293           0 :         langnodes[i].parent = node;
    1294           0 :         langnodes[i].u.jlang = jlang;
    1295             :     }
    1296           0 :     node->children = langnodes;
    1297           0 :     node->cnt = i;
    1298           0 : }
    1299             : 
    1300           0 : static void BuildMClass(struct node *node,struct att_dlg *att) {
    1301           0 :     SplineFont *_sf = att->sf;
    1302             :     struct node *glyphs;
    1303             :     int i;
    1304             :     char *temp;
    1305             : 
    1306           0 :     node->children = glyphs = calloc(_sf->mark_class_cnt,sizeof(struct node));
    1307           0 :     node->cnt = _sf->mark_class_cnt-1;
    1308           0 :     for ( i=1; i<_sf->mark_class_cnt; ++i ) {
    1309           0 :         glyphs[i-1].parent = node;
    1310           0 :         temp = malloc((strlen(_sf->mark_classes[i]) + strlen(_sf->mark_class_names[i]) + 4));
    1311           0 :         strcpy(temp,_sf->mark_class_names[i]);
    1312           0 :         strcat(temp,": ");
    1313           0 :         strcat(temp,_sf->mark_classes[i]);
    1314           0 :         glyphs[i-1].label = temp;
    1315             :     }
    1316           0 : }
    1317             : 
    1318           0 : static void BuildLCarets(struct node *node,struct att_dlg *att) {
    1319           0 :     SplineChar *sc = node->u.sc;
    1320             :     PST *pst;
    1321             :     int i,j;
    1322             :     char buffer[20];
    1323             :     struct node *lcars;
    1324             : 
    1325           0 :     j = -1;
    1326           0 :     for ( pst=sc->possub; pst!=NULL; pst=pst->next ) if ( pst->type==pst_lcaret ) {
    1327           0 :         for ( j=pst->u.lcaret.cnt-1; j>=0; --j )
    1328           0 :             if ( pst->u.lcaret.carets[j]!=0 )
    1329           0 :     goto break2;
    1330             :     }
    1331             :   break2:
    1332           0 :     if ( j==-1 )
    1333           0 : return;
    1334           0 :     ++j;
    1335           0 :     node->children = lcars = calloc(j+1,sizeof(struct node));
    1336           0 :     node->cnt = j;
    1337           0 :     for ( j=i=0; j<pst->u.lcaret.cnt; ++j ) {
    1338           0 :         if ( pst->u.lcaret.carets[j]!=0 ) {
    1339           0 :             sprintf( buffer,"%d", pst->u.lcaret.carets[j] );
    1340           0 :             lcars[i].parent = node;
    1341           0 :             lcars[i++].label = copy(buffer);
    1342             :         }
    1343             :     }
    1344             : }
    1345             : 
    1346           0 : static void BuildLcar(struct node *node,struct att_dlg *att) {
    1347           0 :     SplineFont *sf, *_sf = att->sf;
    1348             :     struct node *glyphs;
    1349             :     int i,j,k,l, lcnt;
    1350             :     PST *pst;
    1351             : 
    1352           0 :     glyphs = NULL;
    1353           0 :     for ( k=0; k<2; ++k ) {
    1354           0 :         lcnt = 0;
    1355           0 :         l = 0;
    1356             :         do {
    1357           0 :             sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
    1358           0 :             for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->ttf_glyph!=-1 ) {
    1359           0 :                 for ( pst=sf->glyphs[i]->possub; pst!=NULL; pst=pst->next ) {
    1360           0 :                     if ( pst->type == pst_lcaret ) {
    1361           0 :                         for ( j=pst->u.lcaret.cnt-1; j>=0; --j )
    1362           0 :                             if ( pst->u.lcaret.carets[j]!=0 )
    1363           0 :                         break;
    1364           0 :                         if ( j!=-1 )
    1365           0 :                 break;
    1366             :                     }
    1367             :                 }
    1368           0 :                 if ( pst!=NULL ) {
    1369           0 :                     if ( glyphs!=NULL ) {
    1370           0 :                         glyphs[lcnt].parent = node;
    1371           0 :                         glyphs[lcnt].build = BuildLCarets;
    1372           0 :                         glyphs[lcnt].u.sc = sf->glyphs[i];
    1373           0 :                         glyphs[lcnt].label = copy(sf->glyphs[i]->name);
    1374             :                     }
    1375           0 :                     ++lcnt;
    1376             :                 }
    1377             :             }
    1378           0 :             ++l;
    1379           0 :         } while ( l<_sf->subfontcnt );
    1380           0 :         if ( lcnt==0 )
    1381           0 :     break;
    1382           0 :         if ( glyphs!=NULL )
    1383           0 :     break;
    1384           0 :         node->children = glyphs = calloc(lcnt+1,sizeof(struct node));
    1385           0 :         node->cnt = lcnt;
    1386             :     }
    1387           0 :     if ( glyphs!=NULL )
    1388           0 :         qsort(glyphs,lcnt,sizeof(struct node), node_alphabetize);
    1389           0 : }
    1390             : 
    1391           0 : static void BuildGdefs(struct node *node,struct att_dlg *att) {
    1392           0 :     SplineFont *sf, *_sf = att->sf;
    1393             :     int i, cmax, l,j, ccnt;
    1394             :     SplineChar *sc;
    1395             :     struct node *chars;
    1396             :     char buffer[100];
    1397             : 
    1398           0 :     cmax = 0;
    1399           0 :     l = 0;
    1400             :     do {
    1401           0 :         sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
    1402           0 :         if ( cmax<sf->glyphcnt ) cmax = sf->glyphcnt;
    1403           0 :         ++l;
    1404           0 :     } while ( l<_sf->subfontcnt );
    1405             : 
    1406           0 :     chars = NULL;
    1407           0 :     for ( j=0; j<2; ++j ) {
    1408           0 :         ccnt = 0;
    1409           0 :         for ( i=0; i<cmax; ++i ) {
    1410           0 :             l = 0;
    1411           0 :             sc = NULL;
    1412             :             do {
    1413           0 :                 sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
    1414           0 :                 if ( l<sf->glyphcnt && sf->glyphs[i]!=NULL ) {
    1415           0 :                     sc = sf->glyphs[i];
    1416           0 :             break;
    1417             :                 }
    1418           0 :                 ++l;
    1419           0 :             } while ( l<_sf->subfontcnt );
    1420           0 :             if ( sc!=NULL && SCWorthOutputting(sc) ) {
    1421           0 :                 if ( chars!=NULL ) {
    1422           0 :                     int gdefc = gdefclass(sc);
    1423           0 :                     sprintf(buffer,"%.70s %s", sc->name,
    1424             :                         gdefc==0 ? _("Not classified") :
    1425           0 :                         gdefc==1 ? _("Base") :
    1426           0 :                         gdefc==2 ? _("Ligature") :
    1427           0 :                         gdefc==3 ? _("Mark") :
    1428             :                             _("Component") );
    1429           0 :                     chars[ccnt].parent = node;
    1430           0 :                     chars[ccnt].label = copy(buffer);;
    1431             :                 }
    1432           0 :                 ++ccnt;
    1433             :             }
    1434             :         }
    1435           0 :         if ( ccnt==0 )
    1436           0 :     break;
    1437           0 :         if ( chars==NULL ) {
    1438           0 :             node->cnt = ccnt;
    1439           0 :             node->children = chars = calloc(ccnt+1,sizeof(struct node));
    1440             :         }
    1441             :     }
    1442           0 : }
    1443             : 
    1444           0 : static void BuildGDEF(struct node *node,struct att_dlg *att) {
    1445           0 :     SplineFont *sf, *_sf = att->sf;
    1446             :     AnchorClass *ac;
    1447             :     PST *pst;
    1448             :     int l,j,i;
    1449             :     int gdef, lcar, mclass;
    1450             : 
    1451           0 :     for ( ac = _sf->anchor; ac!=NULL; ac=ac->next ) {
    1452           0 :         if ( ac->type==act_curs )
    1453           0 :     break;
    1454             :     }
    1455           0 :     gdef = lcar = 0;
    1456           0 :     if ( ac!=NULL )
    1457           0 :         gdef = 1;
    1458           0 :     l = 0;
    1459           0 :     pst = NULL;
    1460             :     do {
    1461           0 :         sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
    1462           0 :         for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->ttf_glyph!=-1 ) {
    1463           0 :             for ( pst=sf->glyphs[i]->possub; pst!=NULL; pst=pst->next ) {
    1464           0 :                 if ( pst->type == pst_lcaret ) {
    1465           0 :                     for ( j=pst->u.lcaret.cnt-1; j>=0; --j )
    1466           0 :                         if ( pst->u.lcaret.carets[j]!=0 ) {
    1467           0 :                             lcar = 1;
    1468           0 :                     break;
    1469             :                         }
    1470           0 :                     if ( j!=-1 )
    1471           0 :             break;
    1472             :                 }
    1473             :             }
    1474           0 :             if ( sf->glyphs[i]->glyph_class!=0 )
    1475           0 :                 gdef = 1;
    1476             :         }
    1477           0 :         ++l;
    1478           0 :     } while ( l<_sf->subfontcnt );
    1479             : 
    1480           0 :     mclass = _sf->mark_class_cnt!=0;
    1481             : 
    1482           0 :     if ( gdef+lcar+mclass!=0 ) {
    1483           0 :         node->children = calloc(gdef+lcar+mclass+1,sizeof(struct node));
    1484           0 :         node->cnt = gdef+lcar+mclass;
    1485           0 :         if ( gdef ) {
    1486           0 :             node->children[0].label = copy(_("Glyph Definition Sub-Table"));
    1487           0 :             node->children[0].build = BuildGdefs;
    1488           0 :             node->children[0].parent = node;
    1489             :         }
    1490           0 :         if ( lcar ) {
    1491             : /* GT: Here caret means where to place the cursor inside a ligature. So OpenType */
    1492             : /* GT: allows there to be a typing cursor inside a ligature (for instance you */
    1493             : /* GT: can have a cursor between f and i in the "fi" ligature) */
    1494           0 :             node->children[gdef].label = copy(_("Ligature Caret Sub-Table"));
    1495           0 :             node->children[gdef].build = BuildLcar;
    1496           0 :             node->children[gdef].parent = node;
    1497             :         }
    1498           0 :         if ( mclass ) {
    1499           0 :             node->children[gdef+lcar].label = copy(_("Mark Attachment Classes"));
    1500           0 :             node->children[gdef+lcar].build = BuildMClass;
    1501           0 :             node->children[gdef+lcar].parent = node;
    1502             :         }
    1503             :     }
    1504           0 : }
    1505             : 
    1506           0 : static void BuildBaseLangs(struct node *node,struct att_dlg *att) {
    1507           0 :     struct baselangextent *bl = node->u.langs, *lf;
    1508             :     int cnt;
    1509             :     struct node *langs;
    1510             :     char buffer[300];
    1511             : 
    1512           0 :     for ( lf = bl, cnt=0; lf!=NULL; lf=lf->next, ++cnt );
    1513             : 
    1514           0 :     node->children = langs = calloc(cnt+1,sizeof(struct node));
    1515           0 :     node->cnt = cnt;
    1516             : 
    1517           0 :     for ( lf = bl, cnt=0; lf!=NULL; lf=lf->next, ++cnt ) {
    1518           0 :         sprintf( buffer, _("%c%c%c%c  Min Extent=%d, Max Extent=%d"),
    1519           0 :                 lf->lang>>24, lf->lang>>16, lf->lang>>8, lf->lang,
    1520           0 :                 lf->descent, lf->ascent );
    1521           0 :         langs[cnt].label = copy(buffer);
    1522           0 :         langs[cnt].parent = node;
    1523           0 :         if ( lf->features!=NULL ) {
    1524           0 :             langs[cnt].build = BuildBaseLangs;
    1525           0 :             langs[cnt].u.langs = lf->features;
    1526             :         }
    1527             :     }
    1528           0 : }
    1529             : 
    1530           0 : static void BuildBASE(struct node *node,struct att_dlg *att) {
    1531           0 :     SplineFont *_sf = att->sf;
    1532           0 :     struct Base *base = node->horizontal ? _sf->horiz_base : _sf->vert_base;
    1533             :     struct basescript *bs;
    1534             :     int cnt, i;
    1535             :     struct node *scripts;
    1536             :     char buffer[300];
    1537             : 
    1538           0 :     for ( bs=base->scripts, cnt=0; bs!=NULL; bs=bs->next, ++cnt );
    1539             : 
    1540           0 :     node->children = scripts = calloc(cnt+1,sizeof(struct node));
    1541           0 :     node->cnt = cnt;
    1542             : 
    1543           0 :     for ( bs=base->scripts, cnt=0; bs!=NULL; bs=bs->next, ++cnt ) {
    1544           0 :         if ( base->baseline_cnt!=0 ) {
    1545           0 :             i = bs->def_baseline;
    1546           0 :             sprintf( buffer, _("Script '%c%c%c%c' on %c%c%c%c "),
    1547           0 :                     bs->script>>24, bs->script>>16, bs->script>>8, bs->script,
    1548           0 :                     base->baseline_tags[i]>>24, base->baseline_tags[i]>>16,
    1549           0 :                     base->baseline_tags[i]>>8, base->baseline_tags[i] );
    1550           0 :             for ( i=0; i<base->baseline_cnt; ++i )
    1551           0 :                 sprintf(buffer+strlen(buffer), " %c%c%c%c: %d ",
    1552           0 :                         base->baseline_tags[i]>>24, base->baseline_tags[i]>>16,
    1553           0 :                         base->baseline_tags[i]>>8, base->baseline_tags[i],
    1554           0 :                         bs->baseline_pos[i]);
    1555             :         } else
    1556           0 :             sprintf( buffer, _("Script '%c%c%c%c' "),
    1557           0 :                     bs->script>>24, bs->script>>16, bs->script>>8, bs->script );
    1558           0 :         scripts[cnt].label = copy(buffer);
    1559           0 :         scripts[cnt].parent = node;
    1560           0 :         if ( bs->langs!=NULL ) {
    1561           0 :             scripts[cnt].build = BuildBaseLangs;
    1562           0 :             scripts[cnt].u.langs = bs->langs;
    1563             :         }
    1564             :     }
    1565           0 : }
    1566             : 
    1567           0 : static void BuildBsLnTable(struct node *node,struct att_dlg *att) {
    1568           0 :     SplineFont *_sf = att->sf;
    1569             :     int def_baseline;
    1570             :     int offsets[32];
    1571             :     int16 *baselines;
    1572             :     char buffer[300];
    1573             :     struct node *glyphs;
    1574             :     int gid,i;
    1575             :     SplineChar *sc;
    1576             : 
    1577           0 :     baselines = PerGlyphDefBaseline(_sf,&def_baseline);
    1578           0 :     FigureBaseOffsets(_sf,def_baseline&0x1f,offsets);
    1579             : 
    1580           0 :     node->children = calloc(3+1,sizeof(struct node));
    1581           0 :     node->cnt = 3;
    1582             : 
    1583           0 :     sprintf( buffer, _("Default Baseline: '%s'"),
    1584           0 :             (def_baseline&0x1f)==0 ? "romn" :
    1585           0 :             (def_baseline&0x1f)==1 ? "idcn" :
    1586           0 :             (def_baseline&0x1f)==2 ? "ideo" :
    1587           0 :             (def_baseline&0x1f)==3 ? "hang" :
    1588           0 :             (def_baseline&0x1f)==4 ? "math" : "????" );
    1589           0 :     node->children[0].label = copy(buffer);
    1590           0 :     node->children[0].parent = node;
    1591           0 :     sprintf( buffer, _("Offsets from def. baseline:  romn: %d  idcn: %d  ideo: %d  hang: %d  math: %d"),
    1592             :             offsets[0], offsets[1], offsets[2], offsets[3], offsets[4] );
    1593           0 :     node->children[1].label = copy(buffer);
    1594           0 :     node->children[1].parent = node;
    1595           0 :     if ( def_baseline&0x100 ) {
    1596           0 :         node->children[2].label = copy(_("All glyphs have the same baseline"));
    1597           0 :         node->children[2].parent = node;
    1598             :     } else {
    1599           0 :         node->children[2].label = copy(_("Per glyph baseline data"));
    1600           0 :         node->children[2].parent = node;
    1601           0 :         node->children[2].children_checked = true;
    1602           0 :         node->children[2].children = glyphs = calloc(_sf->glyphcnt+1,sizeof(struct node));
    1603           0 :         for ( gid=i=0; gid<_sf->glyphcnt; ++gid ) if ( (sc=_sf->glyphs[gid])!=NULL ) {
    1604           0 :             sprintf( buffer, "%s: %s", sc->name,
    1605           0 :                     (baselines[gid])==0 ? "romn" :
    1606           0 :                     (baselines[gid])==1 ? "idcn" :
    1607           0 :                     (baselines[gid])==2 ? "ideo" :
    1608           0 :                     (baselines[gid])==3 ? "hang" :
    1609           0 :                     (baselines[gid])==4 ? "math" : "????" );
    1610           0 :             glyphs[i].label = copy(buffer);
    1611           0 :             glyphs[i++].parent = &node->children[2];
    1612             :         }
    1613           0 :         node->children[2].cnt = i;
    1614             :     }
    1615           0 :     free(baselines);
    1616           0 : }
    1617             : 
    1618           0 : static void BuildOpticalBounds(struct node *node,struct att_dlg *att) {
    1619           0 :     SplineFont *sf, *_sf = att->sf;
    1620             :     int i, cmax, l,j, ccnt;
    1621             :     SplineChar *sc;
    1622             :     struct node *chars;
    1623             :     char buffer[200];
    1624             :     PST *left, *right;
    1625             : 
    1626           0 :     cmax = 0;
    1627           0 :     l = 0;
    1628             :     do {
    1629           0 :         sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
    1630           0 :         if ( cmax<sf->glyphcnt ) cmax = sf->glyphcnt;
    1631           0 :         ++l;
    1632           0 :     } while ( l<_sf->subfontcnt );
    1633             : 
    1634           0 :     chars = NULL;
    1635           0 :     for ( j=0; j<2; ++j ) {
    1636           0 :         ccnt = 0;
    1637           0 :         for ( i=0; i<cmax; ++i ) {
    1638           0 :             l = 0;
    1639           0 :             sc = NULL;
    1640             :             do {
    1641           0 :                 sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
    1642           0 :                 if ( i<sf->glyphcnt && sf->glyphs[i]!=NULL ) {
    1643           0 :                     sc = sf->glyphs[i];
    1644           0 :             break;
    1645             :                 }
    1646           0 :                 ++l;
    1647           0 :             } while ( l<_sf->subfontcnt );
    1648           0 :             if ( sc!=NULL && SCWorthOutputting(sc) &&
    1649           0 :                     haslrbounds(sc,&left,&right)) {
    1650           0 :                 if ( chars!=NULL ) {
    1651           0 :                     strncpy(buffer,sc->name,70);
    1652           0 :                     if ( left!=NULL )
    1653           0 :                         sprintf(buffer+strlen(buffer), _("  Left Bound=%d"),
    1654           0 :                                 left->u.pos.xoff );
    1655           0 :                     if ( right!=NULL )
    1656           0 :                         sprintf(buffer+strlen(buffer), _("  Right Bound=%d"),
    1657           0 :                                 -right->u.pos.h_adv_off );
    1658           0 :                     chars[ccnt].parent = node;
    1659           0 :                     chars[ccnt].label = copy(buffer);
    1660             :                 }
    1661           0 :                 ++ccnt;
    1662             :             }
    1663             :         }
    1664           0 :         if ( ccnt==0 )
    1665           0 : return;
    1666           0 :         if ( chars==NULL ) {
    1667           0 :             node->children = chars = calloc(ccnt+1,sizeof(struct node));
    1668           0 :             node->cnt = ccnt;
    1669             :         }
    1670             :     }
    1671             : }
    1672             : 
    1673           0 : static void BuildProperties(struct node *node,struct att_dlg *att) {
    1674           0 :     SplineFont *sf, *_sf = att->sf;
    1675             :     int i, cmax, l,j,k, ccnt;
    1676             :     SplineChar *sc;
    1677             :     struct node *chars;
    1678             :     uint16 *props;
    1679             :     char buffer[200];
    1680             : 
    1681           0 :     cmax = 0;
    1682           0 :     l = 0;
    1683             :     do {
    1684           0 :         sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
    1685           0 :         if ( cmax<sf->glyphcnt ) cmax = sf->glyphcnt;
    1686           0 :         ++l;
    1687           0 :     } while ( l<_sf->subfontcnt );
    1688             : 
    1689           0 :     chars = NULL; props = NULL;
    1690           0 :     for ( j=0; j<2; ++j ) {
    1691           0 :         ccnt = 0;
    1692           0 :         for ( i=0; i<cmax; ++i ) {
    1693           0 :             l = 0;
    1694           0 :             sc = NULL;
    1695             :             do {
    1696           0 :                 sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[l];
    1697           0 :                 if ( i<sf->glyphcnt && sf->glyphs[i]!=NULL ) {
    1698           0 :                     sc = sf->glyphs[i];
    1699           0 :             break;
    1700             :                 }
    1701           0 :                 ++l;
    1702           0 :             } while ( l<_sf->subfontcnt );
    1703           0 :             if ( sc!=NULL ) {
    1704           0 :                 if ( chars==NULL ) {
    1705           0 :                     if ( SCWorthOutputting(sc))
    1706           0 :                         sc->ttf_glyph = ccnt++;
    1707             :                     else
    1708           0 :                         sc->ttf_glyph = -1;
    1709           0 :                 } else if ( sc->ttf_glyph!=-1 ) {
    1710           0 :                     int prop = props[sc->ttf_glyph], offset;
    1711           0 :                     sprintf( buffer, "%.70s  dir=%s", sc->name,
    1712           0 :                         (prop&0x7f)==0 ? _("Strong Left to Right"):
    1713           0 :                         (prop&0x7f)==1 ? _("Strong Right to Left"):
    1714           0 :                         (prop&0x7f)==2 ? _("Arabic Right to Left"):
    1715           0 :                         (prop&0x7f)==3 ? _("European Number"):
    1716           0 :                         (prop&0x7f)==4 ? _("European Number Separator"):
    1717           0 :                         (prop&0x7f)==5 ? _("European Number Terminator"):
    1718           0 :                         (prop&0x7f)==6 ? _("Arabic Number"):
    1719           0 :                         (prop&0x7f)==7 ? _("Common Number Separator"):
    1720           0 :                         (prop&0x7f)==8 ? _("Block Separator"):
    1721           0 :                         (prop&0x7f)==9 ? _("Segment Separator"):
    1722           0 :                         (prop&0x7f)==10 ? _("White Space"):
    1723           0 :                         (prop&0x7f)==11 ? _("Neutral"):
    1724             :                             _("<Unknown direction>") );
    1725           0 :                     if ( prop&0x8000 )
    1726           0 :                         strcat(buffer,_("  Floating accent"));
    1727           0 :                     if ( prop&0x4000 )
    1728           0 :                         strcat(buffer,_("  Hang left"));
    1729           0 :                     if ( prop&0x2000 )
    1730           0 :                         strcat(buffer,_("  Hang right"));
    1731           0 :                     if ( prop&0x80 )
    1732           0 :                         strcat(buffer,_("  Attach right"));
    1733           0 :                     if ( prop&0x1000 ) {
    1734           0 :                         offset = (prop&0xf00)>>8;
    1735           0 :                         if ( offset&0x8 )
    1736           0 :                             offset |= 0xfffffff0;
    1737           0 :                         if ( offset>0 ) {
    1738           0 :                             for ( k=i+offset; k<sf->glyphcnt; ++k )
    1739           0 :                                 if ( sf->glyphs[k]!=NULL && sf->glyphs[k]->ttf_glyph==sc->ttf_glyph+offset ) {
    1740           0 :                                     sprintf( buffer+strlen(buffer), _("  Mirror=%.30s"), sf->glyphs[k]->name );
    1741           0 :                             break;
    1742             :                                 }
    1743             :                         } else {
    1744           0 :                             for ( k=i+offset; k>=0; --k )
    1745           0 :                                 if ( sf->glyphs[k]!=NULL && sf->glyphs[k]->ttf_glyph==sc->ttf_glyph+offset ) {
    1746           0 :                                     sprintf( buffer+strlen(buffer), _("  Mirror=%.30s"), sf->glyphs[k]->name );
    1747           0 :                             break;
    1748             :                                 }
    1749             :                         }
    1750             :                     }
    1751           0 :                     chars[ccnt].parent = node;
    1752           0 :                     chars[ccnt++].label = copy(buffer);
    1753             :                 }
    1754             :             }
    1755             :         }
    1756           0 :         if ( chars==NULL ) {
    1757             :             struct glyphinfo gi;
    1758           0 :             memset(&gi,0,sizeof(gi)); gi.gcnt = _sf->glyphcnt;
    1759           0 :             props = props_array(_sf,&gi);
    1760           0 :             if ( props==NULL )
    1761           0 : return;
    1762           0 :             node->children = chars = calloc(ccnt+1,sizeof(struct node));
    1763             :         }
    1764           0 :         node->cnt = ccnt;
    1765             :     }
    1766           0 :     free(props);
    1767             : }
    1768             : 
    1769           0 : static void BuildKernTable(struct node *node,struct att_dlg *att) {
    1770           0 :     SplineFont *_sf = att->sf;
    1771             :     OTLookup *otl;
    1772             :     FeatureScriptLangList *fl;
    1773             :     int doit, cnt;
    1774           0 :     struct node *kerns = NULL;
    1775             : 
    1776           0 :     if ( _sf->cidmaster ) _sf = _sf->cidmaster;
    1777             : 
    1778           0 :     for ( doit=0; doit<2; ++doit ) {
    1779           0 :         cnt = 0;
    1780           0 :         for ( otl = _sf->gpos_lookups; otl!=NULL ; otl=otl->next ) {
    1781           0 :             for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
    1782           0 :                 if ( (fl->featuretag==CHR('k','e','r','n') || fl->featuretag==CHR('v','k','r','n')) &&
    1783           0 :                         scriptsHaveDefault(fl->scripts))
    1784           0 :             break;
    1785             :             }
    1786           0 :             if ( otl->lookup_type == gpos_pair && fl!=NULL ) {
    1787           0 :                 if ( doit ) {
    1788           0 :                     kerns[cnt].parent = node;
    1789           0 :                     kerns[cnt].build = BuildGSUBlookups;
    1790           0 :                     kerns[cnt].label = copy(otl->lookup_name);
    1791           0 :                     kerns[cnt].u.otl = otl;
    1792             :                 }
    1793           0 :                 ++cnt;
    1794             :             }
    1795             :         }
    1796           0 :         if ( !doit ) {
    1797           0 :             node->children = kerns = calloc(cnt+1,sizeof(struct node));
    1798           0 :             node->cnt = cnt;
    1799             :         }
    1800             :     }
    1801           0 : }
    1802             : 
    1803           0 : static void BuildMorxTable(struct node *node,struct att_dlg *att) {
    1804           0 :     SplineFont *_sf = att->sf;
    1805             :     OTLookup *otl;
    1806             :     FeatureScriptLangList *fl;
    1807             :     int doit, cnt;
    1808           0 :     struct node *lookups = NULL;
    1809             :     int feat, set;
    1810             : 
    1811           0 :     if ( _sf->cidmaster ) _sf = _sf->cidmaster;
    1812             : 
    1813           0 :     for ( doit=0; doit<2; ++doit ) {
    1814           0 :         cnt = 0;
    1815           0 :         for ( otl = _sf->gsub_lookups; otl!=NULL ; otl=otl->next ) {
    1816           0 :             for ( fl=otl->features; fl!=NULL; fl=fl->next ) {
    1817           0 :                 if ( fl->ismac ||
    1818           0 :                         (OTTagToMacFeature(fl->featuretag,&feat,&set) &&
    1819           0 :                          scriptsHaveDefault(fl->scripts) &&
    1820           0 :                          (otl->lookup_type==gsub_single || otl->lookup_type==gsub_ligature)))
    1821             :                 break;
    1822             :             }
    1823           0 :             if ( fl!=NULL ) {
    1824           0 :                 if ( doit ) {
    1825           0 :                     lookups[cnt].parent = node;
    1826           0 :                     lookups[cnt].build = BuildGSUBlookups;
    1827           0 :                     lookups[cnt].label = copy(otl->lookup_name);
    1828           0 :                     lookups[cnt].u.otl = otl;
    1829             :                 }
    1830           0 :                 ++cnt;
    1831             :             }
    1832             :         }
    1833           0 :         if ( !doit ) {
    1834           0 :             node->children = lookups = calloc(cnt+1,sizeof(struct node));
    1835           0 :             node->cnt = cnt;
    1836             :         }
    1837             :     }
    1838           0 : }
    1839             : 
    1840           0 : static void BuildTable(struct node *node,struct att_dlg *att) {
    1841           0 :     SplineFont *_sf = att->sf;
    1842             :     int script_max;
    1843             :     int i,j;
    1844             :     uint32 *scriptlist;
    1845             :     struct node *scriptnodes;
    1846             :     extern GTextInfo scripts[];
    1847           0 :     int isgsub = node->tag==CHR('G','S','U','B');
    1848             :     char buf[120];
    1849             : 
    1850             :     /* Build the list of scripts that are mentioned in the font */
    1851           0 :     scriptlist = SFScriptsInLookups(_sf,!isgsub);
    1852           0 :     if ( scriptlist==NULL )
    1853           0 : return;
    1854           0 :     for ( i=0; scriptlist[i]!=0; ++i );
    1855           0 :     script_max = i;
    1856           0 :     scriptnodes = calloc(script_max+1,sizeof(struct node));
    1857           0 :     for ( i=0; scriptlist[i]!=0; ++i )
    1858           0 :         scriptnodes[i].tag = scriptlist[i];
    1859           0 :     free( scriptlist );
    1860             : 
    1861           0 :     for ( i=0; i<script_max; ++i ) {
    1862           0 :         for ( j=0; scripts[j].text!=NULL && scriptnodes[i].tag!=(uint32) (intpt) scripts[j].userdata; ++j );
    1863           0 :         buf[0] = '\'';
    1864           0 :         buf[1] = scriptnodes[i].tag>>24;
    1865           0 :         buf[2] = (scriptnodes[i].tag>>16)&0xff;
    1866           0 :         buf[3] = (scriptnodes[i].tag>>8)&0xff;
    1867           0 :         buf[4] = scriptnodes[i].tag&0xff;
    1868           0 :         buf[5] = '\'';
    1869           0 :         buf[6] = ' ';
    1870           0 :         if ( scripts[j].text!=NULL ) {
    1871           0 :             strcpy(buf+7,S_((char*) scripts[j].text));
    1872           0 :             strcat(buf," ");
    1873             :         } else
    1874           0 :             buf[7]='\0';
    1875             : /* GT: See the long comment at "Property|New" */
    1876             : /* GT: The msgstr should contain a translation of "Script", ignore "writing system|" */
    1877             : /* GT: English uses "script" to mean a general writing style (latin, greek, kanji) */
    1878             : /* GT: and the cursive handwriting style. Here we mean the general writing system. */
    1879           0 :         strcat(buf,S_("writing system|Script"));
    1880           0 :         scriptnodes[i].label = copy(buf);
    1881           0 :         scriptnodes[i].build = BuildGSUBscript;
    1882           0 :         scriptnodes[i].parent = node;
    1883             :     }
    1884           0 :     node->children = scriptnodes;
    1885           0 :     node->cnt = i;
    1886             : }
    1887             : 
    1888           0 : static void BuildJSTFTable(struct node *node,struct att_dlg *att) {
    1889           0 :     SplineFont *_sf = att->sf;
    1890             :     int sub_cnt,i,j;
    1891             :     Justify *jscript;
    1892             :     struct node *scriptnodes;
    1893             :     char buf[120];
    1894             :     extern GTextInfo scripts[];
    1895             : 
    1896           0 :     for ( sub_cnt=0, jscript=_sf->justify; jscript!=NULL; jscript=jscript->next, ++sub_cnt );
    1897           0 :     scriptnodes = calloc(sub_cnt+1,sizeof(struct node));
    1898           0 :     for ( i=0, jscript=_sf->justify; jscript!=NULL; jscript=jscript->next, ++i ) {
    1899           0 :         scriptnodes[i].tag = jscript->script;
    1900           0 :         for ( j=0; scripts[j].text!=NULL && scriptnodes[i].tag!=(uint32) (intpt) scripts[j].userdata; ++j );
    1901           0 :         buf[0] = '\'';
    1902           0 :         buf[1] = scriptnodes[i].tag>>24;
    1903           0 :         buf[2] = (scriptnodes[i].tag>>16)&0xff;
    1904           0 :         buf[3] = (scriptnodes[i].tag>>8)&0xff;
    1905           0 :         buf[4] = scriptnodes[i].tag&0xff;
    1906           0 :         buf[5] = '\'';
    1907           0 :         buf[6] = ' ';
    1908           0 :         if ( scripts[j].text!=NULL ) {
    1909           0 :             strcpy(buf+7,S_((char*) scripts[j].text));
    1910           0 :             strcat(buf," ");
    1911             :         } else
    1912           0 :             buf[7]='\0';
    1913             : /* GT: See the long comment at "Property|New" */
    1914             : /* GT: The msgstr should contain a translation of "Script", ignore "writing system|" */
    1915             : /* GT: English uses "script" to me a general writing style (latin, greek, kanji) */
    1916             : /* GT: and the cursive handwriting style. Here we mean the general writing system. */
    1917           0 :         strcat(buf,S_("writing system|Script"));
    1918           0 :         scriptnodes[i].label = copy(buf);
    1919           0 :         scriptnodes[i].build = BuildJSTFscript;
    1920           0 :         scriptnodes[i].parent = node;
    1921           0 :         scriptnodes[i].u.jscript = jscript;
    1922             :     }
    1923           0 :     node->children = scriptnodes;
    1924           0 :     node->cnt = i;
    1925           0 : }
    1926             : 
    1927           0 : static void BuildTop(struct att_dlg *att) {
    1928           0 :     SplineFont *sf, *_sf = att->sf;
    1929           0 :     int hasgsub=0, hasgpos=0, hasgdef=0, hasbase=0, hasjstf=0;
    1930           0 :     int hasmorx=0, haskern=0, hasvkern=0, haslcar=0, hasprop=0, hasopbd=0, hasbsln=0;
    1931           0 :     int haskc=0, hasvkc=0;
    1932             :     int feat, set;
    1933             :     struct node *tables;
    1934             :     PST *pst;
    1935             :     SplineChar *sc;
    1936             :     int i,k,j;
    1937             :     AnchorClass *ac;
    1938             :     OTLookup *otl;
    1939             :     FeatureScriptLangList *fl;
    1940             :     char buffer[200];
    1941             : 
    1942           0 :     SFFindClearUnusedLookupBits(_sf);
    1943             : 
    1944           0 :     for ( otl=_sf->gsub_lookups; otl!=NULL; otl=otl->next ) {
    1945           0 :         for ( fl = otl->features; fl!=NULL ; fl=fl->next ) {
    1946           0 :             if ( !fl->ismac )
    1947           0 :                 hasgsub = true;
    1948           0 :             if ( fl->ismac ||
    1949           0 :                     (OTTagToMacFeature(fl->featuretag,&feat,&set) &&
    1950           0 :                      scriptsHaveDefault(fl->scripts) &&
    1951           0 :                      (otl->lookup_type==gsub_single || otl->lookup_type==gsub_ligature)))
    1952           0 :                 hasmorx = true;
    1953             :         }
    1954             :     }
    1955           0 :     for ( otl=_sf->gpos_lookups; otl!=NULL; otl=otl->next ) {
    1956           0 :         if ( otl->lookup_type==kern_statemachine )
    1957           0 :             haskern = true;
    1958             :         else
    1959           0 :             hasgpos = true;
    1960           0 :         if ( otl->lookup_type == gpos_single )
    1961           0 :             for ( fl = otl->features; fl!=NULL ; fl=fl->next ) {
    1962           0 :                 if ( fl->featuretag==CHR('l','f','b','d') || fl->featuretag==CHR('r','t','b','d') )
    1963           0 :                     hasopbd = true;
    1964             :             }
    1965             :     }
    1966             :             
    1967           0 :     k=0;
    1968             :     do {
    1969           0 :         sf = _sf->subfonts==NULL ? _sf : _sf->subfonts[k];
    1970           0 :         for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc=sf->glyphs[i])!=NULL ) {
    1971           0 :             if (( sc->unicodeenc>=0x10800 && sc->unicodeenc<=0x10fff ) ||
    1972           0 :                     ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10fff &&
    1973           0 :                         isrighttoleft(sc->unicodeenc)) ||
    1974           0 :                         ScriptIsRightToLeft(SCScriptFromUnicode(sc)) ) {
    1975           0 :                 hasprop = true;
    1976             :             }
    1977           0 :             if ( sc->glyph_class!=0 )
    1978           0 :                 hasgdef = true;
    1979           0 :             for ( pst=sc->possub; pst!=NULL; pst=pst->next ) {
    1980           0 :                 if ( pst->type == pst_lcaret ) {
    1981           0 :                     for ( j=pst->u.lcaret.cnt-1; j>=0; --j )
    1982           0 :                         if ( pst->u.lcaret.carets[j]!=0 )
    1983           0 :                     break;
    1984           0 :                     if ( j!=-1 )
    1985           0 :                         hasgdef = haslcar = true;
    1986             :                 }
    1987             :             }
    1988           0 :             if ( sc->kerns!=NULL || sc->vkerns!=NULL )
    1989           0 :                 haskern = hasgpos = true;
    1990             :         }
    1991           0 :         ++k;
    1992           0 :     } while ( k<_sf->subfontcnt );
    1993           0 :     if ( _sf->vkerns!=NULL || _sf->kerns!=NULL )
    1994           0 :         haskern = hasgpos = true;
    1995           0 :     if ( _sf->anchor!=NULL )
    1996           0 :         hasgpos = true;
    1997           0 :     for ( ac = sf->anchor; ac!=NULL; ac=ac->next ) {
    1998           0 :         if ( ac->type==act_curs )
    1999           0 :     break;
    2000             :     }
    2001           0 :     if ( ac!=NULL )
    2002           0 :         hasgdef = true;
    2003           0 :     hasbase = ( _sf->horiz_base!=NULL || _sf->vert_base!=NULL );
    2004           0 :     hasbsln = ( _sf->horiz_base!=NULL && _sf->horiz_base->baseline_cnt!=0 );
    2005           0 :     hasjstf = ( _sf->justify!=NULL );
    2006             : 
    2007           0 :     if ( hasgsub+hasgpos+hasgdef+hasmorx+haskern+haslcar+hasopbd+hasprop+hasbase+hasjstf==0 ) {
    2008           0 :         tables = calloc(2,sizeof(struct node));
    2009           0 :         tables[0].label = copy(_("No Advanced Typography"));
    2010             :     } else {
    2011           0 :         tables = calloc((hasgsub||hasgpos||hasgdef||hasbase||hasjstf)+
    2012           0 :             (hasmorx||haskern||haslcar||hasopbd||hasprop||hasbsln)+1,sizeof(struct node));
    2013           0 :         i=0;
    2014           0 :         if ( hasgsub || hasgpos || hasgdef || hasbase || hasjstf ) {
    2015           0 :             tables[i].label = copy(_("OpenType Tables"));
    2016           0 :             tables[i].children_checked = true;
    2017           0 :             tables[i].children = calloc(hasgsub+hasgpos+hasgdef+hasbase+hasjstf+1,sizeof(struct node));
    2018           0 :             tables[i].cnt = hasgsub + hasgpos + hasgdef + hasbase + hasjstf;
    2019           0 :             if ( hasbase ) {
    2020           0 :                 int sub_cnt= (sf->horiz_base!=NULL) + (sf->vert_base!=NULL), j=0;
    2021           0 :                 tables[i].children[0].label = copy(_("'BASE' Baseline Table"));
    2022           0 :                 tables[i].children[0].tag = CHR('B','A','S','E');
    2023           0 :                 tables[i].children[0].parent = &tables[i];
    2024           0 :                 tables[i].children[0].children_checked = true;
    2025           0 :                 tables[i].children[0].children = calloc(sub_cnt+1,sizeof(struct node));
    2026           0 :                 tables[i].children[0].cnt = sub_cnt;
    2027           0 :                 if ( _sf->horiz_base!=NULL ) {
    2028           0 :                     snprintf(buffer,sizeof(buffer),
    2029           0 :                             P_("Horizontal: %d baseline","Horizontal: %d baselines",_sf->horiz_base->baseline_cnt),
    2030           0 :                             _sf->horiz_base->baseline_cnt );
    2031           0 :                     tables[i].children[0].children[j].label = copy(buffer);
    2032           0 :                     tables[i].children[0].children[j].horizontal = true;
    2033           0 :                     tables[i].children[0].children[j].parent = &tables[i].children[0];
    2034           0 :                     tables[i].children[0].children[j].build = BuildBASE;
    2035           0 :                     ++j;
    2036             :                 }
    2037           0 :                 if ( _sf->vert_base!=NULL ) {
    2038           0 :                     snprintf(buffer,sizeof(buffer),
    2039           0 :                             P_("Vertical: %d baseline","Vertical: %d baselines",_sf->vert_base->baseline_cnt),
    2040           0 :                             _sf->vert_base->baseline_cnt );
    2041           0 :                     tables[i].children[0].children[j].label = copy(buffer);
    2042           0 :                     tables[i].children[0].children[j].horizontal = false;
    2043           0 :                     tables[i].children[0].children[j].parent = &tables[i].children[0];
    2044           0 :                     tables[i].children[0].children[j].build = BuildBASE;
    2045           0 :                     ++j;
    2046             :                 }
    2047             :             }
    2048           0 :             if ( hasgdef ) {
    2049           0 :                 tables[i].children[hasbase].label = copy(_("'GDEF' Glyph Definition Table"));
    2050           0 :                 tables[i].children[hasbase].tag = CHR('G','D','E','F');
    2051           0 :                 tables[i].children[hasbase].build = BuildGDEF;
    2052           0 :                 tables[i].children[hasbase].parent = &tables[i];
    2053             :             }
    2054           0 :             if ( hasgpos ) {
    2055           0 :                 tables[i].children[hasgdef+hasbase].label = copy(_("'GPOS' Glyph Positioning Table"));
    2056           0 :                 tables[i].children[hasgdef+hasbase].tag = CHR('G','P','O','S');
    2057           0 :                 tables[i].children[hasgdef+hasbase].build = BuildTable;
    2058           0 :                 tables[i].children[hasgdef+hasbase].parent = &tables[i];
    2059             :             }
    2060           0 :             if ( hasgsub ) {
    2061           0 :                 tables[i].children[hasgdef+hasgpos+hasbase].label = copy(_("'GSUB' Glyph Substitution Table"));
    2062           0 :                 tables[i].children[hasgdef+hasgpos+hasbase].tag = CHR('G','S','U','B');
    2063           0 :                 tables[i].children[hasgdef+hasgpos+hasbase].build = BuildTable;
    2064           0 :                 tables[i].children[hasgdef+hasgpos+hasbase].parent = &tables[i];
    2065             :             }
    2066           0 :             if ( hasjstf ) {
    2067           0 :                 int k = hasgdef+hasgpos+hasbase+hasgsub;
    2068           0 :                 tables[i].children[k].label = copy(_("'JSTF' Justification Table"));
    2069           0 :                 tables[i].children[k].tag = CHR('J','S','T','F');
    2070           0 :                 tables[i].children[k].parent = &tables[i];
    2071           0 :                 tables[i].children[k].build = BuildJSTFTable;
    2072             :             }
    2073           0 :             ++i;
    2074             :         }
    2075           0 :         if ( hasmorx || haskern || haslcar || hasopbd || hasprop || hasbsln ) {
    2076           0 :             int j = 0;
    2077           0 :             tables[i].label = copy(_("Apple Advanced Typography"));
    2078           0 :             tables[i].children_checked = true;
    2079           0 :             tables[i].children = calloc(hasmorx+haskern+haslcar+hasopbd+hasprop+hasvkern+haskc+hasvkc+hasbsln+1,sizeof(struct node));
    2080           0 :             tables[i].cnt = hasmorx+haskern+hasopbd+hasprop+haslcar+hasvkern+haskc+hasvkc+hasbsln;
    2081           0 :             if ( hasbsln ) {
    2082           0 :                 tables[i].children[j].label = copy(_("'bsln' Horizontal Baseline Table"));
    2083           0 :                 tables[i].children[j].tag = CHR('b','s','l','n');
    2084           0 :                 tables[i].children[j].build = BuildBsLnTable;
    2085           0 :                 tables[i].children[j++].parent = &tables[i];
    2086             :             }
    2087           0 :             if ( haskern ) {
    2088           0 :                 tables[i].children[j].label = copy(_("'kern' Horizontal Kerning Table"));
    2089           0 :                 tables[i].children[j].tag = CHR('k','e','r','n');
    2090           0 :                 tables[i].children[j].build = BuildKernTable;
    2091           0 :                 tables[i].children[j++].parent = &tables[i];
    2092             :             }
    2093           0 :             if ( haslcar ) {
    2094           0 :                 tables[i].children[j].label = copy(_("'lcar' Ligature Caret Table"));
    2095           0 :                 tables[i].children[j].tag = CHR('l','c','a','r');
    2096           0 :                 tables[i].children[j].build = BuildLcar;
    2097           0 :                 tables[i].children[j++].parent = &tables[i];
    2098             :             }
    2099           0 :             if ( hasmorx ) {
    2100           0 :                 tables[i].children[j].label = copy(_("'morx' Glyph Extended Metamorphosis Table"));
    2101           0 :                 tables[i].children[j].tag = CHR('m','o','r','x');
    2102           0 :                 tables[i].children[j].build = BuildMorxTable;
    2103           0 :                 tables[i].children[j++].parent = &tables[i];
    2104             :             }
    2105           0 :             if ( hasopbd ) {
    2106           0 :                 tables[i].children[j].label = copy(_("'opbd' Optical Bounds Table"));
    2107           0 :                 tables[i].children[j].tag = CHR('o','p','b','d');
    2108           0 :                 tables[i].children[j].build = BuildOpticalBounds;
    2109           0 :                 tables[i].children[j++].parent = &tables[i];
    2110             :             }
    2111           0 :             if ( hasprop ) {
    2112           0 :                 tables[i].children[j].label = copy(_("'prop' Glyph Properties Table"));
    2113           0 :                 tables[i].children[j].tag = CHR('p','r','o','p');
    2114           0 :                 tables[i].children[j].build = BuildProperties;
    2115           0 :                 tables[i].children[j++].parent = &tables[i];
    2116             :             }
    2117           0 :             ++i;
    2118             :         }
    2119             :     }
    2120             : 
    2121           0 :     att->tables = tables;
    2122           0 :     att->current = tables;
    2123           0 : }
    2124             : 
    2125           0 : static int _SizeCnt(struct att_dlg *att,struct node *node, int lpos,int depth) {
    2126             :     int i, len;
    2127             : 
    2128           0 :     if ( node->monospace )
    2129           0 :         GDrawSetFont(att->v,att->monofont);
    2130           0 :     node->lpos = lpos++;
    2131           0 :     len = 5+8*depth+ att->as + 5 + GDrawGetText8Width(att->v,node->label,-1);
    2132           0 :     if ( len>att->maxl ) att->maxl = len;
    2133           0 :     if ( node->monospace )
    2134           0 :         GDrawSetFont(att->v,att->font);
    2135             : 
    2136           0 :     if ( node->open ) {
    2137           0 :         if ( !node->children_checked && node->build!=NULL ) {
    2138           0 :             (node->build)(node,att);
    2139           0 :             node->children_checked = true;
    2140             :         }
    2141           0 :         for ( i=0; i<node->cnt; ++i )
    2142           0 :             lpos = _SizeCnt(att,&node->children[i],lpos,depth+1);
    2143             :     }
    2144           0 : return( lpos );
    2145             : }
    2146             : 
    2147           0 : static int SizeCnt(struct att_dlg *att,struct node *node, int lpos) {
    2148           0 :     att->maxl = 0;
    2149           0 :     GDrawSetFont(att->v,att->font);
    2150           0 :     while ( node->label ) {
    2151           0 :         lpos = _SizeCnt(att,node,lpos,0);
    2152           0 :         ++node;
    2153             :     }
    2154             : 
    2155           0 :     GScrollBarSetBounds(att->vsb,0,lpos,att->lines_page);
    2156           0 :     GScrollBarSetBounds(att->hsb,0,att->maxl,att->page_width);
    2157           0 :     att->open_cnt = lpos;
    2158           0 : return( lpos );
    2159             : }
    2160             : 
    2161           0 : static struct node *NodeFindLPos(struct node *node,int lpos,int *depth) {
    2162             :     for (;;) {
    2163           0 :         if ( node->lpos==lpos )
    2164           0 : return( node );
    2165           0 :         if ( node[1].label!=NULL && node[1].lpos<=lpos )
    2166           0 :             ++node;
    2167           0 :         else if ( node->children==NULL || !node->open )
    2168           0 : return( NULL );
    2169             :         else {
    2170           0 :             node = node->children;
    2171           0 :             ++*depth;
    2172             :         }
    2173           0 :     }
    2174             : }
    2175             : 
    2176           0 : static struct node *NodeNext(struct node *node,int *depth) {
    2177           0 :     if ( node->open && node->children && node->children[0].label ) {
    2178           0 :         ++*depth;
    2179           0 : return( node->children );
    2180             :     }
    2181             :     for (;;) {
    2182           0 :         if ( node[1].label )
    2183           0 : return( node+1 );
    2184           0 :         node = node->parent;
    2185           0 :         --*depth;
    2186           0 :         if ( node==NULL )
    2187           0 : return( NULL );
    2188           0 :     }
    2189             : }
    2190             : 
    2191           0 : static struct node *NodePrev(struct att_dlg *att, struct node *node,int *depth) {
    2192           0 :     while ( node->parent!=NULL && node==node->parent->children ) {
    2193           0 :         node = node->parent;
    2194           0 :         --*depth;
    2195             :     }
    2196           0 :     if ( node->parent==NULL && node==att->tables )
    2197           0 : return( NULL );
    2198           0 :     --node;
    2199           0 :     while ( node->open ) {
    2200           0 :         node = &node->children[node->cnt-1];
    2201           0 :         ++*depth;
    2202             :     }
    2203           0 : return( node );
    2204             : }
    2205             : 
    2206           0 : static char *findstartquote(char *str) {
    2207             :     static int quotes[] = { '"', 0x00ab, 0x2018, 0x201b, 0x201c, 0x201e, 0 };
    2208             :     char *last, *cur;
    2209             :     int i, ch;
    2210             : 
    2211           0 :     for ( cur=str; *cur!='\0'; ) {
    2212           0 :         last = cur;
    2213           0 :         ch = utf8_ildb((const char **) &cur);
    2214           0 :         for ( i=0; quotes[i]!=0; ++i )
    2215           0 :             if ( ch==quotes[i] )
    2216           0 : return( last );
    2217             :     }
    2218           0 : return( NULL );
    2219             : }
    2220             : 
    2221           0 : static char *findendquote(char *str) {
    2222             :     static int endquotes[] = { '"', 0x00bb, 0x2019, 0x201b, 0x201d, 0x201e, 0 };
    2223             :     char *last, *cur;
    2224             :     int i, ch;
    2225             : 
    2226           0 :     /* startquote =*/ utf8_ildb((const char **) &str);
    2227           0 :     for ( cur=str; *cur!='\0'; ) {
    2228           0 :         last = cur;
    2229           0 :         ch = utf8_ildb((const char **) &cur);
    2230           0 :         if ( ch==' ' )
    2231           0 : return( NULL );
    2232           0 :         for ( i=0; endquotes[i]!=0; ++i )
    2233           0 :             if ( ch==endquotes[i] )
    2234           0 : return( last );
    2235             :     }
    2236           0 : return( NULL );
    2237             : }
    2238             : 
    2239           0 : static void AttExpose(struct att_dlg *att,GWindow pixmap,GRect *rect) {
    2240             :     int depth, y;
    2241             :     struct node *node;
    2242             :     GRect r;
    2243             :     Color fg;
    2244             :     char *spt, *ept;
    2245             : 
    2246           0 :     GDrawFillRect(pixmap,rect,GDrawGetDefaultBackground(NULL));
    2247           0 :     GDrawSetLineWidth(pixmap,0);
    2248             : 
    2249           0 :     r.height = r.width = att->as;
    2250           0 :     y = (rect->y/att->fh) * att->fh + att->as;
    2251           0 :     depth=0;
    2252           0 :     node = NodeFindLPos(att->tables,rect->y/att->fh+att->off_top,&depth);
    2253           0 :     GDrawSetFont(pixmap,att->font);
    2254           0 :     while ( node!=NULL ) {
    2255           0 :         r.y = y-att->as+1;
    2256           0 :         r.x = 5+8*depth - att->off_left;
    2257           0 :         fg = node==att->current ? 0xff0000 : 0x000000;
    2258           0 :         if ( node->build || node->children ) {
    2259           0 :             GDrawDrawRect(pixmap,&r,fg);
    2260           0 :             GDrawDrawLine(pixmap,r.x+2,r.y+att->as/2,r.x+att->as-2,r.y+att->as/2,
    2261             :                     fg);
    2262           0 :             if ( !node->open )
    2263           0 :                 GDrawDrawLine(pixmap,r.x+att->as/2,r.y+2,r.x+att->as/2,r.y+att->as-2,
    2264             :                         fg);
    2265             :         }
    2266           0 :         if ( node->monospace )
    2267           0 :             GDrawSetFont(pixmap,att->monofont);
    2268           0 :         ept = NULL;
    2269           0 :         if ( att->dlg_type==dt_font_comp ) {
    2270           0 :             if ( (spt = findstartquote(node->label))!=NULL )
    2271           0 :                 ept = findendquote(spt);
    2272             :         }
    2273           0 :         if ( ept==NULL )
    2274           0 :             GDrawDrawText8(pixmap,r.x+r.width+5,y,node->label,-1,fg);
    2275             :         else {
    2276             :             int len;
    2277           0 :             len = GDrawDrawText8(pixmap,r.x+r.width+5,y,node->label,spt-node->label,fg);
    2278           0 :             len += GDrawDrawText8(pixmap,r.x+r.width+5+len,y,spt,ept-spt,0x0000ff);
    2279           0 :             GDrawDrawText8(pixmap,r.x+r.width+5+len,y,ept,-1,fg);
    2280             :         }
    2281           0 :         if ( node->monospace )
    2282           0 :             GDrawSetFont(pixmap,att->font);
    2283           0 :         node = NodeNext(node,&depth);
    2284           0 :         y += att->fh;
    2285           0 :         if ( y-att->fh>rect->y+rect->height )
    2286           0 :     break;
    2287             :     }
    2288           0 : }
    2289             : 
    2290           0 : static void ATTChangeCurrent(struct att_dlg *att,struct node *node) {
    2291           0 :     int oldl = att->current->lpos, newl;
    2292             :     GRect r;
    2293           0 :     if ( node==NULL )
    2294           0 : return;
    2295           0 :     newl = node->lpos;
    2296           0 :     att->current = node;
    2297           0 :     r.x =0; r.width = 3000;
    2298           0 :     if ( newl<att->off_top || newl>=att->off_top+att->lines_page ) {
    2299           0 :         att->off_top = newl-att->lines_page/3;
    2300           0 :         if ( att->off_top<0 ) att->off_top = 0;
    2301           0 :         GScrollBarSetPos(att->vsb,att->off_top);
    2302           0 :         GDrawRequestExpose(att->v,NULL,false);
    2303           0 :     } else if ( newl==oldl+1 ) {
    2304           0 :         r.y = (oldl-att->off_top)*att->fh; r.height = 2*att->fh;
    2305           0 :         GDrawRequestExpose(att->v,&r,false);
    2306           0 :     } else if ( newl==oldl-1 ) {
    2307           0 :         r.y = (newl-att->off_top)*att->fh; r.height = 2*att->fh;
    2308           0 :         GDrawRequestExpose(att->v,&r,false);
    2309             :     } else {
    2310           0 :         r.y = (newl-att->off_top)*att->fh; r.height = att->fh;
    2311           0 :         GDrawRequestExpose(att->v,&r,false);
    2312           0 :         r.y = (oldl-att->off_top)*att->fh; r.height = att->fh;
    2313           0 :         GDrawRequestExpose(att->v,&r,false);
    2314             :     }
    2315             : }
    2316             : 
    2317           0 : static void pututf8(uint32 ch,FILE *file) {
    2318           0 :     if ( ch<0x80 )
    2319           0 :         putc(ch,file);
    2320           0 :     else if ( ch<0x800 ) {
    2321           0 :         putc(0xc0 | (ch>>6), file);
    2322           0 :         putc(0x80 | (ch&0x3f), file);
    2323             :     } else {
    2324           0 :         putc( 0xe0 | (ch>>12),file );
    2325           0 :         putc( 0x80 | ((ch>>6)&0x3f),file );
    2326           0 :         putc( 0x80 | (ch&0x3f),file );
    2327             :     }
    2328           0 : }
    2329             : 
    2330           0 : static void AttSave(struct att_dlg *att) {
    2331           0 :     char *ret = gwwv_save_filename(_("Save"),NULL,
    2332             :             "*.txt");
    2333             :     char *cret;
    2334             :     FILE *file;
    2335             :     char *pt;
    2336             :     struct node *node;
    2337           0 :     int depth=0, d;
    2338             : 
    2339           0 :     if ( ret==NULL )
    2340           0 : return;
    2341           0 :     cret = utf82def_copy(ret);
    2342           0 :     file = fopen(cret,"w");
    2343           0 :     free(cret);
    2344           0 :     if ( file==NULL ) {
    2345           0 :         ff_post_error(_("Save Failed"),_("Save Failed"),ret);
    2346           0 :         free(ret);
    2347           0 : return;
    2348             :     }
    2349           0 :     free(ret);
    2350             : 
    2351           0 :     pututf8(0xfeff,file);       /* Zero width something or other. Marks this as unicode, utf8 */
    2352           0 :     node = NodeFindLPos(att->tables,0,&depth);
    2353           0 :     while ( node!=NULL ) {
    2354           0 :         d = depth;
    2355           0 :         while ( d>=4 ) {
    2356           0 :             pututf8('\t',file);
    2357           0 :             d -= 4;
    2358             :         }
    2359           0 :         while ( d>0 ) {
    2360           0 :             pututf8(' ',file);
    2361           0 :             pututf8(' ',file);
    2362           0 :             --d;
    2363             :         }
    2364           0 :         if ( !node->build && !node->children )
    2365           0 :             pututf8(' ',file);
    2366           0 :         else if ( node->open )
    2367           0 :             pututf8('-',file);
    2368             :         else
    2369           0 :             pututf8('+',file);
    2370           0 :         for ( pt=node->label; *pt; ++pt )
    2371           0 :             putc(*pt,file);
    2372           0 :         pututf8('\n',file);
    2373           0 :         node = NodeNext(node,&depth);
    2374             :     }
    2375           0 :     fclose(file);
    2376             : }
    2377             : 
    2378           0 : static void AttSaveM(GWindow gw, GMenuItem *mi,GEvent *e) {
    2379           0 :     struct att_dlg *att = (struct att_dlg *) GDrawGetUserData(gw);
    2380           0 :     AttSave(att);
    2381           0 : }
    2382             : 
    2383             : static GMenuItem att_popuplist[] = {
    2384             :     { { (unichar_t *) N_("Save"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 'S' }, 'S', ksm_control, NULL, NULL, AttSaveM, 0 },
    2385             :     GMENUITEM_EMPTY
    2386             : };
    2387             : 
    2388           0 : static FontView *FVVerify(FontView *fv) {
    2389             :     FontView *test;
    2390             : 
    2391           0 :     for ( test= fv_list; test!=NULL && test!=fv; test=(FontView *) test->b.next );
    2392           0 : return( test );
    2393             : }
    2394             : 
    2395           0 : static void FontCompActivate(struct att_dlg *att,struct node *node) {
    2396             :     char *pt, *pt2; int ch;
    2397           0 :     SplineChar *sc1=NULL, *sc2=NULL;
    2398           0 :     int size=0, depth=0;
    2399             :     BDFFont *bdf1, *bdf2;
    2400             : 
    2401           0 :     att->fv1 = FVVerify(att->fv1);
    2402           0 :     att->fv2 = FVVerify(att->fv2);
    2403             : 
    2404           0 :     pt = findstartquote(node->label);
    2405           0 :     if ( pt==NULL )
    2406           0 : return;
    2407           0 :     pt2 = findendquote(pt);
    2408           0 :     if ( pt2==NULL )
    2409           0 : return;
    2410           0 :     utf8_ildb((const char **) &pt);
    2411           0 :     ch = *pt2; *pt2 = '\0';
    2412           0 :     if ( att->fv1!=NULL )
    2413           0 :         sc1 = SFGetChar(att->fv1->b.sf,-1,pt);
    2414           0 :     if ( att->fv2!=NULL )
    2415           0 :         sc2 = SFGetChar(att->fv2->b.sf,-1,pt);
    2416           0 :     *pt2 = ch;
    2417             : 
    2418           0 :     pt = strchr(node->label,'@');
    2419           0 :     if ( pt!=NULL ) {
    2420           0 :         for (pt2 = pt-1; pt2>=node->label && isdigit(*pt2); --pt2 );
    2421           0 :         size = strtol(pt2+1,NULL,10);
    2422           0 :         depth = strtol(pt+1,NULL,10);
    2423             :     }
    2424           0 :     if ( size!=0 && depth!=0 ) {
    2425           0 :         for ( bdf1 = att->fv1->b.sf->bitmaps; bdf1!=NULL &&
    2426           0 :                 (bdf1->pixelsize!=size || BDFDepth(bdf1)!=depth); bdf1=bdf1->next );
    2427           0 :         for ( bdf2 = att->fv2->b.sf->bitmaps; bdf2!=NULL &&
    2428           0 :                 (bdf2->pixelsize!=size || BDFDepth(bdf2)!=depth); bdf2=bdf2->next );
    2429           0 :         if ( bdf1!=NULL && sc1!=NULL && sc1->orig_pos<bdf1->glyphcnt &&
    2430           0 :                 bdf1->glyphs[sc1->orig_pos]!=NULL )
    2431           0 :             BitmapViewCreate(bdf1->glyphs[sc1->orig_pos],bdf1,att->fv1,
    2432           0 :                     att->fv1->b.map->backmap[sc1->orig_pos]);
    2433           0 :         if ( bdf2!=NULL && sc2!=NULL && sc2->orig_pos<bdf2->glyphcnt &&
    2434           0 :                 bdf2->glyphs[sc2->orig_pos]!=NULL )
    2435           0 :             BitmapViewCreate(bdf2->glyphs[sc2->orig_pos],bdf2,att->fv2,
    2436           0 :                     att->fv2->b.map->backmap[sc2->orig_pos]);
    2437             :     } else {
    2438           0 :         if ( sc1!=NULL )
    2439           0 :             CharViewCreate(sc1,att->fv1,att->fv1->b.map->backmap[sc1->orig_pos]);
    2440           0 :         if ( sc2!=NULL )
    2441           0 :             CharViewCreate(sc2,att->fv2,att->fv2->b.map->backmap[sc2->orig_pos]);
    2442             :     }
    2443             : }
    2444             : 
    2445           0 : static void _ATT_FreeImage(const void *_ci, GImage *img) {
    2446           0 :     GImageDestroy(img);
    2447           0 : }
    2448             : 
    2449           0 : static GImage *_ATT_PopupImage(const void *_att) {
    2450           0 :     const struct att_dlg *att = _att;
    2451             :     char *start, *pt;
    2452             :     int ch;
    2453             :     SplineChar *sc;
    2454             :     int isliga;
    2455             : 
    2456           0 :     if ( att->popup_node==NULL || att->popup_node->label==NULL )
    2457           0 : return( NULL );
    2458           0 :     for ( start=att->popup_node->label; *start==' ' || isdigit(*start); ++start );
    2459           0 :     for ( pt=start; *pt!='\0' && *pt!=' '; ++pt );
    2460           0 :     ch = *pt; *pt = '\0';
    2461           0 :     sc = SFGetChar(att->sf,-1,start);
    2462           0 :     *pt = ch;
    2463           0 :     if ( sc==NULL )
    2464           0 : return( NULL );
    2465             : 
    2466           0 :     isliga = -1;
    2467           0 :     while ( *pt==' ' || *pt=='=' || *pt=='>' || *pt=='<' ) {
    2468           0 :         if ( *pt=='<' ) isliga = true;
    2469           0 :         if ( *pt=='>' ) isliga = false;
    2470           0 :         ++pt;
    2471             :     }
    2472           0 :     if ( !isalpha(*pt))         /* If alphabetic, then show the glyph names that follow too. Otherwise show nothing for gpos lookups */
    2473           0 :         pt = "";
    2474           0 : return( NameList_GetImage(att->sf,sc,att->def_layer,pt,isliga));
    2475             : }
    2476             : 
    2477           0 : static void ATTStartPopup(struct att_dlg *att,struct node *node) {
    2478           0 :     att->popup_node = node;
    2479           0 :     GGadgetPreparePopupImage(att->v,NULL,att,_ATT_PopupImage,_ATT_FreeImage);
    2480           0 : }
    2481             : 
    2482           0 : static void AttMouse(struct att_dlg *att,GEvent *event) {
    2483             :     int l, depth;
    2484             :     struct node *node;
    2485             :     GRect r;
    2486             : 
    2487           0 :     if ( event->type==et_mousedown ) {
    2488           0 :         GGadgetEndPopup();
    2489           0 :         if ( event->u.mouse.button==3 ) {
    2490             :             static int done=false;
    2491           0 :             if ( !done ) {
    2492           0 :                 done = true;
    2493           0 :                 att_popuplist[0].ti.text = (unichar_t *) _( (char *)att_popuplist[0].ti.text);
    2494             :             }
    2495           0 :             GMenuCreatePopupMenu(att->v,event, att_popuplist);
    2496             :         }
    2497           0 : return;
    2498             :     }
    2499             : 
    2500           0 :     l = (event->u.mouse.y/att->fh);
    2501           0 :     depth=0;
    2502           0 :     node = NodeFindLPos(att->tables,l+att->off_top,&depth);
    2503           0 :     if ( event->type==et_mouseup ) {
    2504           0 :         ATTChangeCurrent(att,node);
    2505           0 :         if ( event->u.mouse.y > l*att->fh+att->as ||
    2506           0 :                 event->u.mouse.x<5+8*depth ||
    2507           0 :                 event->u.mouse.x>=5+8*depth+att->as || node==NULL ) {
    2508             :                 /* Not in +/- rectangle */
    2509           0 :             if ( event->u.mouse.x > 5+8*depth+att->as && node!=NULL &&
    2510           0 :                     att->dlg_type == dt_font_comp && event->u.mouse.clicks>1 )
    2511           0 :                 FontCompActivate(att,node);
    2512           0 : return;
    2513             :         }
    2514           0 :         node->open = !node->open;
    2515             : 
    2516           0 :         SizeCnt(att,att->tables,0);
    2517             : 
    2518           0 :         r.x = 0; r.width = 3000;
    2519           0 :         r.y = l*att->fh; r.height = 3000;
    2520           0 :         GDrawRequestExpose(att->v,&r,false);
    2521           0 :     } else if ( event->type == et_mousemove ) {
    2522           0 :         GGadgetEndPopup();
    2523           0 :         ATTStartPopup(att,node);
    2524             :     }
    2525             : }
    2526             : 
    2527           0 : static void AttScroll(struct att_dlg *att,struct sbevent *sb) {
    2528           0 :     int newpos = att->off_top;
    2529             : 
    2530           0 :     switch( sb->type ) {
    2531             :       case et_sb_top:
    2532           0 :         newpos = 0;
    2533           0 :       break;
    2534             :       case et_sb_uppage:
    2535           0 :         newpos -= att->lines_page;
    2536           0 :       break;
    2537             :       case et_sb_up:
    2538           0 :         --newpos;
    2539           0 :       break;
    2540             :       case et_sb_down:
    2541           0 :         ++newpos;
    2542           0 :       break;
    2543             :       case et_sb_downpage:
    2544           0 :         newpos += att->lines_page;
    2545           0 :       break;
    2546             :       case et_sb_bottom:
    2547           0 :         newpos = att->open_cnt-att->lines_page;
    2548           0 :       break;
    2549             :       case et_sb_thumb:
    2550             :       case et_sb_thumbrelease:
    2551           0 :         newpos = sb->pos;
    2552           0 :       break;
    2553             :     }
    2554           0 :     if ( newpos>att->open_cnt-att->lines_page )
    2555           0 :         newpos = att->open_cnt-att->lines_page;
    2556           0 :     if ( newpos<0 ) newpos =0;
    2557           0 :     if ( newpos!=att->off_top ) {
    2558           0 :         int diff = newpos-att->off_top;
    2559           0 :         att->off_top = newpos;
    2560           0 :         GScrollBarSetPos(att->vsb,att->off_top);
    2561           0 :         GDrawScroll(att->v,NULL,0,diff*att->fh);
    2562             :     }
    2563           0 : }
    2564             : 
    2565             : 
    2566           0 : static void AttHScroll(struct att_dlg *att,struct sbevent *sb) {
    2567           0 :     int newpos = att->off_left;
    2568             : 
    2569           0 :     switch( sb->type ) {
    2570             :       case et_sb_top:
    2571           0 :         newpos = 0;
    2572           0 :       break;
    2573             :       case et_sb_uppage:
    2574           0 :         newpos -= att->page_width;
    2575           0 :       break;
    2576             :   /* I'd like to scroll by one character. .6*em is right for courier. */
    2577             :       case et_sb_up:
    2578           0 :         newpos -= (6*att->fh)/10;
    2579           0 :       break;
    2580             :       case et_sb_down:
    2581           0 :         newpos += (6*att->fh)/10;
    2582           0 :       break;
    2583             :       case et_sb_downpage:
    2584           0 :         newpos += att->page_width;
    2585           0 :       break;
    2586             :       case et_sb_bottom:
    2587           0 :         newpos = att->maxl-att->page_width;
    2588           0 :       break;
    2589             :       case et_sb_thumb:
    2590             :       case et_sb_thumbrelease:
    2591           0 :         newpos = sb->pos;
    2592           0 :       break;
    2593             :     }
    2594           0 :     if ( newpos>att->maxl-att->page_width )
    2595           0 :         newpos = att->maxl-att->page_width;
    2596           0 :     if ( newpos<0 ) newpos =0;
    2597           0 :     if ( newpos!=att->off_left ) {
    2598           0 :         int diff = newpos-att->off_left;
    2599           0 :         att->off_left = newpos;
    2600           0 :         GScrollBarSetPos(att->hsb,att->off_left);
    2601           0 :         GDrawScroll(att->v,NULL,-diff,0);
    2602             :     }
    2603           0 : }
    2604             : 
    2605           0 : static void AttResize(struct att_dlg *att,GEvent *event) {
    2606             :     GRect size, wsize;
    2607             :     int lcnt;
    2608           0 :     int sbsize = GDrawPointsToPixels(att->gw,_GScrollBar_Width);
    2609             : 
    2610           0 :     GDrawGetSize(att->gw,&size);
    2611           0 :     lcnt = (size.height-att->bmargin)/att->fh;
    2612           0 :     GGadgetResize(att->vsb,sbsize,lcnt*att->fh);
    2613           0 :     GGadgetMove(att->vsb,size.width-sbsize,0);
    2614           0 :     GGadgetResize(att->hsb,size.width-sbsize,sbsize);
    2615           0 :     GGadgetMove(att->hsb,0,lcnt*att->fh);
    2616           0 :     GDrawResize(att->v,size.width-sbsize,lcnt*att->fh);
    2617           0 :     att->page_width = size.width-sbsize;
    2618           0 :     att->lines_page = lcnt;
    2619           0 :     GScrollBarSetBounds(att->vsb,0,att->open_cnt,att->lines_page);
    2620           0 :     GScrollBarSetBounds(att->hsb,0,att->maxl,att->page_width);
    2621             : 
    2622           0 :     GGadgetGetSize(att->cancel,&wsize);
    2623           0 :     GGadgetMove(att->cancel,(size.width-wsize.width)/2,lcnt*att->fh+sbsize+5);
    2624           0 :     GDrawRequestExpose(att->v,NULL,true);
    2625           0 :     GDrawRequestExpose(att->gw,NULL,true);
    2626           0 : }
    2627             : 
    2628           0 : static int AttChar(struct att_dlg *att,GEvent *event) {
    2629           0 :     int depth = 0;
    2630             :     int pos;
    2631             : 
    2632           0 :     switch (event->u.chr.keysym) {
    2633             :       case GK_F1: case GK_Help:
    2634           0 :         help("showatt.html");
    2635           0 : return( true );
    2636             :       case GK_Return: case GK_KP_Enter:
    2637           0 :         att->current->open = !att->current->open;
    2638             : 
    2639           0 :         att->open_cnt = SizeCnt(att,att->tables,0);
    2640           0 :         GScrollBarSetBounds(att->vsb,0,att->open_cnt,att->lines_page);
    2641             : 
    2642           0 :         GDrawRequestExpose(att->v,NULL,false);
    2643           0 : return( true );
    2644             :       case GK_Page_Down: case GK_KP_Page_Down:
    2645           0 :         pos = att->off_top+(att->lines_page<=1?1:att->lines_page-1);
    2646           0 :         if ( pos >= att->open_cnt-att->lines_page )
    2647           0 :             pos = att->open_cnt-att->lines_page;
    2648           0 :         if ( pos<0 ) pos = 0;
    2649           0 :         att->off_top = pos;
    2650           0 :         GScrollBarSetPos(att->vsb,pos);
    2651           0 :         GDrawRequestExpose(att->v,NULL,false);
    2652           0 : return( true );
    2653             :       case GK_Down: case GK_KP_Down:
    2654           0 :         ATTChangeCurrent(att,NodeNext(att->current,&depth));
    2655           0 : return( true );
    2656             :       case GK_Up: case GK_KP_Up:
    2657           0 :         ATTChangeCurrent(att,NodePrev(att,att->current,&depth));
    2658           0 : return( true );
    2659             :       case GK_Page_Up: case GK_KP_Page_Up:
    2660           0 :         pos = att->off_top-(att->lines_page<=1?1:att->lines_page-1);
    2661           0 :         if ( pos<0 ) pos = 0;
    2662           0 :         att->off_top = pos;
    2663           0 :         GScrollBarSetPos(att->vsb,pos);
    2664           0 :         GDrawRequestExpose(att->v,NULL,false);
    2665           0 : return( true );
    2666             :       case GK_Left: case GK_KP_Left:
    2667           0 :         ATTChangeCurrent(att,att->current->parent);
    2668           0 : return( true );
    2669             :       case GK_Right: case GK_KP_Right:
    2670           0 :         if ( !att->current->open ) {
    2671           0 :             att->current->open = !att->current->open;
    2672             : 
    2673           0 :             att->open_cnt = SizeCnt(att,att->tables,0);
    2674           0 :             GScrollBarSetBounds(att->vsb,0,att->open_cnt,att->lines_page);
    2675           0 :             if ( att->current->children!=NULL )
    2676           0 :                 att->current = att->current->children;
    2677             : 
    2678           0 :             GDrawRequestExpose(att->v,NULL,false);
    2679             :         } else
    2680           0 :             ATTChangeCurrent(att,att->current->children);
    2681           0 : return( true );
    2682             :       case GK_Home: case GK_KP_Home:
    2683           0 :         ATTChangeCurrent(att,att->tables);
    2684           0 : return( true );
    2685             :       case GK_End: case GK_KP_End:
    2686           0 :         ATTChangeCurrent(att,NodeFindLPos(att->tables,att->open_cnt-1,&depth));
    2687           0 : return( true );
    2688             :       case 'S': case 's':
    2689           0 :         if ( event->u.mouse.state&ksm_control )
    2690           0 :             AttSave(att);
    2691           0 : return( true );
    2692             :     }
    2693           0 : return( false );
    2694             : }
    2695             : 
    2696           0 : static int attv_e_h(GWindow gw, GEvent *event) {
    2697           0 :     struct att_dlg *att = (struct att_dlg *) GDrawGetUserData(gw);
    2698             : 
    2699           0 :     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
    2700           0 :             (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
    2701           0 : return( GGadgetDispatchEvent(att->vsb,event));
    2702             :     }
    2703             : 
    2704           0 :     switch ( event->type ) {
    2705             :       case et_expose:
    2706           0 :         AttExpose(att,gw,&event->u.expose.rect);
    2707           0 :       break;
    2708             :       case et_char:
    2709           0 : return( AttChar(att,event));
    2710             :       case et_mousedown:
    2711             :       case et_mousemove:
    2712             :       case et_mouseup:
    2713           0 :         AttMouse(att,event);
    2714           0 :       break;
    2715             :     }
    2716           0 : return( true );
    2717             : }
    2718             : 
    2719           0 : static int att_e_h(GWindow gw, GEvent *event) {
    2720           0 :     struct att_dlg *att = (struct att_dlg *) GDrawGetUserData(gw);
    2721             : 
    2722           0 :     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
    2723           0 :             (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
    2724           0 : return( GGadgetDispatchEvent(att->vsb,event));
    2725             :     }
    2726             : 
    2727           0 :     switch ( event->type ) {
    2728             :       case et_expose:
    2729           0 :       break;
    2730             :       case et_char:
    2731           0 : return( AttChar(att,event));
    2732             :       break;
    2733             :       case et_resize:
    2734           0 :         if ( event->u.resize.sized )
    2735           0 :             AttResize(att,event);
    2736           0 :       break;
    2737             :       case et_controlevent:
    2738           0 :         switch ( event->u.control.subtype ) {
    2739             :           case et_scrollbarchange:
    2740           0 :             if ( event->u.control.g == att->vsb )
    2741           0 :                 AttScroll(att,&event->u.control.u.sb);
    2742             :             else
    2743           0 :                 AttHScroll(att,&event->u.control.u.sb);
    2744           0 :           break;
    2745             :           case et_buttonactivate:
    2746           0 :             att->done = true;
    2747           0 :             if ( att->dlg_type==dt_font_comp )
    2748           0 :                 GDrawDestroyWindow(gw);
    2749           0 :           break;
    2750             :         }
    2751           0 :       break;
    2752             :       case et_close:
    2753           0 :         att->done = true;
    2754           0 :         if ( att->dlg_type==dt_font_comp )
    2755           0 :             GDrawDestroyWindow(gw);
    2756           0 :       break;
    2757             :       case et_destroy:
    2758           0 :         if ( att!=NULL ) {
    2759           0 :             nodesfree(att->tables);
    2760           0 :             free(att);
    2761             :         }
    2762             :     }
    2763           0 : return( true );
    2764             : }
    2765             : 
    2766           0 : static void ShowAttCreateDlg(struct att_dlg *att, SplineFont *sf, int which,
    2767             :         char *win_title) {
    2768             :     GRect pos;
    2769             :     GWindowAttrs wattrs;
    2770             :     FontRequest rq;
    2771             :     int as, ds, ld;
    2772             :     GGadgetCreateData gcd[5];
    2773             :     GTextInfo label[4];
    2774           0 :     int sbsize = GDrawPointsToPixels(NULL,_GScrollBar_Width);
    2775             :     static GFont *monofont=NULL, *propfont=NULL;
    2776             : 
    2777           0 :     if ( sf->cidmaster ) sf = sf->cidmaster;
    2778             : 
    2779           0 :     memset( att,0,sizeof(*att));
    2780           0 :     att->sf = sf;
    2781           0 :     att->dlg_type = which;
    2782             : 
    2783           0 :     memset(&wattrs,0,sizeof(wattrs));
    2784           0 :     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
    2785           0 :     wattrs.event_masks = ~(1<<et_charup);
    2786           0 :     wattrs.is_dlg = true;
    2787           0 :     wattrs.restrict_input_to_me = which==dt_show_att;
    2788           0 :     wattrs.undercursor = 1;
    2789           0 :     wattrs.cursor = ct_pointer;
    2790           0 :     wattrs.utf8_window_title = win_title;
    2791           0 :     pos.x = pos.y = 0;
    2792           0 :     pos.width = GDrawPointsToPixels(NULL,GGadgetScale(which==0?200:450));
    2793           0 :     pos.height = GDrawPointsToPixels(NULL,300);
    2794           0 :     att->gw = GDrawCreateTopWindow(NULL,&pos,att_e_h,att,&wattrs);
    2795             : 
    2796           0 :     if ( propfont==NULL ) {
    2797           0 :         memset(&rq,'\0',sizeof(rq));
    2798           0 :         rq.utf8_family_name = SANS_UI_FAMILIES;
    2799           0 :         rq.point_size = 12;
    2800           0 :         rq.weight = 400;
    2801           0 :         propfont = GDrawInstanciateFont(att->gw,&rq);
    2802           0 :         propfont = GResourceFindFont("ShowATT.Font",propfont);
    2803             : 
    2804           0 :         GDrawDecomposeFont(propfont, &rq);
    2805           0 :         rq.utf8_family_name = MONO_UI_FAMILIES; /* I want to show tabluar data sometimes */
    2806           0 :         monofont = GDrawInstanciateFont(att->gw,&rq);
    2807           0 :         monofont = GResourceFindFont("ShowATT.MonoFont",monofont);
    2808             :     }
    2809           0 :     att->font = propfont;
    2810           0 :     att->monofont = monofont;
    2811           0 :     GDrawWindowFontMetrics(att->gw,att->font,&as,&ds,&ld);
    2812           0 :     att->fh = as+ds; att->as = as;
    2813             : 
    2814           0 :     att->bmargin = GDrawPointsToPixels(NULL,32)+sbsize;
    2815             : 
    2816           0 :     att->lines_page = (pos.height-att->bmargin)/att->fh;
    2817           0 :     att->page_width = pos.width-sbsize;
    2818           0 :     wattrs.mask = wam_events|wam_cursor/*|wam_bordwidth|wam_bordcol*/;
    2819           0 :     wattrs.border_width = 1;
    2820           0 :     wattrs.border_color = 0x000000;
    2821           0 :     pos.x = 0; pos.y = 0;
    2822           0 :     pos.width -= sbsize; pos.height = att->lines_page*att->fh;
    2823           0 :     att->v = GWidgetCreateSubWindow(att->gw,&pos,attv_e_h,att,&wattrs);
    2824           0 :     GDrawSetVisible(att->v,true);
    2825             : 
    2826           0 :     memset(&label,0,sizeof(label));
    2827           0 :     memset(&gcd,0,sizeof(gcd));
    2828             : 
    2829           0 :     gcd[0].gd.pos.x = pos.width; gcd[0].gd.pos.y = 0;
    2830           0 :     gcd[0].gd.pos.width = sbsize;
    2831           0 :     gcd[0].gd.pos.height = pos.height;
    2832           0 :     gcd[0].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels | gg_sb_vert;
    2833           0 :     gcd[0].creator = GScrollBarCreate;
    2834             : 
    2835           0 :     gcd[1].gd.pos.x = 0; gcd[1].gd.pos.y = pos.height;
    2836           0 :     gcd[1].gd.pos.height = sbsize;
    2837           0 :     gcd[1].gd.pos.width = pos.width;
    2838           0 :     gcd[1].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
    2839           0 :     gcd[1].creator = GScrollBarCreate;
    2840             : 
    2841           0 :     gcd[2].gd.pos.width = -1;
    2842           0 :     gcd[2].gd.pos.x = (pos.width-sbsize-GIntGetResource(_NUM_Buttonsize)*100/GIntGetResource(_NUM_ScaleFactor))/2;
    2843           0 :     gcd[2].gd.pos.y = pos.height+sbsize+5;
    2844           0 :     gcd[2].gd.flags = gg_visible | gg_enabled | gg_but_default | gg_but_cancel | gg_pos_in_pixels;
    2845           0 :     label[2].text = (unichar_t *) _("_OK");
    2846           0 :     label[2].text_is_1byte = true;
    2847           0 :     label[2].text_in_resource = true;
    2848           0 :     gcd[2].gd.label = &label[2];
    2849           0 :     gcd[2].creator = GButtonCreate;
    2850             : 
    2851           0 :     GGadgetsCreate(att->gw,gcd);
    2852           0 :     att->vsb = gcd[0].ret;
    2853           0 :     att->hsb = gcd[1].ret;
    2854           0 :     att->cancel = gcd[2].ret;
    2855           0 : }
    2856             : 
    2857           0 : void ShowAtt(SplineFont *sf,int def_layer) {
    2858             :     struct att_dlg att;
    2859             : 
    2860           0 :     ShowAttCreateDlg(&att, sf, 0, _("Show ATT"));
    2861           0 :     att.def_layer = def_layer;
    2862             : 
    2863           0 :     BuildTop(&att);
    2864           0 :     att.open_cnt = SizeCnt(&att,att.tables,0);
    2865           0 :     GScrollBarSetBounds(att.vsb,0,att.open_cnt,att.lines_page);
    2866             : 
    2867           0 :     GDrawSetVisible(att.gw,true);
    2868             : 
    2869           0 :     while ( !att.done )
    2870           0 :         GDrawProcessOneEvent(NULL);
    2871           0 :     GDrawSetUserData(att.gw,NULL);
    2872           0 :     nodesfree(att.tables);
    2873           0 :     GDrawDestroyWindow(att.gw);
    2874           0 : }
    2875             : 
    2876             : /* ************************************************************************** */
    2877             : /* ****************************** Font Compare ****************************** */
    2878             : /* ************************ (reuse the show att dlg) ************************ */
    2879             : /* ************************************************************************** */
    2880             : struct nested_file {
    2881             :     FILE *file;
    2882             :     char *linebuf;
    2883             :     int linemax;
    2884             :     int read_nest;
    2885             : };
    2886             : 
    2887           0 : static int ReadNestedLine(struct nested_file *nf) {
    2888             :     char *pt, *end;
    2889             :     int ch;
    2890           0 :     int nest = 0;
    2891             : 
    2892           0 :     pt = nf->linebuf; end = pt + nf->linemax-1;
    2893           0 :     while ( (ch=getc(nf->file))==' ' )
    2894           0 :         ++nest;
    2895           0 :     if ( ch==EOF ) {
    2896           0 :         nf->read_nest = -1;
    2897           0 : return( -1 );
    2898             :     }
    2899           0 :     while ( ch!=EOF && ch!='\n' ) {
    2900           0 :         if ( pt>=end ) {
    2901           0 :             char *old = nf->linebuf;
    2902           0 :             nf->linemax += 200;
    2903           0 :             nf->linebuf = realloc(nf->linebuf, nf->linemax);
    2904           0 :             pt = nf->linebuf + (pt-old);
    2905           0 :             end = nf->linebuf + nf->linemax - 1;
    2906             :         }
    2907           0 :         *pt++ = ch;
    2908           0 :         ch = getc(nf->file);
    2909             :     }
    2910           0 :     *pt = '\0';
    2911           0 :     nf->read_nest = nest;
    2912           0 : return( nest );
    2913             : }
    2914             : 
    2915           0 : static void ReadKids(struct nested_file *nf,int desired_nest,struct node *parent) {
    2916           0 :     int i=0, max=0, j, k;
    2917             : 
    2918           0 :     ReadNestedLine(nf);
    2919             :     for (;;) {
    2920           0 :         if ( nf->read_nest < desired_nest )
    2921           0 :     break;
    2922           0 :         if ( i>=max-1 ) {
    2923           0 :             parent->children = realloc(parent->children,(max+=10)*sizeof( struct node ));
    2924           0 :             memset(parent->children+i,0,(max-i)*sizeof(struct node));
    2925             :         }
    2926           0 :         parent->children[i].label = copy(nf->linebuf);
    2927           0 :         parent->children[i].parent = parent;
    2928           0 :         ReadKids(nf,desired_nest+1,&parent->children[i]);
    2929           0 :         ++i;
    2930           0 :     }
    2931           0 :     if ( i!=0 ) {
    2932           0 :         if ( i<max-1 )
    2933           0 :             parent->children = realloc(parent->children,(i+1)*sizeof(struct node));
    2934             :         /* The reallocs can invalidate the parent field */
    2935           0 :         for ( j=0; j<i; ++j )
    2936           0 :             for ( k=0; k<parent->children[j].cnt; ++k )
    2937           0 :                 parent->children[j].children[k].parent = &parent->children[j];
    2938           0 :         parent->cnt = i;
    2939             :     }
    2940           0 : }
    2941             :     
    2942           0 : static void BuildFCmpNodes(struct att_dlg *att, SplineFont *sf1, SplineFont *sf2,int flags) {
    2943           0 :     FILE *tmp = tmpfile();
    2944             :     struct node *tables;
    2945             :     struct nested_file nf;
    2946             : 
    2947           0 :     att->tables = tables = calloc(2,sizeof(struct node));
    2948           0 :     att->current = tables;
    2949           0 :     if ( !CompareFonts(sf1,att->fv1->b.map,sf2,tmp,flags) && ftell(tmp)==0 ) {
    2950           0 :         tables[0].label = copy(_("No differences found"));
    2951             :     } else {
    2952           0 :         tables[0].label = copy(_("Differences..."));
    2953           0 :         rewind(tmp);
    2954           0 :         memset(&nf,0,sizeof(nf));
    2955           0 :         nf.file = tmp;
    2956           0 :         nf.linebuf = malloc( nf.linemax = 300 );
    2957           0 :         ReadKids(&nf,0,&tables[0]);
    2958           0 :         free(nf.linebuf);
    2959             :     }
    2960           0 :     fclose(tmp);
    2961           0 : }
    2962             : 
    2963           0 : static void FontCmpDlg(FontView *fv1, FontView *fv2,int flags) {
    2964             :     struct att_dlg *att;
    2965             :     char buffer[300];
    2966           0 :     SplineFont *sf1 = fv1->b.sf, *sf2=fv2->b.sf;
    2967             : 
    2968           0 :     if ( strcmp(sf1->fontname,sf2->fontname)!=0 )
    2969           0 :         snprintf( buffer, sizeof(buffer), _("Compare %s to %s"), sf1->fontname, sf2->fontname);
    2970           0 :     else if ( sf1->version!=NULL && sf2->version!=NULL &&
    2971           0 :             strcmp(sf1->version,sf2->version)!=0 )
    2972           0 :         snprintf( buffer, sizeof(buffer), _("Compare version %s of %s to %s"),
    2973             :                 sf1->version, sf1->fontname, sf2->version);
    2974             :     else
    2975           0 :         strcpy( buffer, _("Font Compare")); 
    2976             : 
    2977           0 :     att = malloc(sizeof(struct att_dlg));
    2978           0 :     ShowAttCreateDlg(att, sf1, dt_font_comp, buffer);
    2979           0 :     att->fv1 = fv1; att->fv2 = fv2;
    2980             : 
    2981           0 :     GDrawSetCursor(fv1->v,ct_watch);
    2982           0 :     GDrawSetCursor(fv2->v,ct_watch);
    2983           0 :     BuildFCmpNodes(att,sf1,sf2,flags);
    2984           0 :     GDrawSetCursor(fv1->v,ct_pointer);
    2985           0 :     GDrawSetCursor(fv2->v,ct_pointer);
    2986             :     
    2987           0 :     att->open_cnt = SizeCnt(att,att->tables,0);
    2988           0 :     GScrollBarSetBounds(att->vsb,0,att->open_cnt,att->lines_page);
    2989             : 
    2990           0 :     GDrawSetVisible(att->gw,true);
    2991           0 : }
    2992             : 
    2993           0 : static void FCAskFilename(FontView *fv,int flags) {
    2994           0 :     char *filename = GetPostScriptFontName(NULL,false);
    2995             :     FontView *otherfv;
    2996             : 
    2997           0 :     if ( filename==NULL )
    2998           0 : return;
    2999           0 :     otherfv = (FontView *) ViewPostScriptFont(filename,0);
    3000           0 :     free(filename); filename = NULL;
    3001           0 :     if ( otherfv==NULL )
    3002           0 : return;
    3003           0 :     FontCmpDlg(fv,otherfv,flags);
    3004             : }
    3005             : 
    3006             : struct mf_data {
    3007             :     int done;
    3008             :     FontView *fv;
    3009             :     GGadget *other;
    3010             :     GGadget *amount;
    3011             : };
    3012             : #define CID_Outlines    1
    3013             : #define CID_Exact       2
    3014             : #define CID_Warn        3
    3015             : #define CID_Fuzzy       4
    3016             : #define CID_Hinting     5
    3017             : #define CID_Bitmaps     6
    3018             : #define CID_Names       7
    3019             : #define CID_GPos        8
    3020             : #define CID_GSub        9
    3021             : #define CID_HintMasks   10
    3022             : #define CID_HintMasksWConflicts 11
    3023             : #define CID_NoHintMasks 12
    3024             : #define CID_RefContourWarn      13
    3025             : #define CID_AddDiffs    14
    3026             : #define CID_AddMissing  15
    3027             : 
    3028             : static int last_flags = fcf_outlines|fcf_hinting|fcf_bitmaps|fcf_names|fcf_gpos|fcf_gsub|fcf_adddiff2sf1;
    3029             : 
    3030           0 : static int FC_OK(GGadget *g, GEvent *e) {
    3031           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
    3032           0 :         GWindow gw = GGadgetGetWindow(g);
    3033           0 :         struct mf_data *d = GDrawGetUserData(gw);
    3034           0 :         int i, index = GGadgetGetFirstListSelectedItem(d->other);
    3035             :         FontView *fv;
    3036           0 :         int flags = 0;
    3037           0 :         for ( i=0, fv=fv_list; fv!=NULL; fv=(FontView *) (fv->b.next) ) {
    3038           0 :             if ( fv==d->fv )
    3039           0 :         continue;
    3040           0 :             if ( i==index )
    3041           0 :         break;
    3042           0 :             ++i;
    3043             :         }
    3044             : 
    3045           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Outlines)) )
    3046           0 :             flags |= fcf_outlines;
    3047           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Exact)) )
    3048           0 :             flags |= fcf_exact;
    3049           0 :         else if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Warn)) )
    3050           0 :             flags |= fcf_warn_not_exact;
    3051           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_RefContourWarn)) )
    3052           0 :             flags |= fcf_warn_not_ref_exact;
    3053           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Hinting)) )
    3054           0 :             flags |= fcf_hinting;
    3055           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_HintMasks)) )
    3056           0 :             flags |= fcf_hintmasks;
    3057           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_HintMasksWConflicts)) )
    3058           0 :             flags |= fcf_hmonlywithconflicts|fcf_hintmasks;
    3059           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Bitmaps)) )
    3060           0 :             flags |= fcf_bitmaps;
    3061           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_Names)) )
    3062           0 :             flags |= fcf_names;
    3063           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_GPos)) )
    3064           0 :             flags |= fcf_gpos;
    3065           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_GSub)) )
    3066           0 :             flags |= fcf_gsub;
    3067           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_AddDiffs)) )
    3068           0 :             flags |= fcf_adddiff2sf1;
    3069           0 :         if ( GGadgetIsChecked(GWidgetGetControl(gw,CID_AddMissing)) )
    3070           0 :             flags |= fcf_addmissing;
    3071           0 :         last_flags = flags;
    3072             : 
    3073           0 :         GDrawDestroyWindow(gw);
    3074           0 :         if ( fv==NULL )
    3075           0 :             FCAskFilename(d->fv,flags);
    3076             :         else
    3077           0 :             FontCmpDlg(d->fv,fv,flags);
    3078           0 :         d->done = true;
    3079             :     }
    3080           0 : return( true );
    3081             : }
    3082             : 
    3083           0 : static int FC_Cancel(GGadget *g, GEvent *e) {
    3084           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
    3085           0 :         GWindow gw = GGadgetGetWindow(g);
    3086           0 :         struct mf_data *d = GDrawGetUserData(gw);
    3087           0 :         d->done = true;
    3088           0 :         GDrawDestroyWindow(gw);
    3089             :     }
    3090           0 : return( true );
    3091             : }
    3092             : 
    3093           0 : static int fc_e_h(GWindow gw, GEvent *event) {
    3094           0 :     if ( event->type==et_close ) {
    3095           0 :         struct mf_data *d = GDrawGetUserData(gw);
    3096           0 :         d->done = true;
    3097           0 :         GDrawDestroyWindow(gw);
    3098           0 :     } else if ( event->type == et_char ) {
    3099           0 : return( false );
    3100             :     }
    3101           0 : return( true );
    3102             : }
    3103             : 
    3104           0 : void FontCompareDlg(FontView *fv) {
    3105             :     GRect pos;
    3106             :     GWindow gw;
    3107             :     GWindowAttrs wattrs;
    3108             :     GGadgetCreateData gcd[25];
    3109             :     GTextInfo label[25];
    3110             :     struct mf_data d;
    3111             :     char buffer[80];
    3112             :     int k;
    3113             : 
    3114           0 :         memset(&wattrs,0,sizeof(wattrs));
    3115           0 :         wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_restrict|wam_isdlg;
    3116           0 :         wattrs.event_masks = ~(1<<et_charup);
    3117           0 :         wattrs.restrict_input_to_me = 1;
    3118           0 :         wattrs.is_dlg = 1;
    3119           0 :         wattrs.undercursor = 1;
    3120           0 :         wattrs.cursor = ct_pointer;
    3121           0 :         wattrs.utf8_window_title = _("Font Compare");
    3122           0 :         pos.x = pos.y = 0;
    3123           0 :         pos.width = GGadgetScale(GDrawPointsToPixels(NULL,180));
    3124           0 :         pos.height = GDrawPointsToPixels(NULL,88+15*14+2);
    3125           0 :         gw = GDrawCreateTopWindow(NULL,&pos,fc_e_h,&d,&wattrs);
    3126             : 
    3127           0 :         memset(&label,0,sizeof(label));
    3128           0 :         memset(&gcd,0,sizeof(gcd));
    3129             : 
    3130           0 :         k=0;
    3131           0 :         sprintf( buffer, _("Font to compare with %.20s"), fv->b.sf->fontname );
    3132           0 :         label[k].text = (unichar_t *) buffer;
    3133           0 :         label[k].text_is_1byte = true;
    3134           0 :         gcd[k].gd.label = &label[k];
    3135           0 :         gcd[k].gd.pos.x = 12; gcd[k].gd.pos.y = 6; 
    3136           0 :         gcd[k].gd.flags = gg_visible | gg_enabled;
    3137           0 :         gcd[k++].creator = GLabelCreate;
    3138             : 
    3139           0 :         gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = 21;
    3140           0 :         gcd[k].gd.pos.width = 145;
    3141           0 :         gcd[k].gd.flags = gg_visible | gg_enabled;
    3142           0 :         gcd[k].gd.u.list = BuildFontList(fv);
    3143           0 :         gcd[k].gd.label = &gcd[k].gd.u.list[0];
    3144           0 :         gcd[k].gd.u.list[0].selected = true;
    3145           0 :         gcd[k++].creator = GListButtonCreate;
    3146             : 
    3147           0 :         gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+24;
    3148           0 :         if ( fv->b.sf->onlybitmaps )
    3149           0 :             gcd[k].gd.flags = gg_visible;
    3150             :         else
    3151           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | ((last_flags&fcf_outlines)?gg_cb_on:0);
    3152           0 :         label[k].text = (unichar_t *) _("Compare _Outlines");
    3153           0 :         label[k].text_is_1byte = true;
    3154           0 :         label[k].text_in_resource = true;
    3155           0 :         gcd[k].gd.label = &label[k];
    3156           0 :         gcd[k].gd.cid = CID_Outlines;
    3157           0 :         gcd[k++].creator = GCheckBoxCreate;
    3158             : 
    3159           0 :         gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3160           0 :         if ( fv->b.sf->onlybitmaps )
    3161           0 :             gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    3162             :         else
    3163           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | ((last_flags&fcf_exact)?gg_cb_on:0);
    3164           0 :         label[k].text = (unichar_t *) _("_Exact");
    3165           0 :         label[k].text_is_1byte = true;
    3166           0 :         label[k].text_in_resource = true;
    3167           0 :         gcd[k].gd.label = &label[k];
    3168           0 :         gcd[k].gd.cid = CID_Exact;
    3169           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("Accept outlines which exactly match the original");
    3170           0 :         gcd[k++].creator = GRadioCreate;
    3171             : 
    3172           0 :         gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3173           0 :         if ( fv->b.sf->onlybitmaps )
    3174           0 :             gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    3175             :         else
    3176           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | ((last_flags&fcf_exact)?0:gg_cb_on);
    3177           0 :         label[k].text = (unichar_t *) _("_Accept inexact");
    3178           0 :         label[k].text_is_1byte = true;
    3179           0 :         label[k].text_in_resource = true;
    3180           0 :         gcd[k].gd.label = &label[k];
    3181           0 :         gcd[k].gd.cid = CID_Fuzzy;
    3182           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("Accept an outline which is a close approximation to the original.\nIt may be off by an em-unit, or have a reference which matches a contour.");
    3183           0 :         gcd[k++].creator = GRadioCreate;
    3184             : 
    3185           0 :         gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3186           0 :         if ( fv->b.sf->onlybitmaps )
    3187           0 :             gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    3188             :         else
    3189           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | ((last_flags&fcf_warn_not_exact)?gg_cb_on:0);
    3190           0 :         label[k].text = (unichar_t *) _("_Warn if inexact");
    3191           0 :         label[k].text_is_1byte = true;
    3192           0 :         label[k].text_in_resource = true;
    3193           0 :         gcd[k].gd.label = &label[k];
    3194           0 :         gcd[k].gd.cid = CID_Warn;
    3195           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("Warn if the outlines are close but not exactly the same");
    3196           0 :         gcd[k++].creator = GCheckBoxCreate;
    3197             : 
    3198           0 :         gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3199           0 :         if ( fv->b.sf->onlybitmaps )
    3200           0 :             gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    3201             :         else
    3202           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | ((last_flags&fcf_warn_not_ref_exact)?gg_cb_on:0);
    3203           0 :         label[k].text = (unichar_t *) _("Warn if _unlinked references");
    3204           0 :         label[k].text_is_1byte = true;
    3205           0 :         label[k].text_in_resource = true;
    3206           0 :         gcd[k].gd.label = &label[k];
    3207           0 :         gcd[k].gd.cid = CID_RefContourWarn;
    3208           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("Warn if one glyph contains an outline while the other contains a reference (but the reference describes the same outline)");
    3209           0 :         gcd[k++].creator = GCheckBoxCreate;
    3210             : 
    3211           0 :         gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3212           0 :         if ( fv->b.sf->onlybitmaps )
    3213           0 :             gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    3214             :         else
    3215           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | ((last_flags&fcf_hinting)?gg_cb_on:0);
    3216           0 :         label[k].text = (unichar_t *) _("Compare _Hints");
    3217           0 :         label[k].text_is_1byte = true;
    3218           0 :         label[k].text_in_resource = true;
    3219           0 :         gcd[k].gd.label = &label[k];
    3220           0 :         gcd[k].gd.cid = CID_Hinting;
    3221           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("Compare postscript hints and hintmasks and truetype instructions");
    3222           0 :         gcd[k++].creator = GCheckBoxCreate;
    3223             : 
    3224           0 :         gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3225           0 :         if ( fv->b.sf->onlybitmaps )
    3226           0 :             gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    3227             :         else
    3228           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | (((last_flags&fcf_hintmasks) && !(last_flags&fcf_hmonlywithconflicts))?gg_cb_on:0);
    3229           0 :         label[k].text = (unichar_t *) _("Compare Hint_Masks");
    3230           0 :         label[k].text_is_1byte = true;
    3231           0 :         label[k].text_in_resource = true;
    3232           0 :         gcd[k].gd.label = &label[k];
    3233           0 :         gcd[k].gd.cid = CID_HintMasks;
    3234           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("Compare hintmasks");
    3235           0 :         gcd[k++].creator = GRadioCreate;
    3236             : 
    3237           0 :         gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3238           0 :         if ( fv->b.sf->onlybitmaps )
    3239           0 :             gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    3240             :         else
    3241           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | ((last_flags&fcf_hmonlywithconflicts)?gg_cb_on:0);
    3242           0 :         label[k].text = (unichar_t *) _("HintMasks only if conflicts");
    3243           0 :         label[k].text_is_1byte = true;
    3244           0 :         label[k].text_in_resource = true;
    3245           0 :         gcd[k].gd.label = &label[k];
    3246           0 :         gcd[k].gd.cid = CID_HintMasksWConflicts;
    3247           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("Don't compare hintmasks if the glyph has no hint conflicts");
    3248           0 :         gcd[k++].creator = GRadioCreate;
    3249             : 
    3250           0 :         gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3251           0 :         if ( fv->b.sf->onlybitmaps )
    3252           0 :             gcd[k].gd.flags = gg_visible ;
    3253             :         else
    3254           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | ((last_flags&fcf_hintmasks)?0:gg_cb_on);
    3255           0 :         label[k].text = (unichar_t *) _("Don't Compare HintMasks");
    3256           0 :         label[k].text_is_1byte = true;
    3257           0 :         label[k].text_in_resource = true;
    3258           0 :         gcd[k].gd.label = &label[k];
    3259           0 :         gcd[k].gd.cid = CID_NoHintMasks;
    3260           0 :         gcd[k++].creator = GRadioCreate;
    3261             : 
    3262           0 :         gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3263           0 :         if ( fv->b.sf->onlybitmaps )
    3264           0 :             gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    3265             :         else
    3266           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | ((last_flags&fcf_adddiff2sf1)?gg_cb_on:0);
    3267           0 :         label[k].text = (unichar_t *) _("_Add Diff Outlines to Background");
    3268           0 :         label[k].text_is_1byte = true;
    3269           0 :         label[k].text_in_resource = true;
    3270           0 :         gcd[k].gd.label = &label[k];
    3271           0 :         gcd[k].gd.cid = CID_AddDiffs;
    3272           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("If two glyphs differ, then add the outlines of the second glyph\nto the background layer of the first (So when opening the first\nthe differences will be visible).");
    3273           0 :         gcd[k++].creator = GCheckBoxCreate;
    3274             : 
    3275           0 :         gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3276           0 :         if ( fv->b.sf->onlybitmaps )
    3277           0 :             gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    3278             :         else
    3279           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | ((last_flags&fcf_addmissing)?gg_cb_on:0);
    3280           0 :         label[k].text = (unichar_t *) _("Add _Missing Glyphs");
    3281           0 :         label[k].text_is_1byte = true;
    3282           0 :         label[k].text_in_resource = true;
    3283           0 :         gcd[k].gd.label = &label[k];
    3284           0 :         gcd[k].gd.cid = CID_AddMissing;
    3285           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("If a glyph in the second font is missing from the first, then\nadd it to the first with the outlines of the second font in\nthe background");
    3286           0 :         gcd[k++].creator = GCheckBoxCreate;
    3287             : 
    3288           0 :         gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16;
    3289           0 :         if ( fv->b.sf->bitmaps==NULL )
    3290           0 :             gcd[k].gd.flags = gg_visible;
    3291             :         else
    3292           0 :             gcd[k].gd.flags = gg_visible | gg_enabled | ((last_flags&fcf_bitmaps)?gg_cb_on:0);
    3293           0 :         label[k].text = (unichar_t *) _("Compare _Bitmaps");
    3294           0 :         label[k].text_is_1byte = true;
    3295           0 :         label[k].text_in_resource = true;
    3296           0 :         gcd[k].gd.label = &label[k];
    3297           0 :         gcd[k].gd.cid = CID_Bitmaps;
    3298           0 :         gcd[k++].creator = GCheckBoxCreate;
    3299             : 
    3300           0 :         gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3301           0 :         gcd[k].gd.flags = gg_visible | gg_enabled | ((last_flags&fcf_names)?gg_cb_on:0);
    3302           0 :         label[k].text = (unichar_t *) _("Compare _Names");
    3303           0 :         label[k].text_is_1byte = true;
    3304           0 :         label[k].text_in_resource = true;
    3305           0 :         gcd[k].gd.label = &label[k];
    3306           0 :         gcd[k].gd.cid = CID_Names;
    3307           0 :         gcd[k++].creator = GCheckBoxCreate;
    3308             : 
    3309           0 :         gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3310           0 :         gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | ((last_flags&fcf_gpos)?gg_cb_on:0);
    3311           0 :         label[k].text = (unichar_t *) _("Compare Glyph _Positioning");
    3312           0 :         label[k].text_is_1byte = true;
    3313           0 :         label[k].text_in_resource = true;
    3314           0 :         gcd[k].gd.label = &label[k];
    3315           0 :         gcd[k].gd.cid = CID_GPos;
    3316           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("Kerning & such");
    3317           0 :         gcd[k++].creator = GCheckBoxCreate;
    3318             : 
    3319           0 :         gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    3320           0 :         gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup | ((last_flags&fcf_gsub)?gg_cb_on:0);
    3321           0 :         label[k].text = (unichar_t *) _("Compare Glyph _Substitution");
    3322           0 :         label[k].text_is_1byte = true;
    3323           0 :         label[k].text_in_resource = true;
    3324           0 :         gcd[k].gd.label = &label[k];
    3325           0 :         gcd[k].gd.cid = CID_GSub;
    3326           0 :         gcd[k].gd.popup_msg = (unichar_t *) _("Ligatures & such");
    3327           0 :         gcd[k++].creator = GCheckBoxCreate;
    3328             : 
    3329           0 :         gcd[k].gd.pos.x = 15-3; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+23-3;
    3330           0 :         gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
    3331           0 :         gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
    3332           0 :         label[k].text = (unichar_t *) _("_OK");
    3333           0 :         label[k].text_is_1byte = true;
    3334           0 :         label[k].text_in_resource = true;
    3335           0 :         gcd[k].gd.label = &label[k];
    3336           0 :         gcd[k].gd.handle_controlevent = FC_OK;
    3337           0 :         gcd[k++].creator = GButtonCreate;
    3338             : 
    3339           0 :         gcd[k].gd.pos.x = -15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
    3340           0 :         gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
    3341           0 :         gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
    3342           0 :         label[k].text = (unichar_t *) _("_Cancel");
    3343           0 :         label[k].text_is_1byte = true;
    3344           0 :         label[k].text_in_resource = true;
    3345           0 :         gcd[k].gd.label = &label[k];
    3346           0 :         gcd[k].gd.handle_controlevent = FC_Cancel;
    3347           0 :         gcd[k++].creator = GButtonCreate;
    3348             : 
    3349           0 :         gcd[k].gd.pos.x = 2; gcd[k].gd.pos.y = 2;
    3350           0 :         gcd[k].gd.pos.width = pos.width-4; gcd[k].gd.pos.height = pos.height-2;
    3351           0 :         gcd[k].gd.flags = gg_enabled | gg_visible | gg_pos_in_pixels;
    3352           0 :         gcd[k++].creator = GGroupCreate;
    3353             : 
    3354           0 :         GGadgetsCreate(gw,gcd);
    3355             : 
    3356           0 :         memset(&d,'\0',sizeof(d));
    3357           0 :         d.other = gcd[1].ret;
    3358           0 :         d.fv = fv;
    3359             : 
    3360           0 :         GWidgetHidePalettes();
    3361           0 :         GDrawSetVisible(gw,true);
    3362           0 :         while ( !d.done )
    3363           0 :             GDrawProcessOneEvent(NULL);
    3364           0 :         TFFree(gcd[1].gd.u.list);
    3365           0 : }

Generated by: LCOV version 1.10