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 "dumppfa.h"
31 #include "encoding.h"
32 #include "fontforgeui.h"
33 #include "fvfonts.h"
34 #include "gkeysym.h"
35 #include "macenc.h"
36 #include "mem.h"
37 #include "mm.h"
38 #include "parsettf.h"
39 #include "psread.h"
40 #include "sfd.h"
41 #include "splinefill.h"
42 #include "splineutil.h"
43 #include "splineutil2.h"
44 #include "tottfvar.h"
45 #include "ttf.h"
46 #include "ustring.h"
47 #include "utype.h"
48 
49 #include <locale.h>
50 #include <math.h>
51 
52 /* As far as I can tell, the CDV in AdobeSansMM is half gibberish */
53 /* This is disturbing */
54 /* But at least the CDV in Type1_supp.pdf for Myriad appears correct */
55 static char *standard_cdvs[5] = {
56 /* 0 axes? Impossible */
57     "{}",
58 /* 1 axis */
59     "{\n"
60     "  1 1 index sub 2 1 roll\n"
61     "  0 index 2 1 roll\n"
62     "  pop\n"
63     "}",
64 /* 2 axes */
65     "{\n"
66     "  1 2 index sub 1 2 index sub mul 3 1 roll\n"
67     "  1 index 1 2 index sub mul 3 1 roll\n"
68     "  1 2 index sub 1 index mul 3 1 roll\n"
69     "  1 index 1 index mul 3 1 roll\n"
70     "  pop pop\n"
71     "}",
72 /* 3 axes */
73     "{\n"
74     "  1 3 index sub 1 3 index sub mul 1 2 index sub mul 4 1 roll\n"
75     "  2 index 1 3 index sub mul 1 2 index sub mul 4 1 roll\n"
76     "  1 3 index sub 2 index mul 1 2 index sub mul 4 1 roll\n"
77     "  2 index 2 index mul 1 2 index sub mul 4 1 roll\n"
78     "  1 3 index sub 1 3 index sub mul 1 index mul 4 1 roll\n"
79     "  2 index 1 3 index sub mul 1 index mul 4 1 roll\n"
80     "  1 3 index sub 2 index mul 1 index mul 4 1 roll\n"
81     "  2 index 2 index mul 1 index mul 4 1 roll\n"
82     "  pop pop pop\n"
83     "}",
84 /* 4 axes */
85 /* This requires too big a string. We must build it at runtime */
86     NULL
87 };
88 static char *cdv_4axis[3] = {
89     "{\n"
90     "  1 4 index sub 1 4 index sub mul 1 3 index sub mul 1 2 index sub mul 5 1 roll\n"
91     "  3 index 1 4 index sub mul 1 3 index sub mul 1 2 index sub mul 5 1 roll\n"
92     "  1 4 index sub 3 index mul 1 3 index sub mul 1 2 index sub mul 5 1 roll\n"
93     "  3 index 3 index mul 1 3 index sub mul 1 2 index sub mul 5 1 roll\n"
94     "  1 4 index sub 1 4 index sub mul 2 index mul 1 2 index sub mul 5 1 roll\n"
95     "  3 index 1 4 index sub mul 2 index mul 1 2 index sub mul 5 1 roll\n",
96     "  1 4 index sub 3 index mul 2 index mul 1 2 index sub mul 5 1 roll\n"
97     "  3 index 3 index mul 2 index mul 1 2 index sub mul 5 1 roll\n"
98     "  1 4 index sub 1 4 index sub mul 1 3 index sub mul 1 index mul 5 1 roll\n"
99     "  3 index 1 4 index sub mul 1 3 index sub mul 1 index mul 5 1 roll\n"
100     "  1 4 index sub 3 index mul 1 3 index sub mul 1 index mul 5 1 roll\n",
101     "  3 index 3 index mul 1 3 index sub mul 1 index mul 5 1 roll\n"
102     "  1 4 index sub 1 4 index sub mul 2 index mul 1 index mul 5 1 roll\n"
103     "  3 index 1 4 index sub mul 2 index mul 1 index mul 5 1 roll\n"
104     "  1 4 index sub 3 index mul 2 index mul 1 index mul 5 1 roll\n"
105     "  3 index 3 index mul 2 index mul 1 index mul 5 1 roll\n"
106     "  pop pop pop pop\n"
107     "}"
108 };
109 
110 static char *axistablab[] = { N_("Axis 1"), N_("Axis 2"), N_("Axis 3"), N_("Axis 4") };
111 
ExecConvertDesignVector(real * designs,int dcnt,char * ndv,char * cdv,real * stack)112 static int ExecConvertDesignVector(real *designs, int dcnt, char *ndv, char *cdv,
113 	real *stack) {
114     char *temp, dv[101];
115     int j, len, cnt;
116 
117     /* PostScript parses things in "C" locale too */
118     locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
119     switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
120     len = 0;
121     for ( j=0; j<dcnt; ++j ) {
122 	sprintf(dv+len, "%g ", (double) designs[j]);
123 	len += strlen(dv+len);
124     }
125     switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
126 
127     temp = malloc(len+strlen(ndv)+strlen(cdv)+20);
128     strcpy(temp,dv);
129     /*strcpy(temp+len++," ");*/		/* dv always will end in a space */
130 
131     while ( isspace(*ndv)) ++ndv;
132     if ( *ndv=='{' )
133 	++ndv;
134     strcpy(temp+len,ndv);
135     len += strlen(temp+len);
136     while ( len>0 && (temp[len-1]==' '||temp[len-1]=='\n') ) --len;
137     if ( len>0 && temp[len-1]=='}' ) --len;
138 
139     while ( isspace(*cdv)) ++cdv;
140     if ( *cdv=='{' )
141 	++cdv;
142     strcpy(temp+len,cdv);
143     len += strlen(temp+len);
144     while ( len>0 && (temp[len-1]==' '||temp[len-1]=='\n') ) --len;
145     if ( len>0 && temp[len-1]=='}' ) --len;
146 
147     cnt = EvaluatePS(temp,stack,MmMax);
148     free(temp);
149 return( cnt );
150 }
151 
StandardPositions(MMSet * mm,int instance_count,int axis_count,int isapple)152 static int StandardPositions(MMSet *mm,int instance_count, int axis_count,int isapple) {
153     int i,j,factor,v;
154 
155     if ( !isapple ) {
156 	for ( i=0; i<instance_count; ++i ) {
157 	    for ( j=0; j<axis_count; ++j )
158 		if ( mm->positions[i*mm->axis_count+j]!= ( (i&(1<<j)) ? 1 : 0 ))
159     return( false );
160 	}
161     } else {
162 	for ( i=0; i<instance_count; ++i ) {
163 	    factor = 1;
164 	    for ( j=0; j<axis_count; ++j ) {
165 		v = (i/factor)%3 -1;
166 		if ( mm->positions[i*mm->axis_count+j]!= v )
167 return( false );
168 		factor *= 3;
169 	    }
170 	}
171     }
172 return( true );
173 }
174 
OrderedPositions(MMSet * mm,int instance_count,int isapple)175 static int OrderedPositions(MMSet *mm,int instance_count, int isapple) {
176     /* For a 1 axis system, check that the positions are ordered */
177     int i;
178 
179     if ( mm->positions[0]!=isapple?-1:0 )		/* must start at 0 */
180 return( false );
181     if ( mm->positions[(instance_count-1)*4]!=1 )	/* and end at 1 */
182 return( false );
183     for ( i=1; i<mm->instance_count; ++i )
184 	if ( mm->positions[i*mm->axis_count]<=mm->positions[(i-1)*mm->axis_count] )
185 return( false );
186 
187 return( true );
188 }
189 
MMDesignCoords(MMSet * mm)190 static unichar_t *MMDesignCoords(MMSet *mm) {
191     char buffer[80], *pt;
192     int i;
193     real axiscoords[4];
194 
195     if ( mm->instance_count!=(1<<mm->axis_count) ||
196 	    !StandardPositions(mm,mm->instance_count,mm->axis_count,false))
197 return( uc_copy(""));
198     MMWeightsUnMap(mm->defweights,axiscoords,mm->axis_count);
199     pt = buffer;
200     for ( i=0; i<mm->axis_count; ++i ) {
201 	sprintf( pt,"%g ", (double) MMAxisUnmap(mm,i,axiscoords[i]));
202 	pt += strlen(pt);
203     }
204     pt[-1] = ' ';
205 return( uc_copy( buffer ));
206 }
207 
DoDelta(int16 ** deltas,int pt,int is_y,real * blends,int instance_count)208 static real DoDelta(int16 **deltas,int pt,int is_y,real *blends,int instance_count) {
209     real diff = 0;
210     int j;
211 
212     for ( j=0; j<instance_count; ++j ) {
213 	if ( blends[j]!=0 && deltas[2*j+is_y]!=NULL )
214 	    diff += blends[j]*deltas[2*j+is_y][pt];
215     }
216 return( diff );
217 }
218 
DistortChar(SplineFont * sf,MMSet * mm,int gid,real * blends)219 static void DistortChar(SplineFont *sf,MMSet *mm,int gid,real *blends) {
220     int i,j,ptcnt;
221     int16 **deltas;
222     SplineSet *ss;
223     SplinePoint *sp;
224     RefChar *ref;
225     SplineChar *sc = sf->glyphs[gid];
226     Spline *s, *first;
227 
228     if ( sc==NULL )
229 return;
230     deltas = SCFindDeltas(mm,gid,&ptcnt);
231     if ( deltas==NULL )
232 return;
233     /* I never delta the left side bearing or top */
234     sc->width += DoDelta(deltas,ptcnt-3,0,blends,mm->instance_count);
235     sc->vwidth += DoDelta(deltas,ptcnt-1,1,blends,mm->instance_count);
236     if ( sc->layers[ly_fore].refs!=NULL ) {
237 	for ( i=0,ref = sc->layers[ly_fore].refs; ref!=NULL; ref=ref->next, ++i ) {
238 	    ref->transform[4] += DoDelta(deltas,i,0,blends,mm->instance_count);
239 	    ref->transform[5] += DoDelta(deltas,i,1,blends,mm->instance_count);
240 	}
241     } else {
242 	for ( ss=sc->layers[ly_fore].splines; ss!=NULL; ss=ss->next ) {
243 	    for ( sp=ss->first;; ) {
244 		if ( sp->ttfindex!=0xffff ) {
245 		    sp->me.x += DoDelta(deltas,sp->ttfindex,0,blends,mm->instance_count);
246 		    sp->me.y += DoDelta(deltas,sp->ttfindex,1,blends,mm->instance_count);
247 		}
248 		if ( sp->nextcpindex!=0xffff ) {
249 		    sp->nextcp.x += DoDelta(deltas,sp->nextcpindex,0,blends,mm->instance_count);
250 		    sp->nextcp.y += DoDelta(deltas,sp->nextcpindex,1,blends,mm->instance_count);
251 		} else
252 		    sp->nextcp = sp->me;
253 		if ( sp->next!=NULL )
254 		    sp->next->to->prevcp = sp->nextcp;
255 		if ( sp->next==NULL )
256 	    break;
257 		sp = sp->next->to;
258 		if ( sp==ss->first )
259 	    break;
260 	    }
261 	    for ( sp=ss->first;; ) {
262 		if ( sp->ttfindex==0xffff ) {
263 		    sp->me.x = (sp->prevcp.x+sp->nextcp.x)/2;
264 		    sp->me.y = (sp->prevcp.y+sp->nextcp.y)/2;
265 		}
266 		if ( sp->next==NULL )
267 	    break;
268 		sp = sp->next->to;
269 		if ( sp==ss->first )
270 	    break;
271 	    }
272 	    first = NULL;
273 	    for ( s=ss->first->next; s!=NULL && s!=first; s=s->to->next ) {
274 		SplineRefigure(s);
275 		if ( first==NULL ) first = s;
276 	    }
277 	}
278     }
279     for ( j=0; j<2*mm->instance_count; ++j )
280 	free( deltas[j]);
281     free(deltas);
282 }
283 
DistortCvt(struct ttf_table * cvt,MMSet * mm,real * blends)284 static void DistortCvt(struct ttf_table *cvt,MMSet *mm,real *blends) {
285     int i,j,ptcnt;
286     real diff;
287     int16 **deltas;
288 
289     deltas = CvtFindDeltas(mm,&ptcnt);
290     if ( deltas==NULL )
291 return;
292     for ( i=0; i<ptcnt; ++i ) {
293 	diff = 0;
294 	for ( j=0; j<mm->instance_count; ++j ) {
295 	    if ( blends[j]!=0 && deltas[j]!=NULL )
296 		diff += blends[j]*deltas[j][i];
297 	}
298 	memputshort(cvt->data,2*i,memushort(cvt->data,cvt->len,2*i)+rint(diff));
299     }
300     for ( j=0; j<mm->instance_count; ++j )
301 	free( deltas[j]);
302     free(deltas);
303 }
304 
MakeAppleBlend(FontView * fv,MMSet * mm,real * blends,real * normalized)305 static void MakeAppleBlend(FontView *fv,MMSet *mm,real *blends,real *normalized) {
306     SplineFont *base = mm->normal;
307     SplineFont *sf = _MMNewFont(mm,-2,base->familyname,normalized);
308     int i;
309     struct ttf_table *tab, *cvt=NULL, *last=NULL, *cur;
310     RefChar *ref;
311 
312     sf->mm = NULL;
313     for ( i=0; i<base->glyphcnt && i<sf->glyphcnt; ++i ) if ( base->glyphs[i]!=NULL ) {
314 	sf->glyphs[i] = SplineCharCopy(base->glyphs[i],base,NULL);
315 	for ( ref=sf->glyphs[i]->layers[ly_fore].refs; ref!=NULL; ref=ref->next )
316 	    ref->sc = NULL;
317 	sf->glyphs[i]->orig_pos = i;
318 	DistortChar(sf,mm,i,blends);
319     }
320     for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
321 	ttfFixupRef(sf->glyphs,i);
322     for ( tab=base->ttf_tables; tab!=NULL; tab=tab->next ) {
323 	cur = chunkalloc(sizeof(struct ttf_table));
324 	cur->tag = tab->tag;
325 	cur->len = tab->len;
326 	cur->data = malloc(tab->len);
327 	memcpy(cur->data,tab->data,tab->len);
328 	if ( cur->tag==CHR('c','v','t',' '))
329 	    cvt = cur;
330 	if ( last==NULL )
331 	    sf->ttf_tables = cur;
332 	else
333 	    last->next = cur;
334 	last = cur;
335     }
336     if ( cvt!=NULL )
337 	DistortCvt(cvt,mm,blends);
338     /* I don't know how to blend kerns */
339     /* Apple's Skia has 5 kerning classes (one for the base font and one for */
340     /*  some of the instances) and the classes have different glyph classes */
341     /* I can't make a kern class out of them. I suppose I could generate a bunch */
342     /*  of kern pairs, but ug. */
343     /* Nor is it clear whether the kerning info is a delta or not */
344 
345     sf->changed = true;
346     EncMapFree(sf->map);
347     sf->map = EncMapFromEncoding(sf,fv->b.map->enc);
348     FontViewCreate(sf,false);
349 }
350 
351 struct mmcb {
352     int done;
353     GWindow gw;
354     MMSet *mm;
355     FontView *fv;
356     int tonew;
357 };
358 
359 #define CID_Explicit		6001
360 #define	CID_ByDesign		6002
361 #define CID_NewBlends		6003
362 #define CID_NewDesign		6004
363 #define CID_Knowns		6005
364 
MMCB_KnownValues(MMSet * mm)365 static GTextInfo *MMCB_KnownValues(MMSet *mm) {
366     GTextInfo *ti = calloc(mm->named_instance_count+2,sizeof(GTextInfo));
367     int i;
368 
369     ti[0].text = uc_copy(" --- ");
370     ti[0].bg = ti[0].fg = COLOR_DEFAULT;
371     for ( i=0; i<mm->named_instance_count; ++i ) {
372 	ti[i+1].text = (unichar_t *) PickNameFromMacName(mm->named_instances[i].names);
373 	ti[i+1].text_is_1byte = true;
374 	ti[i+1].bg = ti[i+1].fg = COLOR_DEFAULT;
375     }
376 return( ti );
377 }
378 
MMCB_PickedKnown(GGadget * g,GEvent * e)379 static int MMCB_PickedKnown(GGadget *g, GEvent *e) {
380     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
381 	struct mmcb *mmcb = GDrawGetUserData(GGadgetGetWindow(g));
382 	int which = GGadgetGetFirstListSelectedItem(g);
383 	char buffer[24];
384 	int i;
385 	unichar_t *temp;
386 
387 	--which;
388 	if ( which<0 )
389 return( true );
390 	for ( i=0; i<mmcb->mm->axis_count; ++i ) {
391 	    sprintf( buffer, "%.4g", (double) mmcb->mm->named_instances[which].coords[i]);
392 	    temp = uc_copy(buffer);
393 	    GGadgetSetTitle(GWidgetGetControl(mmcb->gw,1000+i),temp);
394 	    free(temp);
395 	}
396     }
397 return( true );
398 }
399 
MMCB_Changed(GGadget * g,GEvent * e)400 static int MMCB_Changed(GGadget *g, GEvent *e) {
401     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
402 	GWindow gw = GGadgetGetWindow(g);
403 	int explicitblends = GGadgetIsChecked(GWidgetGetControl(gw,CID_Explicit));
404 	GGadgetSetEnabled(GWidgetGetControl(gw,CID_NewBlends),explicitblends);
405 	GGadgetSetEnabled(GWidgetGetControl(gw,CID_NewDesign),!explicitblends);
406     }
407 return( true );
408 }
409 
GetWeights(GWindow gw,real blends[MmMax],MMSet * mm,int instance_count,int axis_count)410 static int GetWeights(GWindow gw, real blends[MmMax], MMSet *mm,
411 	int instance_count, int axis_count) {
412     int explicitblends = GGadgetIsChecked(GWidgetGetControl(gw,CID_Explicit));
413     const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(gw,
414 	    explicitblends?CID_NewBlends:CID_NewDesign)), *upt;
415     unichar_t *uend;
416     int i;
417     real sum;
418 
419     sum = 0;
420     for ( i=0, upt = ret; i<instance_count && *upt; ++i ) {
421 	blends[i] = u_strtod(upt,&uend);
422 	sum += blends[i];
423 	if ( upt==uend )
424     break;
425 	upt = uend;
426 	while ( *upt==',' || *upt==' ' ) ++upt;
427     }
428     if ( (explicitblends && i!=instance_count ) ||
429 	    (!explicitblends && i!=axis_count ) ||
430 	    *upt!='\0' ) {
431 	ff_post_error(_("Bad MM Weights"),_("Incorrect number of instances weights, or illegal numbers"));
432 return(false);
433     }
434     if ( explicitblends ) {
435 	if ( sum<.99 || sum>1.01 ) {
436 	    ff_post_error(_("Bad MM Weights"),_("The weights for the default version of the font must sum to 1.0"));
437 return(false);
438 	}
439     } else {
440 	i = ExecConvertDesignVector(blends, i, mm->ndv, mm->cdv,
441 		blends);
442 	if ( i!=instance_count ) {
443 	    ff_post_error(_("Bad MM Weights"),_("The results produced by applying the NormalizeDesignVector and ConvertDesignVector functions were not the results expected. You may need to change these functions"));
444 return(false);
445 	}
446     }
447 return( true );
448 }
449 
MMCB_OKApple(GGadget * g,GEvent * e)450 static int MMCB_OKApple(GGadget *g, GEvent *e) {
451     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
452 	struct mmcb *mmcb = GDrawGetUserData(GGadgetGetWindow(g));
453 	real newcoords[4];
454 	int i, j, k, err=false;
455 	real blends[AppleMmMax];
456 	MMSet *mm = mmcb->mm;
457 
458 	for ( i=0; i<mm->axis_count; ++i )
459 	    newcoords[i] = rint(GetReal8(mmcb->gw,1000+i,_(axistablab[i]),&err)*8096)/8096;
460 	if ( err )
461 return( true );
462 	/* Now normalize each */
463 	for ( i=0; i<mm->axis_count; ++i ) {
464 	    for ( j=1; j<mm->axismaps[i].points; ++j ) {
465 		if ( newcoords[i]<=mm->axismaps[i].designs[j] || j==mm->axismaps[i].points-1 ) {
466 		    if ( mm->axismaps[i].designs[j]==mm->axismaps[i].designs[j-1] )
467 			newcoords[i] = mm->axismaps[i].blends[j];
468 		    else
469 			newcoords[i] = mm->axismaps[i].blends[j-1] +
470 				(newcoords[i]-mm->axismaps[i].designs[j-1])/
471 			        (mm->axismaps[i].designs[j]-mm->axismaps[i].designs[j-1]) *
472 			        (mm->axismaps[i].blends[j]-mm->axismaps[i].blends[j-1]);
473 		    newcoords[i] = rint(8096*newcoords[i])/8096;	/* Apple's fixed numbers have a fair amount of rounding error */
474 	    break;
475 		}
476 	    }
477 	}
478 	/* Now figure out the contribution of each design */
479 	for ( k=0; k<mm->instance_count; ++k ) {
480 	    real factor = 1.0;
481 	    for ( i=0; i<mm->axis_count; ++i ) {
482 		if ( (newcoords[i]<=0 && mm->positions[k*mm->axis_count+i]>0) ||
483 			(newcoords[i]>=0 && mm->positions[k*mm->axis_count+i]<0)) {
484 		    factor = 0;
485 	    break;
486 		}
487 		if ( newcoords[i]==0 )
488 	    continue;
489 		if ( newcoords[i]<0 )
490 		    factor *= -newcoords[i];
491 		else
492 		    factor *= newcoords[i];
493 	    }
494 	    blends[k] = factor;
495 	}
496 	MakeAppleBlend(mmcb->fv,mm,blends,newcoords);
497 	mmcb->done = true;
498     }
499 return( true );
500 }
501 
MMCB_OK(GGadget * g,GEvent * e)502 static int MMCB_OK(GGadget *g, GEvent *e) {
503     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
504 	struct mmcb *mmcb = GDrawGetUserData(GGadgetGetWindow(g));
505 	real blends[MmMax];
506 
507 	if ( !GetWeights(mmcb->gw, blends, mmcb->mm, mmcb->mm->instance_count, mmcb->mm->axis_count))
508 return( true );
509 	MMCreateBlendedFont(mmcb->mm,(FontViewBase *) mmcb->fv,blends,mmcb->tonew );
510     }
511 return( true );
512 }
513 
MMCB_Cancel(GGadget * g,GEvent * e)514 static int MMCB_Cancel(GGadget *g, GEvent *e) {
515     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
516 	struct mmcb *mmcb = GDrawGetUserData(GGadgetGetWindow(g));
517 	mmcb->done = true;
518     }
519 return( true );
520 }
521 
mmcb_e_h(GWindow gw,GEvent * event)522 static int mmcb_e_h(GWindow gw, GEvent *event) {
523     if ( event->type==et_close ) {
524 	struct mmcb *mmcb = GDrawGetUserData(gw);
525 	mmcb->done = true;
526     } else if ( event->type==et_char ) {
527 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
528 	    help("ui/menus/mmmenu.html", NULL);
529 return( true );
530 	}
531 return( false );
532     }
533 return( true );
534 }
535 
GCDFillupMacWeights(GGadgetCreateData * gcd,GTextInfo * label,int k,char * axisnames[4],char axisval[4][24],real * defcoords,int axis_count,MMSet * mm)536 static int GCDFillupMacWeights(GGadgetCreateData *gcd, GTextInfo *label, int k,
537 	char *axisnames[4], char axisval[4][24],
538 	real *defcoords,int axis_count,MMSet *mm) {
539     int i;
540     char *an;
541     char axisrange[80];
542 
543     for ( i=0; i<axis_count; ++i ) {
544 	sprintf( axisrange, " [%.4g %.4g %.4g]", (double) mm->axismaps[i].min,
545 		(double) mm->axismaps[i].def, (double) mm->axismaps[i].max );
546 	an = PickNameFromMacName(mm->axismaps[i].axisnames);
547 	if ( an==NULL )
548 	    an = copy(mm->axes[i]);
549 	axisnames[i] = malloc(strlen(axisrange)+3+strlen(an));
550 	strcpy(axisnames[i],an);
551 	strcat(axisnames[i],axisrange);
552 	sprintf(axisval[i],"%.4g", (double) defcoords[i]);
553 	free(an);
554     }
555     for ( ; i<4; ++i ) {
556 	axisnames[i] = _(axistablab[i]);
557 	axisval[i][0] = '\0';
558     }
559 
560     for ( i=0; i<4; ++i ) {
561 	label[k].text = (unichar_t *) axisnames[i];
562 	label[k].text_is_1byte = true;
563 	gcd[k].gd.label = &label[k];
564 	gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = k==0 ? 4 : gcd[k-1].gd.pos.y+28;
565 	gcd[k].gd.flags = i<axis_count ? (gg_visible | gg_enabled) : gg_visible;
566 	gcd[k++].creator = GLabelCreate;
567 
568 	label[k].text = (unichar_t *) axisval[i];
569 	label[k].text_is_1byte = true;
570 	gcd[k].gd.label = &label[k];
571 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+12;
572 	gcd[k].gd.flags = gcd[k-1].gd.flags;
573 	gcd[k].gd.cid = 1000+i;
574 	gcd[k++].creator = GTextFieldCreate;
575     }
576 return( k );
577 }
578 
MMChangeBlend(MMSet * mm,FontView * fv,int tonew)579 void MMChangeBlend(MMSet *mm,FontView *fv,int tonew) {
580     char buffer[MmMax*20], *pt;
581     unichar_t ubuf[MmMax*20];
582     int i, k, j, def_name;
583     struct mmcb mmcb;
584     GRect pos;
585     GWindow gw;
586     GWindowAttrs wattrs;
587     GGadgetCreateData gcd[14];
588     GTextInfo label[14];
589     unichar_t *utemp;
590     char axisval[4][24];
591     char *axisnames[4];
592     real defcoords[4];
593 
594     if ( mm==NULL )
595 return;
596 
597     memset(&mmcb,0,sizeof(mmcb));
598     mmcb.mm = mm;
599     mmcb.fv = fv;
600     mmcb.tonew = tonew;
601 
602     memset(&wattrs,0,sizeof(wattrs));
603     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
604     wattrs.event_masks = ~(1<<et_charup);
605     wattrs.is_dlg = true;
606     wattrs.restrict_input_to_me = true;
607     wattrs.undercursor = 1;
608     wattrs.cursor = ct_pointer;
609     wattrs.utf8_window_title = tonew ? _("Blend to New Font"):_("MM Change Def Weights");
610     pos.x = pos.y = 0;
611 
612     if ( !mm->apple ) {
613 	pt = buffer;
614 	for ( i=0; i<mm->instance_count; ++i ) {
615 	    sprintf( pt, "%g ", (double) mm->defweights[i]);
616 	    pt += strlen(pt);
617 	}
618 	if ( pt>buffer )
619 	    pt[-2] = '\0';
620 	uc_strcpy(ubuf,buffer);
621 
622 	pos.width =GDrawPointsToPixels(NULL,GGadgetScale(270));
623 	pos.height = GDrawPointsToPixels(NULL,200);
624 	mmcb.gw = gw = GDrawCreateTopWindow(NULL,&pos,mmcb_e_h,&mmcb,&wattrs);
625 
626 	memset(&gcd,0,sizeof(gcd));
627 	memset(&label,0,sizeof(label));
628 
629 	k=0;
630 /* GT: The following strings should be concatenated together, the result */
631 /* GT: translated, and then broken into lines by hand. I'm sure it would */
632 /* GT: be better to specify this all as one string, but my widgets won't support */
633 /* GT: that */
634 	label[k].text = (unichar_t *) (tonew ? _("You may specify the new instance of this font") : _("You may change the default instance of this font"));
635 	label[k].text_is_1byte = true;
636 	gcd[k].gd.label = &label[k];
637 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = 10;
638 	gcd[k].gd.flags = gg_visible | gg_enabled;
639 	gcd[k++].creator = GLabelCreate;
640 
641 	label[k].text = (unichar_t *) _("either by explicitly entering the contribution");
642 	label[k].text_is_1byte = true;
643 	gcd[k].gd.label = &label[k];
644 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+13;
645 	gcd[k].gd.flags = gg_visible | gg_enabled;
646 	gcd[k++].creator = GLabelCreate;
647 
648 	label[k].text = (unichar_t *) _("of each master design, or by entering the design");
649 	label[k].text_is_1byte = true;
650 	gcd[k].gd.label = &label[k];
651 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+13;
652 	gcd[k].gd.flags = gg_visible | gg_enabled;
653 	gcd[k++].creator = GLabelCreate;
654 
655 	label[k].text = (unichar_t *) _("values for each axis");
656 	label[k].text_is_1byte = true;
657 	gcd[k].gd.label = &label[k];
658 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+13;
659 	gcd[k].gd.flags = gg_visible | gg_enabled;
660 	gcd[k++].creator = GLabelCreate;
661 
662 	label[k].text = (unichar_t *) _("Contribution of each master design");
663 	label[k].text_is_1byte = true;
664 	gcd[k].gd.label = &label[k];
665 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+16;
666 	gcd[k].gd.flags = gg_visible | gg_enabled | gg_cb_on;
667 	gcd[k].gd.cid = CID_Explicit;
668 	gcd[k].gd.handle_controlevent = MMCB_Changed;
669 	gcd[k++].creator = GRadioCreate;
670 
671 	label[k].text = (unichar_t *) _("Design Axis Values");
672 	label[k].text_is_1byte = true;
673 	gcd[k].gd.label = &label[k];
674 	gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+45;
675 	gcd[k].gd.flags = gg_visible | gg_enabled;
676 	gcd[k].gd.cid = CID_ByDesign;
677 	gcd[k].gd.handle_controlevent = MMCB_Changed;
678 	gcd[k++].creator = GRadioCreate;
679 
680 	label[k].text = ubuf;
681 	gcd[k].gd.label = &label[k];
682 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-2].gd.pos.y+18;
683 	gcd[k].gd.pos.width = 240;
684 	gcd[k].gd.flags = gg_visible | gg_enabled;
685 	gcd[k].gd.cid = CID_NewBlends;
686 	gcd[k++].creator = GTextFieldCreate;
687 
688 	label[k].text = utemp = MMDesignCoords(mm);
689 	gcd[k].gd.label = &label[k];
690 	gcd[k].gd.pos.x = 15; gcd[k].gd.pos.y = gcd[k-2].gd.pos.y+18;
691 	gcd[k].gd.pos.width = 240;
692 	gcd[k].gd.flags = gg_visible;
693 	gcd[k].gd.cid = CID_NewDesign;
694 	gcd[k++].creator = GTextFieldCreate;
695 
696 	gcd[k].gd.pos.x = 30-3; gcd[k].gd.pos.y = GDrawPixelsToPoints(NULL,pos.height)-35-3;
697 	gcd[k].gd.pos.width = -1;
698 	gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
699 	label[k].text = (unichar_t *) _("_OK");
700 	label[k].text_is_1byte = true;
701 	label[k].text_in_resource = true;
702 	gcd[k].gd.label = &label[k];
703 	gcd[k].gd.handle_controlevent = MMCB_OK;
704 	gcd[k++].creator = GButtonCreate;
705 
706 	gcd[k].gd.pos.x = -30; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
707 	gcd[k].gd.pos.width = -1;
708 	gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
709 	label[k].text = (unichar_t *) _("_Cancel");
710 	label[k].text_is_1byte = true;
711 	label[k].text_in_resource = true;
712 	gcd[k].gd.label = &label[k];
713 	gcd[k].gd.handle_controlevent = MMCB_Cancel;
714 	gcd[k++].creator = GButtonCreate;
715 
716 	gcd[k].gd.pos.x = 2; gcd[k].gd.pos.y = 2;
717 	gcd[k].gd.pos.width = pos.width-4; gcd[k].gd.pos.height = pos.height-4;
718 	gcd[k].gd.flags = gg_enabled | gg_visible | gg_pos_in_pixels;
719 	gcd[k].creator = GGroupCreate;
720 
721 	GGadgetsCreate(gw,gcd);
722 	free(utemp);
723     } else {
724 	pos.width =GDrawPointsToPixels(NULL,GGadgetScale(270));
725 	pos.height = GDrawPointsToPixels(NULL,200);
726 	mmcb.gw = gw = GDrawCreateTopWindow(NULL,&pos,mmcb_e_h,&mmcb,&wattrs);
727 
728 	memset(&gcd,0,sizeof(gcd));
729 	memset(&label,0,sizeof(label));
730 
731 	memset(defcoords,0,sizeof(defcoords));
732 	for ( i=0; i<mm->axis_count; ++i )
733 	    defcoords[i] = mm->axismaps[i].def;
734 	def_name = -1;
735 	for ( i=0; i<mm->named_instance_count; ++i ) {
736 	    for ( j=0; j<mm->axis_count; ++j )
737 		if ( !RealNear(mm->named_instances[i].coords[j],defcoords[j]))
738 	    break;
739 	    if ( j==mm->axis_count ) {
740 		def_name = i;
741 	break;
742 	    }
743 	}
744 
745 	k=0;
746 	k = GCDFillupMacWeights(gcd,label,k,axisnames,axisval,defcoords,
747 		mm->axis_count,mm);
748 
749 	gcd[k].gd.pos.x = 130; gcd[k].gd.pos.y = gcd[k-4].gd.pos.y-12;
750 	gcd[k].gd.flags = gg_visible | gg_enabled;
751 	if ( mm->named_instance_count==0 )
752 	    gcd[k].gd.flags = 0;
753 	gcd[k].gd.u.list = MMCB_KnownValues(mm);
754 	if ( def_name!=-1 )
755 	    gcd[k].gd.u.list[def_name+1].selected = true;
756 	gcd[k].gd.cid = CID_Knowns;
757 	gcd[k].gd.handle_controlevent = MMCB_PickedKnown;
758 	gcd[k++].creator = GListButtonCreate;
759 
760 	gcd[k].gd.pos.x = 30-3; gcd[k].gd.pos.y = GDrawPixelsToPoints(NULL,pos.height)-35-3;
761 	gcd[k].gd.pos.width = -1;
762 	gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
763 	label[k].text = (unichar_t *) _("_OK");
764 	label[k].text_is_1byte = true;
765 	label[k].text_in_resource = true;
766 	gcd[k].gd.label = &label[k];
767 	gcd[k].gd.handle_controlevent = MMCB_OKApple;
768 	gcd[k++].creator = GButtonCreate;
769 
770 	gcd[k].gd.pos.x = -30; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
771 	gcd[k].gd.pos.width = -1;
772 	gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
773 	label[k].text = (unichar_t *) _("_Cancel");
774 	label[k].text_is_1byte = true;
775 	label[k].text_in_resource = true;
776 	gcd[k].gd.label = &label[k];
777 	gcd[k].gd.handle_controlevent = MMCB_Cancel;
778 	gcd[k++].creator = GButtonCreate;
779 
780 	gcd[k].gd.pos.x = 2; gcd[k].gd.pos.y = 2;
781 	gcd[k].gd.pos.width = pos.width-4; gcd[k].gd.pos.height = pos.height-4;
782 	gcd[k].gd.flags = gg_enabled | gg_visible | gg_pos_in_pixels;
783 	gcd[k++].creator = GGroupCreate;
784 
785 	GGadgetsCreate(gw,gcd);
786 	for ( i=0; i<mm->axis_count; ++i )
787 	    free(axisnames[i]);
788 	GTextInfoListFree(gcd[k-4].gd.u.list);
789 	GWidgetIndicateFocusGadget(gcd[1].ret);
790     }
791 
792     GDrawSetVisible(gw,true);
793 
794     while ( !mmcb.done )
795 	GDrawProcessOneEvent(NULL);
796     GDrawDestroyWindow(gw);
797 }
798 
799 GTextInfo axiscounts[] = {
800     { (unichar_t *) "1", NULL, 0, 0, (void *) 1, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
801     { (unichar_t *) "2", NULL, 0, 0, (void *) 2, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
802     { (unichar_t *) "3", NULL, 0, 0, (void *) 3, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
803     { (unichar_t *) "4", NULL, 0, 0, (void *) 4, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
804     GTEXTINFO_EMPTY
805 };
806 
807 /* These names are fixed by Adobe & Apple and are not subject to translation */
808 GTextInfo axistypes[] = {
809     { (unichar_t *) "Weight",		NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
810     { (unichar_t *) "Width",		NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
811     { (unichar_t *) "OpticalSize",	NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
812     { (unichar_t *) "Slant",		NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
813     GTEXTINFO_EMPTY
814 };
815 
816 GTextInfo mastercounts[] = {
817     { (unichar_t *) "1", NULL, 0, 0, (void *) 1, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
818     { (unichar_t *) "2", NULL, 0, 0, (void *) 2, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
819     { (unichar_t *) "3", NULL, 0, 0, (void *) 3, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
820     { (unichar_t *) "4", NULL, 0, 0, (void *) 4, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
821     { (unichar_t *) "5", NULL, 0, 0, (void *) 5, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
822     { (unichar_t *) "6", NULL, 0, 0, (void *) 6, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
823     { (unichar_t *) "7", NULL, 0, 0, (void *) 7, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
824     { (unichar_t *) "8", NULL, 0, 0, (void *) 8, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
825     { (unichar_t *) "9", NULL, 0, 0, (void *) 9, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
826     { (unichar_t *) "10", NULL, 0, 0, (void *) 10, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
827     { (unichar_t *) "11", NULL, 0, 0, (void *) 11, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
828     { (unichar_t *) "12", NULL, 0, 0, (void *) 12, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
829     { (unichar_t *) "13", NULL, 0, 0, (void *) 13, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
830     { (unichar_t *) "14", NULL, 0, 0, (void *) 14, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
831     { (unichar_t *) "15", NULL, 0, 0, (void *) 15, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
832     { (unichar_t *) "16", NULL, 0, 0, (void *) 16, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
833     { (unichar_t *) "17", NULL, 0, 0, (void *) 17, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
834     { (unichar_t *) "18", NULL, 0, 0, (void *) 18, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
835     { (unichar_t *) "19", NULL, 0, 0, (void *) 19, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
836     { (unichar_t *) "20", NULL, 0, 0, (void *) 20, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
837     { (unichar_t *) "21", NULL, 0, 0, (void *) 21, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
838     { (unichar_t *) "22", NULL, 0, 0, (void *) 22, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
839     { (unichar_t *) "23", NULL, 0, 0, (void *) 23, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
840     { (unichar_t *) "24", NULL, 0, 0, (void *) 24, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
841     { (unichar_t *) "25", NULL, 0, 0, (void *) 25, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
842     { (unichar_t *) "26", NULL, 0, 0, (void *) 26, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
843     { (unichar_t *) "27", NULL, 0, 0, (void *) 27, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
844 #if AppleMmMax!=26
845  #error "The mastercounts array needs to be expanded to match AppleMmMax"
846     /* Actually it should be one bigger than AppleMmMax */
847 #endif
848     GTEXTINFO_EMPTY
849 };
850 
851 enum mmw_state { mmw_counts, mmw_axes, mmw_designs, mmw_named, mmw_funcs,
852 		 mmw_others };
853 
854 typedef struct mmw {
855     GWindow gw;
856     enum mmw_state state;
857     GWindow subwins[mmw_others+1];
858     MMSet *mm, *old;
859     int isnew;
860     int done;
861     int old_axis_count, old_adobe;
862     int axis_count, instance_count;	/* The data in mm are set to the max for each */
863     int last_instance_count, last_axis_count, lastw_instance_count;
864     struct axismap last_axismaps[4];
865     int canceldrop, subheightdiff;
866     int lcnt, lmax;
867     SplineFont **loaded;
868 } MMW;
869 
870 #define MMW_Width	340
871 #define MMW_Height	300
872 #define ESD_Width	262
873 #define ESD_Height	316
874 
875 #define CID_OK		1001
876 #define CID_Prev	1002
877 #define CID_Next	1003
878 #define CID_Cancel	1004
879 #define CID_Group	1005
880 
881 #define CID_AxisCount	2001
882 #define CID_MasterCount	2002
883 #define CID_Adobe	2003
884 #define CID_Apple	2004
885 
886 #define CID_WhichAxis			3000
887 #define CID_AxisType			3001	/* +[0,3]*100 */
888 #define CID_AxisBegin			3002	/* +[0,3]*100 */
889 #define CID_AxisDefault			3003	/* +[0,3]*100 */
890 #define CID_AxisDefaultLabel		3004	/* +[0,3]*100 */
891 #define CID_AxisEnd			3005	/* +[0,3]*100 */
892 #define CID_IntermediateDesign		3006	/* +[0,3]*100 */
893 #define CID_IntermediateNormalized	3007	/* +[0,3]*100 */
894 
895 #define DesignScaleFactor	20
896 
897 #define CID_WhichDesign	4000
898 #define CID_DesignFonts	4001	/* +[0,26]*DesignScaleFactor */
899 #define CID_AxisWeights	4002	/* +[0,26]*DesignScaleFactor */
900 
901 #define CID_NDV			5002
902 #define CID_CDV			5003
903 
904 /* CID_Explicit-CID_NewDesign already defined */
905 #define CID_ForceBoldThreshold	6005
906 #define CID_FamilyName		6006
907 
908 #define CID_NamedDesigns	7001
909 #define CID_NamedNew		7002
910 #define CID_NamedEdit		7003
911 #define CID_NamedDelete		7004
912 
913 struct esd {
914     GWindow gw;
915     MMW *mmw;
916     GGadget *list;
917     int index;
918     int done;
919 };
920 
ESD_Close(struct esd * esd)921 static void ESD_Close(struct esd *esd) {
922     MacNameListFree(NameGadgetsGetNames(esd->gw));
923     esd->done = true;
924 }
925 
ESD_Cancel(GGadget * g,GEvent * e)926 static int ESD_Cancel(GGadget *g, GEvent *e) {
927     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
928 	struct esd *esd =  GDrawGetUserData(GGadgetGetWindow(g));
929 	ESD_Close(esd);
930     }
931 return( true );
932 }
933 
ESD_OK(GGadget * g,GEvent * e)934 static int ESD_OK(GGadget *g, GEvent *e) {
935     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
936 	struct esd *esd =  GDrawGetUserData(GGadgetGetWindow(g));
937 	int i,axis_count;
938 	int err = false;
939 	real coords[4];
940 	struct macname *mn;
941 	char buffer[120], *pt;
942 	unichar_t *name; char *style;
943 
944 	for ( i=0; i<esd->mmw->axis_count && i<4; ++i )
945 	    coords[i] = rint(GetReal8(esd->gw,1000+i,_(axistablab[i]),&err)*8096)/8096;
946 	if ( err )
947 return( true );
948 	axis_count = i;
949 	mn = NameGadgetsGetNames(esd->gw);
950 	if ( mn==NULL ) {
951 	    ff_post_error(_("Bad Multiple Master Font"),_("You must provide at least one name here"));
952 return( true );
953 	}
954 	pt = buffer; *pt++ = ' '; *pt++ = '[';
955 	for ( i=0; i<axis_count; ++i ) {
956 	    sprintf(pt, "%g ", (double) coords[i]);
957 	    pt += strlen(pt);
958 	}
959 	pt[-1] = ']';
960 	*pt = '\0';
961 	style = PickNameFromMacName(mn);
962 	name = malloc(((pt-buffer) + strlen(style) + 1)*sizeof(unichar_t));
963 	utf82u_strcpy(name,style);
964 	uc_strcat(name,buffer);
965 	free(style);
966 	if ( esd->index==-1 )
967 	    GListAppendLine(esd->list,name,false)->userdata = mn;
968 	else {
969 	    GTextInfo *ti = GGadgetGetListItem(esd->list,esd->index);
970 	    MacNameListFree(ti->userdata);
971 	    GListChangeLine(esd->list,esd->index,name)->userdata = mn;
972 	}
973 	esd->done = true;
974 	free(name);
975     }
976 return( true );
977 }
978 
esd_eh(GWindow gw,GEvent * event)979 static int esd_eh(GWindow gw, GEvent *event) {
980     if ( event->type==et_close ) {
981 	struct esd *esd = GDrawGetUserData(gw);
982 	ESD_Close(esd);
983     } else if ( event->type==et_char ) {
984 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
985 	    help("ui/dialogs/multiplemaster.html", "#multiplemaster-namedstyles");
986 return( true );
987 	} else if ( GMenuIsCommand(event,H_("Quit|Ctl+Q") )) {
988 	    MenuExit(NULL,NULL,NULL);
989 return( true );
990 	} else if ( GMenuIsCommand(event,H_("Close|Ctl+Shft+Q") )) {
991 	    ESD_Close(GDrawGetUserData(gw));
992 return( true );
993 	}
994 return( false );
995     }
996 return( true );
997 }
998 
EditStyleName(MMW * mmw,int index)999 static void EditStyleName(MMW *mmw,int index) {
1000     GGadget *list = GWidgetGetControl(mmw->subwins[mmw_named],CID_NamedDesigns);
1001     GTextInfo *ti = NULL;
1002     int i,k;
1003     unichar_t *pt = NULL, *end;
1004     real axes[4];
1005     struct macname *mn = NULL;
1006     char axisval[4][24];
1007     char *axisnames[4];
1008     GGadgetCreateData gcd[17];
1009     GTextInfo label[17];
1010     GRect pos;
1011     GWindow gw;
1012     GWindowAttrs wattrs;
1013     struct esd esd;
1014 
1015     for ( i=0; i<mmw->axis_count; ++i )
1016 	axes[i] = mmw->mm->axismaps[i].def;
1017     if ( index != -1 ) {
1018 	ti = GGadgetGetListItem(list,index);
1019 	if ( ti!=NULL ) {
1020 	    pt = u_strchr(ti->text,'[');
1021 	    mn = ti->userdata;
1022 	}
1023 	if ( pt!=NULL ) {
1024 	    for ( i=0, ++pt; i<4 && (*pt!=']' && *pt!='\0'); ++i ) {
1025 		axes[i] = u_strtod(pt,&end);
1026 		pt = end;
1027 	    }
1028 	}
1029     }
1030 
1031     memset(&esd,0,sizeof(esd));
1032     esd.mmw = mmw;
1033     esd.index = index;
1034     esd.list = list;
1035 
1036     memset(&wattrs,0,sizeof(wattrs));
1037     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
1038     wattrs.event_masks = ~(1<<et_charup);
1039     wattrs.is_dlg = true;
1040     wattrs.restrict_input_to_me = true;
1041     wattrs.undercursor = 1;
1042     wattrs.cursor = ct_pointer;
1043     wattrs.utf8_window_title = _("Named Styles");
1044     pos.x = pos.y = 0;
1045     pos.width =GDrawPointsToPixels(NULL,GGadgetScale(ESD_Width));
1046     pos.height = GDrawPointsToPixels(NULL,ESD_Height);
1047     esd.gw = gw = GDrawCreateTopWindow(NULL,&pos,esd_eh,&esd,&wattrs);
1048 
1049     memset(gcd,0,sizeof(gcd));
1050     memset(label,0,sizeof(label));
1051     k = 0;
1052 
1053     k = GCDFillupMacWeights(gcd,label,k,axisnames,axisval,axes,
1054 	    mmw->axis_count,mmw->mm);
1055     k = GCDBuildNames(gcd,label,k,mn);
1056 
1057     gcd[k].gd.pos.x = 20; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+33-3;
1058     gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
1059     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
1060     label[k].text = (unichar_t *) _("_OK");
1061     label[k].text_is_1byte = true;
1062     label[k].text_in_resource = true;
1063     gcd[k].gd.label = &label[k];
1064     gcd[k].gd.handle_controlevent = ESD_OK;
1065     gcd[k++].creator = GButtonCreate;
1066 
1067     gcd[k].gd.pos.x = -20; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
1068     gcd[k].gd.pos.width = -1; gcd[k].gd.pos.height = 0;
1069     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
1070     label[k].text = (unichar_t *) _("_Cancel");
1071     label[k].text_is_1byte = true;
1072     label[k].text_in_resource = true;
1073     gcd[k].gd.label = &label[k];
1074     gcd[k].gd.handle_controlevent = ESD_Cancel;
1075     gcd[k++].creator = GButtonCreate;
1076 
1077     gcd[k].gd.pos.x = 2; gcd[k].gd.pos.y = 2;
1078     gcd[k].gd.pos.width = pos.width-4; gcd[k].gd.pos.height = pos.height-4;
1079     gcd[k].gd.flags = gg_enabled | gg_visible | gg_pos_in_pixels;
1080     gcd[k].creator = GGroupCreate;
1081 
1082     GGadgetsCreate(gw,gcd);
1083 
1084     for ( i=0; i<mmw->axis_count; ++i )
1085 	free( axisnames[i]);
1086 
1087     GDrawSetVisible(gw,true);
1088 
1089     while ( !esd.done )
1090 	GDrawProcessOneEvent(NULL);
1091 
1092     GDrawDestroyWindow(gw);
1093 }
1094 
SetMasterToAxis(MMW * mmw,int initial)1095 static void SetMasterToAxis(MMW *mmw, int initial) {
1096     int i, cnt, def, isadobe;
1097 
1098     cnt = GGadgetGetFirstListSelectedItem(GWidgetGetControl(mmw->subwins[mmw_counts],CID_AxisCount))
1099 	    +1;
1100     isadobe = GGadgetIsChecked(GWidgetGetControl(mmw->subwins[mmw_counts],CID_Adobe));
1101     if ( cnt!=mmw->old_axis_count || isadobe!=mmw->old_adobe ) {
1102 	GGadget *list = GWidgetGetControl(mmw->subwins[mmw_counts],CID_MasterCount);
1103 	int32 len;
1104 	GTextInfo **ti = GGadgetGetList(list,&len);
1105 	if ( isadobe ) {
1106 	    for ( i=0; i<MmMax; ++i )
1107 		ti[i]->disabled = (i+1) < (1<<cnt);
1108 	    for ( ; i<AppleMmMax+1 ; ++i )
1109 		ti[i]->disabled = true;
1110 	    def = (1<<cnt);
1111 	} else {
1112 	    for ( i=0; i<AppleMmMax+1; ++i )
1113 		ti[i]->disabled = (i+1) < cnt;
1114 	    def = 1;
1115 	    for ( i=0; i<cnt; ++i )
1116 		def *= 3;
1117 	    if ( def>AppleMmMax+1 )
1118 		def = AppleMmMax+1;
1119 	}
1120 	if ( !initial )
1121 	    GGadgetSelectOneListItem(list,def-1);
1122 	mmw->old_axis_count = cnt;
1123 	mmw->old_adobe = isadobe;
1124     }
1125 }
1126 
MMW_AxisCntChanged(GGadget * g,GEvent * e)1127 static int MMW_AxisCntChanged(GGadget *g, GEvent *e) {
1128 
1129     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
1130 	SetMasterToAxis(GDrawGetUserData(GGadgetGetWindow(g)),false);
1131     }
1132 return( true );
1133 }
1134 
MMW_TypeChanged(GGadget * g,GEvent * e)1135 static int MMW_TypeChanged(GGadget *g, GEvent *e) {
1136     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
1137 	MMW *mmw = GDrawGetUserData(GGadgetGetWindow(g));
1138 	int isapple = GGadgetIsChecked(GWidgetGetControl(mmw->subwins[mmw_counts],CID_Apple));
1139 	int i;
1140 	SetMasterToAxis(mmw,false);
1141 	for ( i=0; i<4; ++i ) {
1142 	    GGadgetSetEnabled(GWidgetGetControl(mmw->subwins[mmw_axes],CID_AxisDefault+i*100),isapple);
1143 	    GGadgetSetEnabled(GWidgetGetControl(mmw->subwins[mmw_axes],CID_AxisDefaultLabel+i*100),isapple);
1144 	    NameGadgetsSetEnabled(GTabSetGetSubwindow(
1145 		    GWidgetGetControl(mmw->subwins[mmw_axes],CID_WhichAxis),i),isapple);
1146 	}
1147     }
1148 return( true );
1149 }
1150 
MMUsurpNew(SplineFont * sf)1151 static void MMUsurpNew(SplineFont *sf) {
1152     /* This is a font that wasn't in the original MMSet */
1153     /* We ARE going to use it in the final result */
1154     /* So if it is attached to a fontview, we must close that window and */
1155     /*  claim the splinefont for ourselves */
1156     FontView *fv, *nextfv;
1157 
1158     if ( sf->fv!=NULL ) {
1159 	if ( sf->kcld!=NULL )
1160 	    KCLD_End(sf->kcld);
1161 	if ( sf->vkcld!=NULL )
1162 	    KCLD_End(sf->vkcld);
1163 	sf->kcld = sf->vkcld = NULL;
1164 
1165 	for ( fv=(FontView *) sf->fv; fv!=NULL; fv=nextfv ) {
1166 	    nextfv = (FontView *) (fv->b.nextsame);
1167 	    fv->b.nextsame = NULL;
1168 	    _FVCloseWindows(fv);
1169 	    fv->b.sf = NULL;
1170 	    GDrawDestroyWindow(fv->gw);
1171 	}
1172 	sf->fv = NULL;
1173 	SFClearAutoSave(sf);
1174     }
1175 }
1176 
MMDetachNew(SplineFont * sf)1177 static void MMDetachNew(SplineFont *sf) {
1178     /* This is a font that wasn't in the original MMSet */
1179     /* We aren't going to use it in the final result */
1180     /* If it is attached to a fontview, then the fontview retains control */
1181     /* If not, then free it */
1182     if ( sf->fv==NULL )
1183 	SplineFontFree(sf);
1184 }
1185 
MMDetachOld(SplineFont * sf)1186 static void MMDetachOld(SplineFont *sf) {
1187     /* This is a font that was in the original MMSet */
1188     /* We aren't going to use it in the final result */
1189     /* So then free it */
1190     sf->mm = NULL;
1191     SplineFontFree(sf);
1192 }
1193 
MMW_Close(MMW * mmw)1194 static void MMW_Close(MMW *mmw) {
1195     int i;
1196     GGadget *list = GWidgetGetControl(mmw->subwins[mmw_named],CID_NamedDesigns);
1197     int32 len;
1198     GTextInfo **ti = GGadgetGetList(list,&len);
1199 
1200     for ( i=0; i<len; ++i )
1201 	if ( ti[i]->userdata!=NULL )
1202 	    MacNameListFree(ti[i]->userdata);
1203     for ( i=0; i<4; ++i )
1204 	MacNameListFree(NameGadgetsGetNames(GTabSetGetSubwindow(
1205 	    GWidgetGetControl(mmw->subwins[mmw_axes],CID_WhichAxis),i)));
1206     for ( i=0; i<mmw->lcnt; ++i )
1207 	MMDetachNew(mmw->loaded[i]);
1208     free(mmw->loaded);
1209     for ( i=0; i<4; ++i )
1210 	mmw->mm->axismaps[i].axisnames = NULL;
1211     MMSetFreeContents(mmw->mm);
1212     chunkfree(mmw->mm,sizeof(MMSet));
1213     mmw->done = true;
1214 }
1215 
MMW_Cancel(GGadget * g,GEvent * e)1216 static int MMW_Cancel(GGadget *g, GEvent *e) {
1217     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
1218 	MMW *mmw = GDrawGetUserData(GGadgetGetWindow(g));
1219 	MMW_Close(mmw);
1220     }
1221 return( true );
1222 }
1223 
MMW_SetState(MMW * mmw)1224 static void MMW_SetState(MMW *mmw) {
1225     unsigned int i;
1226 
1227     GDrawSetVisible(mmw->subwins[mmw->state],true);
1228     for ( i=mmw_counts; i<=mmw_others; ++i )
1229 	if ( i!=mmw->state )
1230 	    GDrawSetVisible(mmw->subwins[i],false);
1231 
1232     GGadgetSetEnabled(GWidgetGetControl(mmw->gw,CID_Prev),mmw->state!=mmw_counts);
1233     GGadgetSetEnabled(GWidgetGetControl(mmw->gw,CID_Next),
1234 	    mmw->state!=mmw_others && mmw->state!=mmw_named);
1235     GGadgetSetEnabled(GWidgetGetControl(mmw->gw,CID_OK),
1236 	    mmw->state==mmw_others || mmw->state==mmw_named);
1237 }
1238 
ParseWeights(GWindow gw,int cid,char * str,real * list,int expected,int tabset_cid,int aspect)1239 static int ParseWeights(GWindow gw,int cid, char *str,
1240 	real *list, int expected, int tabset_cid, int aspect ) {
1241     int cnt=0;
1242     const unichar_t *ret, *pt; unichar_t *endpt;
1243 
1244     ret= _GGadgetGetTitle(GWidgetGetControl(gw,cid));
1245 
1246     for ( pt=ret; *pt==' '; ++pt );
1247     for ( ; *pt; ) {
1248 	list[cnt++] = u_strtod(pt,&endpt);
1249 	if ( pt==endpt || ( *endpt!='\0' && *endpt!=' ' )) {
1250 	    if ( tabset_cid!=-1 )
1251 		GTabSetSetSel(GWidgetGetControl(gw,tabset_cid),aspect);
1252 	    ff_post_error(_("Bad Axis"),_("Bad Number in %s"), str);
1253 return( 0 );
1254 	}
1255 	for ( pt = endpt; *pt==' '; ++pt );
1256     }
1257     if ( cnt!=expected && expected!=-1 ) {
1258 	if ( tabset_cid!=-1 )
1259 	    GTabSetSetSel(GWidgetGetControl(gw,tabset_cid),aspect);
1260 	ff_post_error(_("Bad Axis"),_("Wrong number of entries in %s"), str);
1261 return( 0 );
1262     }
1263 
1264 return( cnt );
1265 }
1266 
ParseList(GWindow gw,int cid,char * str8,int * err,real start,real def,real end,real ** _list,int tabset_cid,int aspect,int isapple)1267 static int ParseList(GWindow gw,int cid, char *str8, int *err, real start,
1268 	real def, real end, real **_list, int tabset_cid, int aspect,
1269 	int isapple ) {
1270     int i, cnt;
1271     const unichar_t *ret, *pt; unichar_t *endpt;
1272     real *list, val;
1273     int defdone = false;
1274 
1275     *_list = NULL;
1276 
1277     ret= _GGadgetGetTitle(GWidgetGetControl(gw,cid));
1278     for ( pt=ret; *pt==' '; ++pt );
1279     cnt = *pt=='\0'?0:1 ;
1280     for ( ; *pt; ++pt ) {
1281 	if ( *pt==' ' ) ++cnt;
1282 	while ( *pt==' ' ) ++pt;
1283     }
1284     if ( start!=end )
1285 	cnt+=2;
1286     if ( isapple && start!=end )
1287 	++cnt;
1288     if ( !isapple || start==end )
1289 	defdone = true;
1290     list = malloc(cnt*sizeof(real));
1291     if ( start==end )
1292 	cnt = 0;
1293     else {
1294 	list[0] = start;
1295 	cnt = 1;
1296     }
1297 
1298     for ( pt=ret; *pt==' '; ++pt );
1299     for ( ; *pt; ) {
1300 	val = u_strtod(pt,&endpt);
1301 	if ( !defdone && val>def ) {
1302 	    list[cnt++] = def;
1303 	    defdone = true;
1304 	}
1305 	list[cnt++] = val;
1306 	if ( pt==endpt || ( *endpt!='\0' && *endpt!=' ' )) {
1307 	    GTabSetSetSel(GWidgetGetControl(gw,tabset_cid),aspect);
1308 	    free(list);
1309 	    ff_post_error(_("Bad Axis"),_("Bad Number in %s"), str8);
1310 	    *err = true;
1311 return( 0 );
1312 	}
1313 	for ( pt = endpt; *pt==' '; ++pt );
1314     }
1315     if ( start!=end )
1316 	list[cnt++] = end;
1317     for ( i=1; i<cnt; ++i )
1318 	if ( list[i-1]>list[i] ) {
1319 	    GTabSetSetSel(GWidgetGetControl(gw,tabset_cid),aspect);
1320 	    ff_post_error(_("Bad Axis"),_("The %s list is not ordered"), str8);
1321 	    free(list);
1322 	    *err = true;
1323 return( 0 );
1324 	}
1325 
1326     *_list = list;
1327 return( cnt );
1328 }
1329 
_ChooseFonts(char * buffer,SplineFont ** sfs,real * positions,int i,int cnt)1330 static char *_ChooseFonts(char *buffer, SplineFont **sfs, real *positions,
1331 	int i, int cnt) {
1332     char *elsepart=NULL, *ret;
1333     int pos;
1334     int k;
1335 
1336     if ( i<cnt-2 )
1337 	elsepart = _ChooseFonts(buffer,sfs,positions,i+1,cnt);
1338 
1339     pos = 0;
1340     if ( positions[i]!=0 ) {
1341 	sprintf(buffer, "%g sub ", (double) positions[i]);
1342 	pos += strlen(buffer);
1343     }
1344     sprintf(buffer+pos, "%g div dup 1 sub exch ", (double) (positions[i+1]-positions[i]));
1345     pos += strlen( buffer+pos );
1346     for ( k=0; k<i; ++k ) {
1347 	strcpy(buffer+pos, "0 ");
1348 	pos += 2;
1349     }
1350     if ( i!=0 ) {
1351 	sprintf(buffer+pos, "%d -2 roll ", i+2 );
1352 	pos += strlen(buffer+pos);
1353     }
1354     for ( k=i+2; k<cnt; ++k ) {
1355 	strcpy(buffer+pos, "0 ");
1356 	pos += 2;
1357     }
1358 
1359     if ( elsepart==NULL )
1360 return( copy(buffer));
1361 
1362     ret = malloc(strlen(buffer)+strlen(elsepart)+40);
1363     sprintf(ret,"dup %g le {%s} {%s} ifelse", (double) positions[i+1], buffer, elsepart );
1364     free(elsepart);
1365 return( ret );
1366 }
1367 
Figure1AxisCDV(MMW * mmw)1368 static unichar_t *Figure1AxisCDV(MMW *mmw) {
1369     real positions[MmMax];
1370     SplineFont *sfs[MmMax];
1371     int i;
1372     char *temp;
1373     unichar_t *ret;
1374     char buffer[400];
1375 
1376     if ( mmw->axis_count!=1 )
1377 return( uc_copy(""));
1378     if ( mmw->instance_count==2 )
1379 return( uc_copy( standard_cdvs[1]));
1380 
1381     for ( i=0; i<mmw->instance_count; ++i ) {
1382 	positions[i] = mmw->mm->positions[4*i];
1383 	sfs[i] = mmw->mm->instances[i];
1384 	if ( i>0 && positions[i-1]>=positions[i] )
1385 return( uc_copy(""));
1386     }
1387     temp = _ChooseFonts(buffer,sfs,positions,0,mmw->instance_count);
1388     ret = uc_copy(temp);
1389     free(temp);
1390 return( ret );
1391 }
1392 
_NormalizeAxis(char * buffer,struct axismap * axis,int i)1393 static char *_NormalizeAxis(char *buffer, struct axismap *axis, int i) {
1394     char *elsepart=NULL, *ret;
1395     int pos;
1396 
1397     if ( i<axis->points-2 )
1398 	elsepart = _NormalizeAxis(buffer,axis,i+1);
1399 
1400     pos = 0;
1401     if ( axis->blends[i+1]==axis->blends[i] ) {
1402 	sprintf( buffer, "%g ", (double) axis->blends[i] );
1403 	pos = strlen(buffer);
1404     } else {
1405 	if ( axis->designs[i]!=0 ) {
1406 	    sprintf(buffer, "%g sub ", (double) axis->designs[i]);
1407 	    pos += strlen(buffer);
1408 	}
1409 	sprintf(buffer+pos, "%g div ", (double) ((axis->designs[i+1]-axis->designs[i])/
1410 		    (axis->blends[i+1]-axis->blends[i])));
1411 	pos += strlen( buffer+pos );
1412 	if ( axis->blends[i]!=0 ) {
1413 	    sprintf(buffer+pos, "%g add ", (double) axis->blends[i]);
1414 	    pos += strlen(buffer+pos);
1415 	}
1416     }
1417 
1418     if ( elsepart==NULL )
1419 return( copy(buffer));
1420 
1421     ret = malloc(strlen(buffer)+strlen(elsepart)+40);
1422     sprintf(ret,"dup %g le {%s} {%s} ifelse", (double) axis->designs[i+1], buffer, elsepart );
1423     free(elsepart);
1424 return( ret );
1425 }
1426 
NormalizeAxis(char * header,struct axismap * axis)1427 static char *NormalizeAxis(char *header,struct axismap *axis) {
1428     char *ret;
1429     char buffer[200];
1430 
1431     ret = _NormalizeAxis(buffer,axis,0);
1432     if ( *header ) {
1433 	char *temp;
1434 	temp = malloc(strlen(header)+strlen(ret)+2);
1435 	strcpy(temp,header);
1436 	strcat(temp,ret);
1437 	strcat(temp,"\n");
1438 	free(ret);
1439 	ret = temp;
1440     }
1441 return( ret );
1442 }
1443 
SameAxes(int cnt1,int cnt2,struct axismap * axismaps1,struct axismap * axismaps2)1444 static int SameAxes(int cnt1,int cnt2,struct axismap *axismaps1,struct axismap *axismaps2) {
1445     int i,j;
1446 
1447     if ( cnt1!=cnt2 )
1448 return( false );
1449     for ( i=0; i<cnt1; ++i ) {
1450 	if ( axismaps1[i].points!=axismaps2[i].points )
1451 return( false );
1452 	for ( j=0; j<axismaps1[i].points; ++j ) {
1453 	    if ( axismaps1[i].designs[j]>=axismaps2[i].designs[j]+.01 ||
1454 		    axismaps1[i].designs[j]<=axismaps2[i].designs[j]-.01 )
1455 return( false );
1456 	    if ( axismaps1[i].blends[j]>=axismaps2[i].blends[j]+.001 ||
1457 		    axismaps1[i].blends[j]<=axismaps2[i].blends[j]-.001 )
1458 return( false );
1459 	}
1460     }
1461 return( true );
1462 }
1463 
AxisDataCopyFree(struct axismap * into,struct axismap * from,int count)1464 static void AxisDataCopyFree(struct axismap *into,struct axismap *from,int count) {
1465     int i;
1466 
1467     for ( i=0; i<4; ++i ) {
1468 	free(into->blends); free(into->designs);
1469 	into->blends = NULL; into->designs = NULL;
1470 	into->points = 0;
1471     }
1472     for ( i=0; i<count; ++i ) {
1473 	into[i].points = from[i].points;
1474 	into[i].blends = malloc(into[i].points*sizeof(real));
1475 	memcpy(into[i].blends,from[i].blends,into[i].points*sizeof(real));
1476 	into[i].designs = malloc(into[i].points*sizeof(real));
1477 	memcpy(into[i].designs,from[i].designs,into[i].points*sizeof(real));
1478     }
1479 }
1480 
PositionsMatch(MMSet * old,MMSet * mm)1481 static int PositionsMatch(MMSet *old,MMSet *mm) {
1482     int i,j;
1483 
1484     for ( i=0; i<old->instance_count; ++i ) {
1485 	for ( j=0; j<old->axis_count; ++j )
1486 	    if ( old->positions[i*old->axis_count+j] != mm->positions[i*mm->axis_count+j] )
1487 return( false );
1488     }
1489 return( true );
1490 }
1491 
MMW_FuncsValid(MMW * mmw)1492 static void MMW_FuncsValid(MMW *mmw) {
1493     unichar_t *ut;
1494     int pos, i;
1495 
1496     if ( !SameAxes(mmw->axis_count,mmw->last_axis_count,mmw->mm->axismaps,mmw->last_axismaps)) {
1497 	if ( mmw->old!=NULL &&
1498 		SameAxes(mmw->axis_count,mmw->old->axis_count,mmw->mm->axismaps,mmw->old->axismaps)) {
1499 	    ut = uc_copy(mmw->old->ndv);
1500 	} else {
1501 	    char *header = mmw->axis_count==1 ?  "  " :
1502 			    mmw->axis_count==2 ? "  exch " :
1503 			    mmw->axis_count==3 ? "  3 -1 roll " :
1504 						 "  4 -1 roll ";
1505 	    char *lines[4];
1506 	    for ( i=0; i<mmw->axis_count; ++i )
1507 		lines[i] = NormalizeAxis(header,&mmw->mm->axismaps[i]);
1508 	    pos = 0;
1509 	    for ( i=0; i<mmw->axis_count; ++i )
1510 		pos += strlen(lines[i]);
1511 	    ut = malloc((pos+20)*sizeof(unichar_t));
1512 	    uc_strcpy(ut,"{\n" ); pos = 2;
1513 	    for ( i=0; i<mmw->axis_count; ++i ) {
1514 		uc_strcpy(ut+pos,lines[i]);
1515 		pos += strlen(lines[i]);
1516 	    }
1517 	    uc_strcpy(ut+pos,"}" );
1518 	}
1519 	GGadgetSetTitle(GWidgetGetControl(mmw->subwins[mmw_funcs],CID_NDV),
1520 		ut);
1521 	free(ut);
1522 	AxisDataCopyFree(mmw->last_axismaps,mmw->mm->axismaps,mmw->axis_count);
1523 	mmw->last_axis_count = mmw->axis_count;
1524     }
1525     if ( mmw->last_instance_count!=mmw->instance_count ) {
1526 	if ( standard_cdvs[4]==NULL ) {
1527 	    standard_cdvs[4] = malloc(strlen(cdv_4axis[0])+strlen(cdv_4axis[1])+
1528 		    strlen(cdv_4axis[2])+2);
1529 	    strcpy(standard_cdvs[4],cdv_4axis[0]);
1530 	    strcat(standard_cdvs[4],cdv_4axis[1]);
1531 	    strcat(standard_cdvs[4],cdv_4axis[2]);
1532 	}
1533 	if ( mmw->old!=NULL &&
1534 		mmw->axis_count==mmw->old->axis_count &&
1535 		mmw->instance_count==mmw->old->instance_count &&
1536 		PositionsMatch(mmw->old,mmw->mm)) {
1537 	    ut = uc_copy(mmw->old->cdv);
1538 	} else if ( mmw->instance_count==(1<<mmw->axis_count) &&
1539 		StandardPositions(mmw->mm,mmw->instance_count,mmw->axis_count,false)) {
1540 	    ut = uc_copy(standard_cdvs[mmw->axis_count]);
1541 	} else if ( mmw->axis_count==1 &&
1542 		OrderedPositions(mmw->mm,mmw->instance_count,false)) {
1543 	    ut = Figure1AxisCDV(mmw);
1544 	} else {
1545 	    ut = uc_copy("");
1546 	}
1547 	GGadgetSetTitle(GWidgetGetControl(mmw->subwins[mmw_funcs],CID_CDV),
1548 		ut);
1549 	free(ut);
1550     }
1551     mmw->last_instance_count = mmw->instance_count;
1552 }
1553 
MMW_WeightsValid(MMW * mmw)1554 static void MMW_WeightsValid(MMW *mmw) {
1555     char *temp;
1556     unichar_t *ut, *utc;
1557     int pos, i;
1558     real axiscoords[4], weights[2*MmMax];
1559 
1560     if ( mmw->lastw_instance_count!=mmw->instance_count ) {
1561 	temp = malloc(mmw->instance_count*20+1);
1562 	pos = 0;
1563 	if ( mmw->old!=NULL && mmw->instance_count==mmw->old->instance_count ) {
1564 	    for ( i=0; i<mmw->instance_count; ++i ) {
1565 		sprintf(temp+pos,"%g ", (double) mmw->old->defweights[i] );
1566 		pos += strlen(temp+pos);
1567 	    }
1568 	    utc = MMDesignCoords(mmw->old);
1569 	} else {
1570 	    for ( i=0; i<mmw->axis_count; ++i ) {
1571 		if ( strcmp(mmw->mm->axes[i],"Weight")==0 &&
1572 			400>=mmw->mm->axismaps[i].designs[0] &&
1573 			400<=mmw->mm->axismaps[i].designs[mmw->mm->axismaps[i].points-1])
1574 		    axiscoords[i] = 400;
1575 		else if ( strcmp(mmw->mm->axes[i],"OpticalSize")==0 &&
1576 			12>=mmw->mm->axismaps[i].designs[0] &&
1577 			12<=mmw->mm->axismaps[i].designs[mmw->mm->axismaps[i].points-1])
1578 		    axiscoords[i] = 12;
1579 		else
1580 		    axiscoords[i] = (mmw->mm->axismaps[i].designs[0]+
1581 			    mmw->mm->axismaps[i].designs[mmw->mm->axismaps[i].points-1])/2;
1582 	    }
1583 	    i = ExecConvertDesignVector(axiscoords,mmw->axis_count,mmw->mm->ndv,mmw->mm->cdv,
1584 		    weights);
1585 	    if ( i!=mmw->instance_count ) {	/* The functions don't work */
1586 		for ( i=0; i<mmw->instance_count; ++i )
1587 		    weights[i] = 1.0/mmw->instance_count;
1588 		utc = uc_copy("");
1589 	    } else {
1590 		for ( i=0; i<mmw->axis_count; ++i ) {
1591 		    sprintf(temp+pos,"%g ", (double) axiscoords[i] );
1592 		    pos += strlen(temp+pos);
1593 		}
1594 		temp[pos-1] = '\0';
1595 		utc = uc_copy(temp);
1596 		pos = 0;
1597 	    }
1598 	    for ( i=0; i<mmw->instance_count; ++i ) {
1599 		sprintf(temp+pos,"%g ", (double) weights[i] );
1600 		pos += strlen(temp+pos);
1601 	    }
1602 	}
1603 	temp[pos-1] = '\0';
1604 	ut = uc_copy(temp);
1605 	GGadgetSetTitle(GWidgetGetControl(mmw->subwins[mmw_others],CID_NewBlends),
1606 		ut);
1607 	free(temp); free(ut);
1608 
1609 	GGadgetSetTitle(GWidgetGetControl(mmw->subwins[mmw_others],CID_NewDesign),utc);
1610 	free(utc);
1611 	mmw->lastw_instance_count = mmw->instance_count;
1612     }
1613 }
1614 
NamedDesigns(MMW * mmw)1615 static GTextInfo *NamedDesigns(MMW *mmw) {
1616     int cnt, i, j;
1617     GTextInfo *ti;
1618     char buffer[120], *pt;
1619     char *ustyle;
1620 
1621     if ( !mmw->mm->apple || mmw->old==NULL )
1622 return( NULL );
1623 
1624     cnt = mmw->old->named_instance_count;
1625     ti = calloc((cnt+1),sizeof(GTextInfo));
1626     for ( i=0; i<mmw->old->named_instance_count; ++i ) {
1627 	pt = buffer; *pt++='[';
1628 	for ( j=0; j<mmw->old->axis_count; ++j ) {
1629 	    sprintf( pt, "%.4g ", (double) mmw->old->named_instances[i].coords[j]);
1630 	    pt += strlen(pt);
1631 	}
1632 	pt[-1] = ']';
1633 	ustyle = PickNameFromMacName(mmw->old->named_instances[i].names);
1634 	ti[i].bg = ti[i].fg = COLOR_DEFAULT;
1635 	ti[i].text = malloc((strlen(buffer)+3+strlen(ustyle))*sizeof(unichar_t));
1636 	utf82u_strcpy(ti[i].text,ustyle);
1637 	uc_strcat(ti[i].text," ");
1638 	uc_strcat(ti[i].text,buffer);
1639 	ti[i].userdata = MacNameCopy(mmw->old->named_instances[i].names);
1640 	free(ustyle);
1641     }
1642 
1643 return(ti);
1644 }
1645 
TiFromFont(SplineFont * sf)1646 static GTextInfo *TiFromFont(SplineFont *sf) {
1647     GTextInfo *ti = calloc(1,sizeof(GTextInfo));
1648     ti->text = uc_copy(sf->fontname);
1649     ti->fg = ti->bg = COLOR_DEFAULT;
1650     ti->userdata = sf;
1651 return( ti );
1652 }
1653 
FontList(MMW * mmw,int instance,int * sel)1654 static GTextInfo **FontList(MMW *mmw, int instance, int *sel) {
1655     FontView *fv;
1656     int cnt, i, pos;
1657     GTextInfo **ti;
1658 
1659     cnt = 0;
1660     if ( mmw->old!=NULL ) {
1661 	cnt = mmw->old->instance_count;
1662 	if ( mmw->old->apple )
1663 	    ++cnt;
1664     }
1665     for ( fv=fv_list; fv!=NULL; fv=(FontView *) (fv->b.next) ) {
1666 	if ( fv->b.cidmaster==NULL && fv->b.sf->mm==NULL )
1667 	    ++cnt;
1668     }
1669     cnt += mmw->lcnt;
1670 
1671     ++cnt;	/* New */
1672     ++cnt;	/* Browse... */
1673 
1674     ti = malloc((cnt+1)*sizeof(GTextInfo *));
1675     pos = -1;
1676     cnt = 0;
1677     if ( mmw->old!=NULL ) {
1678 	for ( i=0; i<mmw->old->instance_count; ++i ) {
1679 	    if ( mmw->old->instances[i]==mmw->mm->instances[instance] ) pos = cnt;
1680 	    ti[cnt++] = TiFromFont(mmw->old->instances[i]);
1681 	}
1682 	if ( mmw->old->apple ) {
1683 	    if ( mmw->old->normal==mmw->mm->instances[instance] ) pos = cnt;
1684 	    ti[cnt++] = TiFromFont(mmw->old->normal);
1685 	}
1686     }
1687     for ( fv=fv_list; fv!=NULL; fv=(FontView *) (fv->b.next) ) {
1688 	if ( fv->b.cidmaster==NULL && fv->b.sf->mm==NULL ) {
1689 	    if ( fv->b.sf==mmw->mm->instances[instance] ) pos = cnt;
1690 	    ti[cnt++] = TiFromFont(fv->b.sf);
1691 	}
1692     }
1693     for ( i=0; i<mmw->lcnt; ++i ) {
1694 	if ( mmw->loaded[i]==mmw->mm->instances[instance] ) pos = cnt;
1695 	ti[cnt++] = TiFromFont( mmw->loaded[i]);
1696     }
1697     if ( pos==-1 ) pos=cnt;
1698     ti[cnt] = calloc(1,sizeof(GTextInfo));
1699     ti[cnt]->text = utf82u_copy(S_("Font|New"));
1700     ti[cnt]->bg = ti[cnt]->fg = COLOR_DEFAULT;
1701     ++cnt;
1702     ti[cnt] = calloc(1,sizeof(GTextInfo));
1703     ti[cnt]->text = utf82u_copy(_("Browse..."));
1704     ti[cnt]->bg = ti[cnt]->fg = COLOR_DEFAULT;
1705     ti[cnt]->userdata = (void *) (-1);
1706     ++cnt;
1707     ti[cnt] = calloc(1,sizeof(GTextInfo));
1708 
1709     ti[pos]->selected = true;
1710     *sel = pos;
1711 
1712 return(ti);
1713 }
1714 
MMW_DesignsSetup(MMW * mmw)1715 static void MMW_DesignsSetup(MMW *mmw) {
1716     int i,j,sel;
1717     char buffer[80], *pt;
1718     unichar_t ubuf[80];
1719     GTextInfo **ti;
1720 
1721     for ( i=0; i<mmw->instance_count; ++i ) {
1722 	GGadget *list = GWidgetGetControl(mmw->subwins[mmw_designs],CID_DesignFonts+i*DesignScaleFactor);
1723 	ti = FontList(mmw,i,&sel);
1724 	GGadgetSetList(list, ti,false);
1725 	GGadgetSetTitle(list, ti[sel]->text);
1726 	pt = buffer;
1727 	for ( j=0; j<mmw->axis_count; ++j ) {
1728 	    sprintf(pt,"%g ",(double) mmw->mm->positions[i*4+j]);
1729 	    pt += strlen(pt);
1730 	}
1731 	if ( pt>buffer ) pt[-1] = '\0';
1732 	uc_strcpy(ubuf,buffer);
1733 	GGadgetSetTitle(GWidgetGetControl(mmw->subwins[mmw_designs],CID_AxisWeights+i*DesignScaleFactor),
1734 		ubuf);
1735     }
1736 }
1737 
MMW_ParseNamedStyles(MMSet * setto,MMW * mmw)1738 static void MMW_ParseNamedStyles(MMSet *setto,MMW *mmw) {
1739     GGadget *list = GWidgetGetControl(mmw->subwins[mmw_named],CID_NamedDesigns);
1740     int32 i,j,len;
1741     GTextInfo **ti = GGadgetGetList(list,&len);
1742     unichar_t *upt, *end;
1743 
1744     setto->named_instance_count = len;
1745     if ( len!=0 ) {
1746 	setto->named_instances = calloc(len,sizeof(struct named_instance));
1747 	for ( i=0; i<len; ++i ) {
1748 	    setto->named_instances[i].coords = calloc(setto->axis_count,sizeof(real));
1749 	    upt = u_strchr(ti[i]->text,'[');
1750 	    if ( upt!=NULL ) {
1751 		for ( j=0, ++upt; j<setto->axis_count; ++j ) {
1752 		    setto->named_instances[i].coords[j] = rint(u_strtod(upt,&end)*8096)/8096;
1753 		    if ( *end==' ' ) ++end;
1754 		    upt = end;
1755 		}
1756 	    }
1757 	    setto->named_instances[i].names = ti[i]->userdata;
1758 	    ti[i]->userdata = NULL;
1759 	}
1760     }
1761 }
1762 
MMW_DoOK(MMW * mmw)1763 static void MMW_DoOK(MMW *mmw) {
1764     real weights[AppleMmMax+1];
1765     real fbt;
1766     int err = false;
1767     char *familyname, *fn, *origname=NULL;
1768     int i,j;
1769     MMSet *setto, *dlgmm;
1770     FontView *fv = NULL;
1771     int isapple = GGadgetIsChecked(GWidgetGetControl(mmw->subwins[mmw_counts],CID_Apple));
1772     int defpos;
1773     struct psdict *oldprivate = NULL;
1774     Encoding *enc = NULL;
1775 
1776     if ( !isapple ) {
1777 	if ( !GetWeights(mmw->gw, weights, mmw->mm, mmw->instance_count, mmw->axis_count))
1778 return;
1779 	fbt = GetReal8(mmw->subwins[mmw_others],CID_ForceBoldThreshold,
1780 			_("Force Bold Threshold:"),&err);
1781 	if ( err )
1782 return;
1783     }
1784 
1785     familyname = cu_copy(_GGadgetGetTitle(GWidgetGetControl(mmw->subwins[mmw_counts],CID_FamilyName)));
1786     /* They only need specify a family name if there are new fonts */
1787     if ( *familyname=='\0' ) {
1788 	free(familyname);
1789 	for ( i=0; i<mmw->instance_count; ++i )
1790 	    if ( mmw->mm->instances[i]==NULL )
1791 	break;
1792 	    else
1793 		fn = mmw->mm->instances[i]->familyname;
1794 	if ( i!=mmw->instance_count ) {
1795 	    ff_post_error(_("Bad Multiple Master Font"),_("A Font Family name is required"));
1796 return;
1797 	}
1798 	familyname = copy(fn);
1799     }
1800 
1801     /* Did we have a fontview open on this mm? */
1802     if ( mmw->old!=NULL ) {
1803 	for ( j=0; j<mmw->old->instance_count; ++j )
1804 	    if ( mmw->old->instances[j]->fv!=NULL ) {
1805 		fv = (FontView *) mmw->old->instances[j]->fv;
1806 		origname = copy(mmw->old->instances[j]->origname);
1807 		enc = fv->b.map->enc;
1808 	break;
1809 	    }
1810     }
1811 
1812     /* Make sure we free all fonts that we have lying around and aren't going */
1813     /* to be using. (ones we opened, ones in the old version of the mm). Also */
1814     /* if any font we want to use is attached to a fontview, then close the */
1815     /* window */
1816     for ( i=0; i<mmw->instance_count; ++i ) if ( mmw->mm->instances[i]!=NULL ) {
1817 	if ( mmw->old!=NULL ) {
1818 	    for ( j=0; j<mmw->old->instance_count; ++j )
1819 		if ( mmw->mm->instances[i]==mmw->old->instances[j] )
1820 	    break;
1821 	    if ( j!=mmw->old->instance_count ) {
1822 		mmw->old->instances[j] = NULL;
1823     continue;
1824 	    } else if ( mmw->old->normal==mmw->mm->instances[i] ) {
1825 		mmw->old->normal = NULL;
1826     continue;
1827 	    }
1828 	}
1829 	for ( j=0; j<mmw->lcnt; ++j )
1830 	    if ( mmw->mm->instances[i]==mmw->loaded[j] )
1831 	break;
1832 	if ( j!=mmw->lcnt ) {
1833 	    mmw->loaded[j] = NULL;
1834 continue;
1835 	}
1836 	if ( enc==NULL && mmw->mm->instances[i]->fv!=NULL )
1837 	    enc = mmw->mm->instances[i]->fv->map->enc;
1838 	MMUsurpNew(mmw->mm->instances[i]);
1839     }
1840     if ( mmw->old!=NULL ) {
1841 	for ( j=0; j<mmw->old->instance_count; ++j )
1842 	    if ( mmw->old->instances[j]!=NULL ) {
1843 		MMDetachOld(mmw->old->instances[j]);
1844 		mmw->old->instances[j] = NULL;
1845 	    }
1846 	if ( mmw->old->normal!=NULL ) {
1847 	    oldprivate = mmw->old->normal->private;
1848 	    mmw->old->normal->private = NULL;
1849 	    MMDetachOld(mmw->old->normal);
1850 	    mmw->old->normal = NULL;
1851 	}
1852     }
1853     for ( j=0; j<mmw->lcnt; ++j ) {
1854 	if ( mmw->loaded[j]!=NULL ) {
1855 	    MMDetachNew(mmw->loaded[j]);
1856 	    mmw->loaded[j] = NULL;
1857 	}
1858     }
1859 
1860     dlgmm = mmw->mm;
1861     setto = mmw->old;
1862     if ( setto!=NULL ) {
1863 	MMSetFreeContents(setto);
1864 	memset(setto,0,sizeof(MMSet));
1865     } else
1866 	setto = chunkalloc(sizeof(MMSet));
1867     setto->apple = isapple;
1868     setto->axis_count = mmw->axis_count;
1869     setto->instance_count = mmw->instance_count;
1870     defpos = mmw->instance_count;
1871     if ( isapple ) {
1872 	for ( defpos=0; defpos<mmw->instance_count; ++defpos ) {
1873 	    for ( j=0; j<mmw->axis_count; ++j )
1874 		if ( dlgmm->positions[defpos*dlgmm->axis_count+j]!=0 )
1875 	    break;
1876 	    if ( j==mmw->axis_count )
1877 	break;
1878 	}
1879 	if ( defpos==mmw->instance_count )
1880 	    --defpos;
1881 	--setto->instance_count;
1882 	setto->normal = dlgmm->instances[defpos];
1883 	if ( setto->normal!=NULL )
1884 	    setto->normal->mm = setto;
1885     }
1886     for ( i=0; i<setto->axis_count; ++i )
1887 	setto->axes[i] = dlgmm->axes[i];
1888     setto->axismaps = dlgmm->axismaps;
1889     setto->defweights = calloc(setto->instance_count,sizeof(real));
1890     if ( !isapple )
1891 	memcpy(setto->defweights,weights,setto->instance_count*sizeof(real));
1892     free(dlgmm->defweights);
1893     setto->positions = malloc(setto->instance_count*setto->axis_count*sizeof(real));
1894     for ( i=0; i<setto->instance_count; ++i ) {
1895 	int k = i<defpos ? i : i+1;
1896 	memcpy(setto->positions+i*setto->axis_count,dlgmm->positions+k*dlgmm->axis_count,
1897 		setto->axis_count*sizeof(real));
1898     }
1899     free(dlgmm->positions);
1900     setto->instances = calloc(setto->instance_count,sizeof(SplineFont *));
1901     for ( i=0; i<setto->instance_count; ++i ) {
1902 	if ( dlgmm->instances[i]!=NULL ) {
1903 	    int k = i<defpos ? i : i+1;
1904 	    setto->instances[i] = dlgmm->instances[k];
1905 	    setto->instances[i]->mm = setto;
1906 	}
1907     }
1908     MMMatchGlyphs(setto);
1909     if ( setto->normal==NULL ) {
1910 	setto->normal = MMNewFont(setto,-1,familyname);
1911 	setto->normal->private = oldprivate;
1912     }
1913     if ( !isapple ) {
1914 	if ( fbt>0 && fbt<=1 ) {
1915 	    char buffer[20];
1916 	    sprintf(buffer,"%g", (double) fbt );
1917 	    if ( oldprivate==NULL )
1918 		setto->normal->private = calloc(1,sizeof(struct psdict));
1919 	    PSDictChangeEntry(setto->normal->private,"ForceBoldThreshold",buffer);
1920 	}
1921     }
1922     if ( !isapple ) {
1923 	setto->cdv = dlgmm->cdv;
1924 	setto->ndv = dlgmm->ndv;
1925     } else
1926 	MMW_ParseNamedStyles(setto,mmw);
1927     for ( i=0; i<setto->instance_count; ++i ) {
1928 	if ( setto->instances[i]==NULL )
1929 	    setto->instances[i] = MMNewFont(setto,i,familyname);
1930 	setto->instances[i]->fv = (FontViewBase *) fv;
1931     }
1932     free(dlgmm->instances);
1933     chunkfree(dlgmm,sizeof(MMSet));
1934     if ( origname!=NULL ) {
1935 	for ( i=0; i<setto->instance_count; ++i ) {
1936 	    free(setto->instances[i]->origname);
1937 	    setto->instances[i]->origname = copy(origname);
1938 	}
1939 	free(setto->normal->origname);
1940 	setto->normal->origname = origname;
1941     } else {
1942 	for ( i=0; i<setto->instance_count; ++i ) {
1943 	    free(setto->instances[i]->origname);
1944 	    setto->instances[i]->origname = copy(setto->normal->origname);
1945 	}
1946     }
1947     if ( !isapple )
1948 	MMReblend((FontViewBase *) fv,setto);
1949     if ( fv!=NULL ) {
1950 	for ( i=0; i<setto->instance_count; ++i )
1951 	    if ( fv->b.sf==setto->instances[i])
1952 	break;
1953 	if ( i==setto->instance_count ) {
1954 	    SplineFont *sf = setto->normal;
1955 	    BDFFont *bdf;
1956 	    int same = fv->filled == fv->show;
1957 	    fv->b.sf = sf;
1958 	    bdf = SplineFontPieceMeal(fv->b.sf,ly_fore,sf->display_size<0?-sf->display_size:default_fv_font_size,72,
1959 		    (fv->antialias?pf_antialias:0)|(fv->bbsized?pf_bbsized:0),
1960 		    NULL);
1961 	    BDFFontFree(fv->filled);
1962 	    fv->filled = bdf;
1963 	    if ( same )
1964 		fv->show = bdf;
1965 	}
1966     }
1967     free(familyname);
1968 
1969     /* Multi-Mastered bitmaps don't make much sense */
1970     /* Well, maybe grey-scaled could be interpolated, but yuck */
1971     for ( i=0; i<setto->instance_count; ++i ) {
1972 	BDFFont *bdf, *bnext;
1973 	for ( bdf = setto->instances[i]->bitmaps; bdf!=NULL; bdf = bnext ) {
1974 	    bnext = bdf->next;
1975 	    BDFFontFree(bdf);
1976 	}
1977 	setto->instances[i]->bitmaps = NULL;
1978     }
1979 
1980     if ( fv==NULL )
1981 	fv = (FontView *) FontViewCreate(setto->normal,false);
1982     if ( enc==NULL )
1983 	enc = default_encoding;
1984     FVReencode((FontViewBase *) fv,enc);
1985     mmw->done = true;
1986 }
1987 
MMW_DoNext(MMW * mmw)1988 static void MMW_DoNext(MMW *mmw) {
1989     int i, err;
1990     real start, end, def, *designs, *norm;
1991     int n, n2;
1992     int isapple = GGadgetIsChecked(GWidgetGetControl(mmw->subwins[mmw_counts],CID_Apple));
1993     char *yesno[3];
1994     yesno[0] = _("_Yes"); yesno[1] = _("_No"); yesno[2] = NULL;
1995 
1996     if ( mmw->state==mmw_others )
1997 return;
1998 
1999     if ( mmw->state==mmw_counts ) {
2000 	mmw->axis_count = GGadgetGetFirstListSelectedItem(GWidgetGetControl(mmw->subwins[mmw_counts],CID_AxisCount))+1;
2001 	mmw->instance_count = GGadgetGetFirstListSelectedItem(GWidgetGetControl(mmw->subwins[mmw_counts],CID_MasterCount))+1;
2002 	/* Arrays are already allocated out to maximum, and we will just leave*/
2003 	/*  them there until user hits OK, then we make them the right size */
2004 	for ( i=0; i<4; ++i )
2005 	    GTabSetSetEnabled(GWidgetGetControl(mmw->subwins[mmw_axes],CID_WhichAxis),
2006 		    i,i<mmw->axis_count);
2007 	for ( i=0; i<AppleMmMax+1; ++i )
2008 	    GTabSetSetEnabled(GWidgetGetControl(mmw->subwins[mmw_designs],CID_WhichDesign),
2009 		    i,i<mmw->instance_count);
2010 	/* If we've changed the axis count, and the old selected axis isn't */
2011 	/*  available any more, choose another one */
2012 	if ( GTabSetGetSel(GWidgetGetControl(mmw->subwins[mmw_axes],CID_WhichAxis))>=
2013 		mmw->axis_count )
2014 	    GTabSetSetSel(GWidgetGetControl(mmw->subwins[mmw_axes],CID_WhichAxis),
2015 		    0);
2016 	if ( isapple ) {
2017 	    int cnt = 1;
2018 	    for ( i=0; i<mmw->axis_count; ++i ) cnt *= 3;
2019 	    if ( mmw->instance_count==cnt ) {
2020 		for ( i=(mmw->old==NULL)?0:mmw->old->instance_count; i<mmw->instance_count-1; ++i ) {
2021 		    int j = (i>=(cnt-1)/2) ? i+1 : i;
2022 		    mmw->mm->positions[i*4  ] = (j%3==0) ? -1: (j%3==1) ? 0 : 1;
2023 		    mmw->mm->positions[i*4+1] = ((j/3)%3==0) ? -1: ((j/3)%3==1) ? 0 : 1;
2024 		    mmw->mm->positions[i*4+2] = ((j/9)%3==0) ? -1: ((j/9)%3==1) ? 0 : 1;
2025 		    mmw->mm->positions[i*4+3] = ((j/27)%3==0) ? -1: ((j/27)%3==1) ? 0 : 1;
2026 		}
2027 		/* Place the default psuedo-instance last */
2028 		mmw->mm->positions[i*4  ] = 0;
2029 		mmw->mm->positions[i*4+1] = 0;
2030 		mmw->mm->positions[i*4+2] = 0;
2031 		mmw->mm->positions[i*4+3] = 0;
2032 	    }
2033 	} else {
2034 	    if ( mmw->instance_count==(1<<mmw->axis_count) ) {
2035 		for ( i=(mmw->old==NULL)?0:mmw->old->instance_count; i<mmw->instance_count; ++i ) {
2036 		    mmw->mm->positions[i*4  ] = (i&1) ? 1 : 0;
2037 		    mmw->mm->positions[i*4+1] = (i&2) ? 1 : 0;
2038 		    mmw->mm->positions[i*4+2] = (i&4) ? 1 : 0;
2039 		    mmw->mm->positions[i*4+3] = (i&8) ? 1 : 0;
2040 		}
2041 	    }
2042 	}
2043     } else if ( mmw->state==mmw_axes ) {
2044 	for ( i=0; i<mmw->axis_count; ++i ) {
2045 	    free(mmw->mm->axes[i]);
2046 	    mmw->mm->axes[i] = cu_copy(_GGadgetGetTitle(GWidgetGetControl(mmw->subwins[mmw_axes],CID_AxisType+i*100)));
2047 	    if ( *mmw->mm->axes[i]=='\0' ) {
2048 		GTabSetSetSel(GWidgetGetControl(mmw->subwins[mmw_axes],CID_WhichAxis),
2049 			i);
2050 		ff_post_error(_("Bad Axis"),_("Please set the Axis Type field"));
2051 return;		/* Failure */
2052 	    }
2053 	    /* Don't free the current value. If it is non-null then it just */
2054 	    /*  points into the data structure that the Names gadgets manipulate */
2055 	    /*  and they will have done any freeing that needs doing. Freeing */
2056 	    /*  it here would destroy the data they work on */
2057 	    /*MacNameListFree(mmw->mm->axismaps[i].axisnames);*/
2058 	    mmw->mm->axismaps[i].axisnames = NULL;
2059 	    if ( isapple ) {
2060 		mmw->mm->axismaps[i].axisnames = NameGadgetsGetNames(GTabSetGetSubwindow(
2061 			GWidgetGetControl(mmw->subwins[mmw_axes],CID_WhichAxis),i));
2062 		if ( mmw->mm->axismaps[i].axisnames == NULL ) {
2063 		    GTabSetSetSel(GWidgetGetControl(mmw->subwins[mmw_axes],CID_WhichAxis),
2064 			    i);
2065 		    ff_post_error(_("Bad Axis"),_("When building an Apple distortable font, you must specify at least one name for the axis"));
2066 return;		    /* Failure */
2067 		}
2068 	    }
2069 	    err = false;
2070 	    start = GetReal8(mmw->subwins[mmw_axes],CID_AxisBegin+i*100,
2071 		    _("Begin:"),&err);
2072 	    end = GetReal8(mmw->subwins[mmw_axes],CID_AxisEnd+i*100,
2073 		    _("End:"),&err);
2074 	    if ( isapple ) {
2075 		def = rint(GetReal8(mmw->subwins[mmw_axes],CID_AxisDefault+i*100,
2076 			S_("AxisValue|Default"),&err)*8096)/8096;
2077 		start = rint(start*8096)/8096;
2078 		end = rint(end*8096)/8096;
2079 	    } else
2080 		def = start;
2081 	    if ( start>=end || def<start || def>end ) {
2082 		GTabSetSetSel(GWidgetGetControl(mmw->subwins[mmw_axes],CID_WhichAxis),
2083 			i);
2084 		ff_post_error(_("Bad Axis"),_("Axis range not valid"));
2085 return;		/* Failure */
2086 	    }
2087 	    n = ParseList(mmw->subwins[mmw_axes],CID_IntermediateDesign+i*100,
2088 		    _("Design Settings:"),&err,start,def,end,&designs,CID_WhichAxis,i,isapple);
2089 	    n2 = ParseList(mmw->subwins[mmw_axes],CID_IntermediateNormalized+i*100,
2090 		    _("Normalized Settings:"),&err,
2091 			isapple?-1:0,0,1,&norm,CID_WhichAxis,i,isapple);
2092 	    if ( n!=n2 || err ) {
2093 		GTabSetSetSel(GWidgetGetControl(mmw->subwins[mmw_axes],CID_WhichAxis),
2094 			i);
2095 		if ( !err )
2096 		    ff_post_error(_("Bad Axis"),_("The number of entries in the design settings must match the number in normalized settings"));
2097 		free(designs); free(norm);
2098 return;		/* Failure */
2099 	    }
2100 	    mmw->mm->axismaps[i].points = n;
2101 	    free(mmw->mm->axismaps[i].blends); free(mmw->mm->axismaps[i].designs);
2102 	    mmw->mm->axismaps[i].blends = norm; mmw->mm->axismaps[i].designs=designs;
2103 	    mmw->mm->axismaps[i].min = start;
2104 	    mmw->mm->axismaps[i].def = def;
2105 	    mmw->mm->axismaps[i].max = end;
2106 	}
2107     } else if ( mmw->state==mmw_designs ) {
2108 	real positions[AppleMmMax+1][4];
2109 	int used[AppleMmMax+1];
2110 	int j,k,mask, mul;
2111 	SplineFont *sfs[AppleMmMax+1];
2112 	GTextInfo *ti;
2113 
2114 	memset(used,0,sizeof(used));
2115 	memset(positions,0,sizeof(positions));
2116 	for ( i=0; i<mmw->instance_count; ++i ) {
2117 	    if ( !ParseWeights(mmw->subwins[mmw_designs],CID_AxisWeights+i*DesignScaleFactor,
2118 		    _("Normalized position of this design along each axis"),positions[i],mmw->axis_count,
2119 		    CID_WhichDesign,i))
2120 return;
2121 	    if ( isapple ) {
2122 		mask = 0; mul = 1;
2123 		for ( j=0; j<mmw->axis_count; ++j ) {
2124 		    if ( positions[i][j]==-1 )
2125 			/* Do Nothing */;
2126 		    else if ( positions[i][j]==0.0 )
2127 			mask += mul;
2128 		    else if ( positions[i][j]==1.0 )
2129 			mask += 2*mul;
2130 		    else
2131 		break;
2132 		}
2133 	    } else {
2134 		mask = 0;
2135 		for ( j=0; j<mmw->axis_count; ++j ) {
2136 		    if ( positions[i][j]==0 )
2137 			/* Do Nothing */;
2138 		    else if ( positions[i][j]==1.0 )
2139 			mask |= (1<<j);
2140 		    else
2141 		break;
2142 		}
2143 	    }
2144 	    if ( j==mmw->axis_count )
2145 		used[mask] = true;
2146 	    for ( j=0; j<i-1; ++j ) {
2147 		for ( k=0; k<mmw->axis_count; ++k )
2148 		    if ( positions[j][k] != positions[i][k] )
2149 		break;
2150 		if ( k==mmw->axis_count ) {
2151 		    char *temp;
2152 		    GTabSetSetSel(GWidgetGetControl(mmw->subwins[mmw_designs],CID_WhichDesign),i);
2153 		    ff_post_error(_("Bad Multiple Master Font"),_("The set of positions, %.30s, is used more than once"),
2154 			    temp = GGadgetGetTitle8(GWidgetGetControl(mmw->subwins[mmw_designs],CID_AxisWeights+i*DesignScaleFactor)));
2155 		    free(temp);
2156 return;
2157 		}
2158 	    }
2159 	    ti = GGadgetGetListItemSelected(GWidgetGetControl(mmw->subwins[mmw_designs],CID_DesignFonts+i*DesignScaleFactor));
2160 	    sfs[i] = ti->userdata;
2161 	    if ( sfs[i]!=NULL ) {
2162 		for ( j=0; j<i; ++j )
2163 		    if ( sfs[i]==sfs[j] ) {
2164 			GTabSetSetSel(GWidgetGetControl(mmw->subwins[mmw_designs],CID_WhichDesign),i);
2165 			ff_post_error(_("Bad Multiple Master Font"),_("The font %.30s is assigned to two master designs"),sfs[i]->fontname);
2166 return;
2167 		    }
2168 	    }
2169 	}
2170 	for ( i=0; i<(1<<mmw->axis_count); ++i ) if ( !used[i] ) {
2171 	    char buffer[20], *pt = buffer;
2172 	    for ( j=0; j<mmw->axis_count; ++j ) {
2173 		sprintf( pt, "%d ", (i&(1<<j))? 1: 0 );
2174 		pt += 2;
2175 	    }
2176 	    if ( !isapple ) {
2177 		ff_post_error(_("Bad Multiple Master Font"),_("The set of positions, %.30s, is not specified in any design (and should be)"), buffer );
2178 return;
2179 	    } else {
2180 		if ( gwwv_ask(_("Bad Multiple Master Font"),(const char **) yesno,0,1,_("The set of positions, %.30s, is not specified in any design.\nIs that what you want?"),buffer)==1 )
2181 return;
2182 	    }
2183 	}
2184 	memcpy(mmw->mm->positions,positions,sizeof(positions));
2185 	for ( i=0; i<mmw->instance_count; ++i )
2186 	    mmw->mm->instances[i] = sfs[i];
2187 	if ( mmw->old!=NULL &&
2188 		mmw->axis_count==mmw->old->axis_count &&
2189 		mmw->instance_count==mmw->old->instance_count &&
2190 		PositionsMatch(mmw->old,mmw->mm))
2191 	    /* It's what the font started with, don't complain, already has a cdv */;
2192 	else if ( mmw->instance_count==(1<<mmw->axis_count) &&
2193 		StandardPositions(mmw->mm,mmw->instance_count,mmw->axis_count,isapple))
2194 	    /* It's arranged as we expect it to be */;
2195 	else if ( mmw->axis_count==1 &&
2196 		OrderedPositions(mmw->mm,mmw->instance_count,isapple))
2197 	    /* It's arranged according to our secondary expectations */;
2198 	else if ( !isapple && (mmw->instance_count==(1<<mmw->axis_count) ||
2199 		mmw->axis_count==1 )) {
2200 	    if ( gwwv_ask(_("Disordered designs"),(const char **) yesno,0,1,_("The master designs are not positioned in the expected order. FontForge will be unable to suggest a ConvertDesignVector for you. Is this what you want?"))==1 )
2201 return;
2202 	}
2203     } else if ( mmw->state==mmw_funcs ) {
2204 	if ( *_GGadgetGetTitle(GWidgetGetControl(mmw->subwins[mmw_funcs],CID_NDV))=='\0' ||
2205 		*_GGadgetGetTitle(GWidgetGetControl(mmw->subwins[mmw_funcs],CID_CDV))=='\0' ) {
2206 	    ff_post_error(_("Bad PostScript function"),_("Bad PostScript function"));
2207 return;
2208 	}
2209 	free(mmw->mm->ndv); free(mmw->mm->cdv);
2210 	mmw->mm->ndv = cu_copy( _GGadgetGetTitle(GWidgetGetControl(mmw->subwins[mmw_funcs],CID_NDV)));
2211 	mmw->mm->cdv = cu_copy( _GGadgetGetTitle(GWidgetGetControl(mmw->subwins[mmw_funcs],CID_CDV)));
2212     }
2213 
2214     if ( mmw->state==mmw_designs && !isapple )
2215 	mmw->state += 2;
2216     else
2217 	++mmw->state;
2218     if ( mmw->state==mmw_others )
2219 	MMW_WeightsValid(mmw);
2220     else if ( mmw->state==mmw_funcs )
2221 	MMW_FuncsValid(mmw);
2222     else if ( mmw->state==mmw_designs )
2223 	MMW_DesignsSetup(mmw);
2224     MMW_SetState(mmw);
2225 }
2226 
MMW_SimulateDefaultButton(MMW * mmw)2227 static void MMW_SimulateDefaultButton(MMW *mmw) {
2228     if ( mmw->state==mmw_others || mmw->state==mmw_named )
2229 	MMW_DoOK(mmw);
2230     else
2231 	MMW_DoNext(mmw);
2232 }
2233 
MMW_OK(GGadget * g,GEvent * e)2234 static int MMW_OK(GGadget *g, GEvent *e) {
2235     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2236 	MMW *mmw = GDrawGetUserData(GGadgetGetWindow(g));
2237 	MMW_DoOK(mmw);
2238     }
2239 return( true );
2240 }
2241 
MMW_Next(GGadget * g,GEvent * e)2242 static int MMW_Next(GGadget *g, GEvent *e) {
2243     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2244 	MMW *mmw = GDrawGetUserData(GGadgetGetWindow(g));
2245 	MMW_DoNext(mmw);
2246     }
2247 return( true );
2248 }
2249 
MMW_Prev(GGadget * g,GEvent * e)2250 static int MMW_Prev(GGadget *g, GEvent *e) {
2251     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2252 	MMW *mmw = GDrawGetUserData(GGadgetGetWindow(g));
2253 	if ( mmw->state!=mmw_counts ) {
2254 	    if ( mmw->state==mmw_funcs )
2255 		mmw->state = mmw_designs;
2256 	    else
2257 		--mmw->state;
2258 	    MMW_SetState(mmw);
2259 	}
2260     }
2261 return( true );
2262 }
2263 
MMW_NamedNew(GGadget * g,GEvent * e)2264 static int MMW_NamedNew(GGadget *g, GEvent *e) {
2265     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2266 	MMW *mmw = GDrawGetUserData(GGadgetGetWindow(g));
2267 	EditStyleName(mmw,-1);
2268     }
2269 return( true );
2270 }
2271 
MMW_NamedEdit(GGadget * g,GEvent * e)2272 static int MMW_NamedEdit(GGadget *g, GEvent *e) {
2273     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2274 	MMW *mmw = GDrawGetUserData(GGadgetGetWindow(g));
2275 	EditStyleName(mmw,GGadgetGetFirstListSelectedItem(GWidgetGetControl(mmw->subwins[mmw_named],CID_NamedDesigns)));
2276     }
2277 return( true );
2278 }
2279 
MMW_NamedDelete(GGadget * g,GEvent * e)2280 static int MMW_NamedDelete(GGadget *g, GEvent *e) {
2281     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
2282 	GWindow gw = GGadgetGetWindow(g);
2283 	GGadget *list = GWidgetGetControl(gw,CID_NamedDesigns);
2284 	int32 i,len;
2285 	GTextInfo **ti = GGadgetGetList(list,&len);
2286 	for ( i=0; i<len; ++i ) {
2287 	    if ( ti[i]->selected ) {
2288 		MacNameListFree(ti[i]->userdata);
2289 		ti[i]->userdata = NULL;
2290 	    }
2291 	}
2292 	GListDelSelected(list);
2293 	GGadgetSetEnabled(GWidgetGetControl(gw,CID_NamedDelete),false);
2294 	GGadgetSetEnabled(GWidgetGetControl(gw,CID_NamedEdit),false);
2295     }
2296 return( true );
2297 }
2298 
MMW_NamedSel(GGadget * g,GEvent * e)2299 static int MMW_NamedSel(GGadget *g, GEvent *e) {
2300     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
2301 	int32 len;
2302 	GTextInfo **ti = GGadgetGetList(g,&len);
2303 	GWindow gw = GGadgetGetWindow(g);
2304 	int i, sel_cnt=0;
2305 	for ( i=0; i<len; ++i )
2306 	    if ( ti[i]->selected ) ++sel_cnt;
2307 	GGadgetSetEnabled(GWidgetGetControl(gw,CID_NamedDelete),sel_cnt!=0);
2308 	GGadgetSetEnabled(GWidgetGetControl(gw,CID_NamedEdit),sel_cnt==1);
2309     } else if ( e->type==et_controlevent && e->u.control.subtype == et_listdoubleclick ) {
2310 	MMW *mmw = GDrawGetUserData(GGadgetGetWindow(g));
2311 	EditStyleName(mmw,GGadgetGetFirstListSelectedItem(g));
2312     }
2313 return( true );
2314 }
2315 
MMW_CheckOptical(GGadget * g,GEvent * e)2316 static int MMW_CheckOptical(GGadget *g, GEvent *e) {
2317     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
2318 	MMW *mmw = GDrawGetUserData(GGadgetGetWindow(g));
2319 	char *top, *bottom, *def;
2320 	unichar_t *ut;
2321 	const unichar_t *ret = _GGadgetGetTitle(g);
2322 	int di = (GGadgetGetCid(g)-CID_AxisType)/100;
2323 	char buf1[20], buf2[20], buf3[20];
2324 
2325 	if ( mmw->old!=NULL && di<mmw->old->axis_count &&
2326 		uc_strcmp(ret,mmw->old->axes[di])==0 ) {
2327 	    sprintf(buf1,"%g", (double) mmw->old->axismaps[di].designs[0]);
2328 	    sprintf(buf2,"%g", (double) mmw->old->axismaps[di].designs[mmw->old->axismaps[di].points-1]);
2329 	    sprintf(buf3,"%g", (double) mmw->old->axismaps[di].def);
2330 	    def = buf3;
2331 	    top = buf2;
2332 	    bottom = buf1;
2333 	} else if ( uc_strcmp(ret,"OpticalSize")==0 ) {
2334 	    top = "72";
2335 	    def = "12";
2336 	    bottom = "6";
2337 	} else if ( uc_strcmp(ret,"Slant")==0 ) {
2338 	    top = "22";
2339 	    def = "0";
2340 	    bottom = "-22";
2341 	} else if ( GGadgetIsChecked(GWidgetGetControl(mmw->subwins[mmw_counts],CID_Apple)) ) {
2342 	    top = "2.0";
2343 	    bottom = "0.5";
2344 	    def = "1.0";
2345 	} else {
2346 	    top = "999";
2347 	    bottom = "50";
2348 	    def = "400";
2349 	}
2350 	ut = uc_copy(top);
2351 	GGadgetSetTitle(GWidgetGetControl(GGadgetGetWindow(g),
2352 		GGadgetGetCid(g)-CID_AxisType + CID_AxisEnd), ut);
2353 	free(ut);
2354 	ut = uc_copy(bottom);
2355 	GGadgetSetTitle(GWidgetGetControl(GGadgetGetWindow(g),
2356 		GGadgetGetCid(g)-CID_AxisType + CID_AxisBegin), ut);
2357 	free(ut);
2358 	ut = uc_copy(def);
2359 	GGadgetSetTitle(GWidgetGetControl(GGadgetGetWindow(g),
2360 		GGadgetGetCid(g)-CID_AxisType + CID_AxisDefault), ut);
2361 	free(ut);
2362     }
2363 return( true );
2364 }
2365 
MMW_CheckBrowse(GGadget * g,GEvent * e)2366 static int MMW_CheckBrowse(GGadget *g, GEvent *e) {
2367     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
2368 	MMW *mmw = GDrawGetUserData(GGadgetGetWindow(g));
2369 	/*int di = (GGadgetGetCid(g)-CID_DesignFonts)/DesignScaleFactor;*/
2370 	GTextInfo *ti = GGadgetGetListItemSelected(g);
2371 	char *temp;
2372 	SplineFont *sf;
2373 	GTextInfo **tis;
2374 	int i,sel,oldsel;
2375 	unichar_t *ut;
2376 
2377 	if ( ti!=NULL && ti->userdata == (void *) -1 ) {
2378 	    temp = GetPostScriptFontName(NULL,false,true);
2379 	    if ( temp==NULL )
2380 return(true);
2381 	    sf = LoadSplineFont(temp,0);
2382 	    free(temp); temp = NULL;
2383 	    if ( sf==NULL )
2384 return(true);
2385 	    if ( sf->cidmaster!=NULL || sf->subfonts!=0 ) {
2386 		ff_post_error(_("Bad Multiple Master Font"),_("CID keyed fonts may not be a master design of a multiple master font"));
2387 return(true);
2388 	    } else if ( sf->mm!=NULL ) {
2389 		ff_post_error(_("Bad Multiple Master Font"),_("CID keyed fonts may not be a master design of a multiple master font"));
2390 return(true);
2391 	    }
2392 	    if ( sf->fv==NULL ) {
2393 		if ( mmw->lcnt>=mmw->lmax ) {
2394 		    if ( mmw->lmax==0 )
2395 			mmw->loaded = malloc((mmw->lmax=10)*sizeof(SplineFont *));
2396 		    else
2397 			mmw->loaded = realloc(mmw->loaded,(mmw->lmax+=10)*sizeof(SplineFont *));
2398 		}
2399 		mmw->loaded[mmw->lcnt++] = sf;
2400 		for ( i=0; i<mmw->instance_count; ++i ) {
2401 		    GGadget *list = GWidgetGetControl(mmw->subwins[mmw_designs],CID_DesignFonts+i*DesignScaleFactor);
2402 		    oldsel = GGadgetGetFirstListSelectedItem(list);
2403 		    tis = FontList(mmw,i,&sel);
2404 		    tis[sel]->selected = false;
2405 		    tis[oldsel]->selected = true;
2406 		    GGadgetSetList(list, tis, false);
2407 		}
2408 	    }
2409 	    GGadgetSetTitle(g,ut = uc_copy(sf->fontname));
2410 	    free(ut);
2411 	}
2412     }
2413 return( true );
2414 }
2415 
mmwsub_e_h(GWindow gw,GEvent * event)2416 static int mmwsub_e_h(GWindow gw, GEvent *event) {
2417     if ( event->type==et_char ) {
2418 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
2419 	    help("ui/dialogs/multiplemaster.html", NULL);
2420 return( true );
2421 	} else if ( event->u.chr.keysym=='q' && (event->u.chr.state&ksm_control)) {
2422 	    if ( event->u.chr.state&ksm_shift )
2423 		MMW_Close(GDrawGetUserData(gw));
2424 return( true );
2425 	} else if ( event->u.chr.chars[0]=='\r' ) {
2426 	    MMW_SimulateDefaultButton( (MMW *) GDrawGetUserData(gw));
2427 return( true );
2428 	}
2429 return( false );
2430     }
2431 return( true );
2432 }
2433 
mmw_e_h(GWindow gw,GEvent * event)2434 static int mmw_e_h(GWindow gw, GEvent *event) {
2435     if ( event->type==et_close ) {
2436 	MMW *mmw = GDrawGetUserData(gw);
2437 	MMW_Close(mmw);
2438     } else if ( event->type==et_char ) {
2439 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
2440 	    help("ui/dialogs/multiplemaster.html", NULL);
2441 return( true );
2442 	} else if ( event->u.chr.keysym=='q' && (event->u.chr.state&ksm_control)) {
2443 	    if ( event->u.chr.state&ksm_shift )
2444 		MMW_Close(GDrawGetUserData(gw));
2445 	    else
2446 		MenuExit(NULL,NULL,NULL);
2447 return( true );
2448 	} else if ( event->u.chr.chars[0]=='\r' ) {
2449 	    MMW_SimulateDefaultButton( (MMW *) GDrawGetUserData(gw));
2450 return( true );
2451 	}
2452 return( false );
2453     }
2454 return( true );
2455 }
2456 
MMCopy(MMSet * orig)2457 static MMSet *MMCopy(MMSet *orig) {
2458     MMSet *mm;
2459     int i;
2460     /* Allocate the arrays out to maximum, we'll fix them up later, and we */
2461     /*  retain the proper counts in the mmw structure. This means we don't */
2462     /*  lose data when they shrink and then restore a value */
2463 
2464     mm = chunkalloc(sizeof(MMSet));
2465     mm->apple = orig->apple;
2466     mm->instance_count = AppleMmMax+1;
2467     mm->axis_count = 4;
2468     for ( i=0; i<orig->axis_count; ++i )
2469 	mm->axes[i] = copy(orig->axes[i]);
2470     mm->instances = calloc(AppleMmMax+1,sizeof(SplineFont *));
2471     memcpy(mm->instances,orig->instances,orig->instance_count*sizeof(SplineFont *));
2472     if ( mm->apple )
2473 	mm->instances[orig->instance_count] = orig->normal;
2474     mm->positions = calloc((AppleMmMax+1)*4,sizeof(real));
2475     for ( i=0; i<orig->instance_count; ++i )
2476 	memcpy(mm->positions+i*4,orig->positions+i*orig->axis_count,orig->axis_count*sizeof(real));
2477     mm->defweights = calloc(AppleMmMax+1,sizeof(real));
2478     memcpy(mm->defweights,orig->defweights,orig->instance_count*sizeof(real));
2479     mm->axismaps = calloc(4,sizeof(struct axismap));
2480     for ( i=0; i<orig->axis_count; ++i ) {
2481 	mm->axismaps[i].points = orig->axismaps[i].points;
2482 	mm->axismaps[i].blends = malloc(mm->axismaps[i].points*sizeof(real));
2483 	memcpy(mm->axismaps[i].blends,orig->axismaps[i].blends,mm->axismaps[i].points*sizeof(real));
2484 	mm->axismaps[i].designs = malloc(mm->axismaps[i].points*sizeof(real));
2485 	memcpy(mm->axismaps[i].designs,orig->axismaps[i].designs,mm->axismaps[i].points*sizeof(real));
2486 	mm->axismaps[i].min = orig->axismaps[i].min;
2487 	mm->axismaps[i].max = orig->axismaps[i].max;
2488 	mm->axismaps[i].def = orig->axismaps[i].def;
2489     }
2490     mm->cdv = copy(orig->cdv);
2491     mm->ndv = copy(orig->ndv);
2492 return( mm );
2493 }
2494 
MMWizard(MMSet * mm)2495 void MMWizard(MMSet *mm) {
2496     MMW mmw;
2497     GRect pos, subpos;
2498     GWindow gw;
2499     GWindowAttrs wattrs;
2500     GTabInfo axisaspects[5], designaspects[AppleMmMax+1+1];
2501     GGadgetCreateData bgcd[8], cntgcd[11], axisgcd[4][20], designgcd[AppleMmMax+1][5],
2502 	    agcd[2], dgcd[3], ogcd[7], ngcd[7];
2503     GTextInfo blabel[8], cntlabel[11], axislabel[4][20], designlabel[AppleMmMax+1][5],
2504 	    dlabel, olabels[7], nlabel[5];
2505     char axisbegins[4][20], axisends[4][20], axisdefs[4][20];
2506     char *normalized[4], *designs[4];
2507     char *pt, *freeme;
2508     int i,k;
2509     int isadobe = mm==NULL || !mm->apple;
2510     int space,blen= GIntGetResource(_NUM_Buttonsize)*100/GGadgetScale(100);
2511     static char *designtablab[] = { "1", "2", "3", "4", "5", "6", "7", "8",
2512 	    "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
2513 	    "20", "21", "22", "23", "24", "25", "26", "27", NULL };
2514 #if AppleMmMax!=26
2515  #error "The designtablab array needs to be expanded to match AppleMmMax"
2516     /* Actually it should be one bigger than AppleMmMax */
2517 #endif
2518 
2519     memset(&mmw,0,sizeof(mmw));
2520     mmw.old = mm;
2521     if ( mm!=NULL ) {
2522 	mmw.mm = MMCopy(mm);
2523 	mmw.axis_count = mm->axis_count;
2524 	mmw.instance_count = mm->instance_count;
2525 	if ( mm->apple )
2526 	    ++mmw.instance_count;		/* Normal (default) design is a master in the mac format */
2527     } else {
2528 	mmw.mm = chunkalloc(sizeof(MMSet));
2529 	mmw.axis_count = 1;
2530 	mmw.instance_count = 2;
2531 	mmw.mm->axis_count = 4; mmw.mm->instance_count = AppleMmMax+1;
2532 	mmw.mm->instances = calloc(AppleMmMax+1,sizeof(SplineFont *));
2533 	mmw.mm->positions = calloc((AppleMmMax+1)*4,sizeof(real));
2534 	mmw.mm->defweights = calloc(AppleMmMax+1,sizeof(real));
2535 	mmw.mm->axismaps = calloc(4,sizeof(struct axismap));
2536 	mmw.isnew = true;
2537     }
2538 
2539     memset(&wattrs,0,sizeof(wattrs));
2540     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
2541     wattrs.event_masks = ~(1<<et_charup);
2542     wattrs.is_dlg = true;
2543     wattrs.restrict_input_to_me = true;
2544     wattrs.undercursor = 1;
2545     wattrs.cursor = ct_pointer;
2546     wattrs.utf8_window_title = mmw.isnew?_("Create MM"):_("MM _Info") ;
2547     pos.x = pos.y = 0;
2548     pos.width =GDrawPointsToPixels(NULL,GGadgetScale(MMW_Width));
2549     pos.height = GDrawPointsToPixels(NULL,MMW_Height);
2550     mmw.gw = gw = GDrawCreateTopWindow(NULL,&pos,mmw_e_h,&mmw,&wattrs);
2551 
2552     memset(&blabel,0,sizeof(blabel));
2553     memset(&bgcd,0,sizeof(bgcd));
2554 
2555     mmw.canceldrop = GDrawPointsToPixels(NULL,30);
2556     bgcd[0].gd.pos.x = 20; bgcd[0].gd.pos.y = GDrawPixelsToPoints(NULL,pos.height)-33;
2557     bgcd[0].gd.pos.width = -1; bgcd[0].gd.pos.height = 0;
2558     bgcd[0].gd.flags = gg_visible | gg_enabled;
2559     blabel[0].text = (unichar_t *) _("_OK");
2560     blabel[0].text_is_1byte = true;
2561     blabel[0].text_in_resource = true;
2562     bgcd[0].gd.label = &blabel[0];
2563     bgcd[0].gd.cid = CID_OK;
2564     bgcd[0].gd.handle_controlevent = MMW_OK;
2565     bgcd[0].creator = GButtonCreate;
2566 
2567     space = (MMW_Width-4*blen-40)/3;
2568     bgcd[1].gd.pos.x = bgcd[0].gd.pos.x+blen+space; bgcd[1].gd.pos.y = bgcd[0].gd.pos.y;
2569     bgcd[1].gd.pos.width = -1; bgcd[1].gd.pos.height = 0;
2570     bgcd[1].gd.flags = gg_visible;
2571     blabel[1].text = (unichar_t *) _("< _Prev");
2572     blabel[1].text_is_1byte = true;
2573     blabel[1].text_in_resource = true;
2574     bgcd[1].gd.label = &blabel[1];
2575     bgcd[1].gd.handle_controlevent = MMW_Prev;
2576     bgcd[1].gd.cid = CID_Prev;
2577     bgcd[1].creator = GButtonCreate;
2578 
2579     bgcd[2].gd.pos.x = bgcd[1].gd.pos.x+blen+space; bgcd[2].gd.pos.y = bgcd[1].gd.pos.y;
2580     bgcd[2].gd.pos.width = -1; bgcd[2].gd.pos.height = 0;
2581     bgcd[2].gd.flags = gg_visible;
2582     blabel[2].text = (unichar_t *) _("_Next >");
2583     blabel[2].text_in_resource = true;
2584     blabel[2].text_is_1byte = true;
2585     bgcd[2].gd.label = &blabel[2];
2586     bgcd[2].gd.handle_controlevent = MMW_Next;
2587     bgcd[2].gd.cid = CID_Next;
2588     bgcd[2].creator = GButtonCreate;
2589 
2590     bgcd[3].gd.pos.x = -20; bgcd[3].gd.pos.y = bgcd[1].gd.pos.y;
2591     bgcd[3].gd.pos.width = -1; bgcd[3].gd.pos.height = 0;
2592     bgcd[3].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
2593     blabel[3].text = (unichar_t *) _("_Cancel");
2594     blabel[3].text_in_resource = true;
2595     blabel[3].text_is_1byte = true;
2596     bgcd[3].gd.label = &blabel[3];
2597     bgcd[3].gd.handle_controlevent = MMW_Cancel;
2598     bgcd[3].gd.cid = CID_Cancel;
2599     bgcd[3].creator = GButtonCreate;
2600 
2601     bgcd[4].gd.pos.x = 2; bgcd[4].gd.pos.y = 2;
2602     bgcd[4].gd.pos.width = pos.width-4; bgcd[4].gd.pos.height = pos.height-4;
2603     bgcd[4].gd.flags = gg_enabled | gg_visible | gg_pos_in_pixels;
2604     bgcd[4].gd.cid = CID_Group;
2605     bgcd[4].creator = GGroupCreate;
2606 
2607     GGadgetsCreate(gw,bgcd);
2608 
2609     subpos = pos;
2610     subpos.y = subpos.x = 4;
2611     subpos.width -= 8;
2612     mmw.subheightdiff = GDrawPointsToPixels(NULL,44)-8;
2613     subpos.height -= mmw.subheightdiff;
2614     wattrs.mask = wam_events;
2615     for ( i=mmw_counts; i<=mmw_others; ++i )
2616 	mmw.subwins[i] = GWidgetCreateSubWindow(mmw.gw,&subpos,mmwsub_e_h,&mmw,&wattrs);
2617 
2618     memset(&cntlabel,0,sizeof(cntlabel));
2619     memset(&cntgcd,0,sizeof(cntgcd));
2620 
2621     k=0;
2622     cntlabel[k].text = (unichar_t *) _("Type of distortable font:");
2623     cntlabel[k].text_is_1byte = true;
2624     cntgcd[k].gd.label = &cntlabel[k];
2625     cntgcd[k].gd.pos.x = 5; cntgcd[k].gd.pos.y = 11;
2626     cntgcd[k].gd.flags = gg_visible | gg_enabled;
2627     cntgcd[k++].creator = GLabelCreate;
2628 
2629     cntlabel[k].text = (unichar_t *) _("Adobe");
2630     cntlabel[k].text_is_1byte = true;
2631     cntgcd[k].gd.label = &cntlabel[k];
2632     cntgcd[k].gd.pos.x = 10; cntgcd[k].gd.pos.y = cntgcd[k-1].gd.pos.y+12;
2633     cntgcd[k].gd.flags = isadobe ? (gg_visible | gg_enabled | gg_cb_on) :
2634 	    ( gg_visible | gg_enabled );
2635     cntgcd[k].gd.cid = CID_Adobe;
2636     cntgcd[k].gd.handle_controlevent = MMW_TypeChanged;
2637     cntgcd[k++].creator = GRadioCreate;
2638 
2639     cntlabel[k].text = (unichar_t *) _("Apple");
2640     cntlabel[k].text_is_1byte = true;
2641     cntgcd[k].gd.label = &cntlabel[k];
2642     cntgcd[k].gd.pos.x = 70; cntgcd[k].gd.pos.y = cntgcd[k-1].gd.pos.y;
2643     cntgcd[k].gd.flags = !isadobe ? (gg_visible | gg_enabled | gg_cb_on) :
2644 	    ( gg_visible | gg_enabled );
2645     cntgcd[k].gd.cid = CID_Apple;
2646     cntgcd[k].gd.handle_controlevent = MMW_TypeChanged;
2647     cntgcd[k++].creator = GRadioCreate;
2648 
2649     cntlabel[k].text = (unichar_t *) _("Number of Axes:");
2650     cntlabel[k].text_is_1byte = true;
2651     cntgcd[k].gd.label = &cntlabel[k];
2652     cntgcd[k].gd.pos.x = 5; cntgcd[k].gd.pos.y = cntgcd[k-1].gd.pos.y+18;
2653     cntgcd[k].gd.flags = gg_visible | gg_enabled;
2654     cntgcd[k++].creator = GLabelCreate;
2655 
2656     cntgcd[k].gd.pos.x = 10; cntgcd[k].gd.pos.y = cntgcd[k-1].gd.pos.y+12;
2657     cntgcd[k].gd.flags = gg_visible | gg_enabled;
2658     cntgcd[k].gd.u.list = axiscounts;
2659     cntgcd[k].gd.label = &axiscounts[mmw.axis_count-1];
2660     cntgcd[k].gd.cid = CID_AxisCount;
2661     cntgcd[k].gd.handle_controlevent = MMW_AxisCntChanged;
2662     cntgcd[k++].creator = GListButtonCreate;
2663     for ( i=0; i<4; ++i )
2664 	axiscounts[i].selected = false;
2665     axiscounts[mmw.axis_count-1].selected = true;
2666 
2667     cntlabel[k].text = (unichar_t *) _("Number of Master Designs:");
2668     cntlabel[k].text_is_1byte = true;
2669     cntgcd[k].gd.label = &cntlabel[k];
2670     cntgcd[k].gd.pos.x = 5; cntgcd[k].gd.pos.y = cntgcd[k-1].gd.pos.y+30;
2671     cntgcd[k].gd.flags = gg_visible | gg_enabled;
2672     cntgcd[k++].creator = GLabelCreate;
2673 
2674     cntgcd[k].gd.pos.x = 10; cntgcd[k].gd.pos.y = cntgcd[k-1].gd.pos.y+12;
2675     cntgcd[k].gd.flags = gg_visible | gg_enabled;
2676     cntgcd[k].gd.u.list = mastercounts;
2677     cntgcd[k].gd.label = &mastercounts[mmw.instance_count-1];
2678     cntgcd[k].gd.cid = CID_MasterCount;
2679     cntgcd[k++].creator = GListButtonCreate;
2680     for ( i=0; i<AppleMmMax+1; ++i )
2681 	mastercounts[i].selected = false;
2682     mastercounts[mmw.instance_count-1].selected = true;
2683 
2684     cntlabel[k].text = (unichar_t *) _("_Family Name:");
2685     cntlabel[k].text_is_1byte = true;
2686     cntlabel[k].text_in_resource = true;
2687     cntgcd[k].gd.label = &cntlabel[k];
2688     cntgcd[k].gd.pos.x = 10; cntgcd[k].gd.pos.y = cntgcd[k-1].gd.pos.y+30;
2689     cntgcd[k].gd.flags = gg_visible | gg_enabled;
2690     cntgcd[k++].creator = GLabelCreate;
2691 
2692     freeme=NULL;
2693     if ( mmw.old!=NULL && mmw.old->normal->familyname!=NULL )
2694 	cntlabel[k].text = (unichar_t *) (mmw.old->normal->familyname);
2695     else
2696 	cntlabel[k].text = (unichar_t *) (freeme= GetNextUntitledName());
2697     cntlabel[k].text_is_1byte = true;
2698     cntgcd[k].gd.label = &cntlabel[k];
2699     cntgcd[k].gd.pos.x = 15; cntgcd[k].gd.pos.y = cntgcd[k-1].gd.pos.y+14;
2700     cntgcd[k].gd.pos.width = 150;
2701     cntgcd[k].gd.flags = gg_visible | gg_enabled;
2702     cntgcd[k].gd.cid = CID_FamilyName;
2703     cntgcd[k++].creator = GTextFieldCreate;
2704 
2705     GGadgetsCreate(mmw.subwins[mmw_counts],cntgcd);
2706     SetMasterToAxis(&mmw,true);
2707     free(freeme);
2708 
2709     memset(&axisgcd,0,sizeof(axisgcd));
2710     memset(&axislabel,0,sizeof(axislabel));
2711     memset(&agcd,0,sizeof(agcd));
2712     memset(&axisaspects,0,sizeof(axisaspects));
2713 
2714     for ( i=0; i<4; ++i ) {
2715 	k=0;
2716 	axislabel[i][k].text = (unichar_t *) _("Axis Type:");
2717 	axislabel[i][k].text_is_1byte = true;
2718 	axisgcd[i][k].gd.label = &axislabel[i][k];
2719 	axisgcd[i][k].gd.pos.x = 5; axisgcd[i][k].gd.pos.y = 11;
2720 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2721 	axisgcd[i][k++].creator = GLabelCreate;
2722 
2723 	axisgcd[i][k].gd.pos.x = 120; axisgcd[i][k].gd.pos.y = axisgcd[i][k-1].gd.pos.y-4;
2724 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2725 	axisgcd[i][k].gd.u.list = axistypes;
2726 	axisgcd[i][k].gd.cid = CID_AxisType+i*100;
2727 	axisgcd[i][k].gd.handle_controlevent = MMW_CheckOptical;
2728 	if ( i<mmw.axis_count && mmw.mm->axes[i]!=NULL ) {
2729 	    axislabel[i][k].text = uc_copy(mmw.mm->axes[i]);
2730 	    axisgcd[i][k].gd.label = &axislabel[i][k];
2731 	}
2732 	axisgcd[i][k++].creator = GListFieldCreate;
2733 
2734 	axislabel[i][k].text = (unichar_t *) _("Axis Range:");
2735 	axislabel[i][k].text_is_1byte = true;
2736 	axisgcd[i][k].gd.label = &axislabel[i][k];
2737 	axisgcd[i][k].gd.pos.x = 5; axisgcd[i][k].gd.pos.y = axisgcd[i][k-1].gd.pos.y+20;
2738 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2739 	axisgcd[i][k++].creator = GLabelCreate;
2740 
2741 	if ( mmw.mm->axismaps[i].points<2 ) {
2742 	    strcpy(axisbegins[i],"50");
2743 	    strcpy(axisdefs[i],"400");
2744 	    strcpy(axisends[i],"999");
2745 	} else {
2746 	    sprintf(axisbegins[i],"%.4g", (double) mmw.mm->axismaps[i].designs[0]);
2747 	    sprintf(axisends[i],"%.4g", (double) mmw.mm->axismaps[i].designs[mmw.mm->axismaps[i].points-1]);
2748 	    if ( mmw.mm->apple )
2749 		sprintf(axisdefs[i],"%.4g", (double) mmw.mm->axismaps[i].def );
2750 	    else
2751 		sprintf(axisdefs[i],"%g", (double) (mmw.mm->axismaps[i].designs[0]+
2752 			mmw.mm->axismaps[i].designs[mmw.mm->axismaps[i].points-1])/2);
2753 	}
2754 
2755 	axislabel[i][k].text = (unichar_t *) _("Begin:");
2756 	axislabel[i][k].text_is_1byte = true;
2757 	axisgcd[i][k].gd.label = &axislabel[i][k];
2758 	axisgcd[i][k].gd.pos.x = 10; axisgcd[i][k].gd.pos.y = axisgcd[i][k-1].gd.pos.y+16;
2759 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2760 	axisgcd[i][k++].creator = GLabelCreate;
2761 
2762 	axislabel[i][k].text = (unichar_t *) axisbegins[i];
2763 	axislabel[i][k].text_is_1byte = true;
2764 	axisgcd[i][k].gd.label = &axislabel[i][k];
2765 	axisgcd[i][k].gd.pos.x = 50; axisgcd[i][k].gd.pos.y = axisgcd[i][k-1].gd.pos.y-4;
2766 	axisgcd[i][k].gd.pos.width=50;
2767 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2768 	axisgcd[i][k].gd.cid = CID_AxisBegin+i*100;
2769 	axisgcd[i][k++].creator = GTextFieldCreate;
2770 
2771 	axislabel[i][k].text = (unichar_t *) _("Default:");
2772 	axislabel[i][k].text_is_1byte = true;
2773 	axisgcd[i][k].gd.label = &axislabel[i][k];
2774 	axisgcd[i][k].gd.pos.x = 110; axisgcd[i][k].gd.pos.y = axisgcd[i][k-2].gd.pos.y;
2775 	axisgcd[i][k].gd.flags = mmw.mm->apple ? (gg_visible | gg_enabled) : gg_visible;
2776 	axisgcd[i][k].gd.cid = CID_AxisDefaultLabel+i*100;
2777 	axisgcd[i][k++].creator = GLabelCreate;
2778 
2779 	axislabel[i][k].text = (unichar_t *) axisdefs[i];
2780 	axislabel[i][k].text_is_1byte = true;
2781 	axisgcd[i][k].gd.label = &axislabel[i][k];
2782 	axisgcd[i][k].gd.pos.x = 148; axisgcd[i][k].gd.pos.y = axisgcd[i][k-2].gd.pos.y;
2783 	axisgcd[i][k].gd.pos.width=50;
2784 	axisgcd[i][k].gd.flags = mmw.mm->apple ? (gg_visible | gg_enabled) : gg_visible;
2785 	axisgcd[i][k].gd.cid = CID_AxisDefault+i*100;
2786 	axisgcd[i][k++].creator = GTextFieldCreate;
2787 
2788 	axislabel[i][k].text = (unichar_t *) _("End:");
2789 	axislabel[i][k].text_is_1byte = true;
2790 	axisgcd[i][k].gd.label = &axislabel[i][k];
2791 	axisgcd[i][k].gd.pos.x = 210; axisgcd[i][k].gd.pos.y = axisgcd[i][k-2].gd.pos.y;
2792 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2793 	axisgcd[i][k++].creator = GLabelCreate;
2794 
2795 	axislabel[i][k].text = (unichar_t *) axisends[i];
2796 	axislabel[i][k].text_is_1byte = true;
2797 	axisgcd[i][k].gd.label = &axislabel[i][k];
2798 	axisgcd[i][k].gd.pos.x = 240; axisgcd[i][k].gd.pos.y = axisgcd[i][k-2].gd.pos.y;
2799 	axisgcd[i][k].gd.pos.width=50;
2800 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2801 	axisgcd[i][k].gd.cid = CID_AxisEnd+i*100;
2802 	axisgcd[i][k++].creator = GTextFieldCreate;
2803 
2804 	axislabel[i][k].text = (unichar_t *) _("Intermediate Points:");
2805 	axislabel[i][k].text_is_1byte = true;
2806 	axisgcd[i][k].gd.label = &axislabel[i][k];
2807 	axisgcd[i][k].gd.pos.x = 5; axisgcd[i][k].gd.pos.y = axisgcd[i][k-1].gd.pos.y+26;
2808 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2809 	axisgcd[i][k++].creator = GLabelCreate;
2810 
2811 	normalized[i]=NULL;
2812 	designs[i]=NULL;
2813 	if ( mmw.mm->axismaps[i].points>2+mmw.mm->apple ) {
2814 	    int l,j,len1, len2;
2815 	    char buffer[30];
2816 	    len1 = len2 = 0;
2817 	    for ( l=0; l<2; ++l ) {
2818 		for ( j=1; j<mmw.mm->axismaps[i].points-1; ++j ) {
2819 		    if ( mmw.mm->apple && mmw.mm->axismaps[i].designs[j]==mmw.mm->axismaps[i].def )
2820 		continue;
2821 		    /* I wanted to separate things with commas, but that isn't*/
2822 		    /*  a good idea in Europe (comma==decimal point) */
2823 		    sprintf(buffer,"%g ",(double) mmw.mm->axismaps[i].designs[j]);
2824 		    if ( designs[i]!=NULL )
2825 			strcpy(designs[i]+len1, buffer );
2826 		    len1 += strlen(buffer);
2827 		    sprintf(buffer,"%g ",(double) mmw.mm->axismaps[i].blends[j]);
2828 		    if ( normalized[i]!=NULL )
2829 			strcpy(normalized[i]+len2, buffer );
2830 		    len2 += strlen(buffer);
2831 		}
2832 		if ( l==0 ) {
2833 		    normalized[i] = malloc(len2+2);
2834 		    designs[i] = malloc(len1+2);
2835 		} else {
2836 		    normalized[i][len2-1] = '\0';
2837 		    designs[i][len1-1] = '\0';
2838 		}
2839 	    }
2840 	}
2841 
2842 	axislabel[i][k].text = (unichar_t *) _("Design Settings:");
2843 	axislabel[i][k].text_is_1byte = true;
2844 	axisgcd[i][k].gd.label = &axislabel[i][k];
2845 	axisgcd[i][k].gd.pos.x = 10; axisgcd[i][k].gd.pos.y = axisgcd[i][k-1].gd.pos.y+12;
2846 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2847 	axisgcd[i][k++].creator = GLabelCreate;
2848 
2849 	if ( designs[i]!=NULL ) {
2850 	    axislabel[i][k].text = (unichar_t *) designs[i];
2851 	    axislabel[i][k].text_is_1byte = true;
2852 	    axisgcd[i][k].gd.label = &axislabel[i][k];
2853 	}
2854 	axisgcd[i][k].gd.pos.x = 120; axisgcd[i][k].gd.pos.y = axisgcd[i][k-1].gd.pos.y-4;
2855 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2856 	axisgcd[i][k].gd.cid = CID_IntermediateDesign+i*100;
2857 	axisgcd[i][k++].creator = GTextFieldCreate;
2858 
2859 	axislabel[i][k].text = (unichar_t *) _("Normalized Settings:");
2860 	axislabel[i][k].text_is_1byte = true;
2861 	axisgcd[i][k].gd.label = &axislabel[i][k];
2862 	axisgcd[i][k].gd.pos.x = 10; axisgcd[i][k].gd.pos.y = axisgcd[i][k-1].gd.pos.y+28;
2863 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2864 	axisgcd[i][k++].creator = GLabelCreate;
2865 
2866 	if ( normalized[i]!=NULL ) {
2867 	    axislabel[i][k].text = (unichar_t *) normalized[i];
2868 	    axislabel[i][k].text_is_1byte = true;
2869 	    axisgcd[i][k].gd.label = &axislabel[i][k];
2870 	}
2871 	axisgcd[i][k].gd.pos.x = 120; axisgcd[i][k].gd.pos.y = axisgcd[i][k-1].gd.pos.y-4;
2872 	axisgcd[i][k].gd.flags = gg_visible | gg_enabled;
2873 	axisgcd[i][k].gd.cid = CID_IntermediateNormalized+i*100;
2874 	axisgcd[i][k++].creator = GTextFieldCreate;
2875 
2876 	k = GCDBuildNames(axisgcd[i],axislabel[i],k,mm==NULL || i>=mm->axis_count ? NULL :
2877 		mm->axismaps[i].axisnames);
2878 
2879 	axisaspects[i].text = (unichar_t *) _(axistablab[i]);
2880 	axisaspects[i].text_is_1byte = true;
2881 	axisaspects[i].gcd = axisgcd[i];
2882     }
2883     axisaspects[0].selected = true;
2884 
2885     agcd[0].gd.pos.x = 3; agcd[0].gd.pos.y = 3;
2886     agcd[0].gd.pos.width = MMW_Width-10;
2887     agcd[0].gd.pos.height = MMW_Height-45;
2888     agcd[0].gd.u.tabs = axisaspects;
2889     agcd[0].gd.flags = gg_visible | gg_enabled;
2890     agcd[0].gd.cid = CID_WhichAxis;
2891     agcd[0].creator = GTabSetCreate;
2892 
2893     GGadgetsCreate(mmw.subwins[mmw_axes],agcd);
2894     for ( i=0; i<4; ++i ) {
2895 	free(axislabel[i][1].text);
2896 	free(normalized[i]);
2897 	free(designs[i]);
2898     }
2899 
2900     memset(&designgcd,0,sizeof(designgcd));
2901     memset(&designlabel,0,sizeof(designlabel));
2902     memset(&dgcd,0,sizeof(dgcd));
2903     memset(&dlabel,0,sizeof(dlabel));
2904     memset(&designaspects,0,sizeof(designaspects));
2905 
2906     for ( i=0; i<AppleMmMax+1; ++i ) {
2907 	designlabel[i][0].text = (unichar_t *) _("Source from which this design is to be taken");
2908 	designlabel[i][0].text_is_1byte = true;
2909 	designgcd[i][0].gd.label = &designlabel[i][0];
2910 	designgcd[i][0].gd.pos.x = 3; designgcd[i][0].gd.pos.y = 4;
2911 	designgcd[i][0].gd.flags = gg_visible | gg_enabled;
2912 	designgcd[i][0].creator = GLabelCreate;
2913 
2914 	designgcd[i][1].gd.pos.x = 15; designgcd[i][1].gd.pos.y = 18;
2915 	designgcd[i][1].gd.pos.width = 200;
2916 	designgcd[i][1].gd.flags = gg_visible | gg_enabled;
2917 	designgcd[i][1].gd.cid = CID_DesignFonts + i*DesignScaleFactor;
2918 	designgcd[i][1].gd.handle_controlevent = MMW_CheckBrowse;
2919 	designgcd[i][1].creator = GListButtonCreate;
2920 
2921 	designlabel[i][2].text = (unichar_t *) _("Normalized position of this design along each axis");
2922 	designlabel[i][2].text_is_1byte = true;
2923 	designgcd[i][2].gd.label = &designlabel[i][2];
2924 	designgcd[i][2].gd.pos.x = 3; designgcd[i][2].gd.pos.y = 50;
2925 	designgcd[i][2].gd.flags = gg_visible | gg_enabled;
2926 	designgcd[i][2].creator = GLabelCreate;
2927 
2928 	designgcd[i][3].gd.pos.x = 15; designgcd[i][3].gd.pos.y = 64;
2929 	designgcd[i][3].gd.pos.width = 200;
2930 	designgcd[i][3].gd.flags = gg_visible | gg_enabled;
2931 	designgcd[i][3].gd.cid = CID_AxisWeights + i*DesignScaleFactor;
2932 	designgcd[i][3].creator = GTextFieldCreate;
2933 
2934 	designaspects[i].text = (unichar_t *) designtablab[i];
2935 	designaspects[i].text_is_1byte = true;
2936 	designaspects[i].gcd = designgcd[i];
2937     }
2938     designaspects[0].selected = true;
2939 
2940     dlabel.text = (unichar_t *) _("Master Designs");
2941     dlabel.text_is_1byte = true;
2942     dgcd[0].gd.label = &dlabel;
2943     dgcd[0].gd.pos.x = 3; dgcd[0].gd.pos.y = 4;
2944     dgcd[0].gd.flags = gg_visible | gg_enabled;
2945     dgcd[0].creator = GLabelCreate;
2946 
2947     dgcd[1].gd.pos.x = 3; dgcd[1].gd.pos.y = 18;
2948     dgcd[1].gd.pos.width = MMW_Width-10;
2949     dgcd[1].gd.pos.height = MMW_Height-60;
2950     dgcd[1].gd.u.tabs = designaspects;
2951     dgcd[1].gd.flags = gg_visible | gg_enabled;
2952     dgcd[1].gd.cid = CID_WhichDesign;
2953     dgcd[1].creator = GTabSetCreate;
2954 
2955     GGadgetsCreate(mmw.subwins[mmw_designs],dgcd);
2956 
2957     memset(&ngcd,0,sizeof(ngcd));
2958     memset(&nlabel,0,sizeof(nlabel));
2959 
2960     nlabel[0].text = (unichar_t *) _("Named Styles");
2961     nlabel[0].text_is_1byte = true;
2962     ngcd[0].gd.label = &nlabel[0];
2963     ngcd[0].gd.pos.x = 3; ngcd[0].gd.pos.y = 4;
2964     ngcd[0].gd.flags = gg_visible | gg_enabled;
2965     ngcd[0].creator = GLabelCreate;
2966 
2967     ngcd[1].gd.pos.x = 3; ngcd[1].gd.pos.y = 18;
2968     ngcd[1].gd.pos.width = MMW_Width-10;
2969     ngcd[1].gd.pos.height = MMW_Height-100;
2970     ngcd[1].gd.u.list = NamedDesigns(&mmw);
2971     ngcd[1].gd.flags = gg_visible | gg_enabled | gg_list_multiplesel;
2972     ngcd[1].gd.cid = CID_NamedDesigns;
2973     ngcd[1].gd.handle_controlevent = MMW_NamedSel;
2974     ngcd[1].creator = GListCreate;
2975 
2976     ngcd[2].gd.pos.x = 20; ngcd[2].gd.pos.y = ngcd[1].gd.pos.y + ngcd[1].gd.pos.height+5;
2977     ngcd[2].gd.pos.width = -1;
2978     ngcd[2].gd.flags = gg_visible | gg_enabled;
2979     nlabel[2].text = (unichar_t *) S_("Design|_New...");
2980     nlabel[2].text_is_1byte = true;
2981     nlabel[2].text_in_resource = true;
2982     ngcd[2].gd.label = &nlabel[2];
2983     ngcd[2].gd.cid = CID_NamedNew;
2984     ngcd[2].gd.handle_controlevent = MMW_NamedNew;
2985     ngcd[2].creator = GButtonCreate;
2986 
2987     ngcd[3].gd.pos.x = 20+blen+10; ngcd[3].gd.pos.y = ngcd[2].gd.pos.y;
2988     ngcd[3].gd.pos.width = -1;
2989     ngcd[3].gd.flags = gg_visible;
2990     nlabel[3].text = (unichar_t *) _("_Delete");
2991     nlabel[3].text_is_1byte = true;
2992     nlabel[3].text_in_resource = true;
2993     ngcd[3].gd.label = &nlabel[3];
2994     ngcd[3].gd.cid = CID_NamedDelete;
2995     ngcd[3].gd.handle_controlevent = MMW_NamedDelete;
2996     ngcd[3].creator = GButtonCreate;
2997 
2998     ngcd[4].gd.pos.x = 20+2*blen+20; ngcd[4].gd.pos.y = ngcd[2].gd.pos.y;
2999     ngcd[4].gd.pos.width = -1;
3000     ngcd[4].gd.flags = gg_visible;
3001     nlabel[4].text = (unichar_t *) _("_Edit...");
3002     nlabel[4].text_is_1byte = true;
3003     nlabel[4].text_in_resource = true;
3004     ngcd[4].gd.label = &nlabel[4];
3005     ngcd[4].gd.cid = CID_NamedEdit;
3006     ngcd[4].gd.handle_controlevent = MMW_NamedEdit;
3007     ngcd[4].creator = GButtonCreate;
3008 
3009     GGadgetsCreate(mmw.subwins[mmw_named],ngcd);
3010     if ( ngcd[1].gd.u.list!=NULL )
3011 	GTextInfoListFree(ngcd[1].gd.u.list);
3012 
3013     memset(&ogcd,0,sizeof(ogcd));
3014     memset(&olabels,0,sizeof(olabels));
3015 
3016     k=0;
3017     olabels[k].text = (unichar_t *) _("Normalize Design Vector Function:");
3018     olabels[k].text_is_1byte = true;
3019     ogcd[k].gd.label = &olabels[k];
3020     ogcd[k].gd.pos.x = 3; ogcd[k].gd.pos.y = 4;
3021     ogcd[k].gd.flags = gg_visible | gg_enabled;
3022     ogcd[k++].creator = GLabelCreate;
3023 
3024     ogcd[k].gd.pos.x = 3; ogcd[k].gd.pos.y = ogcd[k-1].gd.pos.y+15;
3025     ogcd[k].gd.pos.width = MMW_Width-10;
3026     ogcd[k].gd.pos.height = 8*12+10;
3027     ogcd[k].gd.flags = gg_visible | gg_enabled;
3028     ogcd[k].gd.cid = CID_NDV;
3029     ogcd[k++].creator = GTextAreaCreate;
3030 
3031     olabels[k].text = (unichar_t *) _("Convert Design Vector Function:");
3032     olabels[k].text_is_1byte = true;
3033     ogcd[k].gd.label = &olabels[k];
3034     ogcd[k].gd.pos.x = 3; ogcd[k].gd.pos.y = ogcd[k-1].gd.pos.y+ogcd[k-1].gd.pos.height+5;
3035     ogcd[k].gd.flags = gg_visible | gg_enabled;
3036     ogcd[k++].creator = GLabelCreate;
3037 
3038     ogcd[k].gd.pos.x = 3; ogcd[k].gd.pos.y = ogcd[k-1].gd.pos.y+15;
3039     ogcd[k].gd.pos.width = MMW_Width-10;
3040     ogcd[k].gd.pos.height = 8*12+10;
3041     ogcd[k].gd.flags = gg_visible | gg_enabled;
3042     ogcd[k].gd.cid = CID_CDV;
3043     ogcd[k++].creator = GTextAreaCreate;
3044 
3045     GGadgetsCreate(mmw.subwins[mmw_funcs],ogcd);
3046 
3047     memset(&ogcd,0,sizeof(ogcd));
3048     memset(&olabels,0,sizeof(olabels));
3049 
3050     k=0;
3051     olabels[k].text = (unichar_t *) _("Contribution of each master design");
3052     olabels[k].text_is_1byte = true;
3053     ogcd[k].gd.label = &olabels[k];
3054     ogcd[k].gd.pos.x = 10; ogcd[k].gd.pos.y = 4;
3055     ogcd[k].gd.flags = gg_visible | gg_enabled | gg_cb_on;
3056     ogcd[k].gd.cid = CID_Explicit;
3057     ogcd[k].gd.handle_controlevent = MMCB_Changed;
3058     ogcd[k++].creator = GRadioCreate;
3059 
3060     olabels[k].text = (unichar_t *) _("Design Axis Values");
3061     olabels[k].text_is_1byte = true;
3062     ogcd[k].gd.label = &olabels[k];
3063     ogcd[k].gd.pos.x = 10; ogcd[k].gd.pos.y = ogcd[k-1].gd.pos.y+45;
3064     ogcd[k].gd.flags = gg_visible | gg_enabled;
3065     ogcd[k].gd.cid = CID_ByDesign;
3066     ogcd[k].gd.handle_controlevent = MMCB_Changed;
3067     ogcd[k++].creator = GRadioCreate;
3068 
3069     ogcd[k].gd.pos.x = 15; ogcd[k].gd.pos.y = ogcd[k-2].gd.pos.y+18;
3070     ogcd[k].gd.pos.width = 240;
3071     ogcd[k].gd.flags = gg_visible | gg_enabled;
3072     ogcd[k].gd.cid = CID_NewBlends;
3073     ogcd[k++].creator = GTextFieldCreate;
3074 
3075     ogcd[k].gd.pos.x = 15; ogcd[k].gd.pos.y = ogcd[k-2].gd.pos.y+18;
3076     ogcd[k].gd.pos.width = 240;
3077     ogcd[k].gd.flags = gg_visible;
3078     ogcd[k].gd.cid = CID_NewDesign;
3079     ogcd[k++].creator = GTextFieldCreate;
3080 
3081     olabels[k].text = (unichar_t *) _("Force Bold Threshold:");
3082     olabels[k].text_is_1byte = true;
3083     ogcd[k].gd.label = &olabels[k];
3084     ogcd[k].gd.pos.x = 10; ogcd[k].gd.pos.y = ogcd[k-1].gd.pos.y+45;
3085     ogcd[k].gd.flags = gg_visible | gg_enabled;
3086     ogcd[k++].creator = GLabelCreate;
3087 
3088     if ( mmw.old!=NULL &&
3089 	    (pt = PSDictHasEntry(mmw.old->normal->private,"ForceBoldThreshold"))!=NULL )
3090 	olabels[k].text = (unichar_t *) pt;
3091     else
3092 	olabels[k].text = (unichar_t *) ".3";
3093     olabels[k].text_is_1byte = true;
3094     ogcd[k].gd.label = &olabels[k];
3095     ogcd[k].gd.pos.x = 15; ogcd[k].gd.pos.y = ogcd[k-1].gd.pos.y+13;
3096     ogcd[k].gd.flags = gg_visible | gg_enabled;
3097     ogcd[k].gd.cid = CID_ForceBoldThreshold;
3098     ogcd[k++].creator = GTextFieldCreate;
3099 
3100     GGadgetsCreate(mmw.subwins[mmw_others],ogcd);
3101 
3102     mmw.state = mmw_counts;
3103     MMW_SetState(&mmw);
3104     GDrawSetVisible(mmw.subwins[mmw.state],true);
3105     GDrawSetVisible(mmw.gw,true);
3106 
3107     while ( !mmw.done )
3108 	GDrawProcessOneEvent(NULL);
3109 
3110     GDrawDestroyWindow(gw);
3111 }
3112