1 /* Copyright (C) 2003-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "autowidth2.h"
31 #include "fontforgeui.h"
32 #include "fvfonts.h"
33 #include "gkeysym.h"
34 #include "lookups.h"
35 #include "splinefill.h"
36 #include "splineutil.h"
37 #include "tottfgpos.h"
38 #include "ustring.h"
39 #include "utype.h"
40 
41 #include <math.h>
42 #include <string.h>
43 
44 extern GBox _ggadget_Default_Box;
45 #define ACTIVE_BORDER   (_ggadget_Default_Box.active_border)
46 #define MAIN_FOREGROUND (_ggadget_Default_Box.main_foreground)
47 
48 typedef struct kernclassdlg {
49     struct kernclasslistdlg *kcld;
50     KernClass *orig;
51     struct lookup_subtable *subtable;
52     int first_cnt, second_cnt;
53     char **firsts_names;
54     char **seconds_names;
55     int *firsts_flags;
56     int *seconds_flags;
57     int16 *offsets;
58     int *offsets_flags;
59     DeviceTable *adjusts;
60     DeviceTable active_adjust;		/* The one that is currently active */
61     DeviceTable orig_adjust;		/* Initial value for this the active adjust */
62     GWindow gw, subw;
63     GFont *font;
64     int fh, as;
65     int kernh, kernw;		/* Width of the box containing the kerning val */
66     int xstart, ystart;		/* This is where the headers start */
67     int xstart2, ystart2;	/* This is where the data start */
68     int width, height, fullwidth, subwidth;
69     int canceldrop, sbdrop;
70     int offleft, offtop;
71     GGadget *hsb, *vsb;
72     int isedit, off;
73     int st_pos, old_pos;
74     BDFChar *fsc, *ssc;
75     int pixelsize;
76     int magfactor;
77     int downpos, down, within, orig_kern;
78     SplineFont *sf;
79     int layer;
80     int isv;
81     int first_class_new, r2l, index;
82     int orig_kern_offset;
83 /* For the kern pair dlg */
84     int done;
85     SplineChar *sc1, *sc2;
86     int iskernpair;
87     SplineChar *scf, *scs;
88     struct kernclassdlg *next;
89 
90 } KernClassDlg;
91 
92 typedef struct kernclasslistdlg {
93     SplineFont *sf;
94     int layer;
95     GWindow gw;
96     int isv;
97 } KernClassListDlg;
98 
99 #define KCL_Width	200
100 #define KCL_Height	173
101 #define KC_Width	400
102 #define KC_Height	424
103 #define KC_CANCELDROP	33
104 
105 #define CID_Subtable	1001
106 
107 #define CID_List	1040		/* List of kern class subtables */
108 #define CID_New		1041
109 #define CID_Delete	1042
110 #define CID_Edit	1043
111 
112 #define CID_ClassList	1007		/* And 1107 for second char */
113 #define CID_ClassLabel	1011
114 #define CID_ClassSelect	1014
115 
116 #define CID_OK		1015
117 #define CID_Cancel	1016
118 
119 #define CID_First	1030
120 #define CID_Second	1031
121 #define CID_KernOffset	1032
122 #define CID_Prev2	1033
123 #define CID_Next2	1034
124 #define CID_DisplaySize	1036
125 #define CID_Correction	1037
126 #define CID_FreeType	1038
127 #define CID_Magnifications	1039
128 #define CID_ClearDevice	1040
129 #define CID_Display	1041
130 
131 #define CID_Separation	2008
132 #define CID_MinKern	2009
133 #define CID_Touched	2010
134 #define CID_OnlyCloser	2011
135 #define CID_Autokern	2012
136 
137 #define CID_SizeLabel	3000
138 #define CID_MagLabel	3001
139 #define CID_OffsetLabel	3002
140 #define CID_CorrectLabel	3003
141 #define CID_Revert	3004
142 #define CID_TopBox	3005
143 #define CID_ShowHideKern	3006
144 
145 extern int _GScrollBar_Width;
146 int show_kerning_pane_in_class = true;
147 
148 static GTextInfo magnifications[] = {
149     { (unichar_t *) "100%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 1, 0, 1, 0, 0, '\0'},
150     { (unichar_t *) "200%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
151     { (unichar_t *) "300%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
152     { (unichar_t *) "400%", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
153     GTEXTINFO_EMPTY
154 };
155 
156 static int  KCD_SBReset(KernClassDlg *);
157 static void KCD_HShow(KernClassDlg *, int pos);
158 static void KCD_VShow(KernClassDlg *, int pos);
159 
KCLookupSubtableArray(SplineFont * sf,int isv)160 static GTextInfo **KCLookupSubtableArray(SplineFont *sf,int isv) {
161     int cnt;
162     KernClass *kc, *head = isv ? sf->vkerns : sf->kerns;
163     GTextInfo **ti;
164 
165     if ( sf->cidmaster!=NULL ) sf=sf->cidmaster;
166     else if ( sf->mm!=NULL ) sf = sf->mm->normal;
167 
168     for ( kc=head, cnt=0; kc!=NULL; kc=kc->next, ++cnt );
169     ti = calloc(cnt+1,sizeof(GTextInfo*));
170     for ( kc=head, cnt=0; kc!=NULL; kc=kc->next, ++cnt ) {
171 	ti[cnt] = calloc(1,sizeof(GTextInfo));
172 	ti[cnt]->fg = ti[cnt]->bg = COLOR_DEFAULT;
173 	ti[cnt]->text = utf82u_copy(kc->subtable->subtable_name);
174     }
175     ti[cnt] = calloc(1,sizeof(GTextInfo));
176 return( ti );
177 }
178 
KCLookupSubtableList(SplineFont * sf,int isv)179 static GTextInfo *KCLookupSubtableList(SplineFont *sf,int isv) {
180     int cnt;
181     KernClass *kc, *head = isv ? sf->vkerns : sf->kerns;
182     GTextInfo *ti;
183 
184     if ( sf->cidmaster!=NULL ) sf=sf->cidmaster;
185     else if ( sf->mm!=NULL ) sf = sf->mm->normal;
186 
187     for ( kc=head, cnt=0; kc!=NULL; kc=kc->next, ++cnt );
188     ti = calloc(cnt+1,sizeof(GTextInfo));
189     for ( kc=head, cnt=0; kc!=NULL; kc=kc->next, ++cnt )
190 	ti[cnt].text = utf82u_copy(kc->subtable->subtable_name);
191 return( ti );
192 }
193 
isEverythingElse(char * text)194 static int isEverythingElse(char *text) {
195     /* GT: The string "{Everything Else}" is used in the context of a list */
196     /* GT: of classes (a set of kerning classes) where class 0 designates the */
197     /* GT: default class containing all glyphs not specified in the other classes */
198     int ret = strcmp(text,_("{Everything Else}") );
199 return( ret==0 );
200 }
201 
KCD_AddOffset(void * data,int left_index,int right_index,int kern)202 static void KCD_AddOffset(void *data,int left_index,int right_index, int kern) {
203     KernClassDlg *kcd = data;
204 
205     if ( kcd->first_class_new && !kcd->r2l ) {
206 	left_index = kcd->index;
207 	kcd->offsets[left_index*kcd->second_cnt+right_index] = kern;
208     } else if ( kcd->first_class_new ) {
209 	right_index = kcd->index;
210 	kcd->offsets[right_index*kcd->second_cnt+left_index] = kern;
211     } else if ( !kcd->r2l ) {
212 	right_index = kcd->index;
213 	kcd->offsets[left_index*kcd->second_cnt+right_index] = kern;
214     } else {
215 	left_index = kcd->index;
216 	kcd->offsets[right_index*kcd->second_cnt+left_index] = kern;
217     }
218 }
219 
KCD_AddOffsetAsIs(void * data,int left_index,int right_index,int kern)220 static void KCD_AddOffsetAsIs(void *data,int left_index,int right_index, int kern) {
221     KernClassDlg *kcd = data;
222 
223     if ( !kcd->r2l ) {
224 	kcd->offsets[left_index*kcd->second_cnt+right_index] = kern;
225     } else {
226 	kcd->offsets[right_index*kcd->second_cnt+left_index] = kern;
227     }
228 }
229 
KCD_AutoKernAClass(KernClassDlg * kcd,int index,int is_first)230 static void KCD_AutoKernAClass(KernClassDlg *kcd,int index,int is_first) {
231     char *space[1], **lefts, **rights, **others;
232     int lcnt, rcnt; int ocnt, acnt;
233     // CID_ClassList is a constant. It presumably provides a base for identifying the two list controls in the KernAClass dialogue, and we assign by which is active, not by which is the first list.
234     // Empirical testing suggests that index indexes a unified collection of unique characters spread across the two list views. The empirical testing seems to reflect an incorrect calling sequence, as it turns out.
235     // This function expects things to be indexed separately according to the items in the two visible lists.
236     GGadget *activelist = GWidgetGetControl( kcd->gw, CID_ClassList+(is_first?0:100));
237     GGadget *otherlist = GWidgetGetControl( kcd->gw, CID_ClassList+(is_first?100:0));
238     // Each of these returns a GGadget. We then make the assumption that each gadget is nested in a GMatrixEdit and retrieve the GMatrixEdit object.
239     struct matrix_data *otherdata = GMatrixEditGet(otherlist,&ocnt);
240     struct matrix_data *activedata = GMatrixEditGet(activelist,&acnt);
241     int err, touch=0, separation=0, minkern=0, onlyCloser;
242     int r2l, i;
243 
244     if ( kcd->isv )
245 return;
246     if( acnt <= index )
247 	return;
248 
249     err = false;
250     touch = GGadgetIsChecked(GWidgetGetControl(kcd->gw,CID_Touched));
251     separation = GetInt8(kcd->gw,CID_Separation,_("Separation"),&err);
252     minkern = GetInt8(kcd->gw,CID_MinKern,_("Min Kern"),&err);
253     onlyCloser = GGadgetIsChecked(GWidgetGetControl(kcd->gw,CID_OnlyCloser));
254     if ( err )
255 return;
256 
257     // We next strdup from activedata[index].u.md_str, which causes a segmentation fault sometimes.
258     space[0] = copy(activedata[index].u.md_str);
259     // space keeps just the specified item from activelist. others stores the items from the opposite list.
260     others = malloc((ocnt+1)*sizeof(char *));
261     for ( i=0; i<ocnt; ++i ) {
262 	if ( i==0 && isEverythingElse(otherdata[0].u.md_str))
263 	    others[i] = copy("");
264 	else
265 	    others[i] = copy(otherdata[i].u.md_str);
266     }
267     kcd->first_class_new = is_first;
268     kcd->r2l = r2l = (kcd->subtable->lookup->lookup_flags & pst_r2l)?1:0;
269     kcd->index = index;
270 
271     if ( (is_first && !r2l) || (!is_first && r2l) ) {
272 	lefts = space; lcnt = 1;
273 	rights = others; rcnt = ocnt;
274     } else {
275 	lefts = others; lcnt = ocnt;
276 	rights = space; rcnt=1;
277     }
278     AutoKern2NewClass(kcd->sf,kcd->layer, lefts, rights, lcnt, rcnt,
279 	    KCD_AddOffset, kcd, separation, minkern, touch, onlyCloser, 0);
280     for ( i=0; i<ocnt; ++i )
281 	free(others[i]);
282     free(others);
283     free(space[0]);
284 }
285 
KCD_AutoKernAll(KernClassDlg * kcd)286 static void KCD_AutoKernAll(KernClassDlg *kcd) {
287     char **lefts, **rights, **firsts, **seconds;
288     int lcnt, rcnt; int fcnt, scnt;
289     GGadget *firstlist = GWidgetGetControl( kcd->gw, CID_ClassList+0);
290     GGadget *secondlist = GWidgetGetControl( kcd->gw, CID_ClassList+100);
291     struct matrix_data *seconddata = GMatrixEditGet(secondlist,&scnt);
292     struct matrix_data *firstdata = GMatrixEditGet(firstlist,&fcnt);
293     int err, touch=0, separation=0, minkern=0, onlyCloser;
294     int r2l, i;
295 
296     if ( kcd->isv )
297 return;
298 
299     err = false;
300     touch = GGadgetIsChecked(GWidgetGetControl(kcd->gw,CID_Touched));
301     separation = GetInt8(kcd->gw,CID_Separation,_("Separation"),&err);
302     minkern = GetInt8(kcd->gw,CID_MinKern,_("Min Kern"),&err);
303     onlyCloser = GGadgetIsChecked(GWidgetGetControl(kcd->gw,CID_OnlyCloser));
304     if ( err )
305 return;
306 
307     firsts = malloc((fcnt+1)*sizeof(char *));
308     for ( i=0; i<fcnt; ++i ) {
309 	if ( i==0 && isEverythingElse(firstdata[0].u.md_str))
310 	    firsts[i] = copy("");
311 	else
312 	    firsts[i] = copy(firstdata[i].u.md_str);
313     }
314     seconds = malloc((scnt+1)*sizeof(char *));
315     for ( i=0; i<scnt; ++i ) {
316 	if ( i==0 && isEverythingElse(seconddata[0].u.md_str))
317 	    seconds[i] = copy("");
318 	else
319 	    seconds[i] = copy(seconddata[i].u.md_str);
320     }
321     kcd->r2l = r2l = (kcd->subtable->lookup->lookup_flags & pst_r2l)?1:0;
322 
323     if ( !r2l ) {
324 	lefts = firsts; lcnt = fcnt;
325 	rights = seconds; rcnt = scnt;
326     } else {
327 	lefts = seconds; lcnt = scnt;
328 	rights = firsts; rcnt=fcnt;
329     }
330     AutoKern2NewClass(kcd->sf,kcd->layer, lefts, rights, lcnt, rcnt,
331 	    KCD_AddOffsetAsIs, kcd, separation, minkern, touch, onlyCloser, 0);
332     for ( i=0; i<fcnt; ++i )
333 	free(firsts[i]);
334     free(firsts);
335     for ( i=0; i<scnt; ++i )
336 	free(seconds[i]);
337     free(seconds);
338 }
339 
340 /* ************************************************************************** */
341 /* ************************** Kern Class Display **************************** */
342 /* ************************************************************************** */
343 
KPD_DoCancel(KernClassDlg * kcd)344 static void KPD_DoCancel(KernClassDlg *kcd) {
345     BDFCharFree(kcd->fsc); BDFCharFree(kcd->ssc);
346     kcd->fsc = kcd->ssc = NULL;
347     free(kcd->active_adjust.corrections); kcd->active_adjust.corrections = NULL;
348     free(kcd->orig_adjust.corrections); kcd->orig_adjust.corrections = NULL;
349     kcd->done = true;
350 }
351 
KPD_Cancel(GGadget * g,GEvent * e)352 static int KPD_Cancel(GGadget *g, GEvent *e) {
353     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
354 	KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
355 	KPD_DoCancel(kcd);
356     }
357 return( true );
358 }
359 
360 static int KPD_FinishKP(KernClassDlg *);
361 
KPD_OK(GGadget * g,GEvent * e)362 static int KPD_OK(GGadget *g, GEvent *e) {
363     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
364 	KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
365 	if ( !KPD_FinishKP(kcd))
366 return( true );
367 	BDFCharFree(kcd->fsc); BDFCharFree(kcd->ssc);
368 	kcd->fsc = kcd->ssc = NULL;
369 	free(kcd->active_adjust.corrections); kcd->active_adjust.corrections = NULL;
370 	free(kcd->orig_adjust.corrections); kcd->orig_adjust.corrections = NULL;
371 	kcd->done = true;
372     }
373 return( true );
374 }
375 
KCD_Finalize(KernClassDlg * kcd)376 static void KCD_Finalize(KernClassDlg *kcd) {
377     const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(kcd->gw,CID_KernOffset));
378     unichar_t *end;
379     int val = u_strtol(ret,&end,10);
380 
381     if ( kcd->old_pos==-1 )
382 return;
383 
384     if ( val<-32768 || val>32767 || *end!='\0' ) {
385 	ff_post_error( _("Bad Number"), _("Bad Number") );
386 return;
387     }
388     kcd->offsets[kcd->old_pos] = val;
389     free(kcd->adjusts[kcd->old_pos].corrections);
390     kcd->adjusts[kcd->old_pos] = kcd->active_adjust;
391     kcd->active_adjust.corrections = NULL;
392 
393     BDFCharFree(kcd->fsc); BDFCharFree(kcd->ssc);
394     kcd->fsc = kcd->ssc = NULL;
395     GDrawRequestExpose(kcd->gw,NULL,false);
396     kcd->old_pos = -1;
397 }
398 
KCD_DrawGlyph(GWindow pixmap,int x,int baseline,BDFChar * bdfc,int mag)399 void KCD_DrawGlyph(GWindow pixmap,int x,int baseline,BDFChar *bdfc,int mag) {
400     struct _GImage base;
401     GImage gi;
402     GClut clut;
403 
404     memset(&gi,'\0',sizeof(gi));
405     memset(&base,'\0',sizeof(base));
406     memset(&clut,'\0',sizeof(clut));
407     gi.u.image = &base;
408     base.clut = &clut;
409     if ( !bdfc->byte_data ) {
410 	base.image_type = it_mono;
411 	clut.clut_len = 2;
412 	clut.clut[0] = GDrawGetDefaultBackground(NULL);
413 	clut.clut[1] = 0x000000;
414     } else {
415 	int scale, l;
416 	Color fg, bg;
417 	scale = bdfc->depth == 8 ? 8 : 4;
418 	base.image_type = it_index;
419 	clut.clut_len = 1<<scale;
420 	bg = GDrawGetDefaultBackground(NULL);
421 	fg = GDrawGetDefaultForeground(NULL);
422 	for ( l=0; l<(1<<scale); ++l )
423 	    clut.clut[l] =
424 		COLOR_CREATE(
425 		 COLOR_RED(bg) + (l*(COLOR_RED(fg)-COLOR_RED(bg)))/((1<<scale)-1),
426 		 COLOR_GREEN(bg) + (l*(COLOR_GREEN(fg)-COLOR_GREEN(bg)))/((1<<scale)-1),
427 		 COLOR_BLUE(bg) + (l*(COLOR_BLUE(fg)-COLOR_BLUE(bg)))/((1<<scale)-1) );
428     }
429     base.data = bdfc->bitmap;
430     base.bytes_per_line = bdfc->bytes_per_line;
431     base.width = bdfc->xmax-bdfc->xmin+1;
432     base.height = bdfc->ymax-bdfc->ymin+1;
433     x += mag*bdfc->xmin;
434     if ( mag==1 )
435 	GDrawDrawImage(pixmap,&gi,NULL,x,baseline-bdfc->ymax);
436     else
437 	GDrawDrawImageMagnified(pixmap, &gi, NULL,
438 		x,baseline-mag*bdfc->ymax,
439 		base.width*mag,base.height*mag);
440 }
441 
KCD_RightToLeft(KernClassDlg * kcd)442 static int KCD_RightToLeft(KernClassDlg *kcd) {
443     if ( kcd->subtable!=NULL )
444 return( kcd->subtable->lookup->lookup_flags&pst_r2l );
445 
446     if ( kcd->scf!=NULL ) {
447 	uint32 script = SCScriptFromUnicode(kcd->scf);
448 	if ( script!=DEFAULT_SCRIPT )
449 return( ScriptIsRightToLeft( script ));
450     }
451     if ( kcd->scs!=NULL ) {
452 	uint32 script = SCScriptFromUnicode(kcd->scs);
453 	if ( script!=DEFAULT_SCRIPT )
454 return( ScriptIsRightToLeft( script ));
455     }
456 return( false );
457 }
458 
KCD_KernMouse(KernClassDlg * kcd,GEvent * event)459 static void KCD_KernMouse(KernClassDlg *kcd,GEvent *event) {
460     int x, y, width;
461     char buf[20];
462     unichar_t ubuf[20];
463     int kern, pkern;
464     double scale;
465 //    printf("KCD_KernMouse()\n");
466 
467     scale = kcd->pixelsize/(double) (kcd->sf->ascent+kcd->sf->descent);
468     kern = u_strtol(_GGadgetGetTitle(GWidgetGetControl(kcd->gw,CID_KernOffset)),NULL,10);
469     pkern = kcd->magfactor*rint( kern*scale );	/* rounding can't include magnification */
470 
471     if ( !kcd->isv ) {
472 	/* Horizontal */
473 	width = kcd->magfactor*((kcd->fsc!=NULL?kcd->fsc->width:0)+(kcd->ssc!=NULL?kcd->ssc->width:0))+pkern;
474 	x = (kcd->subwidth - width)/2;
475 
476 	if ( KCD_RightToLeft(kcd) ) {
477 	    if ( kcd->ssc!=NULL )
478 		width -= kcd->magfactor*kcd->ssc->width;
479 	} else {
480 	    if ( kcd->fsc!=NULL ) {
481 		x += kcd->magfactor*kcd->fsc->width + pkern;
482 		width -= kcd->magfactor*kcd->fsc->width + pkern;
483 	    }
484 	}
485 
486 	if ( event->u.mouse.y>2*kcd->pixelsize*kcd->magfactor ||
487 		event->u.mouse.x<x || event->u.mouse.x>x+width ) {
488 	    if ( event->type == et_mousedown )
489 return;
490 	    if ( kcd->within ) {
491 		GDrawSetCursor(kcd->subw,ct_pointer);
492 		if ( kcd->down && kcd->orig_kern!=kern ) {
493 		    sprintf(buf, "%d", kcd->orig_kern);
494 		    uc_strcpy(ubuf,buf);
495 		    GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_KernOffset),ubuf);
496 		    GDrawRequestExpose(kcd->subw,NULL,false);
497 		}
498 		kcd->within = false;
499 	    }
500 	    if ( event->type==et_mouseup )
501 		kcd->down = false;
502 return;
503 	}
504 
505 	if ( !kcd->within ) {
506 	    GDrawSetCursor(kcd->subw,ct_leftright);
507 	    kcd->within = true;
508 	}
509 	if ( event->type == et_mousedown ) {
510 	    kcd->orig_kern = kern;
511 	    kcd->down = true;
512 	    kcd->downpos = event->u.mouse.x;
513 	} else if ( kcd->down ) {
514 	    /* I multiply by 2 here because I center the glyphs, so the kerning */
515 	    /*  changes in both directions */
516 	    int nkern = kcd->orig_kern + rint(2*(event->u.mouse.x-kcd->downpos)/scale/kcd->magfactor);
517 	    if ( kern!=nkern ) {
518 		sprintf(buf, "%d", nkern);
519 		uc_strcpy(ubuf,buf);
520 		GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_KernOffset),ubuf);
521 		GDrawRequestExpose(kcd->subw,NULL,false);
522 	    }
523 	    if ( event->type==et_mouseup ) {
524 		kcd->down = false;
525 		if ( nkern!=kcd->orig_kern && kcd->active_adjust.corrections!=NULL ) {
526 		    free(kcd->active_adjust.corrections);
527 		    kcd->active_adjust.corrections = NULL;
528 		    ubuf[0] = '0'; ubuf[1] = '\0';
529 		    GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_Correction),ubuf);
530 		    GDrawRequestExpose(kcd->subw,NULL,false);
531 		}
532 	    }
533 	}
534     } else {
535 	/* Vertical */
536 	y = kcd->pixelsize/3;
537 	width = (kcd->ssc!=NULL ? kcd->magfactor*rint(kcd->ssc->sc->vwidth * scale) : 0);
538 	if ( kcd->fsc!=NULL )
539 	    y += kcd->magfactor*rint(kcd->fsc->sc->vwidth * scale) + pkern;
540 	x = (kcd->subwidth/2 - kcd->pixelsize/2)*kcd->magfactor;
541 
542 	if ( event->u.mouse.y<y || event->u.mouse.y>y+width ||
543 		event->u.mouse.x<x || event->u.mouse.x>x+kcd->pixelsize ) {
544 	    if ( event->type == et_mousedown )
545 return;
546 	    if ( kcd->within ) {
547 		GDrawSetCursor(kcd->subw,ct_pointer);
548 		if ( kcd->down && kcd->orig_kern!=kern ) {
549 		    sprintf(buf, "%d", kcd->orig_kern);
550 		    uc_strcpy(ubuf,buf);
551 		    GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_KernOffset),ubuf);
552 		    GDrawRequestExpose(kcd->subw,NULL,false);
553 		}
554 		kcd->within = false;
555 	    }
556 	    if ( event->type==et_mouseup )
557 		kcd->down = false;
558 return;
559 	}
560 
561 	if ( !kcd->within ) {
562 	    GDrawSetCursor(kcd->subw,ct_updown);
563 	    kcd->within = true;
564 	}
565 	if ( event->type == et_mousedown ) {
566 	    kcd->orig_kern = kern;
567 	    kcd->down = true;
568 	    kcd->downpos = event->u.mouse.y;
569 	} else if ( kcd->down ) {
570 	    int nkern = kcd->orig_kern + rint((event->u.mouse.y-kcd->downpos)/scale)/kcd->magfactor;
571 	    if ( kern!=nkern ) {
572 		sprintf(buf, "%d", nkern);
573 		uc_strcpy(ubuf,buf);
574 		GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_KernOffset),ubuf);
575 		GDrawRequestExpose(kcd->subw,NULL,false);
576 	    }
577 	    if ( event->type==et_mouseup ) {
578 		kcd->down = false;
579 		if ( nkern!=kcd->orig_kern && kcd->active_adjust.corrections!=NULL ) {
580 		    free(kcd->active_adjust.corrections);
581 		    kcd->active_adjust.corrections = NULL;
582 		    ubuf[0] = '0'; ubuf[1] = '\0';
583 		    GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_Correction),ubuf);
584 		    GDrawRequestExpose(kcd->subw,NULL,false);
585 		}
586 	    }
587 	}
588     }
589 }
590 
KCD_KernExpose(KernClassDlg * kcd,GWindow pixmap,GEvent * event)591 static void KCD_KernExpose(KernClassDlg *kcd,GWindow pixmap,GEvent *event) {
592     int x, y;
593     SplineFont *sf = kcd->sf;
594     int em = sf->ascent+sf->descent;
595     int as = kcd->magfactor*rint(sf->ascent*kcd->pixelsize/(double) em);
596     const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(kcd->gw,CID_KernOffset));
597     int kern = u_strtol(ret,NULL,10);
598     int baseline, xbaseline;
599 
600 //    printf("KCD_KernExpose() ssc:%p fsc:%p\n", kcd->ssc, kcd->fsc );
601 
602     kern = kcd->magfactor*rint(kern*kcd->pixelsize/(double) em);
603 
604     { int correction;
605 	unichar_t *end;
606 
607 	ret = _GGadgetGetTitle(GWidgetGetControl(kcd->gw,CID_Correction));
608 	correction = u_strtol(ret,&end,10);
609 	while ( *end==' ' ) ++end;
610 	if ( *end=='\0' && correction>=-128 && correction<=127 )
611 	    kern += correction*kcd->magfactor;
612     }
613 
614     if ( !kcd->isv ) {
615 	x = (kcd->subwidth-( kcd->magfactor*(kcd->fsc!=NULL?kcd->fsc->width:0)+
616 		kcd->magfactor*(kcd->ssc!=NULL?kcd->ssc->width:0)+
617 		kern))/2;
618 	baseline = 0 + as + kcd->magfactor*kcd->pixelsize/2;
619 	if ( KCD_RightToLeft(kcd) ) {
620 	    if ( kcd->ssc!=NULL ) {
621 		KCD_DrawGlyph(pixmap,x,baseline,kcd->ssc,kcd->magfactor);
622 		x += kcd->magfactor*kcd->ssc->width + kern;
623 	    }
624 	    if ( kcd->fsc!=NULL )
625 		KCD_DrawGlyph(pixmap,x,baseline,kcd->fsc,kcd->magfactor);
626 	} else {
627 	    if ( kcd->fsc!=NULL ) {
628 		KCD_DrawGlyph(pixmap,x,baseline,kcd->fsc,kcd->magfactor);
629 		x += kcd->fsc->width*kcd->magfactor + kern;
630 	    }
631 	    if ( kcd->ssc!=NULL )
632 		KCD_DrawGlyph(pixmap,x,baseline,kcd->ssc,kcd->magfactor);
633 	}
634     } else {
635 	/* I don't support top to bottom vertical */
636 	y = kcd->magfactor*kcd->pixelsize/3 + as;
637 	xbaseline = kcd->subwidth/2;
638 	if ( kcd->fsc!=NULL ) {
639 	    KCD_DrawGlyph(pixmap,xbaseline-kcd->magfactor*kcd->pixelsize/2,y,kcd->fsc,kcd->magfactor);
640 	    y += kcd->magfactor*rint(kcd->fsc->sc->vwidth * kcd->pixelsize/(double) em) + kern;
641 	}
642 	if ( kcd->ssc!=NULL )
643 	    KCD_DrawGlyph(pixmap,xbaseline-kcd->magfactor*kcd->pixelsize/2,y,kcd->ssc,kcd->magfactor);
644     }
645 }
646 
KCD_KernOffChanged(GGadget * g,GEvent * e)647 static int KCD_KernOffChanged(GGadget *g, GEvent *e) {
648     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
649     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged )
650 	GDrawRequestExpose(kcd->subw,NULL,false);
651 return( true );
652 }
653 
KCD_UpdateGlyphFromName(KernClassDlg * kcd,int which,char * glyphname)654 static void KCD_UpdateGlyphFromName(KernClassDlg *kcd,int which,char* glyphname)
655 {
656     BDFChar **scpos = which==0 ? &kcd->fsc : &kcd->ssc;
657     SplineChar **possc = which==0 ? &kcd->scf : &kcd->scs;
658     SplineChar *sc;
659     void *freetypecontext=NULL;
660 //    printf("KCD_UpdateGlyphFromName() which:%d iskp:%d\n", which, kcd->iskernpair);
661 
662     char* localglyphname = copy( glyphname );
663     char* p = 0;
664     if((p = strstr( localglyphname, " " )))
665 	*p = '\0';
666 
667     BDFCharFree(*scpos);
668     *scpos = NULL;
669 
670     *possc = sc = SFGetChar( kcd->sf, -1, localglyphname);
671     free( localglyphname );
672 
673     if ( sc==NULL )
674 	return;
675 
676     if ( GGadgetIsChecked(GWidgetGetControl(kcd->gw,CID_FreeType)) )
677 	freetypecontext = FreeTypeFontContext(sc->parent,sc,sc->parent->fv,kcd->layer);
678     if ( freetypecontext )
679     {
680 	*scpos = SplineCharFreeTypeRasterize(freetypecontext,sc->orig_pos,kcd->pixelsize,72,8);
681 	FreeTypeFreeContext(freetypecontext);
682     }
683     else
684     {
685 	*scpos = SplineCharAntiAlias(sc,kcd->layer,kcd->pixelsize,4);
686     }
687 
688 //    printf("KCD_UpdateGlyph() scpos:%p\n", *scpos );
689 }
690 
KCD_UpdateGlyph(KernClassDlg * kcd,int which)691 static void KCD_UpdateGlyph(KernClassDlg *kcd,int which) {
692     BDFChar **scpos = which==0 ? &kcd->fsc : &kcd->ssc;
693     SplineChar **possc = which==0 ? &kcd->scf : &kcd->scs;
694     SplineChar *sc;
695     char *temp;
696     void *freetypecontext=NULL;
697 //    printf("KCD_UpdateGlyph() which:%d iskp:%d\n", which, kcd->iskernpair);
698 
699     BDFCharFree(*scpos);
700     *scpos = NULL;
701     if ( kcd->iskernpair )
702     {
703 	temp = cu_copy(_GGadgetGetTitle(GWidgetGetControl(kcd->gw,
704 		which==0 ? CID_First : CID_Second )));
705     }
706     else
707     {
708 	GTextInfo *sel = GGadgetGetListItemSelected(GWidgetGetControl(kcd->gw,
709 		which==0 ? CID_First : CID_Second ));
710 	if ( sel==NULL )
711 	{
712 //	    printf("KCD_UpdateGlyph() which:%d no selection...returning\n", which );
713 	    return;
714 	}
715 	else
716 	{
717 	    temp = cu_copy(sel->text);
718 	}
719 
720 //	printf("KCD_UpdateGlyph() temp:%s\n", temp );
721     }
722 
723     *possc = sc = SFGetChar(kcd->sf,-1,temp);
724     free(temp);
725     if ( sc==NULL )
726 	return;
727     if ( GGadgetIsChecked(GWidgetGetControl(kcd->gw,CID_FreeType)) )
728 	freetypecontext = FreeTypeFontContext(sc->parent,sc,sc->parent->fv,kcd->layer);
729     if ( freetypecontext )
730     {
731 	*scpos = SplineCharFreeTypeRasterize(freetypecontext,sc->orig_pos,kcd->pixelsize,72,8);
732 	FreeTypeFreeContext(freetypecontext);
733     }
734     else
735     {
736 	*scpos = SplineCharAntiAlias(sc,kcd->layer,kcd->pixelsize,4);
737     }
738 
739 //    printf("KCD_UpdateGlyph() scpos:%p\n", *scpos );
740 }
741 
_KCD_DisplaySizeChanged(KernClassDlg * kcd)742 static void _KCD_DisplaySizeChanged(KernClassDlg *kcd) {
743     const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(kcd->gw,CID_DisplaySize));
744     unichar_t *end;
745     int pixelsize = u_strtol(ret,&end,10);
746 
747     while ( *end==' ' ) ++end;
748     if ( pixelsize>4 && pixelsize<400 && *end=='\0' ) {
749 	unichar_t ubuf[20]; char buffer[20];
750 	ubuf[0] = '0'; ubuf[1] = '\0';
751 	if ( kcd->active_adjust.corrections!=NULL &&
752 		pixelsize>=kcd->active_adjust.first_pixel_size &&
753 		pixelsize<=kcd->active_adjust.last_pixel_size ) {
754 	    sprintf( buffer, "%d", kcd->active_adjust.corrections[
755 		    pixelsize-kcd->active_adjust.first_pixel_size]);
756 	    uc_strcpy(ubuf,buffer);
757 	}
758 	GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_Correction),ubuf);
759 	kcd->pixelsize = pixelsize;
760 	KCD_UpdateGlyph(kcd,0);
761 	KCD_UpdateGlyph(kcd,1);
762 	GDrawRequestExpose(kcd->subw,NULL,false);
763     }
764 }
765 
KCD_DisplaySizeChanged(GGadget * g,GEvent * e)766 static int KCD_DisplaySizeChanged(GGadget *g, GEvent *e) {
767     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
768     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
769 	_KCD_DisplaySizeChanged(kcd);
770     }
771 return( true );
772 }
773 
KCD_MagnificationChanged(GGadget * g,GEvent * e)774 static int KCD_MagnificationChanged(GGadget *g, GEvent *e) {
775     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
776     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
777 	int mag = GGadgetGetFirstListSelectedItem(GWidgetGetControl(kcd->gw,CID_Magnifications));
778 
779 	if ( mag!=-1 && mag!=kcd->magfactor-1 ) {
780 	    kcd->magfactor = mag+1;
781 	    GDrawRequestExpose(kcd->subw,NULL,false);
782 	}
783     }
784 return( true );
785 }
786 
KCB_FreeTypeChanged(GGadget * g,GEvent * e)787 static int KCB_FreeTypeChanged(GGadget *g, GEvent *e) {
788     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
789     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
790 	KCD_UpdateGlyph(kcd,0);
791 	KCD_UpdateGlyph(kcd,1);
792 	GDrawRequestExpose(kcd->subw,NULL,false);
793     }
794 return( true );
795 }
796 
KCD_CorrectionChanged(GGadget * g,GEvent * e)797 static int KCD_CorrectionChanged(GGadget *g, GEvent *e) {
798     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
799     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
800 	const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(kcd->gw,CID_Correction));
801 	unichar_t *end;
802 	int correction = u_strtol(ret,&end,10);
803 
804 	while ( *end==' ' ) ++end;
805 	if ( *end!='\0' )
806 return( true );
807 	if ( correction<-128 || correction>127 ) {
808 	    ff_post_error(_("Value out of range"),_("Value out of range"));
809 return( true );
810 	}
811 
812 	DeviceTableSet(&kcd->active_adjust,kcd->pixelsize,correction);
813 	GDrawRequestExpose(kcd->subw,NULL,false);
814 	GGadgetSetEnabled(GWidgetGetControl(kcd->gw,CID_ClearDevice),
815 		kcd->active_adjust.corrections!=NULL);
816     }
817 return( true );
818 }
819 
KCD_ClearDevice(GGadget * g,GEvent * e)820 static int KCD_ClearDevice(GGadget *g, GEvent *e) {
821     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
822     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
823 	free(kcd->active_adjust.corrections);
824 	kcd->active_adjust.corrections = NULL;
825 	kcd->active_adjust.first_pixel_size = kcd->active_adjust.last_pixel_size = 0;
826 	GGadgetSetTitle8(GWidgetGetControl(kcd->gw,CID_Correction),"0");
827 	GGadgetSetEnabled(g,false);
828     }
829 return( true );
830 }
831 
KCD_RevertKerning(GGadget * g,GEvent * e)832 static int KCD_RevertKerning(GGadget *g, GEvent *e) {
833     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
834     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
835 	char buf[20];
836 	sprintf( buf, "%d", kcd->orig_kern_offset );
837 	GGadgetSetTitle8(GWidgetGetControl(kcd->gw,CID_KernOffset),buf);
838 	free(kcd->active_adjust.corrections);
839 	kcd->active_adjust = kcd->orig_adjust;
840 	if ( kcd->orig_adjust.corrections!=NULL ) {
841 	    int len = kcd->orig_adjust.last_pixel_size-kcd->orig_adjust.first_pixel_size+1;
842 	    kcd->active_adjust = kcd->orig_adjust;
843 	    kcd->active_adjust.corrections = malloc(len);
844 	    memcpy(kcd->active_adjust.corrections,kcd->orig_adjust.corrections,len);
845 	}
846 	_KCD_DisplaySizeChanged(kcd);
847     }
848 return( true );
849 }
850 
KPD_RestoreGlyphs(KernClassDlg * kcd)851 static void KPD_RestoreGlyphs(KernClassDlg *kcd) {
852     if ( kcd->scf!=NULL )
853 	GGadgetSetTitle8(GWidgetGetControl(kcd->gw,CID_First),kcd->scf->name);
854     if ( kcd->scs!=NULL )
855 	GGadgetSetTitle8(GWidgetGetControl(kcd->gw,CID_Second),kcd->scs->name);
856 }
857 
KPD_FinishKP(KernClassDlg * kcd)858 static int KPD_FinishKP(KernClassDlg *kcd) {
859     KernPair *kp;
860     const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(kcd->gw,CID_KernOffset));
861     int offset = u_strtol(ret,NULL,10);
862 
863     if ( kcd->scf!=NULL && kcd->scs!=NULL ) {
864 	for ( kp = kcd->isv?kcd->scf->vkerns:kcd->scf->kerns; kp!=NULL && kp->sc!=kcd->scs; kp=kp->next );
865 	if ( kp==NULL && offset==0 && kcd->active_adjust.corrections==NULL )
866 return(true);
867 	if ( kcd->subtable==NULL ) {
868 	    ff_post_error(_("No lookup selected"),_("You must select a lookup subtable to contain this kerning pair" ));
869 return(false);
870 	}
871 	if ( kp==NULL ) {
872 	    kp = chunkalloc(sizeof(KernPair));
873 	    kp->next = kcd->isv?kcd->scf->vkerns:kcd->scf->kerns;
874 	    kp->sc = kcd->scs;
875 	    if ( kcd->isv )
876 		kcd->scf->vkerns = kp;
877 	    else
878 		kcd->scf->kerns = kp;
879 	}
880 	kp->subtable = kcd->subtable;
881 	kp->off = offset;
882 	if ( kp->adjust!=NULL && kcd->active_adjust.corrections!=NULL ) {
883 	    free(kp->adjust->corrections);
884 	    *kp->adjust = kcd->active_adjust;
885 	} else if ( kcd->active_adjust.corrections!=NULL ) {
886 	    kp->adjust = chunkalloc(sizeof(DeviceTable));
887 	    *kp->adjust = kcd->active_adjust;
888 	} else if ( kp->adjust!=NULL ) {
889 	    DeviceTableFree(kp->adjust);
890 	    kp->adjust = NULL;
891 	}
892 	memset(&kcd->active_adjust,0,sizeof(DeviceTable));
893     }
894 return( true );
895 }
896 
KCD_SetDevTab(KernClassDlg * kcd)897 static void KCD_SetDevTab(KernClassDlg *kcd) {
898     unichar_t ubuf[20];
899 
900     ubuf[0] = '0'; ubuf[1] = '\0';
901     GGadgetClearList(GWidgetGetControl(kcd->gw,CID_DisplaySize));
902     if ( kcd->active_adjust.corrections!=NULL ) {
903 	int i;
904 	int len = kcd->active_adjust.last_pixel_size - kcd->active_adjust.first_pixel_size +1;
905 	char buffer[20];
906 	GTextInfo **ti = malloc((len+1)*sizeof(GTextInfo *));
907 	for ( i=0; i<len; ++i ) {
908 	    ti[i] = calloc(1,sizeof(GTextInfo));
909 	    sprintf( buffer, "%d", i+kcd->active_adjust.first_pixel_size);
910 	    ti[i]->text = uc_copy(buffer);
911 	    ti[i]->fg = ti[i]->bg = COLOR_DEFAULT;
912 	}
913 	ti[i] = calloc(1,sizeof(GTextInfo));
914 	GGadgetSetList(GWidgetGetControl(kcd->gw,CID_DisplaySize),ti,false);
915 	if ( kcd->pixelsize>=kcd->active_adjust.first_pixel_size &&
916 		kcd->pixelsize<=kcd->active_adjust.last_pixel_size ) {
917 	    sprintf( buffer, "%d", kcd->active_adjust.corrections[
918 		    kcd->pixelsize-kcd->active_adjust.first_pixel_size]);
919 	    uc_strcpy(ubuf,buffer);
920 	}
921     }
922     GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_Correction),ubuf);
923     GGadgetSetEnabled(GWidgetGetControl(kcd->gw,CID_ClearDevice),
924 	    kcd->active_adjust.corrections!=NULL);
925 }
926 
KP_SelectSubtable(KernClassDlg * kcd,struct lookup_subtable * sub)927 static void KP_SelectSubtable(KernClassDlg *kcd,struct lookup_subtable *sub) {
928     int32 len;
929     GTextInfo **ti = GGadgetGetList(GWidgetGetControl(kcd->gw,CID_Subtable),&len);
930     int i, new_pos = -1;
931 
932     for ( i=0; i<len; ++i ) if ( !ti[i]->line ) {
933 	if ( ti[i]->userdata == sub )
934     break;
935 	else if ( ti[i]->userdata == NULL )
936 	    new_pos = i;
937     }
938     if ( i==len )
939 	i = new_pos;
940     if ( i!=-1 )
941 	GGadgetSelectOneListItem(GWidgetGetControl(kcd->gw,CID_Subtable),i);
942     if ( sub!=NULL )
943 	kcd->subtable = sub;
944 }
945 
KP_Subtable(GGadget * g,GEvent * e)946 static int KP_Subtable(GGadget *g, GEvent *e) {
947     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
948     GTextInfo *ti;
949     struct lookup_subtable *sub;
950     struct subtable_data sd;
951 
952     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
953 	ti = GGadgetGetListItemSelected(g);
954 	if ( ti!=NULL ) {
955 	    if ( ti->userdata!=NULL )
956 		kcd->subtable = ti->userdata;
957 	    else {
958 		memset(&sd,0,sizeof(sd));
959 		sd.flags = (kcd->isv ? sdf_verticalkern : sdf_horizontalkern ) |
960 			sdf_kernpair;
961 		sub = SFNewLookupSubtableOfType(kcd->sf,gpos_pair,&sd,kcd->layer);
962 		if ( sub!=NULL ) {
963 		    kcd->subtable = sub;
964 		    GGadgetSetList(g,SFSubtablesOfType(kcd->sf,gpos_pair,false,false),false);
965 		}
966 		KP_SelectSubtable(kcd,kcd->subtable);
967 	    }
968 	}
969     }
970 return( true );
971 }
972 
KPD_PairSearch(KernClassDlg * kcd)973 static void KPD_PairSearch(KernClassDlg *kcd) {
974     int offset = 0;
975     KernPair *kp=NULL;
976     char buf[20];
977     unichar_t ubuf[20];
978 
979     free(kcd->active_adjust.corrections); kcd->active_adjust.corrections = NULL;
980     if ( kcd->scf!=NULL && kcd->scs!=NULL ) {
981 	for ( kp = kcd->isv?kcd->scf->vkerns:kcd->scf->kerns; kp!=NULL && kp->sc!=kcd->scs; kp=kp->next );
982 	if ( kp!=NULL ) {
983 	    offset = kp->off;
984 	    kcd->orig_kern_offset = offset;
985 	    KP_SelectSubtable(kcd,kp->subtable);
986 	    if ( kp->adjust!=NULL ) {
987 		int len = kp->adjust->last_pixel_size-kp->adjust->first_pixel_size+1;
988 		kcd->active_adjust = *kp->adjust;
989 		kcd->active_adjust.corrections = malloc(len);
990 		memcpy(kcd->active_adjust.corrections,kp->adjust->corrections,len);
991 		kcd->orig_adjust = *kp->adjust;
992 		kcd->orig_adjust.corrections = malloc(len);
993 		memcpy(kcd->orig_adjust.corrections,kp->adjust->corrections,len);
994 	    }
995 	}
996     }
997     if ( kp==NULL && kcd->scf!=NULL ) {
998 	int32 len;
999 	GTextInfo **ti = GGadgetGetList(GWidgetGetControl(kcd->gw,CID_Subtable),&len);
1000 	uint32 script = SCScriptFromUnicode(kcd->scf);
1001 	int i;
1002 	struct lookup_subtable *sub = NULL;
1003 
1004 	for ( i=0; i<len; ++i ) {
1005 	    struct lookup_subtable *test = ti[i]->userdata;
1006 	    if ( test!=NULL && ScriptInFeatureScriptList(script,test->lookup->features)) {
1007 		sub = test;
1008 	break;
1009 	    }
1010 	}
1011 	KP_SelectSubtable(kcd,sub);
1012     }
1013 
1014     sprintf(buf, "%d", offset);
1015     uc_strcpy(ubuf,buf);
1016     GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_KernOffset),ubuf);
1017     KCD_SetDevTab(kcd);
1018 }
1019 
KPD_BuildKernList(KernClassDlg * kcd)1020 static void KPD_BuildKernList(KernClassDlg *kcd) {
1021     int len;
1022     KernPair *kp;
1023     GTextInfo **ti;
1024 
1025     len = 0;
1026     if ( kcd->scf!=NULL )
1027 	for ( kp=kcd->isv?kcd->scf->vkerns:kcd->scf->kerns, len=0; kp!=NULL; kp=kp->next )
1028 	    ++len;
1029     ti = calloc(len+1,sizeof(GTextInfo*));
1030     if ( kcd->scf!=NULL )
1031 	for ( kp=kcd->isv?kcd->scf->vkerns:kcd->scf->kerns, len=0; kp!=NULL; kp=kp->next, ++len ) {
1032 	    ti[len] = calloc(1,sizeof(GTextInfo));
1033 	    ti[len]->fg = ti[len]->bg = COLOR_DEFAULT;
1034 	    ti[len]->text = uc_copy(kp->sc->name);
1035 	}
1036     ti[len] = calloc(1,sizeof(GTextInfo));
1037     GGadgetSetList(GWidgetGetControl(kcd->gw,CID_Second),ti,false);
1038 }
1039 
KCD_GlyphSelected(GGadget * g,GEvent * e)1040 static int KCD_GlyphSelected(GGadget *g, GEvent *e) {
1041     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
1042     int which = GGadgetGetCid(g)==CID_Second;
1043     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
1044 	KCD_UpdateGlyph(kcd,which);
1045 	GDrawRequestExpose(kcd->subw,NULL,false);
1046     } else if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
1047 	if ( !KPD_FinishKP(kcd)) {
1048 	    KPD_RestoreGlyphs(kcd);
1049 return( true );
1050 	}
1051 	KCD_UpdateGlyph(kcd,which);
1052 	if ( which==0 )
1053 	    KPD_BuildKernList(kcd);
1054 	KPD_PairSearch(kcd);
1055 	GDrawRequestExpose(kcd->subw,NULL,false);
1056     }
1057 return( true );
1058 }
1059 
TiNamesFromClass(GGadget * list,int class_index)1060 static GTextInfo **TiNamesFromClass(GGadget *list,int class_index) {
1061     /* Return a list containing all the names in this class */
1062     char *pt, *end;
1063     GTextInfo **ti;
1064     int cnt;
1065     struct matrix_data *classes = GMatrixEditGet(list,&cnt);
1066     char *class_str = classes[class_index].u.md_str;
1067     int i, k;
1068 
1069     if ( class_str==NULL || isEverythingElse(class_str) ) {
1070 	i=0;
1071 	ti = malloc((i+1)*sizeof(GTextInfo*));
1072     } else {
1073 	for ( k=0 ; k<2; ++k ) {
1074 	    for ( i=0, pt=class_str; *pt; ) {
1075 		while ( *pt==' ' ) ++pt;
1076 		if ( *pt=='\0' )
1077 	    break;
1078 		for ( end = pt; *end!='\0' && *end!=' '; ++end );
1079 		if ( k==1 ) {
1080 		    ti[i] = calloc(1,sizeof(GTextInfo));
1081 		    ti[i]->text = utf82u_copyn(pt,end-pt);
1082 		    ti[i]->bg = ti[i]->fg = COLOR_DEFAULT;
1083 		}
1084 		++i;
1085 		pt = end;
1086 	    }
1087 	    if ( k==0 )
1088 		ti = malloc((i+1)*sizeof(GTextInfo*));
1089 	}
1090     }
1091     if ( i>0 )
1092 	ti[0]->selected = true;
1093     ti[i] = calloc(1,sizeof(GTextInfo));
1094 return( ti );
1095 }
1096 
KCD_EditOffset(KernClassDlg * kcd,int first,int second)1097 static void KCD_EditOffset(KernClassDlg *kcd, int first, int second) {
1098     char buf[12];
1099     unichar_t ubuf[12];
1100     GTextInfo **ti;
1101     static unichar_t nullstr[] = { 0 };
1102 
1103     KCD_Finalize(kcd);
1104     if ( GMatrixEditGetActiveRow(GWidgetGetControl(kcd->gw,CID_ClassList))!=first )
1105 	GMatrixEditActivateRowCol(GWidgetGetControl(kcd->gw,CID_ClassList),first,-1);
1106     if ( GMatrixEditGetActiveRow(GWidgetGetControl(kcd->gw,CID_ClassList+100))!=second )
1107 	GMatrixEditActivateRowCol(GWidgetGetControl(kcd->gw,CID_ClassList+100),second,-1);
1108     if ( second==0 )
1109 	ff_post_notice(_("Class 0"),_("The kerning values for class 0 (\"Everything Else\") should always be 0"));
1110     if ( first!=-1 && second!=-1 && first < kcd->first_cnt && second < kcd->second_cnt ) {
1111 	kcd->st_pos = first*kcd->second_cnt+second;
1112 	kcd->old_pos = kcd->st_pos;
1113 	GGadgetSetList(GWidgetGetControl(kcd->gw,CID_First),
1114 		ti = TiNamesFromClass(GWidgetGetControl(kcd->gw,CID_ClassList),first),false);
1115 	GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_First),
1116 		ti==NULL || ti[0]->text==NULL ? nullstr: ti[0]->text);
1117 	GGadgetSetList(GWidgetGetControl(kcd->gw,CID_Second),
1118 		ti = TiNamesFromClass(GWidgetGetControl(kcd->gw,CID_ClassList+100),second),false);
1119 	GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_Second),
1120 		ti==NULL || ti[0]->text==NULL ? nullstr: ti[0]->text);
1121 	KCD_UpdateGlyph(kcd,0);
1122 	KCD_UpdateGlyph(kcd,1);
1123 
1124 	kcd->orig_kern_offset = kcd->offsets[kcd->st_pos];
1125 	sprintf( buf, "%d", kcd->offsets[kcd->st_pos]);
1126 	uc_strcpy(ubuf,buf);
1127 	GGadgetSetTitle(GWidgetGetControl(kcd->gw,CID_KernOffset),ubuf);
1128 
1129 	kcd->active_adjust = kcd->adjusts[kcd->st_pos];
1130 	kcd->orig_adjust = kcd->adjusts[kcd->st_pos];
1131 	if ( kcd->active_adjust.corrections!=NULL ) {
1132 	    int len = kcd->active_adjust.last_pixel_size - kcd->active_adjust.first_pixel_size +1;
1133 	    kcd->active_adjust.corrections = malloc(len);
1134 	    memcpy(kcd->active_adjust.corrections,kcd->adjusts[kcd->st_pos].corrections,len);
1135 	    kcd->orig_adjust.corrections = malloc(len);
1136 	    memcpy(kcd->orig_adjust.corrections,kcd->adjusts[kcd->st_pos].corrections,len);
1137 	}
1138 	KCD_SetDevTab(kcd);
1139     }
1140     GDrawRequestExpose(kcd->subw,NULL,false);
1141     GDrawRequestExpose(kcd->gw,NULL,false);
1142 }
1143 
1144 /* ************************************************************************** */
1145 /* *************************** Kern Class Dialog **************************** */
1146 /* ************************************************************************** */
1147 
KC_DoResize(KernClassDlg * kcd)1148 static void KC_DoResize(KernClassDlg *kcd) {
1149     GRect wsize, csize;
1150 
1151     GDrawGetSize(kcd->gw,&wsize);
1152 
1153     kcd->fullwidth = wsize.width;
1154     kcd->width = wsize.width-kcd->xstart2-5;
1155     kcd->height = wsize.height-kcd->ystart2;
1156     if ( kcd->hsb!=NULL ) {
1157 	GGadgetGetSize(kcd->hsb,&csize);
1158 	kcd->width = csize.width;
1159 	kcd->xstart2 = csize.x;
1160 	GGadgetGetSize(kcd->vsb,&csize);
1161 	kcd->ystart2 = csize.y;
1162 	kcd->height = csize.height;
1163 	kcd->xstart = kcd->xstart2-kcd->kernw;
1164 	kcd->ystart = kcd->ystart2-kcd->fh-1;
1165 	KCD_SBReset(kcd);
1166     }
1167     GDrawRequestExpose(kcd->gw,NULL,false);
1168 }
1169 
KC_ShowHideKernPane(GGadget * g,GEvent * e)1170 static int KC_ShowHideKernPane(GGadget *g, GEvent *e) {
1171     static int cidlist[] = { CID_First, CID_Second, CID_FreeType, CID_SizeLabel,
1172 	    CID_DisplaySize, CID_MagLabel,CID_Magnifications, CID_OffsetLabel,
1173 	    CID_KernOffset,
1174 	    CID_CorrectLabel, CID_Correction, CID_Revert, CID_ClearDevice,
1175 	    CID_Display, 0 };
1176     if ( e==NULL ||
1177 	    (e->type==et_controlevent && e->u.control.subtype == et_radiochanged) ) {
1178 	KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
1179 	int i;
1180 
1181 	show_kerning_pane_in_class = GGadgetIsChecked(g);
1182 
1183 	for ( i=0; cidlist[i]!=0; ++i )
1184 	    GGadgetSetVisible(GWidgetGetControl(kcd->gw,cidlist[i]),show_kerning_pane_in_class);
1185 	GHVBoxReflow(GWidgetGetControl(kcd->gw,CID_TopBox));
1186 	KC_DoResize(kcd);
1187 	if ( e!=NULL )
1188 	    SavePrefs(true);
1189     }
1190 return( true );
1191 }
1192 
KC_OK(GGadget * g,GEvent * e)1193 static int KC_OK(GGadget *g, GEvent *e) {
1194     SplineFont *sf;
1195 
1196     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1197 	KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
1198 	KernClass *kc;
1199 	int i;
1200 	int len;
1201 	struct matrix_data *classes;
1202 	int err, touch=0, separation=0, minkern=0, onlyCloser, autokern;
1203 
1204 	sf = kcd->sf;
1205 	if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
1206 	else if ( sf->mm!=NULL ) sf = sf->mm->normal;
1207 
1208 	err = false;
1209 	touch = GGadgetIsChecked(GWidgetGetControl(kcd->gw,CID_Touched));
1210 	separation = GetInt8(kcd->gw,CID_Separation,_("Separation"),&err);
1211 	minkern = GetInt8(kcd->gw,CID_MinKern,_("Min Kern"),&err);
1212 	onlyCloser = GGadgetIsChecked(GWidgetGetControl(kcd->gw,CID_OnlyCloser));
1213 	autokern = GGadgetIsChecked(GWidgetGetControl(kcd->gw,CID_Autokern));
1214 	if ( err )
1215 return( true );
1216 	KCD_Finalize(kcd);
1217 
1218 	kc = kcd->orig;
1219 	for ( i=1; i<kc->first_cnt; ++i )
1220 	    free( kc->firsts[i]);
1221 	for ( i=1; i<kc->second_cnt; ++i )
1222 	    free( kc->seconds[i]);
1223 	free(kc->firsts);
1224 	free(kc->seconds);
1225 	free(kc->offsets);
1226 	free(kc->adjusts);
1227 
1228 	// Group kerning.
1229 	if (kc->firsts_names)
1230 	  for ( i=1; i<kc->first_cnt; ++i )
1231 	    if (kc->firsts_names[i]) free(kc->firsts_names[i]);
1232 	if (kc->seconds_names)
1233 	  for ( i=1; i<kc->second_cnt; ++i )
1234 	    if (kc->seconds_names[i]) free(kc->seconds_names[i]);
1235 	if (kc->firsts_flags) free(kc->firsts_flags);
1236 	if (kc->seconds_flags) free(kc->seconds_flags);
1237 	if (kc->offsets_flags) free(kc->offsets_flags);
1238 	if (kc->firsts_names) free(kc->firsts_names);
1239 	if (kc->seconds_names) free(kc->seconds_names);
1240 
1241 	kc->subtable->separation = separation;
1242 	kc->subtable->minkern = minkern;
1243 	kc->subtable->kerning_by_touch = touch;
1244 	kc->subtable->onlyCloser = onlyCloser;
1245 	kc->subtable->dontautokern = !autokern;
1246 
1247 	kc->first_cnt = kcd->first_cnt;
1248 	kc->second_cnt = kcd->second_cnt;
1249 	kc->firsts = malloc(kc->first_cnt*sizeof(char *));
1250 	kc->seconds = malloc(kc->second_cnt*sizeof(char *));
1251 	kc->firsts[0] = kc->seconds[0] = NULL;
1252 	classes = GMatrixEditGet(GWidgetGetControl(kcd->gw,CID_ClassList),&len);
1253 	if ( !isEverythingElse(classes[0].u.md_str) )
1254 	    kc->firsts[0] = GlyphNameListDeUnicode(classes[0].u.md_str);
1255 	for ( i=1; i<kc->first_cnt; ++i )
1256 	    kc->firsts[i] = GlyphNameListDeUnicode(classes[i].u.md_str);
1257 	classes = GMatrixEditGet(GWidgetGetControl(kcd->gw,CID_ClassList+100),&len);
1258 	for ( i=1; i<kc->second_cnt; ++i )
1259 	    kc->seconds[i] = GlyphNameListDeUnicode(classes[i].u.md_str);
1260 	kc->offsets = kcd->offsets;
1261 	kc->adjusts = kcd->adjusts;
1262 
1263 	// Group kerning.
1264 	kc->firsts_flags = kcd->firsts_flags;
1265 	kc->seconds_flags = kcd->seconds_flags;
1266 	kc->offsets_flags = kcd->offsets_flags;
1267 	kc->firsts_names = kcd->firsts_names;
1268 	kc->seconds_names = kcd->seconds_names;
1269 
1270 	kcd->sf->changed = true;
1271 	sf->changed = true;
1272 
1273 	GDrawDestroyWindow(kcd->gw);
1274     }
1275 return( true );
1276 }
1277 
KC_DoCancel(KernClassDlg * kcd)1278 static void KC_DoCancel(KernClassDlg *kcd) {
1279     if ( kcd->iskernpair )
1280 	KPD_DoCancel(kcd);
1281     else {
1282 	free(kcd->offsets);
1283 	{ int i;
1284 	    for ( i=0; i<kcd->first_cnt*kcd->second_cnt; ++i )
1285 		free(kcd->adjusts[i].corrections);
1286 	}
1287 	free(kcd->adjusts);
1288 
1289 	// Group kerning.
1290 	if (kcd->firsts_names) {
1291           int i;
1292 	  for ( i=1; i<kcd->first_cnt; ++i )
1293 	    if (kcd->firsts_names[i]) free(kcd->firsts_names[i]);
1294         }
1295 	if (kcd->seconds_names) {
1296           int i;
1297 	  for ( i=1; i<kcd->second_cnt; ++i )
1298 	    if (kcd->seconds_names[i]) free(kcd->seconds_names[i]);
1299         }
1300 	if (kcd->firsts_flags) free(kcd->firsts_flags);
1301 	if (kcd->seconds_flags) free(kcd->seconds_flags);
1302 	if (kcd->offsets_flags) free(kcd->offsets_flags);
1303 	if (kcd->firsts_names) free(kcd->firsts_names);
1304 	if (kcd->seconds_names) free(kcd->seconds_names);
1305 
1306 	GDrawDestroyWindow(kcd->gw);
1307     }
1308 }
1309 
KC_Cancel(GGadget * g,GEvent * e)1310 static int KC_Cancel(GGadget *g, GEvent *e) {
1311     KernClassDlg *kcd;
1312 
1313     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1314 	kcd = GDrawGetUserData(GGadgetGetWindow(g));
1315 
1316 	KC_DoCancel(kcd);
1317     }
1318 return( true );
1319 }
1320 
KCD_TextSelect(GGadget * g,GEvent * e)1321 static int KCD_TextSelect(GGadget *g, GEvent *e) {
1322     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
1323 	KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
1324 	int off = GGadgetGetCid(g)-CID_ClassSelect;
1325 	const unichar_t *uname = _GGadgetGetTitle(g), *upt;
1326 	GGadget *list = GWidgetGetControl(kcd->gw,CID_ClassList+off);
1327 	int rows;
1328 	struct matrix_data *classes = GMatrixEditGet(list,&rows);
1329 	int nlen;
1330 	char *start, *pt, *name;
1331 	int i;
1332 
1333         /* length of initial text contents up until blank, '(' or end-of-string */
1334         for ( upt=uname; *upt!='\0' && *upt!='(' && *upt!=' '; ++upt );
1335 	name = u2utf8_copyn(uname,upt-uname);
1336         /* if string empty or invalid for any reason, quit processing text */
1337         if ( name==NULL )
1338             return( false );
1339 	nlen = strlen(name);
1340 
1341 	for ( i=0; i<rows; ++i ) {
1342 	    for ( start = classes[i].u.md_str; start!=NULL && *start!='\0'; ) {
1343 		while ( *start==' ' ) ++start;
1344                 for ( pt=start; *pt!='\0' && *pt!=' ' && *pt!='('; ++pt );
1345 		if ( pt-start == nlen && strncmp(name,start,nlen)==0 ) {
1346 		    GMatrixEditScrollToRowCol(list,i,0);
1347 		    GMatrixEditActivateRowCol(list,i,0);
1348 		    if ( off==0 )
1349 			KCD_VShow(kcd,i);
1350 		    else
1351 			KCD_HShow(kcd,i);
1352                     return( true );
1353 		}
1354 		if ( *pt=='(' ) {
1355 		    while ( *pt!=')' && *pt!='\0' ) ++pt;
1356 		    if ( *pt==')' ) ++pt;
1357 		}
1358 		start = pt;
1359 	    }
1360 	}
1361 
1362 	/* Otherwise deselect everything */
1363 	if ( nlen!=0 )
1364 	    GMatrixEditActivateRowCol(list,-1,-1);
1365     }
1366 return( true );
1367 }
1368 
1369 #define MID_Clear		1000
1370 #define MID_ClearAll		1001
1371 #define MID_ClearDevTab		1002
1372 #define MID_ClearAllDevTab	1003
1373 #define MID_AutoKernRow		1004
1374 #define MID_AutoKernCol		1005
1375 #define MID_AutoKernAll		1006
1376 
kernmenu_dispatch(GWindow gw,GMenuItem * mi,GEvent * e)1377 static void kernmenu_dispatch(GWindow gw, GMenuItem *mi, GEvent *e) {
1378     KernClassDlg *kcd = GDrawGetUserData(gw);
1379     int i;
1380 
1381     switch ( mi->mid ) {
1382       case MID_AutoKernRow:
1383 	KCD_AutoKernAClass(kcd,kcd->st_pos/kcd->second_cnt,true);
1384       break;
1385       case MID_AutoKernCol:
1386 	KCD_AutoKernAClass(kcd,kcd->st_pos%kcd->second_cnt,false);
1387       break;
1388       case MID_AutoKernAll:
1389 	KCD_AutoKernAll(kcd);
1390       break;
1391       case MID_Clear:
1392 	kcd->offsets[kcd->st_pos] = 0;
1393       break;
1394       case MID_ClearAll:
1395 	for ( i=0; i<kcd->first_cnt*kcd->second_cnt; ++i )
1396 	    kcd->offsets[i] = 0;
1397 	if (kcd->offsets_flags != NULL)
1398 	  for ( i=0; i<kcd->first_cnt*kcd->second_cnt; ++i )
1399 	    kcd->offsets_flags[i] = 0;
1400       break;
1401       case MID_ClearDevTab: {
1402 	DeviceTable *devtab = &kcd->adjusts[kcd->st_pos];
1403 	free(devtab->corrections);
1404 	devtab->corrections = NULL;
1405 	devtab->first_pixel_size = devtab->last_pixel_size = 0;
1406       } break;
1407       case MID_ClearAllDevTab:
1408 	for ( i=0; i<kcd->first_cnt*kcd->second_cnt; ++i ) {
1409 	    DeviceTable *devtab = &kcd->adjusts[i];
1410 	    free(devtab->corrections);
1411 	    devtab->corrections = NULL;
1412 	    devtab->first_pixel_size = devtab->last_pixel_size = 0;
1413 	}
1414       break;
1415     }
1416     kcd->st_pos = -1;
1417     GDrawRequestExpose(kcd->gw,NULL,false);
1418 }
1419 
1420 static GMenuItem kernpopupmenu[] = {
1421     { { (unichar_t *) N_("AutoKern Row"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 't' }, '\0', ksm_control, NULL, NULL, kernmenu_dispatch, MID_AutoKernRow },
1422     { { (unichar_t *) N_("AutoKern Column"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 't' }, '\0', ksm_control, NULL, NULL, kernmenu_dispatch, MID_AutoKernCol },
1423     { { (unichar_t *) N_("AutoKern All"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 't' }, '\0', ksm_control, NULL, NULL, kernmenu_dispatch, MID_AutoKernAll },
1424     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, '\0', 0, NULL, NULL, NULL, 0 }, /* line */
1425 #define Menu_VKern_Offset 4		/* No autokerning for vertical kerning */
1426     { { (unichar_t *) N_("Clear"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 't' }, '\0', ksm_control, NULL, NULL, kernmenu_dispatch, MID_Clear },
1427     { { (unichar_t *) N_("Clear All"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'C' }, '\0', ksm_control, NULL, NULL, kernmenu_dispatch, MID_ClearAll },
1428     { { (unichar_t *) N_("Clear Device Table"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'o' }, '\0', ksm_control, NULL, NULL, kernmenu_dispatch, MID_ClearDevTab },
1429     { { (unichar_t *) N_("Clear All Device Tables"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 1, 0, 'o' }, '\0', ksm_control, NULL, NULL, kernmenu_dispatch, MID_ClearAllDevTab },
1430     GMENUITEM_EMPTY
1431 };
1432 
KCD_PopupMenu(KernClassDlg * kcd,GEvent * event,int pos)1433 static void KCD_PopupMenu(KernClassDlg *kcd,GEvent *event,int pos) {
1434     kcd->st_pos = pos;
1435     if ( kcd->isv )
1436 	GMenuCreatePopupMenu(event->w,event, kernpopupmenu+Menu_VKern_Offset);
1437     else
1438 	GMenuCreatePopupMenu(event->w,event, kernpopupmenu);
1439 }
1440 
KCD_Mouse(KernClassDlg * kcd,GEvent * event)1441 static void KCD_Mouse(KernClassDlg *kcd,GEvent *event) {
1442     static char space[200];
1443     int len;
1444     struct matrix_data *classes;
1445     int pos = ((event->u.mouse.y-kcd->ystart2)/kcd->kernh + kcd->offtop) * kcd->second_cnt +
1446 	    (event->u.mouse.x-kcd->xstart2)/kcd->kernw + kcd->offleft;
1447 
1448     GGadgetEndPopup();
1449 //    printf("KCD_Mouse()\n");
1450 
1451     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
1452 	    (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
1453 	GGadgetDispatchEvent(kcd->vsb,event);
1454 return;
1455     }
1456 
1457     if ( event->u.mouse.x<kcd->xstart || event->u.mouse.x>kcd->xstart2+kcd->fullwidth ||
1458 	    event->u.mouse.y<kcd->ystart || event->u.mouse.y>kcd->ystart2+kcd->height )
1459 return;
1460 
1461     if ( event->type==et_mousemove ) {
1462 	int c = (event->u.mouse.x - kcd->xstart2)/kcd->kernw + kcd->offleft;
1463 	int s = (event->u.mouse.y - kcd->ystart2)/kcd->kernh + kcd->offtop;
1464 	char *str;
1465 	//space[0] = '\0';
1466 	memset(space,'\0',sizeof(space));
1467 	if ( event->u.mouse.y>=kcd->ystart2 && s<kcd->first_cnt ) {
1468 	    sprintf( space, _("First Class %d\n"), s );
1469 	    classes = GMatrixEditGet(GWidgetGetControl(kcd->gw,CID_ClassList),&len);
1470 	    str = classes[s].u.md_str!=NULL ? classes[s].u.md_str :
1471 		    s==0 ? _("{Everything Else}") : "";
1472 	    len = strlen(space);
1473 	    strncpy(space+len,str,sizeof(space)/2-2 - len);
1474 	    space[sizeof(space)/2-2] = '\0';
1475 	    utf8_truncatevalid(space+len);
1476 	    strcat(space+strlen(space),"\n");
1477 	}
1478 	if ( event->u.mouse.x>=kcd->xstart2 && c<kcd->second_cnt ) {
1479 	    len = strlen(space);
1480 	    sprintf( space+len, _("Second Class %d\n"), c );
1481 	    classes = GMatrixEditGet(GWidgetGetControl(kcd->gw,CID_ClassList+100),&len);
1482 	    str = classes[c].u.md_str!=NULL ? classes[c].u.md_str :
1483 		    c==0 ? _("{Everything Else}") : "";
1484 	    len = strlen(space);
1485 	    strncpy(space+len,str,sizeof(space)-1 - len);
1486 	    space[sizeof(space)-1] = '\0';
1487 	    utf8_truncatevalid(space+len);
1488 	}
1489 	if ( space[0]=='\0' )
1490 return;
1491 	if ( space[strlen(space)-1]=='\n' )
1492 	    space[strlen(space)-1]='\0';
1493 	GGadgetPreparePopup8(kcd->gw,space);
1494     } else if ( event->u.mouse.x<kcd->xstart2 || event->u.mouse.y<kcd->ystart2 )
1495 return;
1496     else if ( event->type==et_mousedown && event->u.mouse.button==3 )
1497 	KCD_PopupMenu(kcd,event,pos);
1498     else if ( event->type==et_mousedown )
1499 	kcd->st_pos = pos;
1500     else if ( event->type==et_mouseup ) {
1501 //	printf("KCD_Mouse(up)\n");
1502 	if ( pos==kcd->st_pos )
1503 	    KCD_EditOffset(kcd, pos/kcd->second_cnt, pos%kcd->second_cnt);
1504     }
1505 }
1506 
KCD_NameClass(SplineFont * sf,char * buf,int blen,char * class_str)1507 static int KCD_NameClass(SplineFont *sf,char *buf,int blen,char *class_str) {
1508     char *start, *pt, *bpt;
1509     int i, ch;
1510     SplineChar *sc;
1511 
1512     if ( class_str==NULL ) {
1513 	utf8_idpb(buf,0x2205,0);	/* Empty set glyph */
1514 return( true );
1515     }
1516     if ( isEverythingElse(class_str)) {
1517  /* GT: Short form of {Everything Else}, might use universal? U+2200 */
1518 	strcpy(buf,_("{All}") );
1519 return( true );
1520     }
1521     for ( start=class_str; *start==' '; ++start );
1522     bpt = buf;
1523     for ( i=0; i<2; ++i ) {
1524 	for ( pt=start; *pt!='(' && *pt!=' ' && *pt!='\0'; ++pt );
1525 	if ( *pt=='(' && (pt[2]==')' || pt[3]==')' || pt[4]==')' || pt[5]==')')) {
1526 	    ++pt;
1527 	    while ( *pt!=')' )
1528 		*bpt++ = *pt++;
1529 	    ++pt;
1530 	} else if ( isalpha(*(unsigned char *) start) && pt-start==1 && *pt!='(' ) {
1531 	    *bpt++ = *start;
1532 	} else
1533     break;
1534 	for ( ; *pt==' '; ++pt );
1535 	start = pt;
1536 	if ( *start=='\0' ) {
1537 	    *bpt = '\0';
1538 return( false );
1539 	}
1540 	*bpt++ = ' ';
1541     }
1542     if ( i!=0 ) {
1543 	/* We parsed at least one glyph, and there's more stuff */
1544 	bpt[-1] = '.'; *bpt++ = '.'; *bpt++ = '.';
1545 	*bpt = '\0';
1546 return( false );
1547     }
1548 
1549     ch = *pt; *pt='\0';
1550     sc = SFGetChar(sf,-1,start);
1551     if ( sc==NULL ) {
1552 	snprintf( buf, blen, "!%s", start );
1553 	*pt = ch;
1554 return( true );
1555     } else if ( sc->unicodeenc==-1 || isprivateuse(sc->unicodeenc)
1556 	       || issurrogate(sc->unicodeenc))	/* Pango complains that privateuse code points are "Invalid UTF8 strings" */
1557 	snprintf( buf, blen, "%s", start );
1558     else {
1559 	char *bpt = utf8_idpb(buf,sc->unicodeenc,0);
1560 	*bpt = '\0';
1561     }
1562     *pt = ch;
1563 return( false );
1564 }
1565 
KCD_Expose(KernClassDlg * kcd,GWindow pixmap,GEvent * event)1566 static void KCD_Expose(KernClassDlg *kcd,GWindow pixmap,GEvent *event) {
1567     GRect *area = &event->u.expose.rect;
1568     GRect rect, select,r;
1569     GRect clip,old1,old2,old3;
1570     int len, i, j, x, y;
1571     char buf[100];
1572     int fcnt, scnt;
1573     GGadget *first = GWidgetGetControl(kcd->gw,CID_ClassList);
1574     GGadget *second = GWidgetGetControl(kcd->gw,CID_ClassList+100);
1575     struct matrix_data *fclasses = GMatrixEditGet(first,&fcnt);
1576     struct matrix_data *sclasses = GMatrixEditGet(second,&scnt);
1577     int factive = GMatrixEditGetActiveRow(first);
1578     int sactive = GMatrixEditGetActiveRow(second);
1579 
1580     if ( area->y+area->height<kcd->ystart )
1581 return;
1582     if ( area->y>kcd->ystart2+kcd->height )
1583 return;
1584 
1585     GDrawPushClip(pixmap,area,&old1);
1586     GDrawSetFont(pixmap,kcd->font);
1587     GDrawSetLineWidth(pixmap,0);
1588     rect.x = kcd->xstart; rect.y = kcd->ystart;
1589     rect.width = kcd->width+(kcd->xstart2-kcd->xstart);
1590     rect.height = kcd->height+(kcd->ystart2-kcd->ystart);
1591     clip = rect;
1592     GDrawPushClip(pixmap,&clip,&old2);
1593 
1594     /* In the offsets list, show which classes are selected above in the class*/
1595     /*  lists */
1596     for ( i=0 ; kcd->offtop+i<=kcd->first_cnt && (i-1)*kcd->kernh<kcd->height; ++i ) {
1597 	if ( i+kcd->offtop<fcnt && i+kcd->offtop==factive ) {
1598 	    select.x = kcd->xstart+1; select.y = kcd->ystart2+i*kcd->kernh+1;
1599 	    select.width = rect.width-1; select.height = kcd->kernh-1;
1600 	    GDrawFillRect(pixmap,&select,ACTIVE_BORDER);
1601 	}
1602     }
1603     for ( i=0 ; kcd->offleft+i<=kcd->second_cnt && (i-1)*kcd->kernw<kcd->fullwidth; ++i ) {
1604 	if ( i+kcd->offleft<scnt && i+kcd->offleft==sactive ) {
1605 	    select.x = kcd->xstart2+i*kcd->kernw+1; select.y = kcd->ystart+1;
1606 	    select.width = kcd->kernw-1; select.height = rect.height-1;
1607 	    GDrawFillRect(pixmap,&select,ACTIVE_BORDER);
1608 	}
1609     }
1610 
1611     for ( i=0 ; kcd->offtop+i<=kcd->first_cnt && (i-1)*kcd->kernh<kcd->height; ++i ) {
1612 	GDrawDrawLine(pixmap,kcd->xstart,kcd->ystart2+i*kcd->kernh,kcd->xstart+rect.width,kcd->ystart2+i*kcd->kernh,
1613 		    0x808080);
1614 	if ( i+kcd->offtop<kcd->first_cnt ) {
1615 	    int err = KCD_NameClass(kcd->sf,buf,sizeof(buf),fclasses[i+kcd->offtop].u.md_str);
1616 	    int fg = err ? 0xff0000 : 0x006080;
1617 	    len = GDrawGetText8Width(pixmap,buf,-1);
1618 	    if ( len<=kcd->kernw )
1619 		GDrawDrawText8(pixmap,kcd->xstart+(kcd->kernw-len)/2,kcd->ystart2+i*kcd->kernh+kcd->as+1,
1620 			buf,-1,fg);
1621 	    else {
1622 		r.x = kcd->xstart; r.width = kcd->kernw;
1623 		r.y = kcd->ystart2+i*kcd->kernh-1; r.height = kcd->kernh+1;
1624 		GDrawPushClip(pixmap,&r,&old3);
1625 		GDrawDrawText8(pixmap,r.x,r.y+kcd->as+1,
1626 			buf,-1,fg);
1627 		GDrawPopClip(pixmap,&old3);
1628 	    }
1629 	}
1630     }
1631     for ( i=0 ; kcd->offleft+i<=scnt && (i-1)*kcd->kernw<kcd->fullwidth; ++i ) {
1632 	GDrawDrawLine(pixmap,kcd->xstart2+i*kcd->kernw,kcd->ystart,kcd->xstart2+i*kcd->kernw,kcd->ystart+rect.height,
1633 		0x808080);
1634 	if ( i+kcd->offleft<kcd->second_cnt ) {
1635 	    int err = KCD_NameClass(kcd->sf,buf,sizeof(buf),sclasses[i+kcd->offleft].u.md_str);
1636 	    int fg = err ? 0xff0000 : 0x006080;
1637 	    len = GDrawGetText8Width(pixmap,buf,-1);
1638 	    if ( len<=kcd->kernw )
1639 		GDrawDrawText8(pixmap,kcd->xstart2+i*kcd->kernw+(kcd->kernw-len)/2,kcd->ystart+kcd->as+1,
1640 		    buf,-1,fg);
1641 	    else {
1642 		r.x = kcd->xstart2+i*kcd->kernw; r.width = kcd->kernw;
1643 		r.y = kcd->ystart-1; r.height = kcd->kernh+1;
1644 		GDrawPushClip(pixmap,&r,&old3);
1645 		GDrawDrawText8(pixmap,r.x,r.y+kcd->as+1,
1646 			buf,-1,fg);
1647 		GDrawPopClip(pixmap,&old3);
1648 	    }
1649 	}
1650     }
1651 
1652     for ( i=0 ; kcd->offtop+i<kcd->first_cnt && (i-1)*kcd->kernh<kcd->height; ++i ) {
1653 	y = kcd->ystart2+i*kcd->kernh;
1654 	if ( y>area->y+area->height )
1655     break;
1656 	if ( y+kcd->kernh<area->y )
1657     continue;
1658 	for ( j=0 ; kcd->offleft+j<kcd->second_cnt && (j-1)*kcd->kernw<kcd->fullwidth; ++j ) {
1659 	    x = kcd->xstart2+j*kcd->kernw;
1660 	    if ( x>area->x+area->width )
1661 	break;
1662 	    if ( x+kcd->kernw<area->x )
1663 	continue;
1664 
1665 	    sprintf( buf, "%d", kcd->offsets[(i+kcd->offtop)*kcd->second_cnt+j+kcd->offleft] );
1666 	    len = GDrawGetText8Width(pixmap,buf,-1);
1667 	    GDrawDrawText8(pixmap,x+kcd->kernw-3-len,y+kcd->as+1,
1668 		buf,-1,MAIN_FOREGROUND);
1669 	}
1670     }
1671 
1672     GDrawDrawLine(pixmap,kcd->xstart,kcd->ystart2,kcd->xstart+rect.width,kcd->ystart2,
1673 	    0x000000);
1674     GDrawDrawLine(pixmap,kcd->xstart2,kcd->ystart,kcd->xstart2,kcd->ystart+rect.height,
1675 	    0x000000);
1676     GDrawPopClip(pixmap,&old2);
1677     GDrawPopClip(pixmap,&old1);
1678     --rect.y; ++rect.height;		/* Makes accented letters show better */
1679     GDrawDrawRect(pixmap,&rect,0x000000);
1680     rect.y += rect.height;
1681     rect.x += rect.width;
1682     LogoExpose(pixmap,event,&rect,dm_fore);
1683 }
1684 
KCD_SBReset(KernClassDlg * kcd)1685 static int KCD_SBReset(KernClassDlg *kcd) {
1686     int oldtop = kcd->offtop, oldleft = kcd->offleft;
1687 
1688     if ( kcd->height>=kcd->kernh )
1689 	GScrollBarSetBounds(kcd->vsb,0,kcd->first_cnt, kcd->height/kcd->kernh);
1690     if ( kcd->width>=kcd->kernw )
1691 	GScrollBarSetBounds(kcd->hsb,0,kcd->second_cnt, kcd->width/kcd->kernw);
1692     if ( kcd->offtop + (kcd->height/kcd->kernh) >= kcd->first_cnt )
1693 	kcd->offtop = kcd->first_cnt - (kcd->height/kcd->kernh);
1694     if ( kcd->offtop < 0 ) kcd->offtop = 0;
1695     if ( kcd->offleft + (kcd->width/kcd->kernw) >= kcd->second_cnt )
1696 	kcd->offleft = kcd->second_cnt - (kcd->width/kcd->kernw);
1697     if ( kcd->offleft < 0 ) kcd->offleft = 0;
1698     GScrollBarSetPos(kcd->vsb,kcd->offtop);
1699     GScrollBarSetPos(kcd->hsb,kcd->offleft);
1700 
1701 return( oldtop!=kcd->offtop || oldleft!=kcd->offleft );
1702 }
1703 
KCD_HShow(KernClassDlg * kcd,int pos)1704 static void KCD_HShow(KernClassDlg *kcd, int pos) {
1705     if ( pos>=0 && pos<kcd->second_cnt ) {
1706 	--pos;	/* One line of context */
1707 	if ( pos + (kcd->width/kcd->kernw) >= kcd->second_cnt )
1708 	    pos = kcd->second_cnt - (kcd->width/kcd->kernw);
1709 	if ( pos < 0 ) pos = 0;
1710 	kcd->offleft = pos;
1711 	GScrollBarSetPos(kcd->hsb,pos);
1712     }
1713     GDrawRequestExpose(kcd->gw,NULL,false);
1714 }
1715 
KCD_HScroll(KernClassDlg * kcd,struct sbevent * sb)1716 static void KCD_HScroll(KernClassDlg *kcd,struct sbevent *sb) {
1717     int newpos = kcd->offleft;
1718     GRect rect;
1719 
1720     switch( sb->type ) {
1721       case et_sb_top:
1722         newpos = 0;
1723       break;
1724       case et_sb_uppage:
1725 	if ( kcd->width/kcd->kernw == 1 )
1726 	    --newpos;
1727 	else
1728 	    newpos -= kcd->width/kcd->kernw - 1;
1729       break;
1730       case et_sb_up:
1731         --newpos;
1732       break;
1733       case et_sb_down:
1734         ++newpos;
1735       break;
1736       case et_sb_downpage:
1737 	if ( kcd->width/kcd->kernw == 1 )
1738 	    ++newpos;
1739 	else
1740 	    newpos += kcd->width/kcd->kernw - 1;
1741       break;
1742       case et_sb_bottom:
1743         newpos = kcd->second_cnt - (kcd->width/kcd->kernw);
1744       break;
1745       case et_sb_thumb:
1746       case et_sb_thumbrelease:
1747         newpos = sb->pos;
1748       break;
1749     }
1750     if ( newpos + (kcd->width/kcd->kernw) >= kcd->second_cnt )
1751 	newpos = kcd->second_cnt - (kcd->width/kcd->kernw);
1752     if ( newpos < 0 ) newpos = 0;
1753     if ( newpos!=kcd->offleft ) {
1754 	int diff = newpos-kcd->offleft;
1755 	kcd->offleft = newpos;
1756 	GScrollBarSetPos(kcd->hsb,newpos);
1757 	rect.x = kcd->xstart2+1; rect.y = kcd->ystart;
1758 	rect.width = kcd->width-1;
1759 	rect.height = kcd->height+(kcd->ystart2-kcd->ystart);
1760 	GDrawScroll(kcd->gw,&rect,-diff*kcd->kernw,0);
1761     }
1762 }
1763 
KCD_VShow(KernClassDlg * kcd,int pos)1764 static void KCD_VShow(KernClassDlg *kcd, int pos) {
1765     if ( pos>=0 && pos<kcd->first_cnt ) {
1766 	--pos;	/* One line of context */
1767 	if ( pos + (kcd->height/kcd->kernh) >= kcd->first_cnt )
1768 	    pos = kcd->first_cnt - (kcd->height/kcd->kernh);
1769 	if ( pos < 0 ) pos = 0;
1770 	kcd->offtop = pos;
1771 	GScrollBarSetPos(kcd->vsb,pos);
1772     }
1773     GDrawRequestExpose(kcd->gw,NULL,false);
1774 }
1775 
KCD_VScroll(KernClassDlg * kcd,struct sbevent * sb)1776 static void KCD_VScroll(KernClassDlg *kcd,struct sbevent *sb) {
1777     int newpos = kcd->offtop;
1778     GRect rect;
1779 
1780     switch( sb->type ) {
1781       case et_sb_top:
1782         newpos = 0;
1783       break;
1784       case et_sb_uppage:
1785 	if ( kcd->height/kcd->kernh == 1 )
1786 	    --newpos;
1787 	else
1788 	    newpos -= kcd->height/kcd->kernh - 1;
1789       break;
1790       case et_sb_up:
1791         --newpos;
1792       break;
1793       case et_sb_down:
1794         ++newpos;
1795       break;
1796       case et_sb_downpage:
1797 	if ( kcd->height/kcd->kernh == 1 )
1798 	    ++newpos;
1799 	else
1800 	    newpos += kcd->height/kcd->kernh - 1;
1801       break;
1802       case et_sb_bottom:
1803         newpos = kcd->first_cnt - (kcd->height/kcd->kernh);
1804       break;
1805       case et_sb_thumb:
1806       case et_sb_thumbrelease:
1807         newpos = sb->pos;
1808       break;
1809     }
1810     if ( newpos + (kcd->height/kcd->kernh) >= kcd->first_cnt )
1811 	newpos = kcd->first_cnt - (kcd->height/kcd->kernh);
1812     if ( newpos < 0 ) newpos = 0;
1813     if ( newpos!=kcd->offtop ) {
1814 	int diff = newpos-kcd->offtop;
1815 	kcd->offtop = newpos;
1816 	GScrollBarSetPos(kcd->vsb,newpos);
1817 	rect.x = kcd->xstart; rect.y = kcd->ystart2+1;
1818 	rect.width = kcd->width+(kcd->xstart2-kcd->xstart);
1819 	rect.height = kcd->height-1;
1820 	GDrawScroll(kcd->gw,&rect,0,diff*kcd->kernh);
1821     }
1822 }
1823 
kcd_sub_e_h(GWindow gw,GEvent * event)1824 static int kcd_sub_e_h(GWindow gw, GEvent *event) {
1825     KernClassDlg *kcd = GDrawGetUserData(gw);
1826     switch ( event->type ) {
1827       case et_expose:
1828 	KCD_KernExpose(kcd,gw,event);
1829       break;
1830       case et_mouseup: case et_mousedown: case et_mousemove:
1831 	KCD_KernMouse(kcd,event);
1832       break;
1833       case et_char:
1834 return( false );
1835       case et_resize:
1836 	kcd->subwidth = event->u.resize.size.width;
1837 	GDrawRequestExpose(gw,NULL,false);
1838       break;
1839     }
1840 return( true );
1841 }
1842 
kcd_e_h(GWindow gw,GEvent * event)1843 static int kcd_e_h(GWindow gw, GEvent *event) {
1844     KernClassDlg *kcd = GDrawGetUserData(gw);
1845 
1846     switch ( event->type ) {
1847       case et_close:
1848 	KC_DoCancel(kcd);
1849       break;
1850       case et_char:
1851 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
1852 	    help("ui/mainviews/metricsview.html", kcd->iskernpair ?  "#metricsview-kernpair":
1853 				    "#metricsview-kernclass");
1854 return( true );
1855 	}
1856 return( false );
1857       break;
1858       case et_destroy:
1859 	if ( kcd!=NULL ) {
1860 	    SplineFont *sf = kcd->sf;
1861 	    KernClassListDlg *kcld = kcd->isv ? sf->vkcld : sf->kcld;
1862 	    KernClassDlg *prev, *test;
1863 	    for ( prev=NULL, test=sf->kcd; test!=NULL && test!=kcd; prev=test, test=test->next );
1864 	    if ( test==kcd ) {
1865 		if ( prev==NULL )
1866 		    sf->kcd = test->next;
1867 		else
1868 		    prev->next = test->next;
1869 	    }
1870 	    if ( kcld!=NULL ) {
1871 		GGadgetSetList(GWidgetGetControl(kcld->gw,CID_List),
1872 			KCLookupSubtableArray(sf,kcd->isv),false);
1873 	    }
1874 	    free(kcd);
1875 	}
1876       break;
1877       case et_mouseup: case et_mousemove: case et_mousedown:
1878 	if ( !kcd->iskernpair )
1879 	    KCD_Mouse(kcd,event);
1880       break;
1881       case et_expose:
1882 	if ( !kcd->iskernpair )
1883 	    KCD_Expose(kcd,gw,event);
1884       break;
1885       case et_resize:
1886 	KC_DoResize(kcd);
1887       break;
1888       case et_controlevent:
1889 	switch( event->u.control.subtype ) {
1890 	  case et_scrollbarchange:
1891 	    if ( event->u.control.g == kcd->hsb )
1892 		KCD_HScroll(kcd,&event->u.control.u.sb);
1893 	    else
1894 		KCD_VScroll(kcd,&event->u.control.u.sb);
1895 	  break;
1896 	}
1897       break;
1898     }
1899 return( true );
1900 }
1901 
ME_ListCheck(GGadget * g,int r,int c,SplineFont * sf)1902 void ME_ListCheck(GGadget *g,int r, int c, SplineFont *sf) {
1903     /* Gadget g is a matrix edit and the column "c" contains a list of glyph */
1904     /*  lists. Glyphs may appear multiple times in the list, but glyph names */
1905     /*  should be in the font. */
1906     /* the entry at r,c has just changed. Check to validate the above */
1907     int rows, cols = GMatrixEditGetColCnt(g);
1908     struct matrix_data *classes = _GMatrixEditGet(g,&rows);
1909     char *start1, *pt1, *eow1;
1910     int ch1, off;
1911     int changed = false;
1912 
1913     /* Remove any leading spaces */
1914     for ( start1=classes[r*cols+c].u.md_str; *start1==' '; ++start1 );
1915     if ( start1!=classes[r*cols+c].u.md_str ) {
1916 	off = start1-classes[r*cols+c].u.md_str;
1917 	for ( pt1=start1; *pt1; ++pt1 )
1918 	    pt1[-off] = *pt1;
1919 	pt1[-off] = '\0';
1920 	changed = true;
1921 	pt1 -= off;
1922 	start1 -= off;
1923     } else
1924 	pt1 = start1+strlen(start1);
1925     while ( pt1>start1 && pt1[-1]==' ' ) --pt1;
1926     *pt1 = '\0';
1927 
1928     /* Check for duplicate names in this class */
1929     /*  also check for glyph names which aren't in the font */
1930     while ( *start1!='\0' ) {
1931 	for ( pt1=start1; *pt1!=' ' && *pt1!='(' && *pt1!='{' && *pt1!='\0' ; ++pt1 );
1932 	/* Preserve the {Everything Else} string from splitting */
1933 	if ( *pt1=='{' ) {
1934 	    while ( *pt1!='\0' && *pt1!='}' ) ++pt1;
1935 	    if ( *pt1=='}' ) ++pt1;
1936 	}
1937 	eow1 = pt1;
1938 	if ( *eow1=='(' ) {
1939 	    while ( *eow1!='\0' && *eow1!=')' ) ++eow1;
1940 	    if ( *eow1==')' ) ++eow1;
1941 	}
1942 	while ( *eow1==' ' ) ++eow1;
1943 	ch1 = *pt1; *pt1='\0';
1944 	if ( sf!=NULL && !isEverythingElse( start1 )) {
1945 	    SplineChar *sc = SFGetChar(sf,-1,start1);
1946 	    if ( sc==NULL )
1947 		ff_post_notice(_("Missing glyph"),_("The font does not contain a glyph named %s."), start1 );
1948 	}
1949 	if ( *eow1=='\0' ) {
1950 	    *pt1 = ch1;
1951     break;
1952 	}
1953 	*pt1 = ch1;
1954 	start1 = eow1;
1955     }
1956     if ( changed ) {
1957 	/* Remove trailing spaces too */
1958 	start1=classes[r*cols+c].u.md_str;
1959 	pt1 = start1+strlen(start1);
1960 	while ( pt1>start1 && pt1[-1]==' ' )
1961 	    --pt1;
1962 	*pt1 = '\0';
1963 	GGadgetRedraw(g);
1964     }
1965 }
1966 
ME_SetCheckUnique(GGadget * g,int r,int c,SplineFont * sf)1967 void ME_SetCheckUnique(GGadget *g,int r, int c, SplineFont *sf) {
1968     /* Gadget g is a matrix edit and the column "c" contains a list of glyph */
1969     /*  sets. No glyph may appear twice in a set, and glyph names */
1970     /*  should be in the font. */
1971     /* the entry at r,c has just changed. Check to validate the above */
1972     int rows, cols = GMatrixEditGetColCnt(g);
1973     struct matrix_data *classes = _GMatrixEditGet(g,&rows);
1974     char *start1, *start2, *pt1, *pt2, *eow1, *eow2;
1975     int ch1, ch2, off;
1976     int changed = false;
1977 
1978     /* Remove any leading spaces */
1979     for ( start1=classes[r*cols+c].u.md_str; *start1==' '; ++start1 );
1980     if ( start1!=classes[r*cols+c].u.md_str ) {
1981 	off = start1-classes[r*cols+c].u.md_str;
1982 	for ( pt1=start1; *pt1; ++pt1 )
1983 	    pt1[-off] = *pt1;
1984 	pt1[-off] = '\0';
1985 	changed = true;
1986 	pt1 -= off;
1987 	start1 -= off;
1988     } else
1989 	pt1 = start1+strlen(start1);
1990     while ( pt1>start1 && pt1[-1]==' ' ) --pt1;
1991     *pt1 = '\0';
1992 
1993     /* Check for duplicate names in this class */
1994     /*  also check for glyph names which aren't in the font */
1995     while ( *start1!='\0' ) {
1996 	for ( pt1=start1; *pt1!=' ' && *pt1!='(' && *pt1!='{' && *pt1!='\0' ; ++pt1 );
1997 	/* Preserve the {Everything Else} string from splitting */
1998 	if ( *pt1=='{' ) {
1999 	    while ( *pt1!='\0' && *pt1!='}' ) ++pt1;
2000 	    if ( *pt1=='}' ) ++pt1;
2001 	}
2002 	eow1 = pt1;
2003 	if ( *eow1=='(' ) {
2004 	    while ( *eow1!='\0' && *eow1!=')' ) ++eow1;
2005 	    if ( *eow1==')' ) ++eow1;
2006 	}
2007 	while ( *eow1==' ' ) ++eow1;
2008 	ch1 = *pt1; *pt1='\0';
2009 	if ( sf!=NULL && !isEverythingElse( start1 )) {
2010 	    SplineChar *sc = SFGetChar(sf,-1,start1);
2011 	    if ( sc==NULL )
2012 		ff_post_notice(_("Missing glyph"),_("The font does not contain a glyph named %s."), start1 );
2013 	}
2014 	if ( *eow1=='\0' ) {
2015 	    *pt1 = ch1;
2016     break;
2017 	}
2018 	for ( start2 = eow1; *start2!='\0'; ) {
2019 	    for ( pt2=start2; *pt2!=' ' && *pt2!='(' && *pt2!='\0' ; ++pt2 );
2020 	    eow2 = pt2;
2021 	    if ( *eow2=='(' ) {
2022 		while ( *eow2!='\0' && *eow2!=')' ) ++eow2;
2023 		if ( *eow2==')' ) ++eow2;
2024 	    }
2025 	    while ( *eow2==' ' ) ++eow2;
2026 	    ch2 = *pt2; *pt2='\0';
2027 	    if ( strcmp(start1,start2)==0 ) {
2028 		off = eow2-start2;
2029 		if ( *eow2=='\0' && start2>classes[r*cols+c].u.md_str &&
2030 			start2[-1]==' ' )
2031 		    ++off;
2032 		for ( pt2=eow2; *pt2; ++pt2 )
2033 		    pt2[-off] = *pt2;
2034 		pt2[-off] = '\0';
2035 		changed = true;
2036 	    } else {
2037 		start2 = eow2;
2038 		*pt2 = ch2;
2039 	    }
2040 	}
2041 	*pt1 = ch1;
2042 	start1 = eow1;
2043     }
2044     if ( changed ) {
2045 	GGadgetRedraw(g);
2046 	/* Remove trailing spaces too */
2047 	start1=classes[r*cols+c].u.md_str;
2048 	pt1 = start1+strlen(start1);
2049 	while ( pt1>start1 && pt1[-1]==' ' )
2050 	    --pt1;
2051 	*pt1 = '\0';
2052     }
2053 }
2054 
ME_ClassCheckUnique(GGadget * g,int r,int c,SplineFont * sf)2055 void ME_ClassCheckUnique(GGadget *g,int r, int c, SplineFont *sf) {
2056     /* Gadget g is a matrix edit and column "c" contains a list of glyph */
2057     /*  classes. No glyph may appear in more than one class. */
2058     /*  Also all checks in the above routine should be done. */
2059     /* the entry at r,c has just changed. Check to validate the above */
2060     int rows, cols = GMatrixEditGetColCnt(g);
2061     struct matrix_data *classes = _GMatrixEditGet(g,&rows);
2062     char *start1, *start2, *pt1, *pt2, *eow1, *eow2;
2063     int ch1, ch2, testr, off;
2064     int changed = false;
2065     char *buts[3];
2066 
2067     ME_SetCheckUnique(g,r,c,sf);
2068 
2069     buts[0] = _("_From this class"); buts[1] = _("From the _other class"); buts[2]=NULL;
2070     /* Now check for duplicates in other rows */
2071     for ( start1=classes[r*cols+c].u.md_str; *start1!='\0'; ) {
2072 	for ( pt1=start1; *pt1!=' ' && *pt1!='(' && *pt1!='\0' ; ++pt1 );
2073 	eow1 = pt1;
2074 	if ( *eow1=='(' ) {
2075 	    while ( *eow1!='\0' && *eow1!=')' ) ++eow1;
2076 	    if ( *eow1==')' ) ++eow1;
2077 	}
2078 	while ( *eow1==' ' ) ++eow1;
2079 	ch1 = *pt1; *pt1='\0';
2080 
2081 	for ( testr=0; testr<rows; ++testr ) if ( testr!=r ) {
2082 	    for ( start2 = classes[testr*cols+c].u.md_str; *start2!='\0'; ) {
2083 		for ( pt2=start2; *pt2!=' ' && *pt2!='(' && *pt2!='\0' ; ++pt2 );
2084 		eow2 = pt2;
2085 		if ( *eow2=='(' ) {
2086 		    while ( *eow2!='\0' && *eow2!=')' ) ++eow2;
2087 		    if ( *eow2==')' ) ++eow2;
2088 		}
2089 		while ( *eow2==' ' ) ++eow2;
2090 		ch2 = *pt2; *pt2='\0';
2091 		if ( strcmp(start1,start2)==0 ) {
2092 		    *pt2 = ch2;
2093 		    if ( gwwv_ask(_("Glyph in two classes"),(const char **) buts,0,1,
2094 			    _("The glyph named %s also occurs in the class on row %d which begins with %.20s...\nYou must remove it from one of them."),
2095 			    start1, testr, classes[testr*cols+c].u.md_str )==0 ) {
2096 			off = eow1-start1;
2097 			for ( pt1=eow1; *pt1; ++pt1 )
2098 			    pt1[-off] = *pt1;
2099 			pt1[-off] = '\0';
2100 			changed = true;
2101       goto end_of_outer_loop;
2102 		    } else {
2103 			off = eow2-start2;
2104 			for ( pt2=eow2; *pt2; ++pt2 )
2105 			    pt2[-off] = *pt2;
2106 			pt2[-off] = '\0';
2107 			changed = true;
2108 		    }
2109 		} else {
2110 		    start2 = eow2;
2111 		    *pt2 = ch2;
2112 		}
2113 	    }
2114 	}
2115 	*pt1 = ch1;
2116 	start1 = eow1;
2117       end_of_outer_loop:;
2118     }
2119     if ( changed )
2120 	GGadgetRedraw(g);
2121 }
2122 
KCD_FinishEdit(GGadget * g,int r,int c,int wasnew)2123 static void KCD_FinishEdit(GGadget *g,int r, int c, int wasnew) {
2124     // This function expands the cross-mapping structures and then calls KCD_AutoKernAClass in order to populate them.
2125     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
2126     // CID_ClassList is a macro denoting the identification number for the widget for the first character list.
2127     // If the CID differs, then we assume that we are using the second list.
2128     int is_first = GGadgetGetCid(g) == CID_ClassList;
2129     int i, autokern;
2130 
2131 //    printf("KCD_FinishEdit()\n");
2132     ME_ClassCheckUnique(g, r, c, kcd->sf);
2133 
2134     if ( wasnew ) {
2135 	autokern = GGadgetIsChecked(GWidgetGetControl(kcd->gw,CID_Autokern));
2136 	if ( is_first ) {
2137             // offsets and adjusts are mappings between the characters in the first and second lists.
2138 	    kcd->offsets = realloc(kcd->offsets,(kcd->first_cnt+1)*kcd->second_cnt*sizeof(int16));
2139 	    memset(kcd->offsets+kcd->first_cnt*kcd->second_cnt,
2140 		    0, kcd->second_cnt*sizeof(int16));
2141             // adjusts are resolution-specific.
2142 	    kcd->adjusts = realloc(kcd->adjusts,(kcd->first_cnt+1)*kcd->second_cnt*sizeof(DeviceTable));
2143 	    memset(kcd->adjusts+kcd->first_cnt*kcd->second_cnt,
2144 		    0, kcd->second_cnt*sizeof(DeviceTable));
2145             // Group kerning.
2146 	    if (kcd->firsts_names) {
2147 	      kcd->firsts_names = realloc(kcd->firsts_names,(kcd->first_cnt+1)*sizeof(char*));
2148 	      memset(kcd->firsts_names+kcd->first_cnt, 0, sizeof(char*));
2149 	    }
2150 	    if (kcd->firsts_flags) {
2151 	      kcd->firsts_flags = realloc(kcd->firsts_flags,(kcd->first_cnt+1)*sizeof(int));
2152 	      memset(kcd->firsts_flags+kcd->first_cnt, 0, sizeof(int));
2153 	    }
2154 	    if (kcd->offsets_flags) {
2155 	      kcd->offsets_flags = realloc(kcd->offsets_flags,(kcd->first_cnt+1)*kcd->second_cnt*sizeof(int));
2156 	      memset(kcd->offsets_flags+kcd->first_cnt*kcd->second_cnt,
2157 		    0, kcd->second_cnt*sizeof(int));
2158 	    }
2159 	    ++kcd->first_cnt;
2160 	    if ( autokern )
2161 		KCD_AutoKernAClass(kcd,kcd->first_cnt-1,true);
2162 	} else {
2163             // The procedure for expanding offsets varies here, adding a column, since it is necessary to leave a space on each row for the new column.
2164             {
2165 	    int16 *new = malloc(kcd->first_cnt*(kcd->second_cnt+1)*sizeof(int16));
2166 	        for ( i=0; i<kcd->first_cnt; ++i ) {
2167 		    memcpy(new+i*(kcd->second_cnt+1),kcd->offsets+i*kcd->second_cnt,
2168 			    kcd->second_cnt*sizeof(int16));
2169 		    new[i*(kcd->second_cnt+1)+kcd->second_cnt] = 0;
2170 	        }
2171 	        free( kcd->offsets );
2172 	        kcd->offsets = new;
2173             }
2174 
2175 	    {
2176 		DeviceTable *new = malloc(kcd->first_cnt*(kcd->second_cnt+1)*sizeof(DeviceTable));
2177 		for ( i=0; i<kcd->first_cnt; ++i ) {
2178 		    memcpy(new+i*(kcd->second_cnt+1),kcd->adjusts+i*kcd->second_cnt,
2179 			    kcd->second_cnt*sizeof(DeviceTable));
2180 		    memset(&new[i*(kcd->second_cnt+1)+kcd->second_cnt],0,sizeof(DeviceTable));
2181 		}
2182 		free( kcd->adjusts );
2183 		kcd->adjusts = new;
2184 	    }
2185 
2186             // Group kerning.
2187 	    if (kcd->seconds_names) {
2188 	      kcd->seconds_names = realloc(kcd->seconds_names,(kcd->second_cnt+1)*sizeof(char*));
2189 	      memset(kcd->seconds_names+kcd->second_cnt, 0, sizeof(char*));
2190 	    }
2191 	    if (kcd->seconds_flags) {
2192 	      kcd->seconds_flags = realloc(kcd->seconds_flags,(kcd->second_cnt+1)*sizeof(int));
2193 	      memset(kcd->seconds_flags+kcd->second_cnt, 0, sizeof(int));
2194 	    }
2195             if (kcd->offsets_flags) {
2196 	      int *new = malloc(kcd->first_cnt*(kcd->second_cnt+1)*sizeof(int));
2197 	        for ( i=0; i<kcd->first_cnt; ++i ) {
2198 		    memcpy(new+i*(kcd->second_cnt+1),kcd->offsets_flags+i*kcd->second_cnt,
2199 			    kcd->second_cnt*sizeof(int));
2200 		    new[i*(kcd->second_cnt+1)+kcd->second_cnt] = 0;
2201 	        }
2202 	        free( kcd->offsets_flags );
2203 	        kcd->offsets_flags = new;
2204             }
2205 
2206 	    ++kcd->second_cnt;
2207 	    if ( autokern )
2208 		KCD_AutoKernAClass(kcd,kcd->second_cnt-1,false);
2209 	}
2210 	KCD_SBReset(kcd);
2211 	GDrawRequestExpose(kcd->gw,NULL,false);
2212     }
2213 }
2214 
whichToWidgetID(int which)2215 static int whichToWidgetID( int which )
2216 {
2217     return which==0 ? CID_First : CID_Second;
2218 }
2219 
2220 
KCD_PickGlyphsForClass(GGadget * g,int r,int c)2221 static char *KCD_PickGlyphsForClass(GGadget *g,int r, int c) {
2222     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
2223     int rows, cols = GMatrixEditGetColCnt(g);
2224     struct matrix_data *classes = _GMatrixEditGet(g,&rows);
2225 
2226     int which    = GWidgetGetControl(kcd->gw,CID_ClassList+100) == g;
2227     int widgetid = whichToWidgetID( which );
2228     char *new = GlyphSetFromSelection(kcd->sf,kcd->layer,classes[r*cols+c].u.md_str);
2229     if (new == NULL) new = copy("");
2230     if (new != NULL) {
2231       GGadgetSetTitle8(GWidgetGetControl(kcd->gw,widgetid),new );
2232       KCD_UpdateGlyphFromName(kcd,which,new);
2233     }
2234     char *other = GGadgetGetTitle8(GWidgetGetControl(kcd->gw,whichToWidgetID( !which )));
2235     if( other )
2236     {
2237 	KCD_UpdateGlyphFromName(kcd,!which,other);
2238     }
2239 
2240     GDrawRequestExpose(kcd->subw,NULL,false);
2241 
2242 return( new );
2243 }
2244 
KCD_EnableUpDown(GGadget * g,int r)2245 static enum gme_updown KCD_EnableUpDown(GGadget *g,int r) {
2246     int rows;
2247     enum gme_updown ret = 0;
2248 
2249     (void) GMatrixEditGet(g,&rows);
2250     if ( r>=2 )
2251 	ret = ud_up_enabled;
2252     if ( r>=1 && r<rows-1 )
2253 	ret |= ud_down_enabled;
2254 return( ret );
2255 }
2256 
KCD_RowMotion(GGadget * g,int oldr,int newr)2257 static void KCD_RowMotion(GGadget *g,int oldr, int newr) {
2258     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
2259     int is_first = GGadgetGetCid(g) == CID_ClassList;
2260     int i;
2261     DeviceTable tempdt;
2262 
2263     if ( is_first ) {
2264 	for ( i=0; i<kcd->second_cnt; ++i ) {
2265 	    int16 off = kcd->offsets[oldr*kcd->second_cnt + i];
2266 	    kcd->offsets[oldr*kcd->second_cnt + i] = kcd->offsets[newr*kcd->second_cnt + i];
2267 	    kcd->offsets[newr*kcd->second_cnt + i] = off;
2268 	    tempdt = kcd->adjusts[oldr*kcd->second_cnt + i];
2269 	    kcd->adjusts[oldr*kcd->second_cnt + i] = kcd->adjusts[newr*kcd->second_cnt + i];
2270 	    kcd->adjusts[newr*kcd->second_cnt + i] = tempdt;
2271 	    // Group kerning.
2272 	    if (kcd->offsets_flags) {
2273 	      int offflag = kcd->offsets_flags[oldr*kcd->second_cnt + i];
2274 	      kcd->offsets_flags[oldr*kcd->second_cnt + i] = kcd->offsets_flags[newr*kcd->second_cnt + i];
2275 	      kcd->offsets_flags[newr*kcd->second_cnt + i] = off;
2276 	    }
2277 	}
2278 	// Group kerning.
2279 	if (kcd->firsts_names) {
2280 	    char *name = kcd->firsts_names[oldr];
2281 	    kcd->firsts_names[oldr] = kcd->firsts_names[newr];
2282 	    kcd->firsts_names[newr] = name;
2283 	}
2284 	if (kcd->firsts_flags) {
2285 	    int flags = kcd->firsts_flags[oldr];
2286 	    kcd->firsts_flags[oldr] = kcd->firsts_flags[newr];
2287 	    kcd->firsts_flags[newr] = flags;
2288 	}
2289     } else {
2290 	for ( i=0; i<kcd->first_cnt; ++i ) {
2291 	    int16 off = kcd->offsets[i*kcd->second_cnt + oldr];
2292 	    kcd->offsets[i*kcd->second_cnt + oldr] = kcd->offsets[i*kcd->second_cnt + newr];
2293 	    kcd->offsets[i*kcd->second_cnt + newr] = off;
2294 	    tempdt = kcd->adjusts[i*kcd->second_cnt + oldr];
2295 	    kcd->adjusts[i*kcd->second_cnt + oldr] = kcd->adjusts[i*kcd->second_cnt + newr];
2296 	    kcd->adjusts[i*kcd->second_cnt + newr] = tempdt;
2297 	    // Group kerning.
2298 	    if (kcd->offsets_flags) {
2299 	      int offflag = kcd->offsets_flags[i*kcd->second_cnt + oldr];
2300 	      kcd->offsets_flags[i*kcd->second_cnt + oldr] = kcd->offsets_flags[i*kcd->second_cnt + newr];
2301 	      kcd->offsets_flags[i*kcd->second_cnt + newr] = off;
2302 	    }
2303 	}
2304 	// Group kerning.
2305 	if (kcd->seconds_names) {
2306 	    char *name = kcd->seconds_names[oldr];
2307 	    kcd->seconds_names[oldr] = kcd->seconds_names[newr];
2308 	    kcd->seconds_names[newr] = name;
2309 	}
2310 	if (kcd->seconds_flags) {
2311 	    int flags = kcd->seconds_flags[oldr];
2312 	    kcd->seconds_flags[oldr] = kcd->seconds_flags[newr];
2313 	    kcd->seconds_flags[newr] = flags;
2314 	}
2315     }
2316     GDrawRequestExpose(kcd->gw,NULL,false);
2317 }
2318 
KCD_EnableDeleteClass(GGadget * g,int whichclass)2319 static int KCD_EnableDeleteClass(GGadget *g,int whichclass) {
2320 return( whichclass>0 );
2321 }
2322 
KCD_DeleteClass(GGadget * g,int whichclass)2323 static void KCD_DeleteClass(GGadget *g,int whichclass) {
2324     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
2325     int rows;
2326     int is_first = GGadgetGetCid(g) == CID_ClassList;
2327     int i,j;
2328 
2329     (void) GMatrixEditGet(g,&rows);
2330     if ( is_first ) {
2331 	for ( i=0; i<kcd->second_cnt; ++i ) {
2332 	    free(kcd->adjusts[whichclass*kcd->second_cnt+i].corrections);
2333 	    kcd->adjusts[whichclass*kcd->second_cnt+i].corrections = NULL;
2334 	}
2335 	for ( i=whichclass+1; i<rows; ++i ) {
2336 	    memmove(kcd->offsets+(i-1)*kcd->second_cnt,
2337 		    kcd->offsets+i*kcd->second_cnt,
2338 		    kcd->second_cnt*sizeof(int16));
2339 	    memmove(kcd->adjusts+(i-1)*kcd->second_cnt,
2340 		    kcd->adjusts+i*kcd->second_cnt,
2341 		    kcd->second_cnt*sizeof(DeviceTable));
2342 	    // Group kerning.
2343 	    if (kcd->offsets_flags != NULL) {
2344 	      memmove(kcd->offsets_flags+(i-1)*kcd->second_cnt,
2345 		    kcd->offsets_flags+i*kcd->second_cnt,
2346 		    kcd->second_cnt*sizeof(int));
2347 	    }
2348 	}
2349 	// Group kerning.
2350 	kcd->offsets = realloc(kcd->offsets, (kcd->first_cnt-1)*kcd->second_cnt*sizeof(int16));
2351 	kcd->adjusts = realloc(kcd->adjusts, (kcd->first_cnt-1)*kcd->second_cnt*sizeof(DeviceTable));
2352 	kcd->offsets_flags = realloc(kcd->offsets_flags, (kcd->first_cnt-1)*kcd->second_cnt*sizeof(int));
2353 	if (kcd->firsts_names) {
2354 	  memmove(kcd->firsts_names+whichclass, kcd->firsts_names+whichclass + 1, (kcd->first_cnt - whichclass - 1) * sizeof(char*));
2355 	  kcd->firsts_names = realloc(kcd->firsts_names, (kcd->first_cnt - 1) * sizeof(char*));
2356 	}
2357 	if (kcd->firsts_flags) {
2358 	  memmove(kcd->firsts_flags+whichclass, kcd->firsts_flags+whichclass + 1, (kcd->first_cnt - whichclass - 1) * sizeof(int));
2359 	  kcd->firsts_flags = realloc(kcd->firsts_flags, (kcd->first_cnt - 1) * sizeof(int));
2360 	}
2361 
2362 	-- kcd->first_cnt;
2363     } else {
2364 	int16 *newoffs = malloc(kcd->first_cnt*(kcd->second_cnt-1)*sizeof(int16));
2365 	DeviceTable *newadj = malloc(kcd->first_cnt*(kcd->second_cnt-1)*sizeof(DeviceTable));
2366 	int *newoffflags = NULL;
2367 	if (kcd->offsets_flags != NULL) newoffflags = malloc(kcd->first_cnt*(kcd->second_cnt-1)*sizeof(int));
2368 	for ( i=0; i<kcd->first_cnt; ++i ) {
2369 	    free(kcd->adjusts[i*kcd->second_cnt+whichclass].corrections);
2370 	    kcd->adjusts[i*kcd->second_cnt+whichclass].corrections = NULL;
2371 	}
2372 	for ( i=0; i<rows; ++i ) if ( i!=whichclass ) {
2373 	    int newi = i>whichclass ? i-1 : i;
2374 	    for ( j=0; j<kcd->first_cnt; ++j ) {
2375 		newoffs[j*(kcd->second_cnt-1)+newi] =
2376 			kcd->offsets[j*kcd->second_cnt+i];
2377 		newadj[j*(kcd->second_cnt-1)+newi] =
2378 			kcd->adjusts[j*kcd->second_cnt+i];
2379 		// Group kerning.
2380 		if (newoffflags != NULL)
2381 		  newoffflags[j*(kcd->second_cnt-1)+newi] =
2382 			kcd->offsets_flags[j*kcd->second_cnt+i];
2383 	    }
2384 	}
2385 	// Group kerning.
2386 	if (kcd->seconds_names) {
2387 	  memmove(kcd->seconds_names+whichclass, kcd->seconds_names+whichclass + 1, (kcd->second_cnt - whichclass - 1) * sizeof(char*));
2388 	  kcd->seconds_names = realloc(kcd->seconds_names, (kcd->second_cnt - 1) * sizeof(char*));
2389 	}
2390 	if (kcd->seconds_flags) {
2391 	  memmove(kcd->seconds_flags+whichclass, kcd->seconds_flags+whichclass + 1, (kcd->second_cnt - whichclass - 1) * sizeof(int));
2392 	  kcd->seconds_flags = realloc(kcd->seconds_flags, (kcd->second_cnt - 1) * sizeof(int));
2393 	}
2394 
2395 	-- kcd->second_cnt;
2396 	free(kcd->offsets);
2397 	kcd->offsets = newoffs;
2398 	free(kcd->adjusts);
2399 	kcd->adjusts = newadj;
2400 	// Group kerning.
2401 	if (kcd->offsets_flags != NULL) free(kcd->offsets_flags);
2402 	kcd->offsets_flags = newoffflags;
2403     }
2404 }
2405 
KCD_ClassSelectionChanged(GGadget * g,int whichclass,int c)2406 static void KCD_ClassSelectionChanged(GGadget *g,int whichclass, int c) {
2407     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(g));
2408     int is_first = GGadgetGetCid(g) == CID_ClassList;
2409     int first, second;
2410 
2411     if ( is_first )
2412 	KCD_VShow(kcd,whichclass);
2413     else
2414 	KCD_HShow(kcd,whichclass);
2415     first = GMatrixEditGetActiveRow(GWidgetGetControl(kcd->gw,CID_ClassList));
2416     second = GMatrixEditGetActiveRow(GWidgetGetControl(kcd->gw,CID_ClassList+100));
2417     if ( first!=-1 && second!=-1 )
2418 	KCD_EditOffset(kcd, first, second);
2419 }
2420 
KCD_GlyphListCompletion(GGadget * t,int from_tab)2421 static unichar_t **KCD_GlyphListCompletion(GGadget *t,int from_tab) {
2422     KernClassDlg *kcd = GDrawGetUserData(GDrawGetParentWindow(GGadgetGetWindow(t)));
2423     SplineFont *sf = kcd->sf;
2424 
2425 return( SFGlyphNameCompletion(sf,t,from_tab,true));
2426 }
2427 
KCD_GlyphCompletion(GGadget * t,int from_tab)2428 static unichar_t **KCD_GlyphCompletion(GGadget *t,int from_tab) {
2429     KernClassDlg *kcd = GDrawGetUserData(GGadgetGetWindow(t));
2430     SplineFont *sf = kcd->sf;
2431 
2432 return( SFGlyphNameCompletion(sf,t,from_tab,false));
2433 }
2434 
2435 static struct col_init class_ci[] = {
2436     { me_funcedit, KCD_PickGlyphsForClass, NULL, NULL, N_("Glyphs in the classes") },
2437     };
AddClassList(GGadgetCreateData * gcd,GTextInfo * label,int k,int off,struct matrixinit * mi,GGadgetCreateData ** harray,GGadgetCreateData ** varray,SplineFont * sf,char ** classes,int cnt)2438 static int AddClassList(GGadgetCreateData *gcd, GTextInfo *label, int k, int off,
2439 	struct matrixinit *mi, GGadgetCreateData **harray, GGadgetCreateData **varray,
2440 	SplineFont *sf, char **classes, int cnt) {
2441     static char *empty[] = { NULL };
2442     static int initted = false;
2443     struct matrix_data *md;
2444     int i;
2445 
2446     if ( !initted ) {
2447 	class_ci[0].title = S_(class_ci[0].title);
2448 	initted = true;
2449     }
2450 
2451     label[k].text = (unichar_t *) (off==0?_("First Char"):_("Second Char"));
2452     label[k].text_is_1byte = true;
2453     gcd[k].gd.label = &label[k];
2454     gcd[k].gd.flags = gg_visible | gg_enabled;
2455     gcd[k].gd.cid = CID_ClassLabel+off;
2456     gcd[k++].creator = GLabelCreate;
2457     varray[0] = &gcd[k-1];
2458 
2459     memset(mi,0,sizeof(*mi));
2460     mi->col_cnt = 1;
2461     mi->col_init = class_ci;
2462 
2463     if ( cnt==0 ) {
2464 	cnt=1;
2465 	classes = empty;
2466     }
2467     md = calloc(cnt+10,sizeof(struct matrix_data));
2468     for ( i=0; i<cnt; ++i ) {
2469 	if ( i==0 && classes[i]==NULL ) {
2470 	    md[i+0].u.md_str = copy( _("{Everything Else}") );
2471 	    if ( off!=0 ) md[i+0].frozen = true;
2472 	} else
2473 	    md[i+0].u.md_str = SFNameList2NameUni(sf,classes[i]);
2474     }
2475     mi->matrix_data = md;
2476     mi->initial_row_cnt = cnt;
2477     mi->finishedit = KCD_FinishEdit;
2478     mi->candelete = KCD_EnableDeleteClass;
2479 
2480     gcd[k].gd.flags = gg_enabled | gg_visible;
2481     gcd[k].gd.cid = CID_ClassList+off;
2482     gcd[k].gd.u.matrix = mi;
2483     gcd[k++].creator = GMatrixEditCreate;
2484     varray[1] = &gcd[k-1];
2485 
2486 /* GT: Select the class containing the glyph named in the following text field */
2487     label[k].text = (unichar_t *) _("Select Class Containing:");
2488     label[k].text_is_1byte = true;
2489     label[k].text_in_resource = true;
2490     gcd[k].gd.label = &label[k];
2491     gcd[k].gd.pos.x = gcd[k-3].gd.pos.x+5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+26+4;
2492     gcd[k].gd.flags = gg_visible | gg_enabled;
2493     gcd[k].gd.popup_msg = _("Select the class containing the named glyph");
2494     gcd[k++].creator = GLabelCreate;
2495     harray[0] = &gcd[k-1];
2496 
2497     gcd[k].gd.pos.x = gcd[k-1].gd.pos.x+100; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;
2498     gcd[k].gd.pos.width = 80;
2499     gcd[k].gd.flags = gg_visible | gg_enabled;
2500     gcd[k].gd.popup_msg = _("Select the class containing the named glyph");
2501     gcd[k].gd.handle_controlevent = KCD_TextSelect;
2502     gcd[k].gd.cid = CID_ClassSelect+off;
2503     gcd[k].gd.u.completion = KCD_GlyphCompletion;
2504     gcd[k++].creator = GTextCompletionCreate;
2505     harray[1] = &gcd[k-1]; harray[2] = NULL;
2506 
2507     gcd[k].gd.flags = gg_enabled|gg_visible;
2508     gcd[k].gd.u.boxelements = harray;
2509     gcd[k++].creator = GHBoxCreate;
2510     varray[2] = &gcd[k-1]; varray[3] = NULL;
2511 
2512     gcd[k].gd.flags = gg_enabled|gg_visible;
2513     gcd[k].gd.u.boxelements = varray;
2514     gcd[k++].creator = GVBoxCreate;
2515 
2516 return( k );
2517 }
2518 
FillShowKerningWindow(KernClassDlg * kcd,GGadgetCreateData * left,SplineFont * sf,GGadgetCreateData * topbox)2519 static void FillShowKerningWindow(KernClassDlg *kcd, GGadgetCreateData *left,
2520 	SplineFont *sf, GGadgetCreateData *topbox) {
2521     GGadgetCreateData gcd[31], hbox, flagbox, hvbox, buttonbox, mainbox[2];
2522     GGadgetCreateData *harray[10], *hvarray[20], *flagarray[4], *buttonarray[9], *varray[12];
2523     GGadgetCreateData *bigharray[6];
2524     GTextInfo label[31];
2525     int k,j;
2526     char buffer[20];
2527     int drawable_row;
2528 
2529     kcd->pixelsize = 150;
2530     kcd->magfactor = 1;
2531 
2532     memset(gcd,0,sizeof(gcd));
2533     memset(label,0,sizeof(label));
2534     memset(&hbox,0,sizeof(hbox));
2535     memset(&flagbox,0,sizeof(flagbox));
2536     memset(&hvbox,0,sizeof(hvbox));
2537     memset(&buttonbox,0,sizeof(buttonbox));
2538     memset(&mainbox,0,sizeof(mainbox));
2539     if ( topbox!=NULL )
2540 	memset(topbox,0,2*sizeof(*topbox));
2541     k = j = 0;
2542 
2543     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = 5;
2544     gcd[k].gd.pos.width = 110;
2545     gcd[k].gd.flags = gg_visible | gg_enabled;
2546     gcd[k].gd.handle_controlevent = KCD_GlyphSelected;
2547     gcd[k].gd.cid = CID_First;
2548     gcd[k++].creator = left!=NULL ? GListButtonCreate : GTextFieldCreate;
2549     harray[0] = &gcd[k-1];
2550 
2551     gcd[k].gd.pos.x = 130; gcd[k].gd.pos.y = 5;
2552     gcd[k].gd.pos.width = 110;
2553     gcd[k].gd.flags = gg_visible | gg_enabled;
2554     if ( left==NULL ) gcd[k].gd.flags |= gg_list_alphabetic;
2555     gcd[k].gd.handle_controlevent = KCD_GlyphSelected;
2556     gcd[k].gd.cid = CID_Second;
2557     gcd[k++].creator = left!=NULL ? GListButtonCreate : GListFieldCreate;
2558     harray[1] = &gcd[k-1];
2559 
2560     label[k].text = (unichar_t *) _("Use FreeType");
2561     label[k].text_is_1byte = true;
2562     gcd[k].gd.label = &label[k];
2563     gcd[k].gd.pos.x = 260; gcd[k].gd.pos.y = 7;
2564     if ( !hasFreeType() )
2565 	gcd[k].gd.flags = gg_visible;
2566     else
2567 	gcd[k].gd.flags = gg_enabled|gg_visible|gg_cb_on;
2568     gcd[k].gd.cid = CID_FreeType;
2569     gcd[k].gd.handle_controlevent = KCB_FreeTypeChanged;
2570     gcd[k++].creator = GCheckBoxCreate;
2571     harray[2] = GCD_Glue; harray[3] = &gcd[k-1];
2572     harray[4] = GCD_Glue; harray[5] = GCD_Glue;
2573     harray[6] = GCD_Glue; harray[7] = GCD_Glue; harray[8] = NULL;
2574 
2575     hbox.gd.flags = gg_enabled|gg_visible;
2576     hbox.gd.u.boxelements = harray;
2577     hbox.creator = GHBoxCreate;
2578     varray[j++] = &hbox; varray[j++] = NULL;
2579 
2580     label[k].text = (unichar_t *) _("Display Size:");
2581     label[k].text_is_1byte = true;
2582     gcd[k].gd.label = &label[k];
2583     gcd[k].gd.flags = gg_visible|gg_enabled ;
2584     gcd[k].gd.cid = CID_SizeLabel;
2585     gcd[k++].creator = GLabelCreate;
2586     hvarray[0] = &gcd[k-1];
2587 
2588     sprintf( buffer, "%d", kcd->pixelsize );
2589     label[k].text = (unichar_t *) buffer;
2590     label[k].text_is_1byte = true;
2591     gcd[k].gd.label = &label[k];
2592     gcd[k].gd.pos.width = 80;
2593     gcd[k].gd.flags = gg_visible|gg_enabled ;
2594     gcd[k].gd.cid = CID_DisplaySize;
2595     gcd[k].gd.handle_controlevent = KCD_DisplaySizeChanged;
2596     gcd[k++].creator = GListFieldCreate;
2597     hvarray[1] = &gcd[k-1];
2598 
2599     label[k].text = (unichar_t *) _("Magnification:");
2600     label[k].text_is_1byte = true;
2601     gcd[k].gd.label = &label[k];
2602     gcd[k].gd.flags = gg_visible|gg_enabled ;
2603     gcd[k].gd.cid = CID_MagLabel;
2604     gcd[k++].creator = GLabelCreate;
2605     hvarray[2] = &gcd[k-1];
2606 
2607     gcd[k].gd.flags = gg_visible|gg_enabled ;
2608     gcd[k].gd.cid = CID_Magnifications;
2609     gcd[k].gd.pos.width = 60;
2610     gcd[k].gd.u.list = magnifications;
2611     gcd[k].gd.handle_controlevent = KCD_MagnificationChanged;
2612     gcd[k++].creator = GListButtonCreate;
2613     hvarray[3] = &gcd[k-1]; hvarray[4] = GCD_Glue; hvarray[5] = NULL;
2614 
2615     label[k].text = (unichar_t *) _("Kern Offset:");
2616     label[k].text_is_1byte = true;
2617     gcd[k].gd.label = &label[k];
2618     gcd[k].gd.flags = gg_visible|gg_enabled ;
2619     gcd[k].gd.cid = CID_OffsetLabel;
2620     gcd[k++].creator = GLabelCreate;
2621     hvarray[6] = &gcd[k-1];
2622 
2623     gcd[k].gd.pos.x = 90; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-4;
2624     gcd[k].gd.pos.width = 60;
2625     gcd[k].gd.flags = gg_visible|gg_enabled ;
2626     gcd[k].gd.cid = CID_KernOffset;
2627     gcd[k].gd.handle_controlevent = KCD_KernOffChanged;
2628     gcd[k++].creator = GTextFieldCreate;
2629     hvarray[7] = &gcd[k-1];
2630 
2631     label[k].text = (unichar_t *) _("Device Table Correction:\n  (at display size)");
2632     label[k].text_is_1byte = true;
2633     gcd[k].gd.label = &label[k];
2634     gcd[k].gd.flags = gg_visible|gg_enabled ;
2635     gcd[k].gd.cid = CID_CorrectLabel;
2636     gcd[k++].creator = GLabelCreate;
2637     hvarray[8] = &gcd[k-1];
2638 
2639     label[k].text = (unichar_t *) "0";
2640     label[k].text_is_1byte = true;
2641     gcd[k].gd.label = &label[k];
2642     gcd[k].gd.pos.width = 60;
2643     gcd[k].gd.flags = gg_visible|gg_enabled ;
2644     gcd[k].gd.cid = CID_Correction;
2645     gcd[k].gd.handle_controlevent = KCD_CorrectionChanged;
2646     gcd[k++].creator = GTextFieldCreate;
2647     hvarray[9] = &gcd[k-1]; hvarray[10]=NULL;
2648 
2649     label[k].text = (unichar_t *) _("Revert Kerning");
2650     label[k].text_is_1byte = true;
2651     gcd[k].gd.label = &label[k];
2652     gcd[k].gd.flags = gg_visible|gg_enabled;
2653     gcd[k].gd.popup_msg = _("Resets the kerning offset and device table corrections to what they were originally");
2654     gcd[k].gd.handle_controlevent = KCD_RevertKerning;
2655     gcd[k].gd.cid = CID_Revert;
2656     gcd[k++].creator = GButtonCreate;
2657     hvarray[11] = &gcd[k-1]; hvarray[12] = GCD_ColSpan;
2658 
2659     label[k].text = (unichar_t *) _("Clear Device Table");
2660     label[k].text_is_1byte = true;
2661     gcd[k].gd.label = &label[k];
2662     gcd[k].gd.flags = gg_visible|gg_enabled;
2663     gcd[k].gd.popup_msg = _("Clear all device table corrections associated with this combination");
2664     gcd[k].gd.cid = CID_ClearDevice;
2665     gcd[k].gd.handle_controlevent = KCD_ClearDevice;
2666     gcd[k++].creator = GButtonCreate;
2667     hvarray[13] = &gcd[k-1]; hvarray[14] = GCD_ColSpan; hvarray[15] = NULL;
2668     hvarray[16] = NULL;
2669 
2670     hvbox.gd.flags = gg_enabled|gg_visible;
2671     hvbox.gd.u.boxelements = hvarray;
2672     hvbox.creator = GHVBoxCreate;
2673     varray[j++] = &hvbox; varray[j++] = NULL;
2674 
2675     gcd[k].gd.flags = gg_visible|gg_enabled ;
2676     gcd[k].gd.pos.width = gcd[k].gd.pos.height = 100;
2677     gcd[k].gd.cid = CID_Display;
2678     gcd[k].gd.u.drawable_e_h = kcd_sub_e_h;
2679     gcd[k].data = kcd;
2680     gcd[k++].creator = GDrawableCreate;
2681     drawable_row = j/2;
2682     varray[j++] = &gcd[k-1]; varray[j++] = NULL;
2683 
2684     if ( left==NULL ) {
2685 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = -40;
2686 	gcd[k].gd.flags = gg_enabled ;
2687 	label[k].text = (unichar_t *) _("Lookup subtable:");
2688 	label[k].text_is_1byte = true;
2689 	gcd[k].gd.label = &label[k];
2690 	gcd[k++].creator = GLabelCreate;
2691 	flagarray[0] = &gcd[k-1];
2692 
2693 	gcd[k].gd.flags = gg_enabled|gg_visible;
2694 	gcd[k].gd.cid = CID_Subtable;
2695 	gcd[k].gd.handle_controlevent = KP_Subtable;
2696 	gcd[k++].creator = GListButtonCreate;
2697 	flagarray[1] = &gcd[k-1]; flagarray[2] = GCD_Glue; flagarray[3] = NULL;
2698 
2699 	flagbox.gd.flags = gg_enabled|gg_visible;
2700 	flagbox.gd.u.boxelements = flagarray;
2701 	flagbox.creator = GHBoxCreate;
2702 	varray[j++] = &flagbox; varray[j++] = NULL;
2703 
2704 	label[k].text = (unichar_t *) _("_OK");
2705 	label[k].text_is_1byte = true;
2706 	label[k].text_in_resource = true;
2707 	gcd[k].gd.label = &label[k];
2708 	gcd[k].gd.pos.x = 30; gcd[k].gd.pos.y = KC_Height-KC_CANCELDROP;
2709 	gcd[k].gd.pos.width = -1;
2710 	gcd[k].gd.flags = gg_visible|gg_enabled;
2711 	if ( left==NULL ) gcd[k].gd.flags |= gg_but_default;
2712 	gcd[k].gd.handle_controlevent = KPD_OK;
2713 	gcd[k].gd.cid = CID_Prev2;
2714 	gcd[k++].creator = GButtonCreate;
2715 
2716 	label[k].text = (unichar_t *) _("_Cancel");
2717 	label[k].text_is_1byte = true;
2718 	label[k].text_in_resource = true;
2719 	gcd[k].gd.label = &label[k];
2720 	gcd[k].gd.pos.x = -30+3; gcd[k].gd.pos.y = KC_Height-KC_CANCELDROP;
2721 	gcd[k].gd.pos.width = -1;
2722 	gcd[k].gd.flags = gg_visible|gg_enabled ;
2723 	if ( left==NULL ) gcd[k].gd.flags |= gg_but_cancel;
2724 	gcd[k].gd.handle_controlevent = KPD_Cancel;
2725 	gcd[k].gd.cid = CID_Next2;
2726 	gcd[k++].creator = GButtonCreate;
2727 
2728 	buttonarray[0] = GCD_Glue; buttonarray[1] = &gcd[k-2]; buttonarray[2] = GCD_Glue;
2729 	buttonarray[3] = GCD_Glue; buttonarray[4] = &gcd[k-1]; buttonarray[5] = GCD_Glue;
2730 	buttonarray[6] = NULL;
2731 	buttonbox.gd.flags = gg_enabled|gg_visible;
2732 	buttonbox.gd.u.boxelements = buttonarray;
2733 	buttonbox.creator = GHBoxCreate;
2734 	varray[j++] = &buttonbox; varray[j++] = NULL; varray[j++] = NULL;
2735 
2736 	mainbox[0].gd.pos.x = mainbox[0].gd.pos.y = 2;
2737 	mainbox[0].gd.flags = gg_enabled|gg_visible;
2738 	mainbox[0].gd.u.boxelements = varray;
2739 	mainbox[0].creator = GHVGroupCreate;
2740 
2741 	GGadgetsCreate(kcd->gw,mainbox);
2742 	GHVBoxSetExpandableCol(buttonbox.ret,gb_expandgluesame);
2743     } else {
2744 	varray[j++] = NULL;
2745 	mainbox[0].gd.flags = gg_enabled|gg_visible;
2746 	mainbox[0].gd.u.boxelements = varray;
2747 	mainbox[0].creator = GHVBoxCreate;
2748 
2749 	bigharray[0] = left;
2750 	bigharray[1] = mainbox;
2751 	bigharray[2] = bigharray[3] = NULL;
2752 
2753 	topbox[0].gd.pos.x = mainbox[0].gd.pos.y = 2;
2754 	topbox[0].gd.flags = gg_enabled|gg_visible;
2755 	topbox[0].gd.u.boxelements = bigharray;
2756 	topbox[0].gd.cid = CID_TopBox;
2757 	topbox[0].creator = GHVGroupCreate;
2758 
2759 	GGadgetsCreate(kcd->gw,topbox);
2760 	GHVBoxSetExpandableCol(topbox[0].ret,0);
2761     }
2762 
2763     GHVBoxSetExpandableRow(mainbox[0].ret,drawable_row);
2764     GHVBoxSetExpandableCol(hbox.ret,gb_expandglue);
2765     /*GHVBoxSetExpandableCol(hvbox.ret,gb_expandglue);*/
2766     if ( left==NULL ) {
2767 	GHVBoxSetExpandableCol(flagbox.ret,gb_expandglue);
2768 	GGadgetSetList(flagarray[1]->ret,SFSubtablesOfType(sf,gpos_pair,false,false),false);
2769     }
2770     kcd->subw = GDrawableGetWindow(GWidgetGetControl(kcd->gw,CID_Display));
2771 }
2772 
KernClassD(KernClass * kc,SplineFont * sf,int layer,int isv)2773 void KernClassD(KernClass *kc, SplineFont *sf, int layer, int isv) {
2774     GRect pos;
2775     GWindowAttrs wattrs;
2776     GGadgetCreateData gcd[54], sepbox, classbox, hvbox, buttonbox, mainbox[2], topbox[2], titbox, hbox;
2777     GGadgetCreateData *harray1[17], *harray2[17], *varray1[5], *varray2[5];
2778     GGadgetCreateData *hvarray[13], *buttonarray[8], *varray[19], *h4array[8], *harrayclasses[6], *titlist[4], *h5array[3];
2779     GTextInfo label[54];
2780     KernClassDlg *kcd;
2781     int i, j, kc_width, vi;
2782     int as, ds, ld, sbsize;
2783     FontRequest rq;
2784     static unichar_t kernw[] = { '-', '1', '2', '3', '4', '5', 0 };
2785     GWindow gw;
2786     char titlebuf[300];
2787     static GFont *font;
2788     char sepbuf[40], mkbuf[40];
2789     struct matrixinit firstmi, secondmi;
2790 
2791     for ( kcd = sf->kcd; kcd!=NULL && kcd->orig!=kc; kcd = kcd->next );
2792     if ( kcd!=NULL ) {
2793 	GDrawSetVisible(kcd->gw,true);
2794 	GDrawRaise(kcd->gw);
2795 return;
2796     }
2797     kcd = calloc(1,sizeof(KernClassDlg));
2798     kcd->orig = kc;
2799     kcd->subtable = kc->subtable;
2800     kcd->sf = sf;
2801     kcd->layer = layer;
2802     kcd->isv = isv;
2803     kcd->old_pos = -1;
2804     kcd->next = sf->kcd;
2805     sf->kcd = kcd;
2806 
2807     kcd->first_cnt = kc->first_cnt;
2808     kcd->second_cnt = kc->second_cnt;
2809     kcd->offsets = malloc(kc->first_cnt*kc->second_cnt*sizeof(int16));
2810     memcpy(kcd->offsets,kc->offsets,kc->first_cnt*kc->second_cnt*sizeof(int16));
2811     kcd->adjusts = malloc(kc->first_cnt*kc->second_cnt*sizeof(DeviceTable));
2812     memcpy(kcd->adjusts,kc->adjusts,kc->first_cnt*kc->second_cnt*sizeof(DeviceTable));
2813     for ( i=0; i<kcd->first_cnt*kcd->second_cnt; ++i ) {
2814 	if ( kcd->adjusts[i].corrections!=NULL ) {
2815 	    int len = kcd->adjusts[i].last_pixel_size-kcd->adjusts[i].first_pixel_size+1;
2816 	    kcd->adjusts[i].corrections = malloc(len);
2817 	    memcpy(kcd->adjusts[i].corrections,kc->adjusts[i].corrections,len);
2818 	}
2819     }
2820 
2821     // Group kerning.
2822     if (kc->firsts_names) {
2823       kcd->firsts_names = malloc(kc->first_cnt*sizeof(char*));
2824       int namepos;
2825       for (namepos = 0; namepos < kc->first_cnt; namepos ++)
2826         kcd->firsts_names[namepos] = copy(kc->firsts_names[namepos]);
2827     }
2828     if (kc->seconds_names) {
2829       kcd->seconds_names = malloc(kc->second_cnt*sizeof(char*));
2830       int namepos;
2831       for (namepos = 0; namepos < kc->second_cnt; namepos ++)
2832         kcd->seconds_names[namepos] = copy(kc->seconds_names[namepos]);
2833     }
2834     if (kc->firsts_flags) {
2835       kcd->firsts_flags = malloc(kc->first_cnt*sizeof(int));
2836       memcpy(kcd->firsts_flags,kc->firsts_flags,kc->first_cnt*sizeof(int));
2837     }
2838     if (kc->seconds_flags) {
2839       kcd->seconds_flags = malloc(kc->second_cnt*sizeof(int));
2840       memcpy(kcd->seconds_flags,kc->seconds_flags,kc->second_cnt*sizeof(int));
2841     }
2842     if (kcd->offsets_flags) {
2843       kcd->offsets_flags = malloc(kc->first_cnt*kc->second_cnt*sizeof(int));
2844       memcpy(kcd->offsets_flags,kc->offsets_flags,kc->first_cnt*kc->second_cnt*sizeof(int));
2845     }
2846 
2847     memset(&wattrs,0,sizeof(wattrs));
2848     memset(&gcd,0,sizeof(gcd));
2849     memset(&classbox,0,sizeof(classbox));
2850     memset(&hbox,0,sizeof(hbox));
2851     memset(&hvbox,0,sizeof(hvbox));
2852     memset(&buttonbox,0,sizeof(buttonbox));
2853     memset(&mainbox,0,sizeof(mainbox));
2854     memset(&titbox,0,sizeof(titbox));
2855     memset(&label,0,sizeof(label));
2856 
2857     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
2858     wattrs.event_masks = ~(1<<et_charup);
2859     wattrs.restrict_input_to_me = true;
2860     wattrs.undercursor = 1;
2861     wattrs.cursor = ct_pointer;
2862 /* GT: The %s is the name of the lookup subtable containing this kerning class */
2863     snprintf( titlebuf, sizeof(titlebuf), _("Kerning by Classes: %s"), kc->subtable->subtable_name );
2864     wattrs.utf8_window_title =  titlebuf ;
2865     wattrs.is_dlg = false;
2866     pos.x = pos.y = 0;
2867     pos.width = GGadgetScale(GDrawPointsToPixels(NULL,KC_Width));
2868     pos.height = GDrawPointsToPixels(NULL,KC_Height);
2869     kcd->gw = gw = GDrawCreateTopWindow(NULL,&pos,kcd_e_h,kcd,&wattrs);
2870 
2871     kc_width = GDrawPixelsToPoints(NULL,pos.width*100/GGadgetScale(100));
2872 
2873     if ( font==NULL ) {
2874 	memset(&rq,'\0',sizeof(rq));
2875 	rq.point_size = 12;
2876 	rq.weight = 400;
2877 	rq.utf8_family_name = MONO_UI_FAMILIES;
2878 	font = GDrawInstanciateFont(gw,&rq);
2879 	font = GResourceFindFont("KernClass.Font",font);
2880     }
2881     kcd->font = font;
2882     GDrawWindowFontMetrics(gw,kcd->font,&as,&ds,&ld);
2883     kcd->fh = as+ds; kcd->as = as;
2884     GDrawSetFont(gw,kcd->font);
2885 
2886     kcd->kernh = kcd->fh+3;
2887     kcd->kernw = GDrawGetTextWidth(gw,kernw,-1)+3;
2888 
2889     if ( kc->subtable->separation==0 && !kc->subtable->kerning_by_touch ) {
2890 	kc->subtable->separation = sf->width_separation;
2891 	if ( sf->width_separation==0 )
2892 	    kc->subtable->separation = 15*(sf->ascent+sf->descent)/100;
2893 	kc->subtable->minkern = 0;
2894     }
2895 
2896     i = j = 0;
2897     snprintf( titlebuf, sizeof(titlebuf), _("Lookup Subtable: %s"), kc->subtable->subtable_name );
2898     gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5;
2899     gcd[i].gd.flags = gg_visible | gg_enabled;
2900     label[i].text = (unichar_t *) titlebuf;
2901     label[i].text_is_1byte = true;
2902     gcd[i].gd.label = &label[i];
2903     gcd[i++].creator = GLabelCreate;
2904     titlist[0] = &gcd[i-1]; titlist[1] = GCD_Glue;
2905 
2906 
2907     gcd[i].gd.flags = show_kerning_pane_in_class ? (gg_visible | gg_enabled | gg_cb_on) : (gg_visible | gg_enabled);
2908     label[i].text = (unichar_t *) _("Show Kerning");
2909     label[i].text_is_1byte = true;
2910     gcd[i].gd.label = &label[i];
2911     gcd[i].gd.handle_controlevent = KC_ShowHideKernPane;
2912     gcd[i].gd.cid = CID_ShowHideKern;
2913     gcd[i++].creator = GCheckBoxCreate;
2914     titlist[2] = &gcd[i-1]; titlist[3] = NULL;
2915 
2916     memset(&titbox,0,sizeof(titbox));
2917     titbox.gd.flags = gg_enabled|gg_visible;
2918     titbox.gd.u.boxelements = titlist;
2919     titbox.creator = GHBoxCreate;
2920 
2921     varray[j++] = &titbox; varray[j++] = NULL;
2922 
2923 	label[i].text = (unichar_t *) _("_Default Separation:");
2924 	label[i].text_is_1byte = true;
2925 	label[i].text_in_resource = true;
2926 	gcd[i].gd.label = &label[i];
2927 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
2928 	gcd[i].gd.flags = gg_enabled|gg_visible;
2929 	gcd[i].gd.popup_msg = _(
2930 	    "Add entries to the lookup trying to make the optical\n"
2931 	    "separation between all pairs of glyphs equal to this\n"
2932 	    "value." );
2933 	gcd[i].creator = GLabelCreate;
2934 	h4array[0] = &gcd[i++];
2935 
2936 	sprintf( sepbuf, "%d", kc->subtable->separation );
2937 	label[i].text = (unichar_t *) sepbuf;
2938 	label[i].text_is_1byte = true;
2939 	label[i].text_in_resource = true;
2940 	gcd[i].gd.label = &label[i];
2941 	gcd[i].gd.pos.width = 50;
2942 	gcd[i].gd.flags = gg_enabled|gg_visible;
2943 	gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
2944 	gcd[i].gd.cid = CID_Separation;
2945 	gcd[i].creator = GTextFieldCreate;
2946 	h4array[1] = &gcd[i++]; h4array[2] = GCD_Glue;
2947 
2948 	label[i].text = (unichar_t *) _("_Min Kern:");
2949 	label[i].text_is_1byte = true;
2950 	label[i].text_in_resource = true;
2951 	gcd[i].gd.label = &label[i];
2952 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
2953 	gcd[i].gd.flags = gg_enabled|gg_visible;
2954 	gcd[i].gd.popup_msg = _(
2955 	    "Any computed kerning change whose absolute value is less\n"
2956 	    "that this will be ignored.\n" );
2957 	gcd[i].creator = GLabelCreate;
2958 	h4array[3] = &gcd[i++];
2959 
2960 	sprintf( mkbuf, "%d", kc->subtable->minkern );
2961 	label[i].text = (unichar_t *) mkbuf;
2962 	label[i].text_is_1byte = true;
2963 	label[i].text_in_resource = true;
2964 	gcd[i].gd.label = &label[i];
2965 	gcd[i].gd.pos.width = 50;
2966 	gcd[i].gd.flags = gg_enabled|gg_visible;
2967 	gcd[i].gd.popup_msg = gcd[i-1].gd.popup_msg;
2968 	gcd[i].gd.cid = CID_MinKern;
2969 	gcd[i].creator = GTextFieldCreate;
2970 	h4array[4] = &gcd[i++];
2971 
2972 	label[i].text = (unichar_t *) _("_Touching");
2973 	label[i].text_is_1byte = true;
2974 	label[i].text_in_resource = true;
2975 	gcd[i].gd.label = &label[i];
2976 	gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = 5+4;
2977 	gcd[i].gd.flags = gg_enabled|gg_visible;
2978 	if ( kc->subtable->kerning_by_touch )
2979 	    gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
2980 	gcd[i].gd.popup_msg = _(
2981 	    "Normally kerning is based on achieving a constant (optical)\n"
2982 	    "separation between glyphs, but occasionally it is desirable\n"
2983 	    "to have a kerning table where the kerning is based on the\n"
2984 	    "closest approach between two glyphs (So if the desired separ-\n"
2985 	    "ation is 0 then the glyphs will actually be touching.");
2986 	gcd[i].gd.cid = CID_Touched;
2987 	gcd[i].creator = GCheckBoxCreate;
2988 	h4array[5] = &gcd[i++];
2989 
2990 	h4array[6] = GCD_Glue; h4array[7] = NULL;
2991 
2992 	memset(&sepbox,0,sizeof(sepbox));
2993 	sepbox.gd.flags = gg_enabled|gg_visible;
2994 	sepbox.gd.u.boxelements = h4array;
2995 	sepbox.creator = GHBoxCreate;
2996 	varray[j++] = &sepbox; varray[j++] = NULL;
2997 
2998 	label[i].text = (unichar_t *) _("Only kern glyphs closer");
2999 	label[i].text_is_1byte = true;
3000 	label[i].text_in_resource = true;
3001 	gcd[i].gd.label = &label[i];
3002 	gcd[i].gd.flags = gg_enabled|gg_visible;
3003 	if ( kc->subtable->onlyCloser )
3004 	    gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
3005 	gcd[i].gd.popup_msg = _(
3006 	    "When doing autokerning, only move glyphs closer together,\n"
3007 	    "so the kerning offset will be negative.");
3008 	gcd[i].gd.cid = CID_OnlyCloser;
3009 	gcd[i].creator = GCheckBoxCreate;
3010 	h5array[0] = &gcd[i++];
3011 
3012 	label[i].text = (unichar_t *) _("Autokern new entries");
3013 	label[i].text_is_1byte = true;
3014 	label[i].text_in_resource = true;
3015 	gcd[i].gd.label = &label[i];
3016 	gcd[i].gd.flags = gg_enabled|gg_visible;
3017 	if ( !kc->subtable->dontautokern )
3018 	    gcd[i].gd.flags = gg_enabled|gg_visible|gg_cb_on;
3019 	gcd[i].gd.popup_msg = _(
3020 	    "When adding a new class provide default kerning values\n"
3021 	    "Between it and every class with which it interacts.");
3022 	gcd[i].gd.cid = CID_Autokern;
3023 	gcd[i].creator = GCheckBoxCreate;
3024 	h5array[1] = &gcd[i++]; h5array[2] = NULL;
3025 
3026 	memset(&hbox,0,sizeof(hbox));
3027 	hbox.gd.flags = gg_enabled|gg_visible;
3028 	hbox.gd.u.boxelements = h5array;
3029 	hbox.creator = GHBoxCreate;
3030 
3031 	varray[j++] = &hbox; varray[j++] = NULL;
3032 
3033     gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = GDrawPointsToPixels(gw,gcd[i-1].gd.pos.y+17);
3034     gcd[i].gd.pos.width = pos.width-20;
3035     gcd[i].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
3036     gcd[i++].creator = GLineCreate;
3037     varray[j++] = &gcd[i-1]; varray[j++] = NULL;
3038 
3039     i = AddClassList(gcd,label,i,0,&firstmi,harray1,varray1,
3040 	    sf,kc->firsts,kc->first_cnt);
3041     harrayclasses[0] = &gcd[i-1];
3042     i = AddClassList(gcd,label,i,100,&secondmi,harray2,varray2,
3043 	    sf,kc->seconds,kc->second_cnt);
3044     harrayclasses[2] = &gcd[i-1]; harrayclasses[3] = NULL;
3045 
3046     gcd[i].gd.pos.height = 20;
3047     gcd[i].gd.flags = gg_visible | gg_enabled | gg_line_vert;
3048     gcd[i++].creator = GLineCreate;
3049     harrayclasses[1] = &gcd[i-1];
3050 
3051     classbox.gd.flags = gg_enabled|gg_visible;
3052     classbox.gd.u.boxelements = harrayclasses;
3053     classbox.creator = GHBoxCreate;
3054     varray[j++] = &classbox; varray[j++] = NULL;
3055 
3056     gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = GDrawPointsToPixels(gw,gcd[i-1].gd.pos.y+27);
3057     gcd[i].gd.pos.width = pos.width-20;
3058     gcd[i].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
3059     gcd[i++].creator = GLineCreate;
3060     varray[j++] = &gcd[i-1]; varray[j++] = NULL;
3061 
3062     kcd->canceldrop = GDrawPointsToPixels(gw,KC_CANCELDROP);
3063     kcd->sbdrop = kcd->canceldrop+GDrawPointsToPixels(gw,7);
3064     gcd[i].gd.pos.width = kcd->kernw;
3065     gcd[i].gd.pos.height = kcd->kernh;
3066     gcd[i].gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
3067     gcd[i++].creator = GSpacerCreate;
3068     hvarray[0] = &gcd[i-1]; hvarray[1] = GCD_Glue; hvarray[2] = GCD_Glue; hvarray[3] = NULL;
3069 
3070     gcd[i].gd.pos.width = kcd->kernw;
3071     gcd[i].gd.pos.height = 4*kcd->kernh;
3072     gcd[i].gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
3073     gcd[i++].creator = GSpacerCreate;
3074 
3075     vi = i;
3076     gcd[i].gd.pos.width = sbsize = GDrawPointsToPixels(gw,_GScrollBar_Width);
3077     gcd[i].gd.pos.x = pos.width-sbsize;
3078     gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+8;
3079     gcd[i].gd.pos.height = pos.height-gcd[i].gd.pos.y-sbsize-kcd->sbdrop;
3080     gcd[i].gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_sb_vert;
3081     gcd[i++].creator = GScrollBarCreate;
3082     hvarray[4] = &gcd[i-2]; hvarray[5] = GCD_Glue; hvarray[6] = &gcd[i-1]; hvarray[7] = NULL;
3083 
3084     gcd[i].gd.pos.height = sbsize;
3085     gcd[i].gd.pos.y = pos.height-sbsize-8;
3086     gcd[i].gd.pos.x = 4;
3087     gcd[i].gd.pos.width = pos.width-sbsize;
3088     gcd[i].gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
3089     gcd[i++].creator = GScrollBarCreate;
3090     hvarray[8] = GCD_Glue; hvarray[9] = &gcd[i-1]; hvarray[10] = GCD_Glue; hvarray[11] = NULL;
3091     hvarray[12] = NULL;
3092     kcd->width = gcd[i-1].gd.pos.width;
3093     kcd->xstart = 5;
3094 
3095     hvbox.gd.flags = gg_enabled|gg_visible;
3096     hvbox.gd.u.boxelements = hvarray;
3097     hvbox.creator = GHVBoxCreate;
3098     varray[j++] = &hvbox; varray[j++] = NULL;
3099 
3100     gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+24+3;
3101     gcd[i].gd.pos.width = -1;
3102     gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
3103     label[i].text = (unichar_t *) _("_OK");
3104     label[i].text_is_1byte = true;
3105     label[i].text_in_resource = true;
3106     gcd[i].gd.label = &label[i];
3107     gcd[i].gd.handle_controlevent = KC_OK;
3108     gcd[i].gd.cid = CID_OK;
3109     gcd[i++].creator = GButtonCreate;
3110 
3111     gcd[i].gd.pos.x = -10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+3;
3112     gcd[i].gd.pos.width = -1;
3113     gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
3114     label[i].text = (unichar_t *) _("_Cancel");
3115     label[i].text_is_1byte = true;
3116     label[i].text_in_resource = true;
3117     gcd[i].gd.label = &label[i];
3118     gcd[i].gd.handle_controlevent = KC_Cancel;
3119     gcd[i].gd.cid = CID_Cancel;
3120     gcd[i++].creator = GButtonCreate;
3121 
3122     buttonarray[0] = GCD_Glue; buttonarray[1] = &gcd[i-2]; buttonarray[2] = GCD_Glue;
3123     buttonarray[3] = GCD_Glue; buttonarray[4] = &gcd[i-1]; buttonarray[5] = GCD_Glue;
3124     buttonarray[6] = NULL;
3125     buttonbox.gd.flags = gg_enabled|gg_visible;
3126     buttonbox.gd.u.boxelements = buttonarray;
3127     buttonbox.creator = GHBoxCreate;
3128     varray[j++] = &buttonbox; varray[j++] = NULL; varray[j++] = NULL;
3129 
3130     mainbox[0].gd.pos.x = mainbox[0].gd.pos.y = 2;
3131     mainbox[0].gd.flags = gg_enabled|gg_visible;
3132     mainbox[0].gd.u.boxelements = varray;
3133     mainbox[0].creator = GHVGroupCreate;
3134 
3135     FillShowKerningWindow(kcd, mainbox, kcd->sf, topbox);
3136     kcd->vsb = gcd[vi].ret;
3137     kcd->hsb = gcd[vi+1].ret;
3138 
3139     GHVBoxSetExpandableRow(mainbox[0].ret,6);
3140 
3141     GHVBoxSetExpandableCol(titbox.ret,gb_expandglue);
3142 
3143     GHVBoxSetExpandableCol(buttonbox.ret,gb_expandgluesame);
3144     GHVBoxSetExpandableCol(sepbox.ret,gb_expandglue);
3145 
3146     GHVBoxSetPadding(hvbox.ret,0,0);
3147     GHVBoxSetExpandableRow(hvbox.ret,1);
3148     GHVBoxSetExpandableCol(hvbox.ret,1);
3149 
3150     for ( i=0; i<2; ++i ) {
3151 	GGadgetCreateData *box = harrayclasses[2*i], **boxarray = box->gd.u.boxelements;
3152 	GHVBoxSetExpandableRow(box->ret,1);
3153 	GHVBoxSetExpandableCol(boxarray[2]->ret,1);
3154     }
3155 
3156     for ( i=0; i<2; ++i ) {
3157 	GGadget *list = GWidgetGetControl(kcd->gw,CID_ClassList+i*100);
3158 	GRect size;
3159 	memset(&size,0,sizeof(size));
3160 	size.width = (kc_width-20)/2; size.height = 6;
3161 	GGadgetSetDesiredSize(list,NULL,&size);
3162 	GMatrixEditSetUpDownVisible(list, true);
3163 	GMatrixEditSetCanUpDown(list, KCD_EnableUpDown);
3164 	GMatrixEditSetRowMotionCallback(list, KCD_RowMotion);
3165 	GMatrixEditSetBeforeDelete(list, KCD_DeleteClass);
3166     /* When the selection changes */
3167 	GMatrixEditSetOtherButtonEnable(list, KCD_ClassSelectionChanged);
3168 	GMatrixEditSetColumnCompletion(list,0,KCD_GlyphListCompletion);
3169     }
3170 
3171     KC_ShowHideKernPane(GWidgetGetControl(gw,CID_ShowHideKern),NULL);
3172     GHVBoxFitWindow(topbox[0].ret);
3173     GDrawSetVisible(kcd->gw,true);
3174 }
3175 
KCL_New(GGadget * g,GEvent * e)3176 static int KCL_New(GGadget *g, GEvent *e) {
3177     KernClassListDlg *kcld;
3178     struct subtable_data sd;
3179 
3180     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3181 	kcld = GDrawGetUserData(GGadgetGetWindow(g));
3182 	memset(&sd,0,sizeof(sd));
3183 	sd.flags = (kcld->isv ? sdf_verticalkern : sdf_horizontalkern ) |
3184 		sdf_kernclass;
3185 	SFNewLookupSubtableOfType(kcld->sf,gpos_pair,&sd,kcld->layer);
3186     }
3187 return( true );
3188 }
3189 
KCL_Delete(GGadget * g,GEvent * e)3190 static int KCL_Delete(GGadget *g, GEvent *e) {
3191     int32 len; int i,j;
3192     GTextInfo **old, **new;
3193     GGadget *list;
3194     KernClassListDlg *kcld;
3195     KernClassDlg *kcd;
3196     KernClass *p, *kc, *n;
3197 
3198     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3199 	kcld = GDrawGetUserData(GGadgetGetWindow(g));
3200 	list = GWidgetGetControl(kcld->gw,CID_List);
3201 	old = GGadgetGetList(list,&len);
3202 	new = calloc(len+1,sizeof(GTextInfo *));
3203 	p = NULL; kc = kcld->isv ? kcld->sf->vkerns : kcld->sf->kerns;
3204 	for ( i=j=0; i<len; ++i, kc = n ) {
3205 	    n = kc->next;
3206 	    if ( !old[i]->selected ) {
3207 		new[j] = malloc(sizeof(GTextInfo));
3208 		*new[j] = *old[i];
3209 		new[j]->text = u_copy(new[j]->text);
3210 		++j;
3211 		p = kc;
3212 	    } else {
3213 		if ( p!=NULL )
3214 		    p->next = n;
3215 		else if ( kcld->isv )
3216 		    kcld->sf->vkerns = n;
3217 		else
3218 		    kcld->sf->kerns = n;
3219 		kc->next = NULL;
3220 		for ( kcd=kcld->sf->kcd; kcd!=NULL && kcd->orig!=kc; kcd=kcd->next );
3221 		if ( kcd!=NULL )
3222 		    KC_DoCancel(kcd);
3223 		KernClassListFree(kc);
3224 	    }
3225 	}
3226 	new[j] = calloc(1,sizeof(GTextInfo));
3227 	GGadgetSetList(list,new,false);
3228 	GGadgetSetEnabled(GWidgetGetControl(GGadgetGetWindow(g),CID_Delete),false);
3229 	GGadgetSetEnabled(GWidgetGetControl(GGadgetGetWindow(g),CID_Edit),false);
3230     }
3231 return( true );
3232 }
3233 
KCL_Edit(GGadget * g,GEvent * e)3234 static int KCL_Edit(GGadget *g, GEvent *e) {
3235     int sel, i;
3236     KernClassListDlg *kcld;
3237     KernClass *kc;
3238 
3239     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3240 	kcld = GDrawGetUserData(GGadgetGetWindow(g));
3241 	sel = GGadgetGetFirstListSelectedItem(GWidgetGetControl(GGadgetGetWindow(g),CID_List));
3242 	if ( sel==-1 )
3243 return( true );
3244 	for ( kc=kcld->isv?kcld->sf->vkerns:kcld->sf->kerns, i=0; i<sel; kc=kc->next, ++i );
3245 	KernClassD(kc,kcld->sf,kcld->layer,kcld->isv);
3246     }
3247 return( true );
3248 }
3249 
KCL_Done(GGadget * g,GEvent * e)3250 static int KCL_Done(GGadget *g, GEvent *e) {
3251     KernClassListDlg *kcld;
3252 
3253     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3254 	kcld = GDrawGetUserData(GGadgetGetWindow(g));
3255 	//
3256 	// Update any metrics views for the splinefont.
3257 	//
3258 	if( kcld && kcld->sf )
3259 	{
3260 	    SplineFont *sf = kcld->sf;
3261 
3262 	    MVReFeatureAll( sf );
3263 	    MVReKernAll( sf );
3264 
3265 	    /* KernClass* kc = sf->kerns; */
3266 	    /* int i = 0; */
3267 	    /* for( ; kc; kc = kc->next ) */
3268 	    /* 	i++; */
3269 	    /* printf("kern count:%d\n", i ); */
3270 
3271 	    MetricsView *mv;
3272 	    for ( mv=sf->metrics; mv!=NULL; mv=mv->next )
3273 		MVSelectFirstKerningTable( mv );
3274 
3275 	}
3276 	GDrawDestroyWindow(kcld->gw);
3277     }
3278 return( true );
3279 }
3280 
KCL_SelChanged(GGadget * g,GEvent * e)3281 static int KCL_SelChanged(GGadget *g, GEvent *e) {
3282     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
3283 	KernClassListDlg *kcld = GDrawGetUserData(GGadgetGetWindow(g));
3284 	int sel = GGadgetGetFirstListSelectedItem(g);
3285 	GGadgetSetEnabled(GWidgetGetControl(kcld->gw,CID_Delete),sel!=-1);
3286 	GGadgetSetEnabled(GWidgetGetControl(kcld->gw,CID_Edit),sel!=-1);
3287     } else if ( e->type==et_controlevent && e->u.control.subtype == et_listdoubleclick ) {
3288 	KernClassListDlg *kcld = GDrawGetUserData(GGadgetGetWindow(g));
3289 	e->u.control.subtype = et_buttonactivate;
3290 	e->u.control.g = GWidgetGetControl(kcld->gw,CID_Edit);
3291 	KCL_Edit(e->u.control.g,e);
3292     }
3293 return( true );
3294 }
3295 
kcl_e_h(GWindow gw,GEvent * event)3296 static int kcl_e_h(GWindow gw, GEvent *event) {
3297     if ( event->type==et_close ) {
3298 	KernClassListDlg *kcld = GDrawGetUserData(gw);
3299 	GDrawDestroyWindow(kcld->gw);
3300     } else if ( event->type==et_char ) {
3301 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
3302 	    help("ui/mainviews/metricsview.html", "#metricsview-kernclass");
3303 return( true );
3304 	}
3305 return( false );
3306     } else if ( event->type == et_destroy ) {
3307 	KernClassListDlg *kcld = GDrawGetUserData(gw);
3308 	if ( kcld->isv )
3309 	    kcld->sf->vkcld = NULL;
3310 	else
3311 	    kcld->sf->kcld = NULL;
3312 	free(kcld);
3313     }
3314 return( true );
3315 }
3316 
ShowKernClasses(SplineFont * sf,MetricsView * mv,int layer,int isv)3317 void ShowKernClasses(SplineFont *sf,MetricsView *mv,int layer,int isv) {
3318     KernClassListDlg *kcld;
3319     GRect pos;
3320     GWindowAttrs wattrs;
3321     GGadgetCreateData gcd[7], boxes[4], *varray[10], *harray[9], *harray2[5];
3322     GTextInfo label[7];
3323     int kcl_width = KCL_Width, temp;
3324 
3325     if ( sf->kcld && !isv ) {
3326 	GDrawSetVisible(sf->kcld->gw,true);
3327 	GDrawRaise(sf->kcld->gw);
3328 return;
3329     } else if ( sf->vkcld && isv ) {
3330 	GDrawSetVisible(sf->vkcld->gw,true);
3331 	GDrawRaise(sf->vkcld->gw);
3332 return;
3333     }
3334 
3335     kcld = calloc(1,sizeof(KernClassListDlg));
3336     kcld->sf = sf;
3337     kcld->layer = layer;
3338     kcld->isv = isv;
3339     if ( isv )
3340 	sf->vkcld = kcld;
3341     else
3342 	sf->kcld = kcld;
3343 
3344     memset(&wattrs,0,sizeof(wattrs));
3345     memset(&gcd,0,sizeof(gcd));
3346     memset(&label,0,sizeof(label));
3347     memset(boxes,0,sizeof(boxes));
3348 
3349     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
3350     wattrs.event_masks = ~(1<<et_charup);
3351     wattrs.restrict_input_to_me = false;
3352     wattrs.undercursor = 1;
3353     wattrs.cursor = ct_pointer;
3354     wattrs.utf8_window_title =  isv?_("VKern By Classes"):_("Kern By Classes");
3355     wattrs.is_dlg = false;
3356     pos.x = pos.y = 0;
3357     // temp = 40 + 300*GIntGetResource(_NUM_Buttonsize)/GGadgetScale(100); // The _NUM_Buttonsize value is obsolete.
3358     temp = 40 + 300*114/GGadgetScale(100);
3359     if ( kcl_width<temp ) kcl_width = temp;
3360     pos.width = GGadgetScale(GDrawPointsToPixels(NULL,kcl_width));
3361     pos.height = GDrawPointsToPixels(NULL,KCL_Height);
3362     kcld->gw = GDrawCreateTopWindow(NULL,&pos,kcl_e_h,kcld,&wattrs);
3363 
3364     gcd[0].gd.pos.x = 5; gcd[0].gd.pos.y = 5;
3365     gcd[0].gd.pos.width = kcl_width-10; gcd[0].gd.pos.height = 7*12+10;
3366     gcd[0].gd.flags = gg_visible | gg_enabled | gg_list_multiplesel;
3367     gcd[0].gd.cid = CID_List;
3368     gcd[0].gd.u.list = KCLookupSubtableList(sf,isv);
3369     gcd[0].gd.handle_controlevent = KCL_SelChanged;
3370     gcd[0].creator = GListCreate;
3371     varray[0] = &gcd[0]; varray[1] = NULL;
3372 
3373     gcd[1].gd.pos.x = 10; gcd[1].gd.pos.y = gcd[0].gd.pos.y+gcd[0].gd.pos.height+4;
3374     gcd[1].gd.flags = gg_visible | gg_enabled;
3375     label[1].text = (unichar_t *) S_("KernClass|_New Lookup...");
3376     label[1].text_is_1byte = true;
3377     label[1].text_in_resource = true;
3378     gcd[1].gd.label = &label[1];
3379     gcd[1].gd.cid = CID_New;
3380     gcd[1].gd.handle_controlevent = KCL_New;
3381     gcd[1].creator = GButtonCreate;
3382     harray[0] = GCD_Glue; harray[1] = &gcd[1];
3383 
3384     gcd[2].gd.pos.x = 20+GIntGetResource(_NUM_Buttonsize)*100/GIntGetResource(_NUM_ScaleFactor); gcd[2].gd.pos.y = gcd[1].gd.pos.y;
3385     gcd[2].gd.flags = gg_visible;
3386     label[2].text = (unichar_t *) _("_Delete");
3387     label[2].text_is_1byte = true;
3388     label[2].text_in_resource = true;
3389     gcd[2].gd.label = &label[2];
3390     gcd[2].gd.cid = CID_Delete;
3391     gcd[2].gd.handle_controlevent = KCL_Delete;
3392     gcd[2].creator = GButtonCreate;
3393     harray[2] = GCD_Glue; harray[3] = &gcd[2];
3394 
3395     gcd[3].gd.pos.x = -10; gcd[3].gd.pos.y = gcd[1].gd.pos.y;
3396     gcd[3].gd.pos.width = -1;
3397     gcd[3].gd.flags = gg_visible;
3398     label[3].text = (unichar_t *) _("_Edit...");
3399     label[3].text_is_1byte = true;
3400     label[3].text_in_resource = true;
3401     gcd[3].gd.label = &label[3];
3402     gcd[3].gd.cid = CID_Edit;
3403     gcd[3].gd.handle_controlevent = KCL_Edit;
3404     gcd[3].creator = GButtonCreate;
3405     harray[4] = GCD_Glue; harray[5] = &gcd[3]; harray[6] = GCD_Glue; harray[7] = NULL;
3406 
3407     boxes[0].gd.flags = gg_enabled|gg_visible;
3408     boxes[0].gd.u.boxelements = harray;
3409     boxes[0].creator = GHBoxCreate;
3410     varray[2] = &boxes[0]; varray[3] = NULL;
3411 
3412     gcd[4].gd.pos.x = 10; gcd[4].gd.pos.y = gcd[1].gd.pos.y+28;
3413     gcd[4].gd.pos.width = kcl_width-20;
3414     gcd[4].gd.flags = gg_visible;
3415     gcd[4].creator = GLineCreate;
3416     varray[4] = &gcd[4]; varray[5] = NULL;
3417 
3418     gcd[5].gd.pos.x = (kcl_width-GIntGetResource(_NUM_Buttonsize))/2; gcd[5].gd.pos.y = gcd[4].gd.pos.y+7;
3419     gcd[5].gd.flags = gg_visible|gg_enabled|gg_but_default|gg_but_cancel;
3420     label[5].text = (unichar_t *) _("_Done");
3421     label[5].text_is_1byte = true;
3422     label[5].text_in_resource = true;
3423     gcd[5].gd.label = &label[5];
3424     gcd[5].gd.handle_controlevent = KCL_Done;
3425     gcd[5].creator = GButtonCreate;
3426     harray2[0] = GCD_Glue; harray2[1] = &gcd[5]; harray2[2] = GCD_Glue; harray2[3] = NULL;
3427 
3428     boxes[1].gd.flags = gg_enabled|gg_visible;
3429     boxes[1].gd.u.boxelements = harray2;
3430     boxes[1].creator = GHBoxCreate;
3431     varray[6] = &boxes[1]; varray[7] = NULL; varray[8] = NULL;
3432 
3433     boxes[2].gd.pos.x = boxes[0].gd.pos.y = 2;
3434     boxes[2].gd.flags = gg_enabled|gg_visible;
3435     boxes[2].gd.u.boxelements = varray;
3436     boxes[2].creator = GHVGroupCreate;
3437 
3438     GGadgetsCreate(kcld->gw,&boxes[2]);
3439     GHVBoxSetExpandableRow(boxes[2].ret,0);
3440     GHVBoxSetExpandableCol(boxes[0].ret,gb_expandgluesame);
3441     GHVBoxSetExpandableCol(boxes[1].ret,gb_expandgluesame);
3442     GDrawSetVisible(kcld->gw,true);
3443 }
3444 
KCLD_End(KernClassListDlg * kcld)3445 void KCLD_End(KernClassListDlg *kcld) {
3446     KernClassDlg *kcd, *kcdnext;
3447     for ( kcd= kcld->sf->kcd; kcd!=NULL; kcd=kcdnext ) {
3448 	kcdnext = kcd->next;
3449 	KC_DoCancel(kcd);
3450     }
3451     if ( kcld==NULL )
3452 return;
3453     GDrawDestroyWindow(kcld->gw);
3454 }
3455 
KCLD_MvDetach(KernClassListDlg * kcld,MetricsView * mv)3456 void KCLD_MvDetach(KernClassListDlg *kcld,MetricsView *mv) {
3457     if ( kcld==NULL )
3458 return;
3459 }
3460 
3461 /* ************************************************************************** */
3462 /* *************************** Kern Pair Dialog  **************************** */
3463 /* ************************************************************************** */
3464 
KernPairD(SplineFont * sf,SplineChar * sc1,SplineChar * sc2,int layer,int isv)3465 void KernPairD(SplineFont *sf,SplineChar *sc1,SplineChar *sc2,int layer,int isv) {
3466     GRect pos;
3467     GWindowAttrs wattrs;
3468     KernClassDlg kcd;
3469     GWindow gw;
3470     int gid;
3471 
3472     if ( sc1==NULL ) {
3473 	FontView *fv = (FontView *) sf->fv;
3474 	int start = fv->rowoff*fv->colcnt, end = start+fv->rowcnt*fv->colcnt;
3475 	int i;
3476 	for ( i=start; i<end && i<fv->b.map->enccount; ++i )
3477 	    if ( (gid=fv->b.map->map[i])!=-1 && sf->glyphs[gid]!=NULL &&
3478 		    (isv ? sf->glyphs[gid]->vkerns : sf->glyphs[gid]->kerns)!=NULL )
3479 	break;
3480 	if ( i==end || i==fv->b.map->enccount ) {
3481 	    for ( i=0; i<fv->b.map->enccount; ++i )
3482 		if ( (gid=fv->b.map->map[i])!=-1 && sf->glyphs[gid]!=NULL &&
3483 			(isv ? sf->glyphs[gid]->vkerns : sf->glyphs[gid]->kerns)!=NULL )
3484 	    break;
3485 	}
3486 	if ( i==fv->b.map->enccount ) {
3487 	    for ( i=start; i<end && i<fv->b.map->enccount; ++i )
3488 		if ( (gid=fv->b.map->map[i])!=-1 && sf->glyphs[gid]!=NULL )
3489 	    break;
3490 	    if ( i==end || i==fv->b.map->enccount ) {
3491 		for ( i=0; i<fv->b.map->enccount; ++i )
3492 		    if ( (gid=fv->b.map->map[i])!=-1 && sf->glyphs[gid]!=NULL )
3493 		break;
3494 	    }
3495 	}
3496 	if ( i!=fv->b.map->enccount )
3497 	    sc1 = sf->glyphs[gid];
3498     }
3499     if ( sc2==NULL && sc1!=NULL && (isv ? sc1->vkerns : sc1->kerns)!=NULL )
3500 	sc2 = (isv ? sc1->vkerns : sc1->kerns)->sc;
3501 
3502     memset(&kcd,0,sizeof(kcd));
3503     kcd.sf = sf;
3504     kcd.layer = layer;
3505     kcd.scf = sc1;
3506     kcd.scs = sc2;
3507     kcd.isv = isv;
3508     kcd.iskernpair = true;
3509 
3510     memset(&wattrs,0,sizeof(wattrs));
3511 
3512     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
3513     wattrs.event_masks = ~(1<<et_charup);
3514     wattrs.restrict_input_to_me = true;
3515     wattrs.undercursor = 1;
3516     wattrs.cursor = ct_pointer;
3517     wattrs.utf8_window_title = _("Kern Pair Closeup");
3518     wattrs.is_dlg = true;
3519     pos.x = pos.y = 0;
3520     pos.width = GGadgetScale(GDrawPointsToPixels(NULL,KC_Width));
3521     pos.height = GDrawPointsToPixels(NULL,KC_Height);
3522     kcd.gw = gw = GDrawCreateTopWindow(NULL,&pos,kcd_e_h,&kcd,&wattrs);
3523     kcd.canceldrop = GDrawPointsToPixels(gw,KC_CANCELDROP);
3524 
3525 
3526     FillShowKerningWindow(&kcd, NULL, kcd.sf, NULL);
3527 
3528     if ( sc1!=NULL ) {
3529 	unichar_t *utemp;
3530 	GGadgetSetTitle(GWidgetGetControl(kcd.gw,CID_First),(utemp=uc_copy(sc1->name)));
3531 	free(utemp);
3532 	KPD_BuildKernList(&kcd);
3533 	KCD_UpdateGlyph(&kcd,0);
3534     }
3535     if ( sc2!=NULL ) {
3536 	unichar_t *utemp;
3537 	GGadgetSetTitle(GWidgetGetControl(kcd.gw,CID_Second),(utemp=uc_copy(sc2->name)));
3538 	free(utemp);
3539 	KCD_UpdateGlyph(&kcd,1);
3540 	KPD_PairSearch(&kcd);
3541     }
3542 
3543     GDrawSetVisible(kcd.gw,true);
3544     while ( !kcd.done )
3545 	GDrawProcessOneEvent(NULL);
3546     GDrawSetUserData(kcd.gw,NULL);
3547     GDrawDestroyWindow(kcd.gw);
3548 }
3549 
3550 /* ************************************************************************** */
3551 /* *******************************   kerning   ****************************** */
3552 /* ************************************************************************** */
3553 
SFFindKernClass(SplineFont * sf,SplineChar * first,SplineChar * last,int * index,int allow_zero)3554 KernClass *SFFindKernClass(SplineFont *sf,SplineChar *first,SplineChar *last,
3555 	int *index,int allow_zero) {
3556     int i,f,l,pcnt = 2;
3557     KernClass *kc;
3558 
3559     /* At the first pass we check only combinations between defined classes. */
3560     /* while at the second pass class 0 is also accepted. If zero kerning values are */
3561     /* allowed, then we may need two more passes (again, first checking only defined */
3562     /* classes, and then also class 0, but this time accepting also zero offsets) */
3563     if (allow_zero) pcnt *= 2;
3564     for ( i=0; i<=pcnt; ++i ) {
3565 	for ( kc=sf->kerns; kc!=NULL; kc=kc->next ) {
3566 	    uint8 kspecd = kc->firsts[0] != NULL;
3567 	    f = KCFindName(first->name,kc->firsts ,kc->first_cnt ,i % 2);
3568 	    l = KCFindName(last->name ,kc->seconds,kc->second_cnt,i % 2);
3569 	    if ( f!=-1 && l!=-1 && ( kspecd || f!=0 || l!=0 )  ) {
3570 		if ( i > 1 || kc->offsets[f*kc->second_cnt+l]!=0 ) {
3571 		    *index = f*kc->second_cnt+l;
3572 return( kc );
3573 		}
3574 	    }
3575 	}
3576     }
3577 return( NULL );
3578 }
3579 
SFFindVKernClass(SplineFont * sf,SplineChar * first,SplineChar * last,int * index,int allow_zero)3580 KernClass *SFFindVKernClass(SplineFont *sf,SplineChar *first,SplineChar *last,
3581 	int *index,int allow_zero) {
3582     int i,f,l,pcnt = 2;
3583     KernClass *kc;
3584 
3585     if (allow_zero) pcnt *= 2;
3586     for ( i=0; i<=pcnt; ++i ) {
3587 	for ( kc=sf->vkerns; kc!=NULL; kc=kc->next ) {
3588 	    uint8 kspecd = kc->firsts[0] != NULL;
3589 	    f = KCFindName(first->name,kc->firsts ,kc->first_cnt ,i % 2);
3590 	    l = KCFindName(last->name ,kc->seconds,kc->second_cnt,i % 2);
3591 	    if ( f!=-1 && l!=-1 && ( kspecd || f!=0 || l!=0 ) ) {
3592 		if ( i > 1 || kc->offsets[f*kc->second_cnt+l]!=0 ) {
3593 		    *index = f*kc->second_cnt+l;
3594 return( kc );
3595 		}
3596 	    }
3597 	}
3598     }
3599 return( NULL );
3600 }
3601