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