LCOV - code coverage report
Current view: top level - fontforgeexe - statemachine.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 0 1001 0.0 %
Date: 2017-08-04 Functions: 0 29 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 "lookups.h"
      30             : #include "ttf.h"
      31             : #include <chardata.h>
      32             : #include <utype.h>
      33             : #include <ustring.h>
      34             : #include <gkeysym.h>
      35             : 
      36             : #define CID_Classes     305
      37             : 
      38             : 
      39             : #define CID_Ok          307
      40             : #define CID_Cancel      308
      41             : 
      42             : #define CID_Line1       309
      43             : #define CID_Line2       310
      44             : #define CID_Group       311
      45             : 
      46             : #define CID_Set         313
      47             : #define CID_Select      314
      48             : 
      49             : #define CID_RightToLeft 318
      50             : #define CID_VertOnly    319
      51             : 
      52             : #define SMD_WIDTH       350
      53             : #define SMD_HEIGHT      400
      54             : #define SMD_CANCELDROP  32
      55             : #define SMD_DIRDROP     88
      56             : 
      57             : #define CID_NextState   400
      58             : #define CID_Flag4000    401
      59             : #define CID_Flag8000    402
      60             : #define CID_Flag2000    403
      61             : #define CID_Flag1000    404
      62             : #define CID_Flag0800    405
      63             : #define CID_Flag0400    406
      64             : #define CID_IndicVerb   407
      65             : #define CID_InsCur      408
      66             : #define CID_InsMark     409
      67             : #define CID_TagCur      410
      68             : #define CID_TagMark     411
      69             : #define CID_Kerns       412
      70             : #define CID_StateClass  413
      71             : 
      72             : #define CID_Up          420
      73             : #define CID_Down        421
      74             : #define CID_Left        422
      75             : #define CID_Right       423
      76             : 
      77             : #define SMDE_WIDTH      200
      78             : #define SMDE_HEIGHT     (SMD_DIRDROP+200)
      79             : 
      80             : extern int _GScrollBar_Width;
      81             : 
      82             : typedef struct statemachinedlg {
      83             :     GWindow gw, editgw;
      84             :     int state_cnt, class_cnt, index;
      85             :     struct asm_state *states;
      86             :     GGadget *hsb, *vsb;
      87             :     ASM *sm;
      88             :     SplineFont *sf;
      89             :     struct gfi_data *d;
      90             :     int isnew;
      91             :     GFont *font;
      92             :     int fh, as;
      93             :     int stateh, statew;
      94             :     int xstart, ystart;         /* This is where the headers start */
      95             :     int xstart2, ystart2;       /* This is where the data start */
      96             :     int width, height;
      97             :     int offleft, offtop;
      98             :     int canceldrop, sbdrop;
      99             :     GTextInfo *mactags;
     100             :     int isedit;
     101             :     int st_pos;
     102             :     int edit_done, edit_ok;
     103             :     int done;
     104             : } SMD;
     105             : 
     106             : static int  SMD_SBReset(SMD *smd);
     107             : static void SMD_HShow(SMD *smd,int pos);
     108             : 
     109             : static char *indicverbs[2][16] = {
     110             :     { "", "Ax", "xD", "AxD", "ABx", "ABx", "xCD", "xCD",
     111             :         "AxCD",  "AxCD", "ABxD", "ABxD", "ABxCD", "ABxCD", "ABxCD", "ABxCD" },
     112             :     { "", "xA", "Dx", "DxA", "xAB", "xBA", "CDx", "DCx",
     113             :         "CDxA",  "DCxA", "DxAB", "DxBA", "CDxAB", "CDxBA", "DCxAB", "DCxBA" }};
     114             : 
     115           0 : static void StatesFree(struct asm_state *old,int old_class_cnt,int old_state_cnt,
     116             :         enum asm_type type) {
     117             :     int i, j;
     118             : 
     119           0 :     if ( type==asm_insert ) {
     120           0 :         for ( i=0; i<old_state_cnt ; ++i ) {
     121           0 :             for ( j=0; j<old_class_cnt; ++j ) {
     122           0 :                 struct asm_state *this = &old[i*old_class_cnt+j];
     123           0 :                 free(this->u.insert.mark_ins);
     124           0 :                 free(this->u.insert.cur_ins);
     125             :             }
     126             :         }
     127           0 :     } else if ( type==asm_kern ) {
     128           0 :         for ( i=0; i<old_state_cnt ; ++i ) {
     129           0 :             for ( j=0; j<old_class_cnt; ++j ) {
     130           0 :                 struct asm_state *this = &old[i*old_class_cnt+j];
     131           0 :                 free(this->u.kern.kerns);
     132             :             }
     133             :         }
     134             :     }
     135           0 :     free(old);
     136           0 : }
     137             : 
     138           0 : static struct asm_state *StateCopy(struct asm_state *old,int old_class_cnt,int old_state_cnt,
     139             :         int new_class_cnt,int new_state_cnt, enum asm_type type,int freeold) {
     140           0 :     struct asm_state *new = calloc(new_class_cnt*new_state_cnt,sizeof(struct asm_state));
     141             :     int i,j;
     142           0 :     int minclass = new_class_cnt<old_class_cnt ? new_class_cnt : old_class_cnt;
     143             : 
     144           0 :     for ( i=0; i<old_state_cnt && i<new_state_cnt; ++i ) {
     145           0 :         memcpy(new+i*new_class_cnt, old+i*old_class_cnt, minclass*sizeof(struct asm_state));
     146           0 :         if ( type==asm_insert ) {
     147           0 :             for ( j=0; j<minclass; ++j ) {
     148           0 :                 struct asm_state *this = &new[i*new_class_cnt+j];
     149           0 :                 this->u.insert.mark_ins = copy(this->u.insert.mark_ins);
     150           0 :                 this->u.insert.cur_ins = copy(this->u.insert.cur_ins);
     151             :             }
     152           0 :         } else if ( type==asm_kern ) {
     153           0 :             for ( j=0; j<minclass; ++j ) {
     154           0 :                 struct asm_state *this = &new[i*new_class_cnt+j];
     155             :                 int16 *temp;
     156           0 :                 temp = malloc(this->u.kern.kcnt*sizeof(int16));
     157           0 :                 memcpy(temp,this->u.kern.kerns,this->u.kern.kcnt*sizeof(int16));
     158           0 :                 this->u.kern.kerns = temp;
     159             :             }
     160             :         }
     161             :     }
     162           0 :     for ( ; i<new_state_cnt; ++i )
     163           0 :         new[i*new_class_cnt+2].next_state = i;          /* Deleted glyphs should be treated as noops */
     164             : 
     165           0 :     if ( freeold )
     166           0 :         StatesFree(old,old_class_cnt,old_state_cnt,type);
     167             : 
     168           0 : return( new );
     169             : }
     170             : 
     171           0 : static void StateRemoveClasses(SMD *smd, int removeme ) {
     172             :     struct asm_state *new;
     173             :     int i,j,k;
     174             : 
     175           0 :     if ( removeme<4 )
     176           0 : return;
     177             : 
     178           0 :     new = calloc((smd->class_cnt-1)*smd->state_cnt,sizeof(struct asm_state));
     179           0 :     for ( i=0; i<smd->state_cnt ; ++i ) {
     180           0 :         for ( j=k=0; j<smd->class_cnt; ++j ) {
     181           0 :             if ( j!=removeme )
     182           0 :                 new[i*(smd->class_cnt-1)+k++] = smd->states[i*smd->class_cnt+j];
     183           0 :             else if ( smd->sm->type==asm_insert ) {
     184           0 :                 free(smd->states[i*smd->class_cnt+j].u.insert.mark_ins);
     185           0 :                 free(smd->states[i*smd->class_cnt+j].u.insert.cur_ins);
     186           0 :             } else if ( smd->sm->type==asm_kern ) {
     187           0 :                 free(smd->states[i*smd->class_cnt+j].u.kern.kerns);
     188             :             }
     189             :         }
     190             :     }
     191             : 
     192           0 :     free(smd->states);
     193           0 :     smd->states = new;
     194           0 :     --smd->class_cnt;
     195             : }
     196             : 
     197           0 : static int FindMaxReachableStateCnt(SMD *smd) {
     198           0 :     int max_reachable = 1;
     199             :     int i, j, ns;
     200             : 
     201           0 :     for ( i=0; i<=max_reachable && i<smd->state_cnt; ++i ) {
     202           0 :         for ( j=0; j<smd->class_cnt; ++j ) {
     203           0 :             ns = smd->states[i*smd->class_cnt+j].next_state;
     204           0 :             if ( ns>max_reachable )
     205           0 :                 max_reachable = ns;
     206             :         }
     207             :     }
     208           0 : return( max_reachable+1 );              /* The count is one more than the max */
     209             : }
     210             : 
     211             : /* ************************************************************************** */
     212             : /* ****************************** Edit a State ****************************** */
     213             : /* ************************************************************************** */
     214             : GTextInfo indicverbs_list[] = {
     215             :     { (unichar_t *) N_("No Change"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     216             :     { (unichar_t *) N_("Ax => xA"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     217             :     { (unichar_t *) N_("xD => Dx"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     218             :     { (unichar_t *) N_("AxD => DxA"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     219             :     { (unichar_t *) N_("ABx => xAB"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     220             :     { (unichar_t *) N_("ABx => xBA"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     221             :     { (unichar_t *) N_("xCD => CDx"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     222             :     { (unichar_t *) N_("xCD => DCx"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     223             :     { (unichar_t *) N_("AxCD => CDxA"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     224             :     { (unichar_t *) N_("AxCD => DCxA"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     225             :     { (unichar_t *) N_("ABxD => DxAB"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     226             :     { (unichar_t *) N_("ABxD => DxBA"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     227             :     { (unichar_t *) N_("ABxCD => CDxAB"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     228             :     { (unichar_t *) N_("ABxCD => CDxBA"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     229             :     { (unichar_t *) N_("ABxCD => DCxAB"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     230             :     { (unichar_t *) N_("ABxCD => DCxBA"), NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
     231             :     GTEXTINFO_EMPTY
     232             : };
     233             : 
     234           0 : static char *copy_count(GWindow gw,int cid,int *cnt) {
     235           0 :     const unichar_t *u = _GGadgetGetTitle(GWidgetGetControl(gw,cid)), *upt;
     236             :     char *ret, *pt;
     237             :     int c;
     238             : 
     239           0 :     while ( *u==' ' ) ++u;
     240           0 :     if ( *u=='\0' ) {
     241           0 :         *cnt = 0;
     242           0 : return( NULL );
     243             :     }
     244             : 
     245           0 :     ret = pt = malloc(u_strlen(u)+1);
     246           0 :     c = 0;
     247           0 :     for ( upt=u; *upt; ) {
     248           0 :         if ( *upt==' ' ) {
     249           0 :             while ( *upt==' ' ) ++upt;
     250           0 :             if ( *upt!='\0' ) {
     251           0 :                 ++c;
     252           0 :                 *pt++ = ' ';
     253             :             }
     254             :         } else
     255           0 :             *pt++ = *upt++;
     256             :     }
     257           0 :     *pt = '\0';
     258           0 :     *cnt = c+1;
     259           0 : return( ret );
     260             : }
     261             : 
     262           0 : static void SMD_Fillup(SMD *smd) {
     263           0 :     int state = smd->st_pos/smd->class_cnt;
     264           0 :     int class = smd->st_pos%smd->class_cnt;
     265           0 :     struct asm_state *this = &smd->states[smd->st_pos];
     266             :     char buffer[100];
     267             :     char buf[100], *temp;
     268             :     int j;
     269           0 :     GGadget *list = GWidgetGetControl( smd->gw, CID_Classes );
     270             :     int rows;
     271           0 :     struct matrix_data *classes = GMatrixEditGet(list,&rows);
     272             : 
     273           0 :     snprintf(buffer,sizeof(buffer)/sizeof(buffer[0]),
     274           0 :             _("State %d,  %.40s"), state, classes[class*1+0].u.md_str );
     275           0 :     GGadgetSetTitle8(GWidgetGetControl(smd->editgw,CID_StateClass),buffer);
     276           0 :     sprintf(buf,"%d", this->next_state );
     277           0 :     GGadgetSetTitle8(GWidgetGetControl(smd->editgw,CID_NextState),buf);
     278             : 
     279           0 :     GGadgetSetChecked(GWidgetGetControl(smd->editgw,CID_Flag4000),
     280           0 :             this->flags&0x4000?0:1);
     281           0 :     GGadgetSetChecked(GWidgetGetControl(smd->editgw,CID_Flag8000),
     282           0 :             this->flags&0x8000?1:0);
     283           0 :     if ( smd->sm->type==asm_indic ) {
     284           0 :         GGadgetSetChecked(GWidgetGetControl(smd->editgw,CID_Flag2000),
     285           0 :                 this->flags&0x2000?1:0);
     286           0 :         GGadgetSelectOneListItem(GWidgetGetControl(smd->editgw,CID_IndicVerb),
     287           0 :                 this->flags&0xf);
     288           0 :     } else if ( smd->sm->type==asm_insert ) {
     289           0 :         GGadgetSetChecked(GWidgetGetControl(smd->editgw,CID_Flag2000),
     290           0 :                 this->flags&0x2000?1:0);
     291           0 :         GGadgetSetChecked(GWidgetGetControl(smd->editgw,CID_Flag1000),
     292           0 :                 this->flags&0x1000?1:0);
     293           0 :         GGadgetSetChecked(GWidgetGetControl(smd->editgw,CID_Flag0800),
     294           0 :                 this->flags&0x0800?1:0);
     295           0 :         GGadgetSetChecked(GWidgetGetControl(smd->editgw,CID_Flag0400),
     296           0 :                 this->flags&0x0400?1:0);
     297           0 :         temp = this->u.insert.mark_ins;
     298           0 :         buffer[0]='\0';
     299           0 :         GGadgetSetTitle8(GWidgetGetControl(smd->editgw,CID_InsMark),temp==NULL?buffer:temp);
     300           0 :         temp = this->u.insert.cur_ins;
     301           0 :         GGadgetSetTitle8(GWidgetGetControl(smd->editgw,CID_InsCur),temp==NULL?buffer:temp);
     302           0 :     } else if ( smd->sm->type==asm_kern ) {
     303           0 :         buf[0] = '\0';
     304           0 :         for ( j=0; j<this->u.kern.kcnt; ++j )
     305           0 :             sprintf( buf+strlen(buf), "%d ", this->u.kern.kerns[j]);
     306           0 :         if ( buf[0]!='\0' && buf[strlen(buf)-1]==' ' )
     307           0 :             buf[strlen(buf)-1] = '\0';
     308           0 :         GGadgetSetTitle8(GWidgetGetControl(smd->editgw,CID_Kerns),buf);
     309             :     } else {
     310           0 :         if ( this->u.context.mark_lookup != NULL )
     311           0 :         GGadgetSetTitle8(GWidgetGetControl(smd->editgw,CID_TagMark),this->u.context.mark_lookup->lookup_name);
     312           0 :         if ( this->u.context.cur_lookup != NULL )
     313           0 :             GGadgetSetTitle8(GWidgetGetControl(smd->editgw,CID_TagCur),this->u.context.cur_lookup->lookup_name);
     314             :     }
     315             : 
     316           0 :     GGadgetSetEnabled(GWidgetGetControl(smd->editgw,CID_Up), state!=0 );
     317           0 :     GGadgetSetEnabled(GWidgetGetControl(smd->editgw,CID_Left), class!=0 );
     318           0 :     GGadgetSetEnabled(GWidgetGetControl(smd->editgw,CID_Right), class<smd->class_cnt-1 );
     319           0 :     GGadgetSetEnabled(GWidgetGetControl(smd->editgw,CID_Down), state<smd->state_cnt-1 );
     320           0 : }
     321             : 
     322           0 : static int SMD_DoChange(SMD *smd) {
     323           0 :     struct asm_state *this = &smd->states[smd->st_pos];
     324           0 :     int err=false, ns, flags, cnt;
     325             :     char *mins, *cins;
     326             :     OTLookup *mlook, *clook;
     327             :     const unichar_t *ret;
     328             :     unichar_t *end;
     329             :     char *ret8;
     330             :     int16 kbuf[9];
     331             :     int kerns;
     332           0 :     int oddcomplain=false;
     333             : 
     334           0 :     ns = GetInt8(smd->editgw,CID_NextState,_("Next State:"),&err);
     335           0 :     if ( err )
     336           0 : return( false );
     337           0 :     flags = 0;
     338           0 :     if ( !GGadgetIsChecked(GWidgetGetControl(smd->editgw,CID_Flag4000)) ) flags |= 0x4000;
     339           0 :     if ( GGadgetIsChecked(GWidgetGetControl(smd->editgw,CID_Flag8000)) ) flags |= 0x8000;
     340           0 :     if ( smd->sm->type==asm_indic ) {
     341           0 :         if ( GGadgetIsChecked(GWidgetGetControl(smd->editgw,CID_Flag2000)) ) flags |= 0x2000;
     342           0 :         flags |= GGadgetGetFirstListSelectedItem(GWidgetGetControl(smd->editgw,CID_IndicVerb));
     343           0 :         this->next_state = ns;
     344           0 :         this->flags = flags;
     345           0 :     } else if ( smd->sm->type==asm_kern ) {
     346           0 :         ret = _GGadgetGetTitle(GWidgetGetControl(smd->editgw,CID_Kerns));
     347           0 :         kerns=0;
     348           0 :         while ( *ret!='\0' ) {
     349           0 :             while ( *ret==' ' ) ++ret;
     350           0 :             if ( *ret=='\0' )
     351           0 :         break;
     352           0 :             kbuf[kerns] = u_strtol(ret,&end,10);
     353           0 :             if ( end==ret ) {
     354           0 :                 GGadgetProtest8(_("Kern Values:"));
     355           0 : return( false );
     356           0 :             } else if ( kerns>=8 ) {
     357           0 :                 ff_post_error(_("Too Many Kerns"),_("At most 8 kerning values may be specified here"));
     358           0 : return( false );
     359           0 :             } else if ( kbuf[kerns]&1 ) {
     360           0 :                 kbuf[kerns] &= ~1;
     361           0 :                 if ( !oddcomplain )
     362           0 :                     ff_post_notice(_("Kerning values must be even"),_("Kerning values must be even"));
     363           0 :                 oddcomplain = true;
     364             :             }
     365           0 :             ++kerns;
     366           0 :             ret = end;
     367             :         }
     368           0 :         this->next_state = ns;
     369           0 :         this->flags = flags;
     370           0 :         free(this->u.kern.kerns);
     371           0 :         this->u.kern.kcnt = kerns;
     372           0 :         if ( kerns==0 )
     373           0 :             this->u.kern.kerns = NULL;
     374             :         else {
     375           0 :             this->u.kern.kerns = malloc(kerns*sizeof(int16));
     376           0 :             memcpy(this->u.kern.kerns,kbuf,kerns*sizeof(int16));
     377             :         }
     378           0 :     } else if ( smd->sm->type==asm_context ) {
     379           0 :         ret8 = GGadgetGetTitle8(GWidgetGetControl(smd->editgw,CID_TagMark));
     380           0 :         if ( *ret8=='\0' )
     381           0 :             mlook = NULL;               /* That's ok */
     382           0 :         else if ( (mlook=SFFindLookup(smd->sf,ret8))==NULL ) {
     383           0 :             ff_post_error(_("Unknown lookup"),_("Lookup, %s, does not exist"), ret8 );
     384           0 :             free(ret8);
     385           0 : return( false );
     386           0 :         } else if ( mlook->lookup_type!=gsub_single ) {
     387           0 :             ff_post_error(_("Bad lookup type"),_("Lookups in contextual state machines must be simple substitutions,\n, but %s is not"), ret8 );
     388           0 :             free(ret8);
     389           0 : return( false );
     390             :         }
     391           0 :         free(ret8);
     392           0 :         ret8 = GGadgetGetTitle8(GWidgetGetControl(smd->editgw,CID_TagCur));
     393           0 :         if ( *ret8=='\0' )
     394           0 :             clook = NULL;               /* That's ok */
     395           0 :         else if ( (clook=SFFindLookup(smd->sf,ret8))==NULL ) {
     396           0 :             ff_post_error(_("Unknown lookup"),_("Lookup, %s, does not exist"), ret8 );
     397           0 :             free(ret8);
     398           0 : return( false );
     399           0 :         } else if ( clook->lookup_type!=gsub_single ) {
     400           0 :             ff_post_error(_("Bad lookup type"),_("Lookups in contextual state machines must be simple substitutions,\n, but %s is not"), ret8 );
     401           0 :             free(ret8);
     402           0 : return( false );
     403             :         }
     404           0 :         this->next_state = ns;
     405           0 :         this->flags = flags;
     406           0 :         this->u.context.mark_lookup = mlook;
     407           0 :         this->u.context.cur_lookup = clook;
     408             :     } else {
     409             : 
     410           0 :         if ( GGadgetIsChecked(GWidgetGetControl(smd->editgw,CID_Flag2000)) ) flags |= 0x2000;
     411           0 :         if ( GGadgetIsChecked(GWidgetGetControl(smd->editgw,CID_Flag1000)) ) flags |= 0x1000;
     412           0 :         if ( GGadgetIsChecked(GWidgetGetControl(smd->editgw,CID_Flag0800)) ) flags |= 0x0800;
     413           0 :         if ( GGadgetIsChecked(GWidgetGetControl(smd->editgw,CID_Flag0400)) ) flags |= 0x0400;
     414             : 
     415           0 :         mins = copy_count(smd->editgw,CID_InsMark,&cnt);
     416           0 :         if ( cnt>31 ) {
     417           0 :             ff_post_error(_("Too Many Glyphs"),_("At most 31 glyphs may be specified in an insert list"));
     418           0 :             free(mins);
     419           0 : return( false );
     420             :         }
     421           0 :         flags |= cnt<<5;
     422             : 
     423           0 :         cins = copy_count(smd->editgw,CID_InsCur,&cnt);
     424           0 :         if ( cnt>31 ) {
     425           0 :             ff_post_error(_("Too Many Glyphs"),_("At most 31 glyphs may be specified in an insert list"));
     426           0 :             free(mins);
     427           0 :             free(cins);
     428           0 : return( false );
     429             :         }
     430           0 :         flags |= cnt;
     431           0 :         this->next_state = ns;
     432           0 :         this->flags = flags;
     433           0 :         free(this->u.insert.mark_ins);
     434           0 :         free(this->u.insert.cur_ins);
     435           0 :         this->u.insert.mark_ins = mins;
     436           0 :         this->u.insert.cur_ins = cins;
     437             :     }
     438             : 
     439             :     /* Show changes in main window */
     440           0 :     GDrawRequestExpose(smd->gw,NULL,false);
     441           0 : return( true );
     442             : }
     443             : 
     444           0 : static int SMDE_Arrow(GGadget *g, GEvent *e) {
     445           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
     446           0 :         SMD *smd = GDrawGetUserData(GGadgetGetWindow(g));
     447           0 :         int state = smd->st_pos/smd->class_cnt;
     448           0 :         int class = smd->st_pos%smd->class_cnt;
     449           0 :         switch( GGadgetGetCid(g)) {
     450             :           case CID_Up:
     451           0 :             if ( state!=0 ) --state;
     452           0 :           break;
     453             :           case CID_Left:
     454           0 :             if ( class!=0 ) --class;
     455           0 :           break;
     456             :           case CID_Right:
     457           0 :             if ( class<smd->class_cnt-1 ) ++class;
     458           0 :           break;
     459             :           case CID_Down:
     460           0 :             if ( state<smd->state_cnt-1 ) ++state;
     461           0 :           break;
     462             :         }
     463           0 :         if ( state!=smd->st_pos/smd->class_cnt || class!=smd->st_pos%smd->class_cnt ) {
     464           0 :             if ( SMD_DoChange(smd)) {
     465           0 :                 smd->st_pos = state*smd->class_cnt+class;
     466           0 :                 SMD_Fillup(smd);
     467             :             }
     468             :         }
     469             :     }
     470           0 : return( true );
     471             : }
     472             : 
     473           0 : static int smdedit_e_h(GWindow gw, GEvent *event) {
     474           0 :     SMD *smd = GDrawGetUserData(gw);
     475             : 
     476           0 :     switch ( event->type ) {
     477             :       case et_close:
     478           0 :         smd->edit_done = true;
     479           0 :         smd->edit_ok = false;
     480           0 :       break;
     481             :       case et_char:
     482           0 :         if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
     483           0 :             help("statemachine.html#EditTransition");
     484           0 : return( true );
     485           0 :         } else if ( event->u.chr.keysym == GK_Escape ) {
     486           0 :             smd->edit_done = true;
     487           0 : return( true );
     488           0 :         } else if ( event->u.chr.chars[0] == '\r' ) {
     489           0 :             smd->edit_done = SMD_DoChange(smd);
     490           0 : return( true );
     491             :         }
     492           0 : return( false );
     493             :       case et_controlevent:
     494           0 :         switch( event->u.control.subtype ) {
     495             :           case et_buttonactivate:
     496           0 :             if ( GGadgetGetCid(event->u.control.g)==CID_Ok )
     497           0 :                 smd->edit_done = SMD_DoChange(smd);
     498             :             else
     499           0 :                 smd->edit_done = true;
     500           0 :           break;
     501             :         }
     502           0 :       break;
     503             :     }
     504           0 : return( true );
     505             : }
     506             : 
     507           0 : static void SMD_EditState(SMD *smd) {
     508             :     GWindow gw;
     509             :     GWindowAttrs wattrs;
     510             :     GGadgetCreateData gcd[23];
     511             :     GTextInfo label[23];
     512             :     GRect pos;
     513             :     int k, listk, new_cnt;
     514             :     char stateclass[100];
     515             :     static int indicv_done = false;
     516             : 
     517           0 :     smd->edit_done = false;
     518             : 
     519           0 :     memset(&wattrs,0,sizeof(wattrs));
     520           0 :     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
     521           0 :     wattrs.event_masks = ~(1<<et_charup);
     522           0 :     wattrs.is_dlg = true;
     523           0 :     wattrs.restrict_input_to_me = true;
     524           0 :     wattrs.undercursor = 1;
     525           0 :     wattrs.cursor = ct_pointer;
     526           0 :     wattrs.utf8_window_title = _("Edit State Transition");
     527           0 :     pos.x = pos.y = 0;
     528           0 :     pos.width = GDrawPointsToPixels(NULL,GGadgetScale(SMDE_WIDTH));
     529           0 :     pos.height = GDrawPointsToPixels(NULL,SMDE_HEIGHT);
     530           0 :     smd->editgw = gw = GDrawCreateTopWindow(NULL,&pos,smdedit_e_h,smd,&wattrs);
     531             : 
     532           0 :     memset(gcd,0,sizeof(gcd));
     533           0 :     memset(label,0,sizeof(label));
     534           0 :     k = 0; listk = -1;
     535             : 
     536           0 :     snprintf(stateclass,sizeof(stateclass), _("State %d,  %.40s"),
     537             :             999, _("Class 1: {Everything Else}" ));
     538           0 :     label[k].text = (unichar_t *) stateclass;
     539           0 :     label[k].text_is_1byte = true;
     540           0 :     gcd[k].gd.label = &label[k];
     541           0 :     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = 5;
     542           0 :     gcd[k].gd.flags = gg_enabled|gg_visible;
     543           0 :     gcd[k].gd.cid = CID_StateClass;
     544           0 :     gcd[k++].creator = GLabelCreate;
     545             : 
     546           0 :     label[k].text = (unichar_t *) _("Next State:");
     547           0 :     label[k].text_is_1byte = true;
     548           0 :     gcd[k].gd.label = &label[k];
     549           0 :     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+13+4;
     550           0 :     gcd[k].gd.flags = gg_enabled|gg_visible;
     551           0 :     gcd[k++].creator = GLabelCreate;
     552             : 
     553           0 :     gcd[k].gd.pos.x = 80; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;
     554           0 :     gcd[k].gd.flags = gg_enabled|gg_visible;
     555           0 :     gcd[k].gd.cid = CID_NextState;
     556           0 :     gcd[k++].creator = GTextFieldCreate;
     557             : 
     558           0 :     label[k].text = (unichar_t *) _("Advance To Next Glyph");
     559           0 :     label[k].text_is_1byte = true;
     560           0 :     gcd[k].gd.label = &label[k];
     561           0 :     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+24;
     562           0 :     gcd[k].gd.flags = gg_enabled|gg_visible;
     563           0 :     gcd[k].gd.cid = CID_Flag4000;
     564           0 :     gcd[k++].creator = GCheckBoxCreate;
     565             : 
     566           0 :     label[k].text = (unichar_t *) (smd->sm->type==asm_kern?_("Push Current Glyph"):
     567           0 :                                    smd->sm->type!=asm_indic?_("Mark Current Glyph"):
     568             :                                             _("Mark Current Glyph As First"));
     569           0 :     label[k].text_is_1byte = true;
     570           0 :     gcd[k].gd.label = &label[k];
     571           0 :     gcd[k].gd.pos.x = gcd[k-1].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16;
     572           0 :     gcd[k].gd.flags = gg_enabled|gg_visible;
     573           0 :     gcd[k].gd.cid = CID_Flag8000;
     574           0 :     gcd[k++].creator = GCheckBoxCreate;
     575             : 
     576           0 :     if ( smd->sm->type==asm_indic ) {
     577           0 :         if ( !indicv_done ) {
     578             :             int i;
     579           0 :             for ( i=0; indicverbs_list[i].text!=NULL; ++i )
     580           0 :                 indicverbs_list[i].text = (unichar_t *) _((char *) indicverbs_list[i].text );
     581           0 :             indicv_done = true;
     582             :         }
     583           0 :         label[k].text = (unichar_t *) _("Mark Current Glyph As Last");
     584           0 :         label[k].text_is_1byte = true;
     585           0 :         gcd[k].gd.label = &label[k];
     586           0 :         gcd[k].gd.pos.x = gcd[k-1].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16;
     587           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     588           0 :         gcd[k].gd.cid = CID_Flag2000;
     589           0 :         gcd[k++].creator = GCheckBoxCreate;
     590             : 
     591           0 :         gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+24;
     592           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     593           0 :         gcd[k].gd.u.list = indicverbs_list;
     594           0 :         gcd[k].gd.cid = CID_IndicVerb;
     595           0 :         gcd[k++].creator = GListButtonCreate;
     596           0 :     } else if ( smd->sm->type==asm_insert ) {
     597           0 :         label[k].text = (unichar_t *) _("Current Glyph Is Kashida Like");
     598           0 :         label[k].text_is_1byte = true;
     599           0 :         gcd[k].gd.label = &label[k];
     600           0 :         gcd[k].gd.pos.x = gcd[k-1].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16;
     601           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     602           0 :         gcd[k].gd.cid = CID_Flag2000;
     603           0 :         gcd[k++].creator = GCheckBoxCreate;
     604             : 
     605           0 :         label[k].text = (unichar_t *) _("Marked Glyph Is Kashida Like");
     606           0 :         label[k].text_is_1byte = true;
     607           0 :         gcd[k].gd.label = &label[k];
     608           0 :         gcd[k].gd.pos.x = gcd[k-1].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16;
     609           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     610           0 :         gcd[k].gd.cid = CID_Flag1000;
     611           0 :         gcd[k++].creator = GCheckBoxCreate;
     612             : 
     613           0 :         label[k].text = (unichar_t *) _("Insert Before Current Glyph");
     614           0 :         label[k].text_is_1byte = true;
     615           0 :         gcd[k].gd.label = &label[k];
     616           0 :         gcd[k].gd.pos.x = gcd[k-1].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16;
     617           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     618           0 :         gcd[k].gd.cid = CID_Flag0800;
     619           0 :         gcd[k++].creator = GCheckBoxCreate;
     620             : 
     621           0 :         label[k].text = (unichar_t *) _("Insert Before Marked Glyph");
     622           0 :         label[k].text_is_1byte = true;
     623           0 :         gcd[k].gd.label = &label[k];
     624           0 :         gcd[k].gd.pos.x = gcd[k-1].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16;
     625           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     626           0 :         gcd[k].gd.cid = CID_Flag0400;
     627           0 :         gcd[k++].creator = GCheckBoxCreate;
     628             : 
     629           0 :         label[k].text = (unichar_t *) _("Mark Insert:");
     630           0 :         label[k].text_is_1byte = true;
     631           0 :         gcd[k].gd.label = &label[k];
     632           0 :         gcd[k].gd.pos.x = gcd[k-1].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+26;
     633           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     634           0 :         gcd[k++].creator = GLabelCreate;
     635             : 
     636           0 :         gcd[k].gd.pos.x = gcd[2].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;
     637           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     638           0 :         gcd[k].gd.cid = CID_InsMark;
     639           0 :         gcd[k++].creator = GTextFieldCreate;
     640             : 
     641           0 :         label[k].text = (unichar_t *) _("Current Insert:");
     642           0 :         label[k].text_is_1byte = true;
     643           0 :         gcd[k].gd.label = &label[k];
     644           0 :         gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+30;
     645           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     646           0 :         gcd[k++].creator = GLabelCreate;
     647             : 
     648           0 :         gcd[k].gd.pos.x = gcd[2].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;
     649           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     650           0 :         gcd[k].gd.cid = CID_InsCur;
     651           0 :         gcd[k++].creator = GTextFieldCreate;
     652           0 :     } else if ( smd->sm->type==asm_kern ) {
     653           0 :         label[k].text = (unichar_t *) _("Kern Values:");
     654           0 :         label[k].text_is_1byte = true;
     655           0 :         gcd[k].gd.label = &label[k];
     656           0 :         gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+26;
     657           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     658           0 :         gcd[k++].creator = GLabelCreate;
     659             : 
     660           0 :         gcd[k].gd.pos.x = gcd[2].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;
     661           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     662           0 :         gcd[k].gd.cid = CID_Kerns;
     663           0 :         gcd[k++].creator = GTextFieldCreate;
     664             :     } else {
     665           0 :         label[k].text = (unichar_t *) _("Mark Subs:");
     666           0 :         label[k].text_is_1byte = true;
     667           0 :         gcd[k].gd.label = &label[k];
     668           0 :         gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+26;
     669           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     670           0 :         gcd[k++].creator = GLabelCreate;
     671             : 
     672           0 :         listk = k;
     673           0 :         gcd[k].gd.pos.x = gcd[2].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;
     674           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     675           0 :         gcd[k].gd.cid = CID_TagMark;
     676           0 :         gcd[k++].creator = GListFieldCreate;
     677             : 
     678           0 :         label[k].text = (unichar_t *) _("Current Subs:");
     679           0 :         label[k].text_is_1byte = true;
     680           0 :         gcd[k].gd.label = &label[k];
     681           0 :         gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+30;
     682           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     683           0 :         gcd[k++].creator = GLabelCreate;
     684             : 
     685           0 :         gcd[k].gd.pos.x = gcd[2].gd.pos.x; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;
     686           0 :         gcd[k].gd.flags = gg_enabled|gg_visible;
     687           0 :         gcd[k].gd.cid = CID_TagCur;
     688           0 :         gcd[k].gd.u.list = gcd[k-2].gd.u.list;
     689           0 :         gcd[k++].creator = GListFieldCreate;
     690             :     }
     691             : 
     692           0 :     label[k].text = (unichar_t *) U_("_Up↑");
     693           0 :     label[k].text_is_1byte = true;
     694           0 :     label[k].text_in_resource = true;
     695           0 :     gcd[k].gd.label = &label[k];
     696           0 :     gcd[k].gd.pos.x = (SMDE_WIDTH-GIntGetResource(_NUM_Buttonsize)*100/GIntGetResource(_NUM_ScaleFactor))/2;
     697           0 :     gcd[k].gd.pos.y = SMDE_HEIGHT-SMD_DIRDROP;
     698           0 :     gcd[k].gd.pos.width = -1;
     699           0 :     gcd[k].gd.flags = gg_visible|gg_enabled;
     700           0 :     gcd[k].gd.cid = CID_Up;
     701           0 :     gcd[k].gd.handle_controlevent = SMDE_Arrow;
     702           0 :     gcd[k++].creator = GButtonCreate;
     703             : 
     704           0 :     label[k].text = (unichar_t *) U_("←_Left");
     705           0 :     label[k].text_is_1byte = true;
     706           0 :     label[k].text_in_resource = true;
     707           0 :     gcd[k].gd.label = &label[k];
     708           0 :     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = SMDE_HEIGHT-SMD_DIRDROP+13;
     709           0 :     gcd[k].gd.pos.width = -1;
     710           0 :     gcd[k].gd.flags = gg_visible|gg_enabled;
     711           0 :     gcd[k].gd.cid = CID_Left;
     712           0 :     gcd[k].gd.handle_controlevent = SMDE_Arrow;
     713           0 :     gcd[k++].creator = GButtonCreate;
     714             : 
     715           0 :     label[k].text = (unichar_t *) U_("_Right→");
     716           0 :     label[k].text_is_1byte = true;
     717           0 :     label[k].text_in_resource = true;
     718           0 :     gcd[k].gd.label = &label[k];
     719           0 :     gcd[k].gd.pos.x = -10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y;
     720           0 :     gcd[k].gd.pos.width = -1;
     721           0 :     gcd[k].gd.flags = gg_visible|gg_enabled;
     722           0 :     gcd[k].gd.cid = CID_Right;
     723           0 :     gcd[k].gd.handle_controlevent = SMDE_Arrow;
     724           0 :     gcd[k++].creator = GButtonCreate;
     725             : 
     726           0 :     label[k].text = (unichar_t *) U_("↓_Down");
     727           0 :     label[k].text_is_1byte = true;
     728           0 :     label[k].text_in_resource = true;
     729           0 :     gcd[k].gd.label = &label[k];
     730           0 :     gcd[k].gd.pos.x = gcd[k-3].gd.pos.x; gcd[k].gd.pos.y = SMDE_HEIGHT-SMD_DIRDROP+26;
     731           0 :     gcd[k].gd.pos.width = -1;
     732           0 :     gcd[k].gd.flags = gg_visible|gg_enabled;
     733           0 :     gcd[k].gd.cid = CID_Down;
     734           0 :     gcd[k].gd.handle_controlevent = SMDE_Arrow;
     735           0 :     gcd[k++].creator = GButtonCreate;
     736             : 
     737           0 :     label[k].text = (unichar_t *) _("_OK");
     738           0 :     label[k].text_is_1byte = true;
     739           0 :     label[k].text_in_resource = true;
     740           0 :     gcd[k].gd.label = &label[k];
     741           0 :     gcd[k].gd.pos.x = 30-3; gcd[k].gd.pos.y = SMDE_HEIGHT-SMD_CANCELDROP-3;
     742           0 :     gcd[k].gd.pos.width = -1;
     743           0 :     gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_default;
     744           0 :     gcd[k].gd.cid = CID_Ok;
     745           0 :     gcd[k++].creator = GButtonCreate;
     746             : 
     747           0 :     label[k].text = (unichar_t *) _("_Cancel");
     748           0 :     label[k].text_is_1byte = true;
     749           0 :     label[k].text_in_resource = true;
     750           0 :     gcd[k].gd.label = &label[k];
     751           0 :     gcd[k].gd.pos.x = -30; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
     752           0 :     gcd[k].gd.pos.width = -1;
     753           0 :     gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_cancel;
     754           0 :     gcd[k].gd.cid = CID_Cancel;
     755           0 :     gcd[k++].creator = GButtonCreate;
     756             : 
     757           0 :     gcd[k].gd.pos.x = 2; gcd[k].gd.pos.y = 2;
     758           0 :     gcd[k].gd.pos.width = pos.width-4;
     759           0 :     gcd[k].gd.pos.height = pos.height-4;
     760           0 :     gcd[k].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
     761           0 :     gcd[k++].creator = GGroupCreate;
     762             : 
     763           0 :     GGadgetsCreate(gw,gcd);
     764             : 
     765           0 :     if ( listk!=-1 ) {
     766           0 :         GGadgetSetList(gcd[listk].ret,
     767             :                 SFLookupListFromType(smd->sf,gsub_single),false);
     768           0 :         GGadgetSetList(gcd[listk+2].ret,
     769             :                 SFLookupListFromType(smd->sf,gsub_single),false);
     770             :     }
     771             : 
     772           0 :     SMD_Fillup(smd);
     773             : 
     774           0 :     GDrawSetVisible(gw,true);
     775             : 
     776           0 :     smd->edit_done = false;
     777           0 :     while ( !smd->edit_done )
     778           0 :         GDrawProcessOneEvent(NULL);
     779             : 
     780           0 :     new_cnt = FindMaxReachableStateCnt(smd);
     781           0 :     if ( new_cnt!=smd->state_cnt ) {
     782           0 :         smd->states = StateCopy(smd->states,smd->class_cnt,smd->state_cnt,
     783             :                 smd->class_cnt,new_cnt,
     784           0 :                 smd->sm->type,true);
     785           0 :         smd->state_cnt = new_cnt;
     786           0 :         SMD_SBReset(smd);
     787           0 :         GDrawRequestExpose(smd->gw,NULL,false);
     788             :     }
     789           0 :     smd->st_pos = -1;
     790           0 :     GDrawDestroyWindow(gw);
     791           0 : return;
     792             : }
     793             : 
     794             : /* ************************************************************************** */
     795             : /* ****************************** Main Dialog ******************************* */
     796             : /* ************************************************************************** */
     797             : 
     798           0 : static void _SMD_Finish(SMD *smd, int success) {
     799             : 
     800           0 :     GDrawDestroyWindow(smd->gw);
     801             : 
     802           0 :     GFI_FinishSMNew(smd->d,smd->sm,success,smd->isnew);
     803             : 
     804           0 :     GTextInfoListFree(smd->mactags);
     805           0 :     smd->done = true;
     806           0 : }
     807             : 
     808           0 : static void _SMD_Cancel(SMD *smd) {
     809             : 
     810           0 :     StatesFree(smd->states,smd->state_cnt,smd->class_cnt,smd->sm->type);
     811           0 :     _SMD_Finish(smd,false);
     812           0 : }
     813             : 
     814           0 : static int SMD_Cancel(GGadget *g, GEvent *e) {
     815           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
     816           0 :         SMD *smd = GDrawGetUserData(GGadgetGetWindow(g));
     817             : 
     818           0 :         _SMD_Cancel(smd);
     819             :     }
     820           0 : return( true );
     821             : }
     822             : 
     823           0 : static int SMD_Ok(GGadget *g, GEvent *e) {
     824           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
     825           0 :         SMD *smd = GDrawGetUserData(GGadgetGetWindow(g));
     826             :         int i;
     827           0 :   struct ggadget * classControl = GWidgetGetControl(smd->gw,CID_Classes);
     828             :   int rows;
     829           0 :   struct matrix_data *classes = GMatrixEditGet(classControl,&rows);
     830             : 
     831           0 :         ASM *sm = smd->sm;
     832             :         char *upt;
     833             : 
     834           0 :         for ( i=4; i<sm->class_cnt; ++i )
     835           0 :             free(sm->classes[i]);
     836           0 :         free(sm->classes);
     837           0 :         sm->classes = malloc(smd->class_cnt*sizeof(char *));
     838           0 :         sm->classes[0] = sm->classes[1] = sm->classes[2] = sm->classes[3] = NULL;
     839           0 :         sm->class_cnt = smd->class_cnt;
     840           0 :     for ( i=4; i<sm->class_cnt; ++i ) {
     841           0 :         upt = strstr(classes[i].u.md_str,": ");
     842           0 :         if ( upt==NULL ) upt = classes[i].u.md_str; else upt += 2;
     843           0 :         sm->classes[i] = copy(GlyphNameListDeUnicode(upt));
     844             :     }
     845             : 
     846           0 :         StatesFree(sm->state,sm->state_cnt,sm->class_cnt,
     847           0 :                 sm->type);
     848           0 :         sm->state_cnt = smd->state_cnt;
     849           0 :         sm->state = smd->states;
     850           0 :         sm->flags = (sm->flags & ~0xc000) |
     851           0 :                 (GGadgetIsChecked(GWidgetGetControl(smd->gw,CID_RightToLeft))?0x4000:0) |
     852           0 :                 (GGadgetIsChecked(GWidgetGetControl(smd->gw,CID_VertOnly))?0x8000:0);
     853           0 :         _SMD_Finish(smd,true);
     854             :     }
     855           0 : return( true );
     856             : }
     857             : 
     858           0 : static void SMD_Mouse(SMD *smd,GEvent *event) {
     859             :     static unichar_t space[100];
     860             :     char *pt;
     861             :     char buf[30];
     862             :     int len;
     863             :     struct matrix_data *classes;
     864           0 :     int pos = ((event->u.mouse.y-smd->ystart2)/smd->stateh+smd->offtop) * smd->class_cnt +
     865           0 :             (event->u.mouse.x-smd->xstart2)/smd->statew + smd->offleft;
     866             : 
     867           0 :     GGadgetEndPopup();
     868             : 
     869           0 :     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
     870           0 :             (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
     871           0 :         GGadgetDispatchEvent(smd->vsb,event);
     872           0 : return;
     873             :     }
     874             : 
     875           0 :     if ( event->u.mouse.x<smd->xstart || event->u.mouse.x>smd->xstart2+smd->width ||
     876           0 :             event->u.mouse.y<smd->ystart || event->u.mouse.y>smd->ystart2+smd->height )
     877           0 : return;
     878             : 
     879           0 :     if ( event->type==et_mousemove ) {
     880           0 :         int c = (event->u.mouse.x - smd->xstart2)/smd->statew + smd->offleft;
     881           0 :         int s = (event->u.mouse.y - smd->ystart2)/smd->stateh + smd->offtop;
     882           0 :         space[0] = '\0';
     883           0 :         if ( event->u.mouse.y>=smd->ystart2 && s<smd->state_cnt ) {
     884           0 :             sprintf( buf, "State %d\n", s );
     885           0 :             uc_strcpy(space,buf);
     886             :         }
     887           0 :         if ( event->u.mouse.x>=smd->xstart2 && c<smd->class_cnt ) {
     888           0 :             sprintf( buf, "Class %d\n", c );
     889           0 :             uc_strcat(space,buf);
     890           0 :             classes = GMatrixEditGet(GWidgetGetControl(smd->gw,CID_Classes),&len);
     891           0 :             len = u_strlen(space);
     892           0 :             pt = strstr(classes[c].u.md_str,": ");
     893           0 :             if ( pt==NULL ) pt = classes[c].u.md_str;
     894           0 :             else pt += 2;
     895           0 :             utf82u_strncpy(space+len,pt,(sizeof(space)/sizeof(space[0]))-1 - len);
     896           0 :         } else if ( event->u.mouse.x<smd->xstart2 ) {
     897           0 :             if ( s==0 )
     898           0 :                 utf82u_strcat(space,_("{Start of Input}"));
     899           0 :             else if ( s==1 )
     900           0 :                 utf82u_strcat(space,_("{Start of Line}"));
     901             :         }
     902           0 :         if ( space[0]=='\0' )
     903           0 : return;
     904           0 :         if ( space[u_strlen(space)-1]=='\n' )
     905           0 :             space[u_strlen(space)-1]='\0';
     906           0 :         GGadgetPreparePopup(smd->gw,space);
     907           0 :     } else if ( event->u.mouse.x<smd->xstart2 || event->u.mouse.y<smd->ystart2 )
     908           0 : return;
     909           0 :     else if ( event->type==et_mousedown )
     910           0 :         smd->st_pos = pos;
     911           0 :     else if ( event->type==et_mouseup ) {
     912           0 :         if ( pos==smd->st_pos )
     913           0 :             SMD_EditState(smd);
     914             :     }
     915             : }
     916             : 
     917           0 : static void SMD_Expose(SMD *smd,GWindow pixmap,GEvent *event) {
     918           0 :     GRect *area = &event->u.expose.rect;
     919             :     GRect rect;
     920             :     GRect clip,old1,old2;
     921           0 :     int len, off, i, j, x, y, kddd=false;
     922             :     unichar_t ubuf[8];
     923             :     char buf[101];
     924             : 
     925           0 :     if ( area->y+area->height<smd->ystart )
     926           0 : return;
     927           0 :     if ( area->y>smd->ystart2+smd->height )
     928           0 : return;
     929             : 
     930           0 :     GDrawPushClip(pixmap,area,&old1);
     931           0 :     GDrawSetFont(pixmap,smd->font);
     932           0 :     GDrawSetLineWidth(pixmap,0);
     933           0 :     rect.x = smd->xstart; rect.y = smd->ystart;
     934           0 :     rect.width = smd->width+(smd->xstart2-smd->xstart);
     935           0 :     rect.height = smd->height+(smd->ystart2-smd->ystart);
     936           0 :     clip = rect;
     937           0 :     GDrawPushClip(pixmap,&clip,&old2);
     938           0 :     for ( i=0 ; smd->offtop+i<=smd->state_cnt && (i-1)*smd->stateh<smd->height; ++i ) {
     939           0 :         GDrawDrawLine(pixmap,smd->xstart,smd->ystart2+i*smd->stateh,smd->xstart+rect.width,smd->ystart2+i*smd->stateh,
     940             :                 0x808080);
     941           0 :         if ( i+smd->offtop<smd->state_cnt ) {
     942           0 :             sprintf( buf, i+smd->offtop<100 ? "St%d" : "%d", i+smd->offtop );
     943           0 :             len = strlen( buf );
     944           0 :             off = (smd->stateh-len*smd->fh)/2;
     945           0 :             for ( j=0; j<len; ++j ) {
     946           0 :                 ubuf[0] = buf[j];
     947           0 :                 GDrawDrawText(pixmap,smd->xstart+3,smd->ystart2+i*smd->stateh+off+j*smd->fh+smd->as,
     948             :                     ubuf,1,0xff0000);
     949             :             }
     950             :         }
     951             :     }
     952           0 :     for ( i=0 ; smd->offleft+i<=smd->class_cnt && (i-1)*smd->statew<smd->width; ++i ) {
     953           0 :         GDrawDrawLine(pixmap,smd->xstart2+i*smd->statew,smd->ystart,smd->xstart2+i*smd->statew,smd->ystart+rect.height,
     954             :                 0x808080);
     955           0 :         if ( i+smd->offleft<smd->class_cnt ) {
     956           0 :             GDrawDrawText8(pixmap,smd->xstart2+i*smd->statew+1,smd->ystart+smd->as+1,
     957             :                 "Class",-1,0xff0000);
     958           0 :             sprintf( buf, "%d", i+smd->offleft );
     959           0 :             len = GDrawGetText8Width(pixmap,buf,-1);
     960           0 :             GDrawDrawText8(pixmap,smd->xstart2+i*smd->statew+(smd->statew-len)/2,smd->ystart+smd->fh+smd->as+1,
     961             :                 buf,-1,0xff0000);
     962             :         }
     963             :     }
     964             : 
     965           0 :     for ( i=0 ; smd->offtop+i<smd->state_cnt && (i-1)*smd->stateh<smd->height; ++i ) {
     966           0 :         y = smd->ystart2+i*smd->stateh;
     967           0 :         if ( y>area->y+area->height )
     968           0 :     break;
     969           0 :         if ( y+smd->stateh<area->y )
     970           0 :     continue;
     971           0 :         for ( j=0 ; smd->offleft+j<smd->class_cnt && (j-1)*smd->statew<smd->width; ++j ) {
     972           0 :             struct asm_state *this = &smd->states[(i+smd->offtop)*smd->class_cnt+j+smd->offleft];
     973           0 :             x = smd->xstart2+j*smd->statew;
     974           0 :             if ( x>area->x+area->width )
     975           0 :         break;
     976           0 :             if ( x+smd->statew<area->x )
     977           0 :         continue;
     978             : 
     979           0 :             sprintf( buf, "%d", this->next_state );
     980           0 :             len = GDrawGetText8Width(pixmap,buf,-1);
     981           0 :             GDrawDrawText8(pixmap,x+(smd->statew-len)/2,y+smd->as+1,
     982             :                 buf,-1,0x000000);
     983             : 
     984           0 :             ubuf[0] = (this->flags&0x8000)? 'M' : ' ';
     985           0 :             if ( smd->sm->type==asm_kern && (this->flags&0x8000))
     986           0 :                 ubuf[0] = 'P';
     987           0 :             ubuf[1] = (this->flags&0x4000)? ' ' : 'A';
     988           0 :             ubuf[2] = '\0';
     989           0 :             if ( smd->sm->type==asm_indic ) {
     990           0 :                 ubuf[2] = (this->flags&0x2000) ? 'L' : ' ';
     991           0 :                 ubuf[3] = '\0';
     992             :             }
     993           0 :             len = GDrawGetTextWidth(pixmap,ubuf,-1);
     994           0 :             GDrawDrawText(pixmap,x+(smd->statew-len)/2,y+smd->fh+smd->as+1,
     995             :                 ubuf,-1,0x000000);
     996             : 
     997           0 :             buf[0]='\0';
     998           0 :             if ( smd->sm->type==asm_indic ) {
     999           0 :                 strcpy(buf,indicverbs[0][this->flags&0xf]);
    1000           0 :             } else if ( smd->sm->type==asm_context ) {
    1001           0 :                 if ( this->u.context.mark_lookup!=NULL ) {
    1002           0 :                     strncpy(buf,this->u.context.mark_lookup->lookup_name,6);
    1003           0 :                     buf[6] = '\0';
    1004             :                 }
    1005           0 :             } else if ( smd->sm->type==asm_insert ) {
    1006           0 :                 if ( this->u.insert.mark_ins!=NULL )
    1007           0 :                     strncpy(buf,this->u.insert.mark_ins,5);
    1008             :             } else { /* kern */
    1009           0 :                 if ( this->u.kern.kerns!=NULL ) {
    1010             :                     int j;
    1011           0 :                     buf[0] = '\0';
    1012           0 :                     for ( j=0; j<this->u.kern.kcnt; ++j )
    1013           0 :                         sprintf(buf+strlen(buf),"%d ", this->u.kern.kerns[j]);
    1014           0 :                     kddd = ( strlen(buf)>5 );
    1015           0 :                     buf[5] = '\0';
    1016             :                 } else
    1017           0 :                     kddd = false;
    1018             :             }
    1019           0 :             len = GDrawGetText8Width(pixmap,buf,-1);
    1020           0 :             GDrawDrawText8(pixmap,x+(smd->statew-len)/2,y+2*smd->fh+smd->as+1,
    1021             :                 buf,-1,0x000000);
    1022             : 
    1023           0 :             buf[0] = '\0';
    1024           0 :             if ( smd->sm->type==asm_indic ) {
    1025           0 :                 strncpy(buf,indicverbs[1][this->flags&0xf],sizeof(buf)-1);
    1026           0 :             } else if ( smd->sm->type==asm_context ) {
    1027           0 :                 if ( this->u.context.cur_lookup!=NULL ) {
    1028           0 :                     strncpy(buf,this->u.context.cur_lookup->lookup_name,6);
    1029           0 :                     buf[6] = '\0';
    1030             :                 }
    1031           0 :             } else if ( smd->sm->type==asm_insert ) {
    1032           0 :                 if ( this->u.insert.cur_ins!=NULL )
    1033           0 :                     strncpy(buf,this->u.insert.cur_ins,5);
    1034             :             } else { /* kern */
    1035           0 :                 if ( kddd ) strcpy(buf,"...");
    1036           0 :                 else buf[0] = '\0';
    1037             :             }
    1038           0 :             len = GDrawGetText8Width(pixmap,buf,-1);
    1039           0 :             GDrawDrawText8(pixmap,x+(smd->statew-len)/2,y+3*smd->fh+smd->as+1,
    1040             :                 buf,-1,0x000000);
    1041             :         }
    1042             :     }
    1043             : 
    1044           0 :     GDrawDrawLine(pixmap,smd->xstart,smd->ystart2,smd->xstart+rect.width,smd->ystart2,
    1045             :             0x000000);
    1046           0 :     GDrawDrawLine(pixmap,smd->xstart2,smd->ystart,smd->xstart2,smd->ystart+rect.height,
    1047             :             0x000000);
    1048           0 :     GDrawPopClip(pixmap,&old2);
    1049           0 :     GDrawPopClip(pixmap,&old1);
    1050           0 :     GDrawDrawRect(pixmap,&rect,0x000000);
    1051           0 :     rect.y += rect.height;
    1052           0 :     rect.x += rect.width;
    1053           0 :     LogoExpose(pixmap,event,&rect,dm_fore);
    1054             : }
    1055             : 
    1056           0 : static int SMD_SBReset(SMD *smd) {
    1057           0 :     int oldtop = smd->offtop, oldleft = smd->offleft;
    1058             : 
    1059           0 :     GScrollBarSetBounds(smd->vsb,0,smd->state_cnt, smd->height/smd->stateh);
    1060           0 :     GScrollBarSetBounds(smd->hsb,0,smd->class_cnt, smd->width/smd->statew);
    1061           0 :     if ( smd->offtop + (smd->height/smd->stateh) >= smd->state_cnt )
    1062           0 :         smd->offtop = smd->state_cnt - (smd->height/smd->stateh);
    1063           0 :     if ( smd->offtop < 0 ) smd->offtop = 0;
    1064           0 :     if ( smd->offleft + (smd->width/smd->statew) >= smd->class_cnt )
    1065           0 :         smd->offleft = smd->class_cnt - (smd->width/smd->statew);
    1066           0 :     if ( smd->offleft < 0 ) smd->offleft = 0;
    1067           0 :     GScrollBarSetPos(smd->vsb,smd->offtop);
    1068           0 :     GScrollBarSetPos(smd->hsb,smd->offleft);
    1069             : 
    1070           0 : return( oldtop!=smd->offtop || oldleft!=smd->offleft );
    1071             : }
    1072             : 
    1073           0 : static void SMD_HShow(SMD *smd, int pos) {
    1074           0 :     if ( pos<0 || pos>=smd->class_cnt )
    1075           0 : return;
    1076           0 :     --pos;      /* One line of context */
    1077           0 :     if ( pos + (smd->width/smd->statew) >= smd->class_cnt )
    1078           0 :         pos = smd->class_cnt - (smd->width/smd->statew);
    1079           0 :     if ( pos < 0 ) pos = 0;
    1080           0 :     smd->offleft = pos;
    1081           0 :     GScrollBarSetPos(smd->hsb,pos);
    1082           0 :     GDrawRequestExpose(smd->gw,NULL,false);
    1083             : }
    1084             : 
    1085           0 : static void SMD_HScroll(SMD *smd,struct sbevent *sb) {
    1086           0 :     int newpos = smd->offleft;
    1087             :     GRect rect;
    1088             : 
    1089           0 :     switch( sb->type ) {
    1090             :       case et_sb_top:
    1091           0 :         newpos = 0;
    1092           0 :       break;
    1093             :       case et_sb_uppage:
    1094           0 :         if ( smd->width/smd->statew == 1 )
    1095           0 :             --newpos;
    1096             :         else
    1097           0 :             newpos -= smd->width/smd->statew - 1;
    1098           0 :       break;
    1099             :       case et_sb_up:
    1100           0 :         --newpos;
    1101           0 :       break;
    1102             :       case et_sb_down:
    1103           0 :         ++newpos;
    1104           0 :       break;
    1105             :       case et_sb_downpage:
    1106           0 :         if ( smd->width/smd->statew == 1 )
    1107           0 :             ++newpos;
    1108             :         else
    1109           0 :             newpos += smd->width/smd->statew - 1;
    1110           0 :       break;
    1111             :       case et_sb_bottom:
    1112           0 :         newpos = smd->class_cnt - (smd->width/smd->statew);
    1113           0 :       break;
    1114             :       case et_sb_thumb:
    1115             :       case et_sb_thumbrelease:
    1116           0 :         newpos = sb->pos;
    1117           0 :       break;
    1118             :     }
    1119           0 :     if ( newpos + (smd->width/smd->statew) >= smd->class_cnt )
    1120           0 :         newpos = smd->class_cnt - (smd->width/smd->statew);
    1121           0 :     if ( newpos < 0 ) newpos = 0;
    1122           0 :     if ( newpos!=smd->offleft ) {
    1123           0 :         int diff = newpos-smd->offleft;
    1124           0 :         smd->offleft = newpos;
    1125           0 :         GScrollBarSetPos(smd->hsb,newpos);
    1126           0 :         rect.x = smd->xstart2+1; rect.y = smd->ystart;
    1127           0 :         rect.width = smd->width-1;
    1128           0 :         rect.height = smd->height+(smd->ystart2-smd->ystart);
    1129           0 :         GDrawScroll(smd->gw,&rect,-diff*smd->statew,0);
    1130             :     }
    1131           0 : }
    1132             : 
    1133           0 : static void SMD_VScroll(SMD *smd,struct sbevent *sb) {
    1134           0 :     int newpos = smd->offtop;
    1135             :     GRect rect;
    1136             : 
    1137           0 :     switch( sb->type ) {
    1138             :       case et_sb_top:
    1139           0 :         newpos = 0;
    1140           0 :       break;
    1141             :       case et_sb_uppage:
    1142           0 :         if ( smd->height/smd->stateh == 1 )
    1143           0 :             --newpos;
    1144             :         else
    1145           0 :             newpos -= smd->height/smd->stateh - 1;
    1146           0 :       break;
    1147             :       case et_sb_up:
    1148           0 :         --newpos;
    1149           0 :       break;
    1150             :       case et_sb_down:
    1151           0 :         ++newpos;
    1152           0 :       break;
    1153             :       case et_sb_downpage:
    1154           0 :         if ( smd->height/smd->stateh == 1 )
    1155           0 :             ++newpos;
    1156             :         else
    1157           0 :             newpos += smd->height/smd->stateh - 1;
    1158           0 :       break;
    1159             :       case et_sb_bottom:
    1160           0 :         newpos = smd->state_cnt - (smd->height/smd->stateh);
    1161           0 :       break;
    1162             :       case et_sb_thumb:
    1163             :       case et_sb_thumbrelease:
    1164           0 :         newpos = sb->pos;
    1165           0 :       break;
    1166             :     }
    1167           0 :     if ( newpos + (smd->height/smd->stateh) >= smd->state_cnt )
    1168           0 :         newpos = smd->state_cnt - (smd->height/smd->stateh);
    1169           0 :     if ( newpos < 0 ) newpos = 0;
    1170           0 :     if ( newpos!=smd->offtop ) {
    1171           0 :         int diff = newpos-smd->offtop;
    1172           0 :         smd->offtop = newpos;
    1173           0 :         GScrollBarSetPos(smd->vsb,newpos);
    1174           0 :         rect.x = smd->xstart; rect.y = smd->ystart2+1;
    1175           0 :         rect.width = smd->width+(smd->xstart2-smd->xstart);
    1176           0 :         rect.height = smd->height-1;
    1177           0 :         GDrawScroll(smd->gw,&rect,0,diff*smd->stateh);
    1178             :     }
    1179           0 : }
    1180             : 
    1181           0 : static int smd_e_h(GWindow gw, GEvent *event) {
    1182           0 :     SMD *smd = GDrawGetUserData(gw);
    1183             : 
    1184           0 :     switch ( event->type ) {
    1185             :       case et_close:
    1186           0 :         _SMD_Cancel(smd);
    1187           0 :       break;
    1188             :       case et_char:
    1189           0 :         if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
    1190           0 :             help("statemachine.html");
    1191           0 : return( true );
    1192           0 :         } else if ( event->u.chr.keysym=='q' && (event->u.chr.state&ksm_control)) {
    1193           0 :             if ( event->u.chr.state&ksm_shift )
    1194           0 :                 _SMD_Cancel(smd);
    1195             :             else
    1196           0 :                 MenuExit(NULL,NULL,NULL);
    1197           0 : return( true );
    1198             :         }
    1199           0 : return( false );
    1200             :       case et_expose:
    1201           0 :         SMD_Expose(smd,gw,event);
    1202           0 :       break;
    1203             :       case et_resize: {
    1204           0 :         int blen = GDrawPointsToPixels(NULL,GIntGetResource(_NUM_Buttonsize));
    1205             :         GRect wsize, csize;
    1206             : 
    1207           0 :         GDrawGetSize(smd->gw,&wsize);
    1208           0 :         GGadgetResize(GWidgetGetControl(smd->gw,CID_Group),wsize.width-4,wsize.height-4);
    1209           0 :         GGadgetResize(GWidgetGetControl(smd->gw,CID_Line1),wsize.width-20,1);
    1210           0 :         GGadgetResize(GWidgetGetControl(smd->gw,CID_Line2),wsize.width-20,1);
    1211             : 
    1212           0 :         GGadgetGetSize(GWidgetGetControl(smd->gw,CID_Ok),&csize);
    1213           0 :         GGadgetMove(GWidgetGetControl(smd->gw,CID_Ok),csize.x,wsize.height-smd->canceldrop-3);
    1214           0 :         GGadgetGetSize(GWidgetGetControl(smd->gw,CID_Cancel),&csize);
    1215           0 :         GGadgetMove(GWidgetGetControl(smd->gw,CID_Cancel),wsize.width-blen-30,wsize.height-smd->canceldrop);
    1216             : 
    1217           0 :         GGadgetGetSize(GWidgetGetControl(smd->gw,CID_Classes),&csize);
    1218           0 :         GGadgetResize(GWidgetGetControl(smd->gw,CID_Classes),wsize.width-GDrawPointsToPixels(NULL,10),csize.height);
    1219             : 
    1220           0 :         GGadgetGetSize(smd->hsb,&csize);
    1221           0 :         smd->width = wsize.width-csize.height-smd->xstart2-5;
    1222           0 :         GGadgetResize(smd->hsb,smd->width,csize.height);
    1223           0 :         GGadgetMove(smd->hsb,smd->xstart2,wsize.height-smd->sbdrop-csize.height);
    1224           0 :         GGadgetGetSize(smd->vsb,&csize);
    1225           0 :         smd->height = wsize.height-smd->sbdrop-smd->ystart2-csize.width;
    1226           0 :         GGadgetResize(smd->vsb,csize.width,wsize.height-smd->sbdrop-smd->ystart2-csize.width);
    1227           0 :         GGadgetMove(smd->vsb,wsize.width-csize.width-5,smd->ystart2);
    1228           0 :         SMD_SBReset(smd);
    1229             : 
    1230           0 :         GDrawRequestExpose(smd->gw,NULL,false);
    1231           0 :       } break;
    1232             :       case et_mouseup: case et_mousemove: case et_mousedown:
    1233           0 :         SMD_Mouse(smd,event);
    1234           0 :       break;
    1235             :       case et_controlevent:
    1236           0 :         switch( event->u.control.subtype ) {
    1237             :           case et_scrollbarchange:
    1238           0 :             if ( event->u.control.g == smd->hsb )
    1239           0 :                 SMD_HScroll(smd,&event->u.control.u.sb);
    1240             :             else
    1241           0 :                 SMD_VScroll(smd,&event->u.control.u.sb);
    1242           0 :           break;
    1243             :         }
    1244           0 :       break;
    1245             :     }
    1246           0 : return( true );
    1247             : }
    1248             : 
    1249           0 : static char *SMD_PickGlyphsForClass(GGadget *g,int r, int c) {
    1250           0 :     SMD *smd = GDrawGetUserData(GGadgetGetWindow(g));
    1251           0 :     int rows, cols = GMatrixEditGetColCnt(g);
    1252           0 :     struct matrix_data *classes = _GMatrixEditGet(g,&rows);
    1253           0 :     char *new = GlyphSetFromSelection(smd->sf,ly_fore,classes[r*cols+c].u.md_str);
    1254           0 : return( new );
    1255             : }
    1256             : 
    1257           0 : static void SMD_NewClassRow(GGadget *g,int r) {
    1258           0 :     SMD *smd = GDrawGetUserData(GGadgetGetWindow(g));
    1259             : 
    1260           0 :     smd->states = StateCopy(smd->states,smd->class_cnt,smd->state_cnt,
    1261           0 :             smd->class_cnt+1,smd->state_cnt,
    1262           0 :             smd->sm->type,true);
    1263           0 :     ++smd->class_cnt;
    1264           0 :     SMD_SBReset(smd);
    1265           0 : }
    1266             : 
    1267           0 : static void SMD_FinishEdit(GGadget *g,int r, int c, int wasnew) {
    1268           0 :     SMD *smd = GDrawGetUserData(GGadgetGetWindow(g));
    1269             : 
    1270           0 :     ME_ClassCheckUnique(g, r, c, smd->sf);
    1271           0 : }
    1272             : 
    1273           0 : static int SMD_EnableDeleteClass(GGadget *g,int whichclass) {
    1274           0 : return( whichclass>=4 );
    1275             : }
    1276             : 
    1277           0 : static void SMD_DeleteClass(GGadget *g,int whichclass) {
    1278           0 :     SMD *smd = GDrawGetUserData(GGadgetGetWindow(g));
    1279             : 
    1280           0 :     StateRemoveClasses(smd,whichclass);
    1281           0 :     SMD_SBReset(smd);
    1282           0 :     GDrawRequestExpose(smd->gw,NULL,false);
    1283           0 : }
    1284             : 
    1285           0 : static void SMD_ClassSelectionChanged(GGadget *g,int whichclass, int c) {
    1286           0 :     SMD *smd = GDrawGetUserData(GGadgetGetWindow(g));
    1287           0 :     SMD_HShow(smd,whichclass);
    1288           0 : }
    1289             : 
    1290           0 : static unichar_t **SMD_GlyphListCompletion(GGadget *t,int from_tab) {
    1291           0 :     SMD *smd = GDrawGetUserData(GDrawGetParentWindow(GGadgetGetWindow(t)));
    1292           0 :     SplineFont *sf = smd->sf;
    1293             : 
    1294           0 : return( SFGlyphNameCompletion(sf,t,from_tab,true));
    1295             : }
    1296             : 
    1297             : static struct col_init class_ci[] = {
    1298             :     { me_funcedit, SMD_PickGlyphsForClass, NULL, NULL, N_("Glyphs in the classes") },
    1299             :     };
    1300           0 : void StateMachineEdit(SplineFont *sf,ASM *sm,struct gfi_data *d) {
    1301             :     static char *titles[2][4] = {
    1302             :         { N_("Edit Indic Rearrangement"), N_("Edit Contextual Substitution"), N_("Edit Contextual Glyph Insertion"), N_("Edit Contextual Kerning") },
    1303             :         { N_("New Indic Rearrangement"), N_("New Contextual Substitution"), N_("New Contextual Glyph Insertion"), N_("New Contextual Kerning") }};
    1304             :     SMD smd;
    1305             :     GRect pos;
    1306             :     GWindow gw;
    1307             :     GWindowAttrs wattrs;
    1308             :     GGadgetCreateData gcd[20];
    1309             :     GTextInfo label[20];
    1310             :     int i, k, vk;
    1311             :     int as, ds, ld, sbsize;
    1312             :     FontRequest rq;
    1313             :     static unichar_t statew[] = { '1', '2', '3', '4', '5', 0 };
    1314             :     static GFont *font = NULL;
    1315             :     struct matrix_data *md;
    1316             :     struct matrixinit mi;
    1317             :     static char *specialclasses[4] = { N_("{End of Text}"),
    1318             :             N_("{Everything Else}"),
    1319             :             N_("{Deleted Glyph}"),
    1320             :             N_("{End of Line}") };
    1321             : 
    1322           0 :     memset(&smd,0,sizeof(smd));
    1323           0 :     smd.sf = sf;
    1324           0 :     smd.sm = sm;
    1325           0 :     smd.d = d;
    1326           0 :     smd.isnew = (sm->class_cnt==0);
    1327           0 :     if ( smd.isnew ) {
    1328           0 :         smd.class_cnt = 4;                      /* 4 built in classes */
    1329           0 :         smd.state_cnt = 2;                      /* 2 built in states */
    1330           0 :         smd.states = calloc(smd.class_cnt*smd.state_cnt,sizeof(struct asm_state));
    1331           0 :         smd.states[1*4+2].next_state = 1;       /* deleted glyph is a noop */
    1332             :     } else {
    1333           0 :         smd.class_cnt = sm->class_cnt;
    1334           0 :         smd.state_cnt = sm->state_cnt;
    1335           0 :         smd.states = StateCopy(sm->state,sm->class_cnt,sm->state_cnt,
    1336           0 :                 smd.class_cnt,smd.state_cnt,sm->type,false);
    1337             :     }
    1338           0 :     smd.index = sm->type==asm_indic ? 0 : sm->type==asm_context ? 1 : sm->type==asm_insert ? 2 : 3;
    1339             : 
    1340           0 :     memset(&wattrs,0,sizeof(wattrs));
    1341           0 :     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
    1342           0 :     wattrs.event_masks = ~(1<<et_charup);
    1343           0 :     wattrs.is_dlg = true;
    1344           0 :     wattrs.restrict_input_to_me = true;
    1345           0 :     wattrs.undercursor = 1;
    1346           0 :     wattrs.cursor = ct_pointer;
    1347           0 :     wattrs.utf8_window_title = _(titles[smd.isnew][smd.index]);
    1348           0 :     pos.x = pos.y = 0;
    1349           0 :     pos.width =GDrawPointsToPixels(NULL,GGadgetScale(SMD_WIDTH));
    1350           0 :     pos.height = GDrawPointsToPixels(NULL,SMD_HEIGHT);
    1351           0 :     smd.gw = gw = GDrawCreateTopWindow(NULL,&pos,smd_e_h,&smd,&wattrs);
    1352             : 
    1353           0 :     memset(gcd,0,sizeof(gcd));
    1354           0 :     memset(label,0,sizeof(label));
    1355           0 :     k = 0;
    1356             : 
    1357           0 :     label[k].text = (unichar_t *) _("Right To Left");
    1358           0 :     label[k].text_is_1byte = true;
    1359           0 :     gcd[k].gd.label = &label[k];
    1360           0 :     gcd[k].gd.pos.x = 150; gcd[k].gd.pos.y = 5;
    1361           0 :     gcd[k].gd.flags = gg_enabled|gg_visible | (sm->flags&0x4000?gg_cb_on:0);
    1362           0 :     gcd[k].gd.cid = CID_RightToLeft;
    1363           0 :     gcd[k++].creator = GCheckBoxCreate;
    1364           0 :     if ( smd.sm->type == asm_kern ) {
    1365           0 :         gcd[k-1].gd.flags = gg_enabled;         /* I'm not sure why kerning doesn't have an r2l bit */
    1366             :     }
    1367             : 
    1368           0 :     label[k].text = (unichar_t *) _("Vertical Only");
    1369           0 :     label[k].text_is_1byte = true;
    1370           0 :     gcd[k].gd.label = &label[k];
    1371           0 :     gcd[k].gd.pos.x = 150; gcd[k].gd.pos.y = 5+16;
    1372           0 :     gcd[k].gd.flags = gg_enabled|gg_visible | (sm->flags&0x8000?gg_cb_on:0);
    1373           0 :     gcd[k].gd.cid = CID_VertOnly;
    1374           0 :     gcd[k++].creator = GCheckBoxCreate;
    1375             : 
    1376           0 :     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = GDrawPointsToPixels(gw,gcd[k-1].gd.pos.y+15);
    1377           0 :     gcd[k].gd.pos.width = pos.width-20;
    1378           0 :     gcd[k].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
    1379           0 :     gcd[k].gd.cid = CID_Line1;
    1380           0 :     gcd[k++].creator = GLineCreate;
    1381             : 
    1382           0 :     memset(&mi,0,sizeof(mi));
    1383           0 :     mi.col_cnt = 1;
    1384           0 :     mi.col_init = class_ci;
    1385             : 
    1386           0 :     if ( sm->class_cnt<4 ) sm->class_cnt=4;
    1387           0 :     md = calloc(sm->class_cnt+1,sizeof(struct matrix_data));
    1388           0 :     for ( i=0; i<sm->class_cnt; ++i ) {
    1389           0 :         if ( i<4 ) {
    1390           0 :             md[i+0].u.md_str = copy( _(specialclasses[i]) );
    1391           0 :             md[i+0].frozen = true;
    1392             :         } else
    1393           0 :       if (sm->classes[i])
    1394           0 :         md[i+0].u.md_str = SFNameList2NameUni(sf,sm->classes[i]);
    1395             :     }
    1396           0 :     mi.matrix_data = md;
    1397           0 :     mi.initial_row_cnt = i;
    1398           0 :     mi.initrow = SMD_NewClassRow;
    1399           0 :     mi.finishedit = SMD_FinishEdit;
    1400           0 :     mi.candelete = SMD_EnableDeleteClass;
    1401             : 
    1402           0 :     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = GDrawPixelsToPoints(gw,gcd[k-1].gd.pos.y)+5;
    1403           0 :     gcd[k].gd.pos.width = SMD_WIDTH-10;
    1404           0 :     gcd[k].gd.pos.height = 8*12+34;
    1405           0 :     gcd[k].gd.flags = gg_visible | gg_enabled;
    1406           0 :     gcd[k].gd.cid = CID_Classes;
    1407           0 :     gcd[k].gd.u.matrix = &mi;
    1408           0 :     gcd[k++].creator = GMatrixEditCreate;
    1409             : 
    1410           0 :     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = GDrawPointsToPixels(gw,gcd[k-1].gd.pos.y+gcd[k-1].gd.pos.height+5);
    1411           0 :     gcd[k].gd.pos.width = pos.width-20;
    1412           0 :     gcd[k].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
    1413           0 :     gcd[k].gd.cid = CID_Line2;
    1414           0 :     gcd[k++].creator = GLineCreate;
    1415             : 
    1416           0 :     smd.canceldrop = GDrawPointsToPixels(gw,SMD_CANCELDROP);
    1417           0 :     smd.sbdrop = smd.canceldrop+GDrawPointsToPixels(gw,7);
    1418             : 
    1419           0 :     vk = k;
    1420           0 :     gcd[k].gd.pos.width = sbsize = GDrawPointsToPixels(gw,_GScrollBar_Width);
    1421           0 :     gcd[k].gd.pos.x = pos.width-sbsize;
    1422           0 :     gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+8;
    1423           0 :     gcd[k].gd.pos.height = pos.height-gcd[k].gd.pos.y-sbsize-smd.sbdrop;
    1424           0 :     gcd[k].gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_sb_vert;
    1425           0 :     gcd[k++].creator = GScrollBarCreate;
    1426           0 :     smd.height = gcd[k-1].gd.pos.height;
    1427           0 :     smd.ystart = gcd[k-1].gd.pos.y;
    1428             : 
    1429           0 :     gcd[k].gd.pos.height = sbsize;
    1430           0 :     gcd[k].gd.pos.y = pos.height-sbsize-8;
    1431           0 :     gcd[k].gd.pos.x = 4;
    1432           0 :     gcd[k].gd.pos.width = pos.width-sbsize;
    1433           0 :     gcd[k].gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
    1434           0 :     gcd[k++].creator = GScrollBarCreate;
    1435           0 :     smd.width = gcd[k-1].gd.pos.width;
    1436           0 :     smd.xstart = 5;
    1437             : 
    1438           0 :     label[k].text = (unichar_t *) _("_OK");
    1439           0 :     label[k].text_is_1byte = true;
    1440           0 :     label[k].text_in_resource = true;
    1441           0 :     gcd[k].gd.label = &label[k];
    1442           0 :     gcd[k].gd.pos.x = 30-3; gcd[k].gd.pos.y = SMD_HEIGHT-SMD_CANCELDROP-3;
    1443           0 :     gcd[k].gd.pos.width = -1;
    1444           0 :     gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_default;
    1445           0 :     gcd[k].gd.handle_controlevent = SMD_Ok;
    1446           0 :     gcd[k].gd.cid = CID_Ok;
    1447           0 :     gcd[k++].creator = GButtonCreate;
    1448             : 
    1449           0 :     label[k].text = (unichar_t *) _("_Cancel");
    1450           0 :     label[k].text_is_1byte = true;
    1451           0 :     label[k].text_in_resource = true;
    1452           0 :     gcd[k].gd.label = &label[k];
    1453           0 :     gcd[k].gd.pos.x = -30; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
    1454           0 :     gcd[k].gd.pos.width = -1;
    1455           0 :     gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_cancel;
    1456           0 :     gcd[k].gd.handle_controlevent = SMD_Cancel;
    1457           0 :     gcd[k].gd.cid = CID_Cancel;
    1458           0 :     gcd[k++].creator = GButtonCreate;
    1459             : 
    1460           0 :     gcd[k].gd.pos.x = 2; gcd[k].gd.pos.y = 2;
    1461           0 :     gcd[k].gd.pos.width = pos.width-4;
    1462           0 :     gcd[k].gd.pos.height = pos.height-4;
    1463           0 :     gcd[k].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
    1464           0 :     gcd[k].gd.cid = CID_Group;
    1465           0 :     gcd[k++].creator = GGroupCreate;
    1466             : 
    1467           0 :     GGadgetsCreate(gw,gcd);
    1468           0 :     smd.vsb = gcd[vk].ret;
    1469           0 :     smd.hsb = gcd[vk+1].ret;
    1470             : 
    1471             :     {
    1472           0 :         GGadget *list = GWidgetGetControl(smd.gw,CID_Classes);
    1473           0 :         GMatrixEditSetBeforeDelete(list, SMD_DeleteClass);
    1474             :     /* When the selection changes */
    1475           0 :         GMatrixEditSetOtherButtonEnable(list, SMD_ClassSelectionChanged);
    1476           0 :         GMatrixEditSetColumnCompletion(list,0,SMD_GlyphListCompletion);
    1477             :     }
    1478             : 
    1479           0 :     if ( font==NULL ) {
    1480           0 :         memset(&rq,'\0',sizeof(rq));
    1481           0 :         rq.point_size = 12;
    1482           0 :         rq.weight = 400;
    1483           0 :         rq.utf8_family_name = MONO_UI_FAMILIES;
    1484           0 :         font = GDrawInstanciateFont(gw,&rq);
    1485           0 :         font = GResourceFindFont("StateMachine.Font",font);
    1486             :     }
    1487           0 :     smd.font = font;
    1488           0 :     GDrawWindowFontMetrics(gw,smd.font,&as,&ds,&ld);
    1489           0 :     smd.fh = as+ds; smd.as = as;
    1490           0 :     GDrawSetFont(gw,smd.font);
    1491             : 
    1492           0 :     smd.stateh = 4*smd.fh+3;
    1493           0 :     smd.statew = GDrawGetTextWidth(gw,statew,-1)+3;
    1494           0 :     smd.xstart2 = smd.xstart+smd.statew/2;
    1495           0 :     smd.ystart2 = smd.ystart+2*smd.fh+1;
    1496             : 
    1497           0 :     GDrawSetVisible(gw,true);
    1498           0 :     while ( !smd.done )
    1499           0 :         GDrawProcessOneEvent(NULL);
    1500           0 : }

Generated by: LCOV version 1.10