1 /* -*- coding: utf-8 -*- */
2 /* Copyright (C) 2000-2012 by George Williams */
3 /*
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6 
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9 
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13 
14  * The name of the author may not be used to endorse or promote products
15  * derived from this software without specific prior written permission.
16 
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <fontforge-config.h>
30 
31 #include "autotrace.h"
32 #include "autowidth.h"
33 #include "bitmapchar.h"
34 #include "bvedit.h"
35 #include "cvundoes.h"
36 #include "fontforgeui.h"
37 #include "fvcomposite.h"
38 #include "fvfonts.h"
39 #include "gfile.h"
40 #include "gkeysym.h"
41 #include "gresedit.h"
42 #include "gresource.h"
43 #include "lookups.h"
44 #include "mm.h"
45 #include "splinefill.h"
46 #include "splineoverlap.h"
47 #include "splineutil.h"
48 #include "splineutil2.h"
49 #include "tottfgpos.h"
50 #include "ustring.h"
51 #include "utype.h"
52 #include "wordlistparser.h"
53 
54 #include <math.h>
55 
56 extern char* SFDCreateUndoForLookup( SplineFont *sf, int lookup_type ) ;
57 
58 
59 int mv_width = 800, mv_height = 300;
60 // Maximum number of characters to transfer from CharView to MetricsView
61 int fvmv_selectmax = 15;
62 int mvshowgrid = mv_hidegrid;
63 int mv_type = mv_widthonly;
64 static int mv_antialias = true;
65 static double mv_scales[] = { 8.0, 4.0, 2.0, 1.5, 1.0, 2.0/3.0, .5, 1.0/3.0, .25, .2, 1.0/6.0, .125, .1 };
66 #define SCALE_INDEX_NORMAL	4
67 
68 static Color widthcol = 0x808080;
69 static Color italicwidthcol = 0x909090;
70 static Color selglyphcol = 0x909090;
71 static Color kernlinecol = 0x008000;
72 static Color rbearinglinecol = 0x000080;
73 
74 int pref_mv_shift_and_arrow_skip = 10;
75 int pref_mv_control_shift_and_arrow_skip = 5;
76 
77 static void MVSelectChar(MetricsView *mv, int i);
78 static void MVSelectSetForAll(MetricsView *mv, int selected );
79 static void MVMoveInWordListByOffset( MetricsView *mv, int offset );
80 
MVMoveToNextInWordList(GGadget * g,GEvent * e)81 static int MVMoveToNextInWordList(GGadget *g, GEvent *e)
82 {
83     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
84         MetricsView *mv = GDrawGetUserData(GGadgetGetWindow(g));
85         MVMoveInWordListByOffset( mv, 1 );
86     }
87     return 1;
88 }
MVMoveToPrevInWordList(GGadget * g,GEvent * e)89 static int MVMoveToPrevInWordList(GGadget *g, GEvent *e)
90 {
91     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
92         MetricsView *mv = GDrawGetUserData(GGadgetGetWindow(g));
93         MVMoveInWordListByOffset( mv, -1 );
94     }
95     return 1;
96 }
97 
MVMoveInTableByColumnByOffset(MetricsView * mv,int offset)98 static void MVMoveInTableByColumnByOffset(MetricsView *mv, int offset) {
99 	int current_pos = 0;
100 	// Find the currently selected record.
101 	for (current_pos = 0; current_pos < mv->clen && mv->perchar[current_pos].selected == 0; current_pos ++);
102 	// Return on failure.
103 	if (current_pos >= mv->clen || mv->perchar[current_pos].selected == 0) return;
104 	// Ensure that we can move ahead the selected number of records. Return otherwise.
105 	if (current_pos + offset >= mv->clen) return;
106 	// Change the selection.
107 	mv->perchar[current_pos].selected = 0;
108 	mv->perchar[current_pos + offset].selected = 1;
109 	// Find the currently selected gadget.
110 	GGadget *current_gadget = GWindowGetFocusGadgetOfWindow(mv->gw);
111 	// Find which control in the current record it is.
112 	int current_gadget_type = 0; // 0 is nothing, 1 is Name, 2 is Width, 3 is LBearing, 4 is RBearing, 5 is Kern.
113 	// We do not currently use this value, but it seems likely to be useful.
114 	GGadget *target_gadget = NULL;
115 	if (current_gadget == mv->perchar[current_pos].name) {
116 		current_gadget_type = 1;
117 		target_gadget = mv->perchar[current_pos+offset].name;
118 	} else if (current_gadget == mv->perchar[current_pos].width) {
119 		current_gadget_type = 2;
120 		target_gadget = mv->perchar[current_pos+offset].width;
121 	} else if (current_gadget == mv->perchar[current_pos].lbearing) {
122 		current_gadget_type = 3;
123 		target_gadget = mv->perchar[current_pos+offset].lbearing;
124 	} else if (current_gadget == mv->perchar[current_pos].rbearing) {
125 		current_gadget_type = 4;
126 		target_gadget = mv->perchar[current_pos+offset].rbearing;
127 	} else if (current_gadget == mv->perchar[current_pos].kern) {
128 		current_gadget_type = 5;
129 		target_gadget = mv->perchar[current_pos+offset].kern;
130 	}
131 	// Abort if there is no selected control for the current record.
132 	if (current_gadget_type == 0) return;
133 	// Change the control focus.
134 	if (target_gadget != NULL) {
135 		GWidgetIndicateFocusGadget(target_gadget);
136 	}
137 	return;
138 }
139 
140 /**
141  * This doesn't need to be a perfect test by any means. It should
142  * return true if the currently active kerning lookup includes some
143  * class based kerning which might require GUI elements other than the
144  * currently active one to be drawn. The only price to pay by
145  * returning true all the time is a slight performance one when
146  * redrawing something that doesn't absolutely need to be redrawn.
147  */
haveClassBasedKerningInView(MetricsView * mv)148 static int haveClassBasedKerningInView( MetricsView* mv )
149 {
150     if( mv->cur_subtable )
151     {
152 	return mv->cur_subtable->kc > 0;
153     }
154     return 0;
155 }
156 
getSelectedChar(MetricsView * mv)157 static SplineChar* getSelectedChar( MetricsView* mv ) {
158     int i=0;
159     for ( i=0; i<mv->glyphcnt; ++i ) {
160 	if ( mv->perchar[i].selected ) {
161 	    return mv->chars[i];
162 	}
163     }
164     if( mv->glyphcnt==1 ) {
165 	return mv->chars[0];
166     }
167     return 0;
168 }
169 
170 
selectUserChosenWordListGlyphs(MetricsView * mv,void * userdata)171 static void selectUserChosenWordListGlyphs( MetricsView *mv, void* userdata )
172 {
173 //    printf("selectUserChosenWordListGlyphs(top)\n");
174     MVSelectSetForAll( mv, 0 );
175     // The previous check thought that userdata was in integer and wanted to verify that
176     // it was positive and not equal to -1 or to -2. Frank changed it.
177     if( userdata != NULL)
178     {
179 	if (userdata == (void*)(-1) || userdata == (void*)(-2))
180 	  fprintf(stderr, "Possible error; see the code here.\n");
181 
182 	WordListLine wll = (WordListLine)userdata;
183 	for( ; wll->sc; wll++ ) {
184 	    if( wll->isSelected ) {
185 		MVSelectChar( mv, wll->currentGlyphIndex );
186 	    }
187 	}
188     }
189 }
190 
MVGetSplineFontPieceMealFlags(MetricsView * mv)191 static int MVGetSplineFontPieceMealFlags( MetricsView *mv )
192 {
193     int ret = 0;
194 
195     ret = pf_ft_recontext;
196 
197     if( mv->antialias )
198 	ret |= pf_antialias;
199     if( !mv->usehinting )
200 	ret |= pf_ft_nohints;
201 
202     return ret;
203 }
204 
205 
MVColInit(void)206 void MVColInit( void ) {
207     static int cinit=false;
208     GResStruct mvcolors[] = {
209 	{ "AdvanceWidthColor", rt_color, &widthcol, NULL, 0 },
210 	{ "ItalicAdvanceColor", rt_color, &italicwidthcol, NULL, 0 },
211 	{ "SelectedGlyphColor", rt_color, &selglyphcol, NULL, 0 },
212 	{ "KernLineColor", rt_color, &kernlinecol, NULL, 0 },
213 	{ "SideBearingLineColor", rt_color, &rbearinglinecol, NULL, 0 },
214 	GRESSTRUCT_EMPTY
215     };
216     if ( !cinit ) {
217 	GResourceFind( mvcolors, "MetricsView.");
218 	cinit = true;
219     }
220 }
221 
222 static int MVSetVSb(MetricsView *mv);
223 
MVShowGrid(MetricsView * mv)224 static int MVShowGrid(MetricsView *mv) {
225     if ( mv->showgrid==mv_hidegrid || (mv->showgrid==mv_hidemovinggrid && mv->pressed ))
226 return( false );
227 
228 return( true );
229 }
230 
MVDrawLine(MetricsView * mv,GWindow pixmap,int xtop,int top,int xbot,int bot,Color col)231 static void MVDrawLine(MetricsView *mv,GWindow pixmap,
232 	int xtop, int top,int xbot,int bot,Color col) {
233     if ( mv->showgrid == mv_partialgrid ) {
234 	int y1, y2;
235 	int x1, x2;
236 	y1 =  bot +   ( top- bot)/4;
237 	x1 = xbot +   (xtop-xbot)/4;
238 	y2 =  bot + 4*( top- bot)/5;
239 	x2 = xbot + 4*(xtop-xbot)/5;
240 	GDrawDrawLine(pixmap,xtop,top,x2,y2,col);
241 	GDrawDrawLine(pixmap,x1,y1,xbot,bot,col);
242     } else
243 	GDrawDrawLine(pixmap,xtop,top,xbot,bot,col);
244 }
245 
MVSubVExpose(MetricsView * mv,GWindow pixmap,GEvent * event)246 static void MVSubVExpose(MetricsView *mv, GWindow pixmap, GEvent *event) {
247     /* Expose routine for vertical metrics */
248     GRect *clip;
249     int xbase, y, si, i, x, width, height;
250     double iscale = mv->pixelsize_set_by_window ? 1.0 : mv_scales[mv->scale_index];
251     int as = rint(iscale*mv->pixelsize*mv->sf->ascent/(double) (mv->sf->ascent+mv->sf->descent));
252     BDFChar *bdfc;
253     struct _GImage base;
254     GImage gi;
255     GClut clut;
256 
257     clip = &event->u.expose.rect;
258 
259     xbase = mv->vwidth/2;
260     if ( mv->showgrid )
261 	GDrawDrawLine(pixmap,xbase,0,xbase,mv->vheight,widthcol);
262 
263     if ( mv->bdf==NULL && MVShowGrid(mv) ) {
264 	y = mv->perchar[0].dy-mv->yoff;
265 	MVDrawLine(mv,pixmap,0,y,mv->vwidth,y,widthcol);
266     }
267 
268     si = -1;
269     for ( i=0; i<mv->glyphcnt; ++i ) {
270 	if ( mv->perchar[i].selected ) si = i;
271 	y = mv->perchar[i].dy-mv->yoff;
272 	if ( mv->bdf==NULL &&  MVShowGrid(mv)) {
273 	    int yp = y+mv->perchar[i].dheight+mv->perchar[i].kernafter;
274 	    MVDrawLine(mv,pixmap,0, yp,mv->vwidth,yp,
275 		    mv->type==mv_kernonly  && i!=mv->glyphcnt-1 ?kernlinecol :
276 		    mv->type==mv_widthonly                      ?rbearinglinecol :
277 			widthcol);
278 	}
279 	y += mv->perchar[i].yoff;
280 	bdfc = mv->bdf==NULL ?	BDFPieceMealCheck(mv->show,mv->glyphs[i].sc->orig_pos) :
281 				BDFGetMergedChar( mv->bdf->glyphs[mv->glyphs[i].sc->orig_pos]);
282 	if ( bdfc==NULL )
283     continue;
284 	y += as-rint(iscale * bdfc->ymax);
285 	if ( mv->perchar[i].selected )
286 	    y += mv->activeoff;
287 	x = xbase - rint(iscale * (mv->pixelsize/2 + bdfc->xmin) - mv->perchar[i].xoff);
288 	width = bdfc->xmax-bdfc->xmin+1; height = bdfc->ymax-bdfc->ymin+1;
289 	if ( clip->y+clip->height<y )
290     break;
291 	if ( y+iscale*height>=clip->y && x<clip->x+clip->width && x+iscale*width >= clip->x ) {
292 	    memset(&gi,'\0',sizeof(gi));
293 	    memset(&base,'\0',sizeof(base));
294 	    memset(&clut,'\0',sizeof(clut));
295 	    gi.u.image = &base;
296 	    base.clut = &clut;
297 	    if ( !bdfc->byte_data ) {
298 		base.image_type = it_mono;
299 		clut.clut_len = 2;
300 		clut.clut[0] = 0xffffff;
301 		if ( mv->perchar[i].selected )
302 		    clut.clut[1] = selglyphcol;
303 	    } else {
304 		int scale, l;
305 		Color fg, bg;
306 		if ( mv->bdf!=NULL )
307 		    scale = BDFDepth(mv->bdf);
308 		else
309 		    scale = BDFDepth(mv->show);
310 		base.image_type = it_index;
311 		clut.clut_len = 1<<scale;
312 		bg = view_bgcol;
313 		fg = ( mv->perchar[i].selected ) ? selglyphcol : 0x000000;
314 		for ( l=0; l<(1<<scale); ++l )
315 		    clut.clut[l] =
316 			COLOR_CREATE(
317 			 COLOR_RED(bg) + (l*(COLOR_RED(fg)-COLOR_RED(bg)))/((1<<scale)-1),
318 			 COLOR_GREEN(bg) + (l*(COLOR_GREEN(fg)-COLOR_GREEN(bg)))/((1<<scale)-1),
319 			 COLOR_BLUE(bg) + (l*(COLOR_BLUE(fg)-COLOR_BLUE(bg)))/((1<<scale)-1) );
320 	    }
321 	    base.data = bdfc->bitmap;
322 	    base.bytes_per_line = bdfc->bytes_per_line;
323 	    base.width = width;
324 	    base.height = height;
325 	    if ( mv->pixelsize_set_by_window || mv->scale_index==SCALE_INDEX_NORMAL )
326 		GDrawDrawGlyph(pixmap,&gi,NULL,x,y);
327 	    else
328 		GDrawDrawImageMagnified(pixmap, &gi, NULL, x,y,
329 			(int) rint((width*mv_scales[mv->scale_index])),
330 			(int) rint((height*mv_scales[mv->scale_index])));
331 	}
332 	if ( mv->bdf!=NULL ) BDFCharFree( bdfc );
333     }
334     if ( si!=-1 && mv->bdf==NULL &&  MVShowGrid(mv) && mv->type==mv_kernwidth ) {
335 	y = mv->perchar[si].dy-mv->yoff;
336 	if ( si!=0 )
337 	    MVDrawLine(mv,pixmap,0,y,mv->vwidth,y,kernlinecol);
338 	y += mv->perchar[si].dheight+mv->perchar[si].kernafter;
339 	MVDrawLine(mv,pixmap,0,y,mv->vwidth,y,rbearinglinecol);
340     }
341 }
342 
MVSubExpose(MetricsView * mv,GWindow pixmap,GEvent * event)343 static void MVSubExpose(MetricsView *mv, GWindow pixmap, GEvent *event) {
344     GRect old, *clip;
345     int x,y,ybase, width,height, i;
346     BDFChar *bdfc;
347     struct _GImage base;
348     GImage gi;
349     GClut clut;
350     int si;
351     double iscale = mv->pixelsize_set_by_window ? 1.0 : mv_scales[mv->scale_index];
352     double s = sin(-mv->sf->italicangle*FF_PI/180.);
353     int x_iaoffh = 0, x_iaoffl = 0;
354 
355     clip = &event->u.expose.rect;
356     GDrawPushClip(pixmap,clip,&old);
357     GDrawSetLineWidth(pixmap,0);
358 
359     if ( mv->vertical ) {
360 	MVSubVExpose(mv,pixmap,event);
361 	GDrawPopClip(pixmap,&old);
362 return;
363     }
364 
365     ybase = mv->ybaseline - mv->yoff;
366 
367     if ( mv->showgrid )
368 	GDrawDrawLine(pixmap,0,ybase,mv->dwidth,ybase,widthcol);
369 
370     if ( mv->bdf==NULL && MVShowGrid(mv) ) {
371 	x = mv->perchar[0].dx-mv->xoff;
372 	if ( mv->right_to_left )
373 	    x = mv->vwidth - x - mv->perchar[0].dwidth - mv->perchar[0].kernafter;
374 	MVDrawLine(mv,pixmap,x,0,x,mv->vheight,widthcol);
375 	x_iaoffh = rint(ybase*s), x_iaoffl = rint((mv->vheight-ybase)*s);
376 	if ( ItalicConstrained && x_iaoffh!=0 ) {
377 	    MVDrawLine(mv,pixmap,x+x_iaoffh,0,x-x_iaoffl,mv->vheight,italicwidthcol);
378 	}
379     }
380     si = -1;
381     for ( i=0; i<mv->glyphcnt; ++i ) {
382 	if ( mv->perchar[i].selected ) si = i;
383 	x = mv->perchar[i].dx-mv->xoff;
384 	if ( mv->right_to_left )
385 	    x = mv->vwidth - x - mv->perchar[i].dwidth - mv->perchar[i].kernafter;
386 	if ( mv->bdf==NULL && MVShowGrid(mv) ) {
387 	    int xp = x+mv->perchar[i].dwidth+mv->perchar[i].kernafter;
388 	    MVDrawLine(mv,pixmap,xp, 0,xp,mv->vheight,
389 		    mv->type==mv_kernonly  && i!=mv->glyphcnt-1 ?kernlinecol :
390 		    mv->type==mv_widthonly                      ?rbearinglinecol :
391 			widthcol);
392 	    if ( ItalicConstrained && x_iaoffh!=0 ) {
393 		MVDrawLine(mv,pixmap,xp+x_iaoffh,0,xp-x_iaoffl,mv->vheight,italicwidthcol);
394 	    }
395 	}
396 	if ( mv->right_to_left )
397 	    x += mv->perchar[i].kernafter-mv->perchar[i].xoff;
398 	else
399 	    x += mv->perchar[i].xoff;
400 	bdfc = mv->bdf==NULL ?	BDFPieceMealCheck(mv->show,mv->glyphs[i].sc->orig_pos) :
401 				BDFGetMergedChar( mv->bdf->glyphs[mv->glyphs[i].sc->orig_pos]);
402 	if ( bdfc==NULL )
403     continue;
404 	x += rint( iscale * bdfc->xmin );
405 	if ( mv->perchar[i].selected )
406 	    x += mv->activeoff;
407 	y = ybase - rint( iscale * bdfc->ymax ) - (iscale-1) - mv->perchar[i].yoff;
408 	width = bdfc->xmax-bdfc->xmin+1; height = bdfc->ymax-bdfc->ymin+1;
409 	if ( !mv->right_to_left && clip->x+clip->width<x )
410     break;
411 	if ( x+rint(iscale*width)>=clip->x && y<clip->y+clip->height && y+rint(iscale*height) >= clip->y ) {
412 	    memset(&gi,'\0',sizeof(gi));
413 	    memset(&base,'\0',sizeof(base));
414 	    memset(&clut,'\0',sizeof(clut));
415 	    gi.u.image = &base;
416 	    base.clut = &clut;
417 	    if ( !bdfc->byte_data ) {
418 		base.image_type = it_mono;
419 		clut.clut_len = 2;
420 		clut.clut[0] = 0xffffff;
421 		if ( mv->perchar[i].selected )
422 		    clut.clut[1] = selglyphcol;
423 	    } else {
424 		int lscale = 3000/mv->pixelsize, l;
425 		Color fg, bg;
426 		int scale;
427 		if ( mv->bdf!=NULL )
428 		    lscale = BDFDepth(mv->bdf);
429 		else
430 		    lscale = BDFDepth(mv->show);
431 		base.image_type = it_index;
432 		scale = lscale==8?256:lscale==4?16:4;
433 		clut.clut_len = scale;
434 		bg = view_bgcol;
435 		fg = ( mv->perchar[i].selected ) ? selglyphcol : 0x000000;
436 		for ( l=0; l<scale; ++l )
437 		    clut.clut[l] =
438 			COLOR_CREATE(
439 			 COLOR_RED(bg) + ((int32) (l*(COLOR_RED(fg)-COLOR_RED(bg))))/(scale-1),
440 			 COLOR_GREEN(bg) + ((int32) (l*(COLOR_GREEN(fg)-COLOR_GREEN(bg))))/(scale-1),
441 			 COLOR_BLUE(bg) + ((int32) (l*(COLOR_BLUE(fg)-COLOR_BLUE(bg))))/(scale-1) );
442 	    }
443 	    base.data = bdfc->bitmap;
444 	    base.bytes_per_line = bdfc->bytes_per_line;
445 	    base.width = width;
446 	    base.height = height;
447 	    if ( mv->pixelsize_set_by_window || mv->scale_index==SCALE_INDEX_NORMAL )
448 		GDrawDrawGlyph(pixmap,&gi,NULL,x,y);
449 	    else
450 		GDrawDrawImageMagnified(pixmap, &gi, NULL, x,y,
451 			(int) rint((width*mv_scales[mv->scale_index])),
452 			(int) rint((height*mv_scales[mv->scale_index])));
453 	}
454 	if ( mv->bdf!=NULL ) BDFCharFree( bdfc );
455     }
456     if ( si!=-1 && mv->bdf==NULL && MVShowGrid(mv) && mv->type==mv_kernwidth ) {
457 	x = mv->perchar[si].dx-mv->xoff;
458 	if ( mv->right_to_left )
459 	    x = mv->vwidth - x;
460 	if ( si!=0 )
461 	    MVDrawLine(mv,pixmap,x,0,x,mv->vheight,kernlinecol);
462 	if ( mv->right_to_left )
463 	    x -= mv->perchar[si].dwidth+mv->perchar[si].kernafter;
464 	else
465 	    x += mv->perchar[si].dwidth+mv->perchar[si].kernafter;
466 	 MVDrawLine(mv,pixmap,x, 0,x,mv->vheight,rbearinglinecol);
467     }
468     GDrawPopClip(pixmap,&old);
469 }
470 
MVExpose(MetricsView * mv,GWindow pixmap,GEvent * event)471 static void MVExpose(MetricsView *mv, GWindow pixmap, GEvent *event) {
472     GRect old, *clip;
473     int x;
474     int ke = mv->height-mv->sbh-(mv->fh+4);
475 
476     clip = &event->u.expose.rect;
477     if ( clip->y+clip->height < mv->topend )
478 return;
479     GDrawPushClip(pixmap,clip,&old);
480     GDrawSetLineWidth(pixmap,0);
481     for ( x=mv->mwidth; x<mv->width; x+=mv->mwidth ) {
482 	GDrawDrawLine(pixmap,x,mv->displayend,x,ke,0x000000);
483 	GDrawDrawLine(pixmap,x+mv->mwidth/2,ke,x+mv->mwidth/2,mv->height-mv->sbh,0x000000);
484     }
485     GDrawDrawLine(pixmap,0,mv->topend,mv->width,mv->topend,0x000000);
486     GDrawDrawLine(pixmap,0,mv->displayend,mv->width,mv->displayend,0x000000);
487     GDrawDrawLine(pixmap,0,mv->displayend+mv->fh+4,mv->width,mv->displayend+mv->fh+4,0x000000);
488     GDrawDrawLine(pixmap,0,mv->displayend+2*(mv->fh+4),mv->width,mv->displayend+2*(mv->fh+4),0x000000);
489     GDrawDrawLine(pixmap,0,mv->displayend+3*(mv->fh+4),mv->width,mv->displayend+3*(mv->fh+4),0x000000);
490     GDrawDrawLine(pixmap,0,mv->displayend+4*(mv->fh+4),mv->width,mv->displayend+4*(mv->fh+4),0x000000);
491     GDrawDrawLine(pixmap,0,mv->displayend+5*(mv->fh+4),mv->width,mv->displayend+5*(mv->fh+4),0x000000);
492     GDrawPopClip(pixmap,&old);
493 }
494 
MVSetSubtables(SplineFont * sf)495 static void MVSetSubtables(SplineFont *sf) {
496     GTextInfo **ti;
497     OTLookup *otl;
498     struct lookup_subtable *sub;
499     int cnt, doit;
500     MetricsView *mvs;
501     int selected;
502 
503     if ( sf->cidmaster ) sf = sf->cidmaster;
504 
505     /* There might be more than one metricsview wandering around. Update them all */
506     for ( mvs = sf->metrics; mvs!=NULL; mvs=mvs->next ) {
507 	selected = false;
508 	for ( doit = 0; doit<2; ++doit ) {
509 	    cnt = 0;
510 	    for ( otl=sf->gpos_lookups; otl!=NULL; otl=otl->next ) {
511 		if ( otl->lookup_type == gpos_pair && FeatureTagInFeatureScriptList(
512 			    mvs->vertical?CHR('v','k','r','n') : CHR('k','e','r','n'),
513 			    otl->features)) {
514 		    for ( sub=otl->subtables; sub!=NULL; sub=sub->next ) {
515 			if ( doit ) {
516 			    ti[cnt] = calloc(1,sizeof(GTextInfo));
517 			    ti[cnt]->text = utf82u_copy(sub->subtable_name);
518 			    ti[cnt]->userdata = sub;
519 			    if ( sub==mvs->cur_subtable )
520 				ti[cnt]->selected = selected = true;
521 			    ti[cnt]->disabled = sub->kc!=NULL;
522 			    ti[cnt]->fg = ti[cnt]->bg = COLOR_DEFAULT;
523 			}
524 			++cnt;
525 		    }
526 		}
527 	    }
528 	    if ( !doit )
529 		ti = calloc(cnt+3,sizeof(GTextInfo *));
530 	    else {
531 		if ( cnt!=0 ) {
532 		    ti[cnt] = calloc(1,sizeof(GTextInfo));
533 		    ti[cnt]->line = true;
534 		    ti[cnt]->fg = ti[cnt]->bg = COLOR_DEFAULT;
535 		    ++cnt;
536 		}
537 		ti[cnt] = calloc(1,sizeof(GTextInfo));
538 		ti[cnt]->text = utf82u_copy(_("New Lookup Subtable..."));
539 		ti[cnt]->userdata = NULL;
540 		ti[cnt]->fg = ti[cnt]->bg = COLOR_DEFAULT;
541 		ti[cnt]->selected = !selected;
542 		++cnt;
543 		ti[cnt] = calloc(1,sizeof(GTextInfo));
544 	    }
545 	}
546 	if ( !selected )
547 	    mvs->cur_subtable = NULL;
548 
549 	GGadgetSetList(mvs->subtable_list,ti,false);
550     }
551 }
552 
MVSetFeatures(MetricsView * mv)553 static void MVSetFeatures(MetricsView *mv) {
554     SplineFont *sf = mv->sf;
555     int i, j, cnt;
556     GTextInfo **ti=NULL;
557     uint32 *tags = NULL, script, lang;
558     char buf[16];
559     uint32 *stds;
560     const unichar_t *pt = _GGadgetGetTitle(mv->script);
561 
562     if ( sf->cidmaster ) sf=sf->cidmaster;
563 
564     script = DEFAULT_SCRIPT;
565     lang = DEFAULT_LANG;
566     if ( u_strlen(pt)>=4 )
567 	script = (pt[0]<<24) | (pt[1]<<16) | (pt[2]<<8) | pt[3];
568     if ( pt[4]=='{' && u_strlen(pt)>=9 )
569 	lang = (pt[5]<<24) | (pt[6]<<16) | (pt[7]<<8) | pt[8];
570     if ( (uint32)mv->oldscript!=script || (uint32)mv->oldlang!=lang )
571 	stds = StdFeaturesOfScript(script);
572     else {		/* features list may have changed, but retain those set */
573 	int32 len, sc;
574 	ti = GGadgetGetList(mv->features,&len);
575 	stds = malloc((len+1)*sizeof(uint32));
576 	for ( i=sc=0; i<len; ++i ) if ( ti[i]->selected )
577 	    stds[sc++] = (uint32) (intpt) ti[i]->userdata;
578 	stds[sc] = 0;
579     }
580 
581     tags = SFFeaturesInScriptLang(sf,-2,script,lang);
582     /* Never returns NULL */
583     for ( cnt=0; tags[cnt]!=0; ++cnt );
584 
585     /*qsort(tags,cnt,sizeof(uint32),tag_comp);*/ /* The glist will do this for us */
586 
587     ti = malloc((cnt+2)*sizeof(GTextInfo *));
588     for ( i=0; i<cnt; ++i ) {
589 	ti[i] = calloc( 1,sizeof(GTextInfo));
590 	ti[i]->fg = ti[i]->bg = COLOR_DEFAULT;
591 	if ( (tags[i]>>24)<' ' || (tags[i]>>24)>0x7e )
592 	    sprintf( buf, "<%d,%d>", tags[i]>>16, tags[i]&0xffff );
593 	else {
594 	    buf[0] = tags[i]>>24; buf[1] = tags[i]>>16; buf[2] = tags[i]>>8; buf[3] = tags[i]; buf[4] = 0;
595 	}
596 	ti[i]->text = uc_copy(buf);
597 	ti[i]->userdata = (void *) (intpt) tags[i];
598 	for ( j=0; stds[j]!=0; ++j ) {
599 	    if ( stds[j] == tags[i] ) {
600 		ti[i]->selected = true;
601 	break;
602 	    }
603 	}
604     }
605     ti[i] = calloc(1,sizeof(GTextInfo));
606     GGadgetSetList(mv->features,ti,false);
607     mv->oldscript = script; mv->oldlang = lang;
608 }
609 
MVSelectSubtable(MetricsView * mv,struct lookup_subtable * sub)610 static void MVSelectSubtable(MetricsView *mv, struct lookup_subtable *sub) {
611     int32 len; int i;
612     GTextInfo **old = GGadgetGetList(mv->subtable_list,&len);
613 
614     for ( i=0; i<len && (old[i]->userdata!=sub || old[i]->line); ++i )
615     {
616 	// nothing //
617     }
618 //    printf("MVSelectSubtable() i:%d sub:%p\n", i, sub );
619     GGadgetSelectOneListItem(mv->subtable_list,i);
620     if ( sub )
621 	mv->cur_subtable = sub;
622 }
623 
MVRedrawI(MetricsView * mv,int i,int oldxmin,int oldxmax)624 static void MVRedrawI(MetricsView *mv,int i,int oldxmin,int oldxmax) {
625     GRect r;
626     BDFChar *bdfc;
627     int off = 0;
628 
629     if ( mv->right_to_left || mv->vertical ) {
630 	/* right to left clipping is hard to think about, it doesn't happen */
631 	/*  often enough (I think) for me to put the effort to make it efficient */
632 	GDrawRequestExpose(mv->v,NULL,false);
633 return;
634     }
635     if ( mv->perchar[i].selected )
636 	off = mv->activeoff;
637     r.y = 0; r.height = mv->vheight;
638     r.x = mv->perchar[i].dx-mv->xoff; r.width = mv->perchar[i].dwidth;
639     if ( mv->perchar[i].kernafter>0 )
640 	r.width += mv->perchar[i].kernafter;
641     if ( mv->perchar[i].xoff<0 ) {
642 	r.x += mv->perchar[i].xoff;
643 	r.width -= mv->perchar[i].xoff;
644     } else
645 	r.width += mv->perchar[i].xoff;
646     bdfc = mv->bdf==NULL ?  BDFPieceMealCheck(mv->show,mv->glyphs[i].sc->orig_pos) :
647 			    mv->bdf->glyphs[mv->glyphs[i].sc->orig_pos];
648     if ( bdfc==NULL )
649 return;
650     if ( bdfc->xmax+off+1>r.width ) r.width = bdfc->xmax+off+1;
651     if ( oldxmax+1>r.width ) r.width = oldxmax+1;
652     if ( bdfc->xmin+off<0 ) {
653 	r.x += bdfc->xmin+off;
654 	r.width -= bdfc->xmin+off;
655     }
656     if ( oldxmin<bdfc->xmin ) {
657 	r.width += (bdfc->xmin+off-oldxmin);
658 	r.x -= (bdfc->xmin+off-oldxmin);
659     }
660     if ( mv->right_to_left )
661 	r.x = mv->dwidth - r.x - r.width;
662     GDrawRequestExpose(mv->v,&r,false);
663     if ( mv->perchar[i].selected && i!=0 ) {
664 	struct lookup_subtable *sub = mv->glyphs[i].kp!=NULL ? mv->glyphs[i].kp->subtable : mv->glyphs[i].kc!=NULL && mv->glyphs[i].kc->offsets[mv->glyphs[i].kc_index]!=0 ? mv->glyphs[i].kc->subtable : NULL;
665 	if ( sub!=NULL )
666 	    MVSelectSubtable(mv,sub);
667     }
668 }
669 
MVDeselectChar(MetricsView * mv,int i)670 static void MVDeselectChar(MetricsView *mv, int i) {
671 
672     mv->perchar[i].selected = false;
673     if ( mv->perchar[i].name!=NULL )
674 	GGadgetSetEnabled(mv->perchar[i].name,mv->bdf==NULL);
675     MVRedrawI(mv,i,0,0);
676 }
677 
MVSelectSubtableForScript(MetricsView * mv,uint32 script)678 static void MVSelectSubtableForScript(MetricsView *mv,uint32 script) {
679     int32 len;
680     GTextInfo **ti = GGadgetGetList(mv->subtable_list,&len);
681     struct lookup_subtable *sub;
682     int i;
683 
684     if ( mv->cur_subtable != NULL && FeatureScriptTagInFeatureScriptList(
685 	    mv->vertical?CHR('v','k','r','n') : CHR('k','e','r','n'),script,
686 	    mv->cur_subtable->lookup->features ))
687 return;
688 
689     sub = NULL;
690     for ( i=0; i<len; ++i )
691 	if ( ti[i]->userdata!=NULL &&
692 		FeatureScriptTagInFeatureScriptList(
693 		    mv->vertical?CHR('v','k','r','n') : CHR('k','e','r','n'),
694 		    script,((struct lookup_subtable *) (ti[i]->userdata))->lookup->features)) {
695 	    sub = ti[i]->userdata;
696     break;
697 	}
698     if ( sub!=NULL )
699 	MVSelectSubtable(mv,sub);
700 }
701 
702 
MVSelectChar(MetricsView * mv,int i)703 static void MVSelectChar(MetricsView *mv, int i) {
704 
705     if ( i>=mv->glyphcnt || i<0 )
706 return;
707     mv->perchar[i].selected = true;
708     if ( mv->perchar[i].name!=NULL )
709 	GGadgetSetEnabled(mv->perchar[i].name,false);
710     if ( mv->glyphs[i].kp!=NULL )
711 	MVSelectSubtable(mv,mv->glyphs[i].kp->subtable);
712     else if ( mv->glyphs[i].kc!=NULL && mv->glyphs[i].kc->offsets[mv->glyphs[i].kc_index]!=0 )
713 	MVSelectSubtable(mv,mv->glyphs[i].kc->subtable);
714     else
715 	MVSelectSubtableForScript(mv,SCScriptFromUnicode(mv->glyphs[i].sc));
716     MVRedrawI(mv,i,0,0);
717 }
718 
MVDoSelect(MetricsView * mv,int i)719 static void MVDoSelect(MetricsView *mv, int i) {
720     int j;
721 
722     for ( j=0; j<mv->glyphcnt; ++j )
723 	if ( j!=i && mv->perchar[j].selected )
724 	    MVDeselectChar(mv,j);
725     MVSelectChar(mv,i);
726 }
727 
MVSelectSetForAll(MetricsView * mv,int selected)728 static void MVSelectSetForAll(MetricsView *mv, int selected )
729 {
730     int i;
731 
732     for ( i=0 ; i<mv->glyphcnt; ++i )
733     {
734 	if( selected )
735 	    MVSelectChar(mv,i);
736 	else
737 	    MVDeselectChar(mv,i);
738     }
739 }
740 
MVRefreshChar(MetricsView * mv,SplineChar * sc)741 void MVRefreshChar(MetricsView *mv, SplineChar *sc) {
742     int i;
743     for ( i=0; i<mv->glyphcnt; ++i ) if ( mv->glyphs[i].sc == sc )
744 	MVRedrawI(mv,i,0,0);
745 }
746 
MVRefreshValues(MetricsView * mv,int i)747 static void MVRefreshValues(MetricsView *mv, int i) {
748     char buf[40];
749     DBounds bb;
750     SplineChar *sc = mv->glyphs[i].sc;
751     int kern_offset;
752 
753     SplineCharFindBounds(sc,&bb);
754 
755     if( !mv->perchar[i].name )
756 	return;
757     GGadgetSetTitle8(mv->perchar[i].name,sc->name);
758 
759 if( !mv->perchar[i].width )
760 return;
761 
762     //printf("MVRefreshValues() **** setting width to %d\n", sc->width );
763     sprintf(buf,"%d",mv->vertical ? sc->vwidth : sc->width);
764     GGadgetSetTitle8(mv->perchar[i].width,buf);
765 
766     sprintf(buf,"%.2f",mv->vertical ? sc->parent->ascent-(double) bb.maxy : (double) bb.minx);
767     if ( buf[strlen(buf)-1]=='0' ) {
768 	buf[strlen(buf)-1] = '\0';
769 	if ( buf[strlen(buf)-1]=='0' ) {
770 	    buf[strlen(buf)-1] = '\0';
771 	    if ( buf[strlen(buf)-1]=='.' )
772 		buf[strlen(buf)-1] = '\0';
773 	}
774     }
775     GGadgetSetTitle8(mv->perchar[i].lbearing,buf);
776 
777     sprintf(buf,"%.2f",(double) (mv->vertical ? sc->vwidth-(sc->parent->ascent-bb.miny) : sc->width-bb.maxx));
778     if ( buf[strlen(buf)-1]=='0' ) {
779 	buf[strlen(buf)-1] = '\0';
780 	if ( buf[strlen(buf)-1]=='0' ) {
781 	    buf[strlen(buf)-1] = '\0';
782 	    if ( buf[strlen(buf)-1]=='.' )
783 		buf[strlen(buf)-1] = '\0';
784 	}
785     }
786     GGadgetSetTitle8(mv->perchar[i].rbearing,buf);
787 
788     kern_offset = 0x7ffffff;
789     if ( mv->glyphs[i].kp!=NULL )
790 	kern_offset = mv->glyphs[i].kp->off;
791     else if ( mv->glyphs[i].kc!=NULL )
792 	kern_offset = mv->glyphs[i].kc->offsets[ mv->glyphs[i].kc_index ];
793 if( !mv->perchar[i+1].kern )
794   return;
795 
796     if ( kern_offset!=0x7ffffff && i!=mv->glyphcnt-1 ) {
797 	sprintf(buf,"%d",kern_offset);
798 	GGadgetSetTitle8(mv->perchar[i+1].kern,buf);
799     } else if ( i!=mv->glyphcnt-1 )
800 	GGadgetSetTitle8(mv->perchar[i+1].kern,"");
801 }
802 
MVMakeLabels(MetricsView * mv)803 static void MVMakeLabels(MetricsView *mv) {
804     static GBox small = GBOX_EMPTY;
805     GGadgetData gd;
806     GTextInfo label;
807 
808     small.main_background = small.main_foreground = COLOR_DEFAULT;
809     memset(&gd,'\0',sizeof(gd));
810     memset(&label,'\0',sizeof(label));
811 
812     mv->mwidth = GGadgetScale(60);
813     mv->displayend = mv->height- mv->sbh - 5*(mv->fh+4);
814     /* We might not have set mv->vheight-2 yet */
815     if ( mv->pixelsize_set_by_window )
816 	mv->pixelsize = mv_scales[mv->scale_index]*(mv->displayend - mv->topend - 4);
817 
818     label.text = (unichar_t *) _("Name:");
819     label.text_is_1byte = true;
820     label.font = mv->font;
821     gd.pos.x = 2; gd.pos.width = mv->mwidth-4;
822     gd.pos.y = mv->displayend+2;
823     gd.pos.height = mv->fh;
824     gd.label = &label;
825     gd.box = &small;
826     gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels | gg_dontcopybox;
827     mv->namelab = GLabelCreate(mv->gw,&gd,NULL);
828 
829     label.text = (unichar_t *) (mv->vertical ? _("Height:") : _("Width:") );
830     gd.pos.y += mv->fh+4;
831     mv->widthlab = GLabelCreate(mv->gw,&gd,NULL);
832 
833 /* GT: Top/Left (side) bearing */
834     label.text = (unichar_t *) (mv->vertical ? _("TBearing:") : _("LBearing:") );
835     gd.pos.y += mv->fh+4;
836     mv->lbearinglab = GLabelCreate(mv->gw,&gd,NULL);
837 
838 /* GT: Bottom/Right (side) bearing */
839     label.text = (unichar_t *) (mv->vertical ? _("BBearing:") : _("RBearing:") );
840     gd.pos.y += mv->fh+4;
841     mv->rbearinglab = GLabelCreate(mv->gw,&gd,NULL);
842 
843     label.text = (unichar_t *) (mv->vertical ? _("VKern:") : _("Kern:"));
844     gd.pos.y += mv->fh+4;
845     mv->kernlab = GLabelCreate(mv->gw,&gd,NULL);
846 }
847 
848 static int MV_KernChanged(GGadget *g, GEvent *e);
849 static int MV_RBearingChanged(GGadget *g, GEvent *e);
850 static int MV_LBearingChanged(GGadget *g, GEvent *e);
851 static int MV_WidthChanged(GGadget *g, GEvent *e);
852 
MVCreateFields(MetricsView * mv,int i)853 static void MVCreateFields(MetricsView *mv,int i) {
854     static GBox small = GBOX_EMPTY;
855     GGadgetData gd;
856     GTextInfo label;
857     static unichar_t nullstr[1] = { 0 };
858     int j;
859     extern GBox _GGadget_gtextfield_box;
860     int udaidx = 1; // we leave element zero to be NULL to allow bounds checking.
861 
862     small = _GGadget_gtextfield_box;
863     small.flags = 0;
864     small.border_type = bt_none;
865     small.border_shape = bs_rect;
866     small.border_width = 0;
867     small.padding = 0;
868 
869     memset(&gd,'\0',sizeof(gd));
870     memset(&label,'\0',sizeof(label));
871     memset(mv->perchar[i].updownkparray,'\0',sizeof(GGadget*)*10);
872     label.text = nullstr;
873     label.font = mv->font;
874     mv->perchar[i].mx = gd.pos.x = mv->mbase+(i+1-mv->coff)*mv->mwidth+2;
875     mv->perchar[i].mwidth = gd.pos.width = mv->mwidth-4;
876     gd.pos.y = mv->displayend+2;
877     gd.pos.height = mv->fh;
878     gd.label = &label;
879     gd.box = &small;
880     gd.flags = gg_visible | gg_pos_in_pixels | gg_dontcopybox;
881     if ( mv->bdf==NULL )
882 	gd.flags |= gg_enabled;
883     mv->perchar[i].name = GLabelCreate(mv->gw,&gd,(void *) (intpt) i);
884     if ( mv->perchar[i].selected )
885 	GGadgetSetEnabled(mv->perchar[i].name,false);
886 
887     gd.pos.y += mv->fh+4;
888     gd.handle_controlevent = MV_WidthChanged;
889     mv->perchar[i].width = GTextFieldCreate(mv->gw,&gd,(void *) (intpt) i);
890     mv->perchar[i].updownkparray[udaidx++] = mv->perchar[i].width;
891 
892     gd.pos.y += mv->fh+4;
893     gd.handle_controlevent = MV_LBearingChanged;
894     mv->perchar[i].lbearing = GTextFieldCreate(mv->gw,&gd,(void *) (intpt) i);
895     mv->perchar[i].updownkparray[udaidx++] = mv->perchar[i].lbearing;
896 
897     gd.pos.y += mv->fh+4;
898     gd.handle_controlevent = MV_RBearingChanged;
899     mv->perchar[i].rbearing = GTextFieldCreate(mv->gw,&gd,(void *) (intpt) i);
900     mv->perchar[i].updownkparray[udaidx++] = mv->perchar[i].rbearing;
901 
902     if ( i!=0 ) {
903 	gd.pos.y += mv->fh+4;
904 	gd.pos.x -= mv->mwidth/2;
905 	gd.handle_controlevent = MV_KernChanged;
906 	mv->perchar[i].kern = GTextFieldCreate(mv->gw,&gd,(void *) (intpt) i);
907 	if( i==1 ) {
908 	    mv->perchar[i-1].updownkparray[udaidx] = mv->perchar[i].kern;
909 	}
910 	mv->perchar[i].updownkparray[udaidx++] = mv->perchar[i].kern;
911 
912 	if ( i>=mv->glyphcnt ) {
913 	    for ( j=mv->glyphcnt+1; j<=i ; ++ j )
914 		mv->perchar[j].dx = mv->perchar[j-1].dx;
915 	    mv->glyphcnt = i+1;
916 	}
917     }
918 
919     GWidgetIndicateFocusGadget(mv->text);
920 }
921 
922 static void MVSetSb(MetricsView *mv);
923 static int MVSetVSb(MetricsView *mv);
924 
MVRefreshMetric(MetricsView * mv)925 void MVRefreshMetric(MetricsView *mv) {
926     double iscale = mv->pixelsize_set_by_window ? 1.0 : mv_scales[mv->scale_index];
927     double scale = iscale*mv->pixelsize/(double) (mv->sf->ascent+mv->sf->descent);
928     SplineFont *sf = mv->sf;
929     int cnt;
930     // Count the valid glyphs and segfault if there is no null splinechar terminator.
931     for ( cnt=0; mv->glyphs[cnt].sc!=NULL; ++cnt );
932     // Calculate positions.
933     int x = 10; int y = 10;
934     for ( int i=0; i<cnt; ++i ) {
935 	MVRefreshValues(mv,i);
936 	SplineChar * sc = mv->glyphs[i].sc;
937 	BDFChar * bdfc = mv->bdf!=NULL ? mv->bdf->glyphs[sc->orig_pos] : BDFPieceMealCheck(mv->show,sc->orig_pos);
938 	mv->perchar[i].dwidth = rint(iscale * bdfc->width);
939 	mv->perchar[i].dx = x;
940 	mv->perchar[i].xoff = rint(iscale * mv->glyphs[i].vr.xoff);
941 	mv->perchar[i].yoff = rint(iscale * mv->glyphs[i].vr.yoff);
942 	mv->perchar[i].kernafter = rint(iscale * mv->glyphs[i].vr.h_adv_off);
943 	x += mv->perchar[i].dwidth + mv->perchar[i].kernafter;
944 
945 	mv->perchar[i].dheight = rint(sc->vwidth*scale);
946 	mv->perchar[i].dy = y;
947 	if ( mv->vertical ) {
948 	    mv->perchar[i].kernafter = rint( iscale * mv->glyphs[i].vr.v_adv_off);
949 	    y += mv->perchar[i].dheight + mv->perchar[i].kernafter;
950 	}
951     }
952     MVSetVSb(mv);
953     MVSetSb(mv);
954 }
955 
MVRemetric(MetricsView * mv)956 static void MVRemetric(MetricsView *mv) {
957     SplineChar *anysc, *goodsc;
958     int i, cnt, x, y, goodpos;
959     const unichar_t *_script = _GGadgetGetTitle(mv->script);
960     uint32 script, lang, *feats;
961     char buf[20];
962     int32 len;
963     GTextInfo **ti;
964     SplineChar *sc;
965     BDFChar *bdfc;
966     double iscale = mv->pixelsize_set_by_window ? 1.0 : mv_scales[mv->scale_index];
967     double scale = iscale*mv->pixelsize/(double) (mv->sf->ascent+mv->sf->descent);
968     SplineFont *sf;
969 
970     anysc = goodsc = NULL; goodpos = -1;
971     // We recurse through all of the characters in the metrics view.
972     for ( i=0; i<mv->clen && mv->chars[i] ; ++i ) {
973         // We assign the first splinechar to anysc.
974 	if ( anysc==NULL ) anysc = mv->chars[i];
975         // We assign the first splinechar of a non-default script to goodsc.
976 	if ( SCScriptFromUnicode(mv->chars[i])!=DEFAULT_SCRIPT ) {
977 	    goodsc = mv->chars[i];
978 	    goodpos = i;
979     break;
980 	}
981     }
982     if ( _script[0]=='D' && _script[1]=='F' && _script[2]=='L' && _script[3]=='T' ) {
983 	if ( goodsc!=NULL ) {
984 	    /* Set the script */ /* Remember if we get here the script is DFLT */
985             // To be clear, in this case, we set the script of the metrics view according to the first non-default script in the set of selected characters.
986 	    script = SCScriptFromUnicode(goodsc);
987 	    buf[0] = script>>24; buf[1] = script>>16; buf[2] = script>>8; buf[3] = script;
988 	    strcpy(buf+4,"{dflt}");
989 	    GGadgetSetTitle8(mv->script,buf);
990 	    MVSelectSubtableForScript(mv,script);
991 	    MVSetFeatures(mv);
992 	}
993     } else {
994 	if ( anysc==NULL ) {
995 	    /* If we get here the script is not DFLT */
996 	    GGadgetSetTitle8(mv->script,"DFLT{dflt}");
997             // Why do se set the title to DFLT then?
998 	    MVSetFeatures(mv);
999 	}
1000     }
1001     _script = _GGadgetGetTitle(mv->script);
1002     script = DEFAULT_SCRIPT; lang = DEFAULT_LANG;
1003     if ( u_strlen(_script)>=4 && (u_strchr(_script,'{')==NULL || u_strchr(_script,'{')-_script>=4)) {
1004         // If there is a four-character script identifier, pack it in script.
1005         // If there is a language identifier, pack that in lang.
1006 	unichar_t *pt;
1007 	script = (_script[0]<<24) | (_script[1]<<16) | (_script[2]<<8) | _script[3];
1008 	if ( (pt = u_strchr(_script,'{'))!=NULL && u_strlen(pt+1)>=4 &&
1009 		(u_strchr(pt+1,'}')==NULL || u_strchr(pt+1,'}')-(pt+1)>=4 ))
1010 	    lang = (pt[1]<<24) | (pt[2]<<16) | (pt[3]<<8) | pt[4];
1011     }
1012 
1013     // Parse the current list of features into feats.
1014     ti = GGadgetGetList(mv->features,&len);
1015     for ( i=cnt=0; i<len; ++i )
1016 	if ( ti[i]->selected ) ++cnt;
1017     feats = calloc(cnt+1,sizeof(uint32));
1018     for ( i=cnt=0; i<len; ++i )
1019 	if ( ti[i]->selected )
1020 	    feats[cnt++] = (intpt) ti[i]->userdata;
1021 
1022     // Regenerate glyphs for the selected characters according to features, script, and resolution.
1023     free(mv->glyphs); mv->glyphs = NULL;
1024     sf = mv->sf;
1025     if ( sf->cidmaster ) sf = sf->cidmaster;
1026     mv->glyphs = ApplyTickedFeatures(sf,feats,script, lang, mv->pixelsize, mv->chars);
1027     free(feats);
1028     if ( goodsc!=NULL )
1029 	mv->right_to_left = SCRightToLeft(goodsc)?1:0;
1030 
1031     // Count the valid glyphs and segfault if there is no null splinechar terminator.
1032     for ( cnt=0; mv->glyphs[cnt].sc!=NULL; ++cnt );
1033     // If there are too many relative to the available rows, make space.
1034     if ( cnt>=mv->max ) {
1035 	int oldmax=mv->max;
1036 	mv->max = cnt+10;
1037 	mv->perchar = realloc(mv->perchar,mv->max*sizeof(struct metricchar));
1038 	memset(mv->perchar+oldmax,'\0',(mv->max-oldmax)*sizeof(struct metricchar));
1039     }
1040     // Null names of controls in rows to be abandoned, starting at the last valid glyph and continuing to the end of mv->glyphs.
1041     // This may segfault here if mv->max is less than mv->glyphcnt, thus if cnt was 10 less than mv->glyphcnt.
1042     // It may segfault in GGadgetSetTitle if the gadgets do not exist.
1043     for ( i=cnt; i<mv->glyphcnt; ++i ) {
1044 	static unichar_t nullstr[] = { 0 };
1045 	GGadgetSetTitle(mv->perchar[i].name,nullstr);
1046 	GGadgetSetTitle(mv->perchar[i].width,nullstr);
1047 	GGadgetSetTitle(mv->perchar[i].lbearing,nullstr);
1048 	GGadgetSetTitle(mv->perchar[i].rbearing,nullstr);
1049 	if ( mv->perchar[i].kern!=NULL )
1050 	    GGadgetSetTitle(mv->perchar[i].kern,nullstr);
1051     }
1052     // So we set mv->glyphcnt to something possibly less than the size of mv->glyphs.
1053     mv->glyphcnt = cnt;
1054     // We now populate any new rows with controls.
1055     for ( i=0; i<cnt; ++i ) {
1056 	if ( mv->perchar[i].width==NULL ) {
1057 	    MVCreateFields(mv,i);
1058 	}
1059     }
1060     // Refresh.
1061     MVRefreshMetric(mv);
1062 }
1063 
MVReKern(MetricsView * mv)1064 void MVReKern(MetricsView *mv) {
1065     MVRemetric(mv);
1066     GDrawRequestExpose(mv->v,NULL,false);
1067 }
1068 
MVRegenChar(MetricsView * mv,SplineChar * sc)1069 void MVRegenChar(MetricsView *mv, SplineChar *sc) {
1070     int i;
1071 
1072     if( !sc->suspendMetricsViewEventPropagation )
1073     {
1074 	if ( mv->bdf==NULL && sc->orig_pos<mv->show->glyphcnt )
1075 	{
1076 	    BDFCharFree(mv->show->glyphs[sc->orig_pos]);
1077 	    mv->show->glyphs[sc->orig_pos] = NULL;
1078 	}
1079     }
1080 
1081     for ( i=0; i<mv->glyphcnt; ++i ) {
1082 	MVRefreshValues(mv,i);
1083     }
1084     for ( i=0; i<mv->glyphcnt; ++i )
1085     {
1086 	if ( mv->glyphs[i].sc == sc )
1087 	    break;
1088     }
1089     if ( i>=mv->glyphcnt )
1090 	return;		/* Not displayed */
1091     MVRemetric(mv);
1092     GDrawRequestExpose(mv->v,NULL,false);
1093 }
1094 
MVChangeDisplayFont(MetricsView * mv,BDFFont * bdf)1095 static void MVChangeDisplayFont(MetricsView *mv, BDFFont *bdf) {
1096     int i;
1097 
1098     if ( mv->bdf==bdf )
1099 return;
1100     if ( (mv->bdf==NULL) != (bdf==NULL) ) {
1101 	for ( i=0; i<mv->max; ++i ) if ( mv->perchar[i].width!=NULL ) {
1102 	    GGadgetSetEnabled(mv->perchar[i].width,bdf==NULL);
1103 	    GGadgetSetEnabled(mv->perchar[i].lbearing,bdf==NULL);
1104 	    GGadgetSetEnabled(mv->perchar[i].rbearing,bdf==NULL);
1105 	    if ( i!=0 )
1106 		GGadgetSetEnabled(mv->perchar[i].kern,bdf==NULL);
1107 	}
1108     }
1109     if ( mv->bdf==NULL ) {
1110 	BDFFontFree(mv->show);
1111 	mv->show = NULL;
1112     } else if ( bdf==NULL ) {
1113 	BDFFontFree(mv->show);
1114 	mv->show = SplineFontPieceMeal(mv->sf,mv->layer,mv->ptsize,mv->dpi,
1115 				       MVGetSplineFontPieceMealFlags( mv ), NULL );
1116     }
1117     mv->bdf = bdf;
1118     MVRemetric(mv);
1119 }
1120 
isValidInt(unichar_t * end)1121 static int isValidInt(unichar_t *end) {
1122     if ( *end && !(*end=='-' && end[1]=='\0'))
1123 	return 0;
1124     return 1;
1125 }
1126 
1127 /*
1128  * Unused
1129 static int GGadgetToInt(GGadget *g)
1130 {
1131     unichar_t *end;
1132     int val = u_strtol(_GGadgetGetTitle(g),&end,10);
1133     return val;
1134 }
1135 */
1136 
GGadgetToReal(GGadget * g)1137 static real GGadgetToReal(GGadget *g)
1138 {
1139     unichar_t *end;
1140     real val = u_strtod(_GGadgetGetTitle(g),&end);
1141     return val;
1142 }
1143 
MV_WidthChanged(GGadget * g,GEvent * e)1144 static int MV_WidthChanged(GGadget *g, GEvent *e) {
1145 /* This routines called during "Advanced Width Metrics" viewing */
1146 /* any time "Width" changed or screen is updated		*/
1147     MetricsView *mv = GDrawGetUserData(GGadgetGetWindow(g));
1148     int which = (intpt) GGadgetGetUserData(g);
1149     int i;
1150 
1151     if ( e->type!=et_controlevent )
1152 return( true );
1153     if ( which>=mv->glyphcnt )
1154 return( true );
1155     if ( e->u.control.subtype == et_textchanged ) {
1156 	unichar_t *end;
1157 	int val = u_strtol(_GGadgetGetTitle(g),&end,10);
1158 	SplineChar *sc = mv->glyphs[which].sc;
1159 	if (!isValidInt(end))
1160 	    GDrawBeep(NULL);
1161 	else if ( !mv->vertical && val!=sc->width ) {
1162 	    SCPreserveWidth(sc);
1163 
1164 	    // set i to the correct column that has the active width gadget
1165 	    for ( i=0; i<mv->glyphcnt; ++i ) {
1166 		if ( mv->perchar[i].width == g )
1167 		    break;
1168 	    }
1169 
1170 	    // Adjust the lbearing to consume or surrender half of the
1171 	    // change that the width value is undergoing.
1172 	    real offset = GGadgetToReal(mv->perchar[i].lbearing);
1173 	    offset += (val - sc->width * 1.0)/2;
1174 	    real transform[6];
1175 	    transform[0] = transform[3] = 1.0;
1176 	    transform[1] = transform[2] = transform[5] = 0;
1177 	    DBounds bb;
1178 	    SplineCharFindBounds(sc,&bb);
1179 	    transform[4] = offset-bb.minx;
1180 	    FVTrans( (FontViewBase *)mv->fv,sc,transform,NULL, 0 | fvt_alllayers );
1181 
1182 	    SCSynchronizeWidth(sc,val,sc->width,NULL);
1183 	    SCCharChangedUpdate(sc,ly_none);
1184 
1185 	} else if ( mv->vertical && val!=sc->vwidth ) {
1186 	    SCPreserveVWidth(sc);
1187 	    sc->vwidth = val;
1188 	    SCCharChangedUpdate(sc,ly_none);
1189 	}
1190     } else if ( e->u.control.subtype == et_textfocuschanged &&
1191 	    e->u.control.u.tf_focus.gained_focus ) {
1192 	for ( i=0 ; i<mv->glyphcnt; ++i )
1193 	    if ( i!=which && mv->perchar[i].selected )
1194 		MVDeselectChar(mv,i);
1195 	MVSelectChar(mv,which);
1196     }
1197 return( true );
1198 }
1199 
MV_LBearingChanged(GGadget * g,GEvent * e)1200 static int MV_LBearingChanged(GGadget *g, GEvent *e)
1201 {
1202 /* This routines called during "Advanced Width Metrics" viewing */
1203 /* any time "LBrearing" changed or screen is updated		*/
1204     MetricsView *mv = GDrawGetUserData(GGadgetGetWindow(g));
1205     int which = (intpt) GGadgetGetUserData(g);
1206     int i;
1207 
1208     if ( e->type!=et_controlevent )
1209 return( true );
1210     if ( which>=mv->glyphcnt )
1211 return( true );
1212     if ( e->u.control.subtype == et_textchanged ) {
1213 	unichar_t *end;
1214 	double val = u_strtod(_GGadgetGetTitle(g),&end);
1215 	SplineChar *sc = mv->glyphs[which].sc;
1216 	DBounds bb;
1217 	SplineCharFindBounds(sc,&bb);
1218 	if (!isValidInt(end))
1219 	    GDrawBeep(NULL);
1220 	else if ( !mv->vertical && val!=bb.minx ) {
1221 	    real transform[6];
1222 	    transform[0] = transform[3] = 1.0;
1223 	    transform[1] = transform[2] = transform[5] = 0;
1224 	    transform[4] = val-bb.minx;
1225 	    FVTrans( (FontViewBase *)mv->fv,sc,transform,NULL,0 | fvt_alllayers );
1226 	} else if ( mv->vertical && val!=sc->parent->ascent-bb.maxy ) {
1227 	    real transform[6];
1228 	    transform[0] = transform[3] = 1.0;
1229 	    transform[1] = transform[2] = transform[4] = 0;
1230 	    transform[5] = sc->parent->ascent-bb.maxy-val;
1231 	    FVTrans( (FontViewBase *)mv->fv,sc,transform,NULL, fvt_dontmovewidth | fvt_alllayers );
1232 	}
1233 
1234     } else if ( e->u.control.subtype == et_textfocuschanged &&
1235 	    e->u.control.u.tf_focus.gained_focus ) {
1236 	for ( i=0 ; i<mv->glyphcnt; ++i )
1237 	    if ( i!=which && mv->perchar[i].selected )
1238 		MVDeselectChar(mv,i);
1239 	MVSelectChar(mv,which);
1240     }
1241 return( true );
1242 }
1243 
MV_RBearingChanged(GGadget * g,GEvent * e)1244 static int MV_RBearingChanged(GGadget *g, GEvent *e) {
1245 /* This routines called during "Advanced Width Metrics" viewing */
1246 /* any time "RBrearing" changed or screen is updated		*/
1247     MetricsView *mv = GDrawGetUserData(GGadgetGetWindow(g));
1248     int which = (intpt) GGadgetGetUserData(g);
1249     int i;
1250 
1251     if ( e->type!=et_controlevent )
1252 return( true );
1253     if ( which>=mv->glyphcnt )
1254 return( true );
1255     if ( e->u.control.subtype == et_textchanged ) {
1256 	unichar_t *end;
1257 	int val = u_strtod(_GGadgetGetTitle(g),&end);
1258 	SplineChar *sc = mv->glyphs[which].sc;
1259 	DBounds bb;
1260 	SplineCharFindBounds(sc,&bb);
1261 	if (!isValidInt(end))
1262 	    GDrawBeep(NULL);
1263 	else if ( !mv->vertical && rint(val+bb.maxx)!=sc->width ) {
1264 	    int newwidth = rint(bb.maxx+val);
1265 	    SCPreserveWidth(sc);
1266 	    /* Width is an integer. Adjust the lbearing so that the rbearing */
1267 	    /*  remains what was just typed in */
1268 	    if ( newwidth!=bb.maxx+val ) {
1269 		real transform[6];
1270 		transform[0] = transform[3] = 1.0;
1271 		transform[1] = transform[2] = transform[5] = 0;
1272 		transform[4] = newwidth-val-bb.maxx;
1273 		FVTrans( (FontViewBase *)mv->fv,sc,transform,NULL,fvt_dontmovewidth);
1274 	    }
1275 	    SCSynchronizeWidth(sc,newwidth,sc->width,NULL);
1276 	    SCCharChangedUpdate(sc,ly_none);
1277 	} else if ( mv->vertical && val!=sc->vwidth-(sc->parent->ascent-bb.miny) ) {
1278 	    double vw = val+(sc->parent->ascent-bb.miny);
1279 	    SCPreserveWidth(sc);
1280 	    sc->vwidth = rint(vw);
1281 	    /* Width is an integer. Adjust the lbearing so that the rbearing */
1282 	    /*  remains what was just typed in */
1283 	    if ( sc->width!=vw ) {
1284 		real transform[6];
1285 		transform[0] = transform[3] = 1.0;
1286 		transform[1] = transform[2] = transform[4] = 0;
1287 		transform[5] = vw-sc->vwidth;
1288 		FVTrans( (FontViewBase *)mv->fv,sc,transform,NULL,fvt_dontmovewidth);
1289 	    }
1290 	    SCCharChangedUpdate(sc,ly_none);
1291 	}
1292     } else if ( e->u.control.subtype == et_textfocuschanged &&
1293 	    e->u.control.u.tf_focus.gained_focus ) {
1294 	for ( i=0 ; i<mv->glyphcnt; ++i )
1295 	    if ( i!=which && mv->perchar[i].selected )
1296 		MVDeselectChar(mv,i);
1297 	MVSelectChar(mv,which);
1298     }
1299 return( true );
1300 }
1301 
AskNewKernClassEntry(SplineChar * fsc,SplineChar * lsc,int first_is_0,int second_is_0)1302 static int AskNewKernClassEntry(SplineChar *fsc,SplineChar *lsc,int first_is_0,int second_is_0) {
1303     char *yesno[3];
1304     yesno[0] = _("_Alter Class");
1305     yesno[1] = _("_Create Pair");
1306     yesno[2] = NULL;
1307 return( gwwv_ask(_("Use Kerning Class?"),(const char **) yesno,0,1,
1308 	_("This kerning pair (%.20s and %.20s) is currently part of a kerning class with a 0 offset for this combination. Would you like to alter this kerning class entry (or create a kerning pair for just these two glyphs)?"),
1309 	first_is_0  ? _("{Everything Else}") : fsc->name,
1310 	second_is_0 ? _("{Everything Else}") : lsc->name)==0 );
1311 }
1312 
1313 
MV_ChangeKerning(MetricsView * mv,int which,int offset,int is_diff)1314 static int MV_ChangeKerning(MetricsView *mv, int which, int offset, int is_diff) {
1315     SplineChar *sc = mv->glyphs[which].sc;
1316     SplineChar *psc = mv->glyphs[which-1].sc;
1317     KernPair *kp = 0;
1318     KernClass *kc; int index;
1319     int i;
1320     struct lookup_subtable *sub = GGadgetGetListItemSelected(mv->subtable_list)->userdata;
1321     double iscale = mv->pixelsize_set_by_window ? 1.0 : mv_scales[mv->scale_index];
1322 
1323     kp = mv->glyphs[which-1].kp;
1324     kc = mv->glyphs[which-1].kc;
1325     index = mv->glyphs[which-1].kc_index;
1326 
1327     if ( kc!=NULL ) {
1328 	if ( index==-1 )
1329 	    kc = NULL;
1330 	else if ( kp != NULL && kp->off != 0 )
1331 	    kc=NULL;
1332 	else if ( (!is_diff && offset==kc->offsets[index]) ||
1333 		  ( is_diff && offset==0))
1334 return( true );		/* No change, don't bother user */
1335 	/* If there is already a kerning pair, then assume it takes the precedence over the kerning class */
1336 	else if ( kc->offsets[index]==0 && !AskNewKernClassEntry(psc,sc,mv->glyphs[which-1].prev_kc0,mv->glyphs[which-1].next_kc0))
1337 	    kc=NULL;
1338 	else
1339 	    offset = kc->offsets[index] = is_diff ? kc->offsets[index]+offset : offset;
1340     }
1341     if ( kc==NULL ) {
1342 	if ( sub!=NULL && sub->kc!=NULL ) {
1343 	    /* If the subtable we were given contains a kern class, and for some reason */
1344 	    /*  we can't, or don't want to, use that kern class, then see */
1345 	    /*  if the lookup contains another subtable with no kern classes */
1346 	    /*  and use that */
1347 	    struct lookup_subtable *s;
1348 	    for ( s = sub->lookup->subtables; s!=NULL && s->kc!=NULL; s=s->next );
1349 	    sub = s;
1350 	}
1351 	if ( sub==NULL ) {
1352 	    struct subtable_data sd;
1353 	    memset(&sd,0,sizeof(sd));
1354 	    sd.flags = (mv->vertical ? sdf_verticalkern : sdf_horizontalkern ) |
1355 		    sdf_kernclass;
1356 	    sub = SFNewLookupSubtableOfType(psc->parent,gpos_pair,&sd,mv->layer);
1357 	    if ( sub==NULL )
1358 return( false );
1359 	    mv->cur_subtable = sub;
1360 	    MVSetSubtables(mv->sf);
1361 	    MVSetFeatures(mv);
1362 	}
1363 
1364 	/* If we change the kerning offset, then any pixel corrections*/
1365 	/*  will no longer apply (they only had meaning with the old  */
1366 	/*  offset) so free the device table, if any */
1367 	if ( kp != NULL && ((!is_diff && kp->off!=offset) || ( is_diff && offset!=0)) ) {
1368 	    DeviceTableFree(kp->adjust);
1369 	    kp->adjust = NULL;
1370 	}
1371 
1372 	offset = is_diff && kp != NULL ? kp->off+offset : offset;
1373 	/* If kern offset has been set to zero by user, then cleanup this kerning pair */
1374 	if ( kp != NULL && offset == 0 ) {
1375 	    KernPair *kpcur, *kpprev;
1376 	    KernPair **kphead = mv->vertical ? &psc->vkerns : &psc->kerns;
1377 	    if ( kp == *kphead ) {
1378 		*kphead = kp->next;
1379 	    } else {
1380 		kpprev = *kphead;
1381 		for ( kpcur=kpprev->next; kpcur != NULL; kpcur = kpcur->next ) {
1382 		    if ( kpcur == kp ) {
1383 			kpprev->next = kp->next;
1384 		break;
1385 		    }
1386 		    kpprev = kpcur;
1387 		}
1388 	    }
1389 
1390 	    // avoid dangling refrences to kp
1391 	    int i = 0;
1392 	    for( i=0; mv->glyphs[i].sc; i++ ) {
1393 		if( i!=which && mv->glyphs[i].kp == kp ) {
1394 		    mv->glyphs[i].kp = 0;
1395 		}
1396 	    }
1397 	    chunkfree( kp,sizeof(KernPair) );
1398 	    kp = mv->glyphs[which-1].kp = NULL;
1399 	} else if ( offset != 0 ) {
1400 	    if ( kp==NULL ) {
1401 		kp = chunkalloc(sizeof(KernPair));
1402 		kp->sc = sc;
1403 		if ( !mv->vertical ) {
1404 		    kp->next = psc->kerns;
1405 		    psc->kerns = kp;
1406 		} else {
1407 		    kp->next = psc->vkerns;
1408 		    psc->vkerns = kp;
1409 		}
1410 		mv->glyphs[which-1].kp = kp;
1411 	    }
1412 	    kp->off = offset;
1413 	    kp->subtable = sub;
1414 	    if ( !mv->vertical )
1415 		MMKern(sc->parent,psc,sc,is_diff?offset:offset-kp->off,sub,kp);
1416 	}
1417     }
1418     int16 newkernafter = iscale * (offset*mv->pixelsize)/
1419 	(mv->sf->ascent+mv->sf->descent);
1420     mv->perchar[which-1].kernafter = newkernafter;
1421 
1422     if ( mv->vertical ) {
1423 	for ( i=which; i<mv->glyphcnt; ++i ) {
1424 	    mv->perchar[i].dy = mv->perchar[i-1].dy+mv->perchar[i-1].dheight +
1425 		    mv->perchar[i-1].kernafter ;
1426 	}
1427     } else {
1428 	for ( i=which; i<mv->glyphcnt; ++i ) {
1429 	    mv->perchar[i].dx = mv->perchar[i-1].dx + mv->perchar[i-1].dwidth +
1430 		    mv->perchar[i-1].kernafter;
1431 	}
1432     }
1433 
1434     /**
1435      * Class based kerning. If we have altered one pair "Tc" then we
1436      * want to find any other pairs in the same class that are shown
1437      * and alter them in a similar way. Note that the update to the
1438      * value shown is already done as that is taken from the
1439      * KernClass. We can get away with just calling MVRefreshValues()
1440      * on the right indexes to update the kern value entry boxes. On
1441      * the other hand, we have to make sure the guide and glyph
1442      * display is adjusted accordingly too otherwise the user will not
1443      * see the currect kerning for all other digraphs in the same
1444      * class even thuogh the kerning entry box is updated.
1445      */
1446     if( kc && psc && sc )
1447     {
1448 	// cache the cell in the kernclass that we are editing for quick comparison
1449 	// in the loop
1450 	int pscidx = KernClassFindIndexContaining( kc->firsts,  kc->first_cnt,  psc->name );
1451 	int  scidx = KernClassFindIndexContaining( kc->seconds, kc->second_cnt,  sc->name );
1452 
1453 	if( pscidx > 0 && scidx > 0 )
1454 	{
1455 	    for ( i=1; i<mv->glyphcnt; ++i )
1456 	    {
1457 		// don't check yourself.
1458 		if( i-1 == which )
1459 		    continue;
1460 
1461 		/* printf("mv->glyphs[i-1].sc.name:%s\n", mv->glyphs[i-1].sc->name ); */
1462 		/* printf("mv->glyphs[i  ].sc.name:%s\n", mv->glyphs[i  ].sc->name ); */
1463 
1464 		int pidx = KernClassFindIndexContaining( kc->firsts,
1465 							 kc->first_cnt,
1466 							 mv->glyphs[i-1].sc->name );
1467 		/*
1468 		 * Same value for firsts in the kernclass matrix
1469 		 */
1470 		if( pidx == pscidx )
1471 		{
1472 		    int idx = KernClassFindIndexContaining( kc->seconds,
1473 							    kc->second_cnt,
1474 							    mv->glyphs[ i ].sc->name );
1475 
1476 		    /*
1477 		     * First and Second match, we have the same cell
1478 		     * in the kernclass and thus the same kern value
1479 		     * should be applied.
1480 		     */
1481 		    if( scidx == idx )
1482 		    {
1483 			// update the kern text entry box in the lower part of
1484 			// the window.
1485 			MVRefreshValues( mv, i-1 );
1486 
1487 			//
1488 			// Shift the guide and kerning for this digraph, and move
1489 			// all the glyphs on the right over or back a bit so that things
1490 			// still all fit as expected.
1491 			//
1492 			mv->perchar[i-1].kernafter = newkernafter;
1493 			int j;
1494 			for ( j=i; j<mv->glyphcnt; ++j ) {
1495 			    mv->perchar[j].dx = mv->perchar[j-1].dx + mv->perchar[j-1].dwidth +
1496 				mv->perchar[j-1].kernafter;
1497 			}
1498 		    }
1499 		}
1500 	    }
1501 	}
1502     }
1503 
1504     // refresh other kerning input boxes if they are the same characters
1505     static int MV_ChangeKerning_Nested = 0;
1506     int refreshOtherPairEntries = true;
1507     if( !MV_ChangeKerning_Nested && refreshOtherPairEntries && mv->glyphs[0].sc )
1508     {
1509 	int i = 1;
1510 	for( ; mv->glyphs[i].sc; i++ )
1511 	{
1512 	    if( i != which
1513 		&& sc  == mv->glyphs[i].sc
1514 		&& psc == mv->glyphs[i-1].sc )
1515 	    {
1516 
1517 		GGadget *g = mv->perchar[i].kern;
1518 		unichar_t *end;
1519 		int val = u_strtol(_GGadgetGetTitle(g),&end,10);
1520 
1521 		MV_ChangeKerning_Nested = 1;
1522 		int which = (intpt) GGadgetGetUserData(g);
1523 		MV_ChangeKerning( mv, which, offset, is_diff );
1524 		GGadgetSetTitle8( g, tostr(offset) );
1525 		MV_ChangeKerning_Nested = 0;
1526 	    }
1527 	}
1528     }
1529 
1530     mv->sf->changed = true;
1531     GDrawRequestExpose(mv->v,NULL,false);
1532 
1533 return( true );
1534 }
1535 
MV_KernChanged(GGadget * g,GEvent * e)1536 static int MV_KernChanged(GGadget *g, GEvent *e) {
1537 /* This routines called during "Advanced Width Metrics" viewing */
1538 /* any time "Kern:" changed or screen is updated		*/
1539     MetricsView *mv = GDrawGetUserData(GGadgetGetWindow(g));
1540     int which = (intpt) GGadgetGetUserData(g);
1541     int i;
1542 
1543     if ( e->type!=et_controlevent )
1544 return( true );
1545     if ( which>mv->glyphcnt-1 || which==0 )
1546 return( true );
1547 
1548     if ( e->u.control.subtype == et_textchanged ) {
1549         char* title = GGadgetGetTitle8(g);
1550 
1551         int negatives = 0;
1552         for (char* p = title; *p != '\0'; p++) {
1553             if (*p == '-') negatives++;
1554         }
1555 
1556         char* new = str_replace_all(title, "-", "", true); // frees title
1557         int val = strtol(new,NULL,10);
1558         free(new);
1559 
1560         if (negatives%2==1) {
1561             val *= -1;
1562         }
1563 
1564         MV_ChangeKerning(mv,which,val, false);
1565         MVRemetric(mv);
1566     } else if ( e->u.control.subtype == et_textfocuschanged &&
1567 	    e->u.control.u.tf_focus.gained_focus ) {
1568 	for ( i=0 ; i<mv->glyphcnt; ++i )
1569 	    if ( i!=which && mv->perchar[i].selected )
1570 		MVDeselectChar(mv,i);
1571 	MVSelectChar(mv,which);
1572     }
1573 
1574     if( haveClassBasedKerningInView(mv) )
1575     {
1576 	MVRefreshMetric(mv);
1577 	GDrawRequestExpose(mv->v,NULL,false);
1578     }
1579 
1580 return( true );
1581 }
1582 
MVToggleVertical(MetricsView * mv)1583 static void MVToggleVertical(MetricsView *mv) {
1584     int size;
1585 
1586     mv->vertical = !mv->vertical;
1587 
1588     GGadgetSetTitle8( mv->widthlab, mv->vertical ? "Height:" : "Width:" );
1589     GGadgetSetTitle8( mv->lbearinglab, mv->vertical ? "TBearing:" : "LBearing:" );
1590     GGadgetSetTitle8( mv->rbearinglab, mv->vertical ? "BBearing:" : "RBearing:" );
1591     GGadgetSetTitle8( mv->kernlab, mv->vertical ? "VKern:" : "Kern:" );
1592 
1593     if ( mv->vertical )
1594 	if ( mv->scale_index<4 ) mv->scale_index = 4;
1595 
1596     if ( mv->pixelsize_set_by_window ) {
1597 	size = (mv->displayend - mv->topend - 4);
1598 	if ( mv->dwidth-20<size )
1599 	    size = mv->dwidth-20;
1600 	size *= mv_scales[mv->scale_index];
1601 	if ( mv->pixelsize != size ) {
1602 	    mv->pixelsize = size;
1603 	    mv->dpi = 72;
1604 	    if ( mv->bdf==NULL ) {
1605 		BDFFontFree(mv->show);
1606 		mv->show = SplineFontPieceMeal(mv->sf,mv->layer,mv->pixelsize,72,
1607 					       MVGetSplineFontPieceMealFlags( mv ), NULL );
1608 	    }
1609 	    MVRemetric(mv);
1610 	}
1611     }
1612 }
1613 
MVSCFromUnicode(MetricsView * mv,SplineFont * sf,EncMap * map,int ch,BDFFont * bdf)1614 static SplineChar *MVSCFromUnicode(MetricsView *mv, SplineFont *sf, EncMap *map, int ch,BDFFont *bdf) {
1615     int i;
1616     SplineChar *sc;
1617 
1618     if ( mv->fake_unicode_base && ch>=mv->fake_unicode_base &&
1619 	    ch<=mv->fake_unicode_base+mv->sf->glyphcnt )
1620 return( mv->sf->glyphs[ch-mv->fake_unicode_base] );
1621 
1622     i = SFFindSlot(sf,map,ch,NULL);
1623     if ( i==-1 )
1624 return( NULL );
1625     else {
1626 	sc = SFMakeChar(sf,map,i);
1627 	if ( bdf!=NULL )
1628 	    BDFMakeChar(bdf,map,i);
1629     }
1630 return( sc );
1631 }
1632 
MVMoveFieldsBy(MetricsView * mv,int diff)1633 static void MVMoveFieldsBy(MetricsView *mv,int diff) {
1634     int i;
1635     int y,x;
1636 
1637     for ( i=0; i<mv->max && mv->perchar[i].width!=NULL; ++i ) {
1638 	y = mv->displayend+2;
1639 	x = mv->perchar[i].mx-diff;
1640 	if ( x<mv->mbase+mv->mwidth ) x = -2*mv->mwidth;
1641 	GGadgetMove(mv->perchar[i].name,x,y);
1642 	y += mv->fh+4;
1643 	GGadgetMove(mv->perchar[i].width,x,y);
1644 	y += mv->fh+4;
1645 	GGadgetMove(mv->perchar[i].lbearing,x,y);
1646 	y += mv->fh+4;
1647 	GGadgetMove(mv->perchar[i].rbearing,x,y);
1648 	y += mv->fh+4;
1649 	if ( i!=0 )
1650 	    GGadgetMove(mv->perchar[i].kern,x-mv->mwidth/2,y);
1651     }
1652 }
1653 
MVDisplayedCnt(MetricsView * mv)1654 static int MVDisplayedCnt(MetricsView *mv) {
1655     int i, wid = mv->mbase;
1656 
1657     for ( i=mv->coff; i<mv->glyphcnt; ++i ) {
1658 	wid += mv->perchar[i].dwidth;
1659 	if ( wid>mv->dwidth )
1660 return( i-mv->coff );
1661     }
1662 return( i-mv->coff );		/* There's extra room. don't know exactly how much but allow for some */
1663 }
1664 
MVSetSb(MetricsView * mv)1665 static void MVSetSb(MetricsView *mv) {
1666     int cnt = (mv->dwidth-mv->mbase-mv->mwidth)/mv->mwidth;
1667     int dcnt = MVDisplayedCnt(mv);
1668 
1669     if ( cnt>dcnt ) cnt = dcnt;
1670     if ( cnt==0 ) cnt = 1;
1671 
1672     GScrollBarSetBounds(mv->hsb,0,mv->glyphcnt,cnt);
1673     GScrollBarSetPos(mv->hsb,mv->coff);
1674 }
1675 
MVSetVSb(MetricsView * mv)1676 static int MVSetVSb(MetricsView *mv) {
1677     int max, min, ret, yoff;
1678     int fudge;
1679 
1680     if ( mv->displayend==0 )
1681 return(0);		/* Setting the scroll bar is premature */
1682 
1683     if ( mv->vertical ) {
1684 	min = max = 0;
1685 	if ( mv->glyphcnt!=0 )
1686 	    max = mv->perchar[mv->glyphcnt-1].dy + mv->perchar[mv->glyphcnt-1].dheight;
1687 	fudge = 10;
1688     } else {
1689 	SplineFont *sf = mv->sf;
1690 	int pixels = mv->pixelsize_set_by_window ? mv->vheight : mv->pixelsize;
1691 	fudge = pixels/4;
1692 	min = -(pixels*sf->descent)/(sf->ascent+sf->descent);
1693 	max = pixels + min;
1694 	min *= mv_scales[mv->scale_index];
1695 	max *= mv_scales[mv->scale_index];
1696     }
1697     mv->ybaseline = max;
1698     max += fudge*mv_scales[mv->scale_index] + mv->vheight;
1699     min -= fudge*mv_scales[mv->scale_index];
1700     GScrollBarSetBounds(mv->vsb,min,max,mv->vheight);
1701     yoff = mv->yoff;
1702     if ( yoff+mv->vheight > max )
1703 	yoff = max - mv->vheight;
1704     if ( yoff<min ) yoff = min;
1705     ret = yoff!=mv->yoff;
1706     mv->yoff = yoff;
1707     GScrollBarSetPos(mv->vsb,yoff);
1708 return( ret );
1709 }
1710 
MVHScroll(MetricsView * mv,struct sbevent * sb)1711 static void MVHScroll(MetricsView *mv,struct sbevent *sb) {
1712     int newpos = mv->coff;
1713     int cnt = (mv->dwidth-mv->mbase-mv->mwidth)/mv->mwidth;
1714     int dcnt = MVDisplayedCnt(mv);
1715 
1716     if ( cnt>dcnt ) cnt = dcnt;
1717     if ( cnt==0 ) cnt = 1;
1718 
1719     switch( sb->type ) {
1720       case et_sb_top:
1721         newpos = 0;
1722       break;
1723       case et_sb_uppage:
1724         newpos -= cnt;
1725       break;
1726       case et_sb_up:
1727         --newpos;
1728       break;
1729       case et_sb_down:
1730         ++newpos;
1731       break;
1732       case et_sb_downpage:
1733         newpos += cnt;
1734       break;
1735       case et_sb_bottom:
1736         newpos = mv->glyphcnt-cnt;
1737       break;
1738       case et_sb_thumb:
1739       case et_sb_thumbrelease:
1740         newpos = sb->pos;
1741       break;
1742     }
1743     if ( newpos>mv->glyphcnt-cnt )
1744         newpos = mv->glyphcnt-cnt;
1745     if ( newpos<0 ) newpos =0;
1746     if ( newpos!=mv->coff ) {
1747 	int old = mv->coff;
1748 	int diff = newpos-mv->coff;
1749 	int charsize = mv->perchar[newpos].dx-mv->perchar[old].dx;
1750 	GRect fieldrect, charrect;
1751 
1752 	mv->coff = newpos;
1753 	charrect.x = 0; charrect.width = mv->vwidth;
1754 	charrect.y = 0; charrect.height = mv->vheight;
1755 	fieldrect.x = mv->mbase+mv->mwidth; fieldrect.width = mv->width-mv->mbase;
1756 	fieldrect.y = mv->displayend; fieldrect.height = mv->height-mv->sbh-mv->displayend;
1757 	GScrollBarSetBounds(mv->hsb,0,mv->glyphcnt,cnt);
1758 	GScrollBarSetPos(mv->hsb,mv->coff);
1759 	MVMoveFieldsBy(mv,newpos*mv->mwidth);
1760 	GDrawScroll(mv->gw,&fieldrect,-diff*mv->mwidth,0);
1761 	mv->xoff = mv->perchar[newpos].dx-mv->perchar[0].dx;
1762 	if ( mv->right_to_left ) {
1763 	    charsize = -charsize;
1764 	}
1765 	GDrawScroll(mv->v,&charrect,-charsize,0);
1766     }
1767 }
1768 
MVVScroll(MetricsView * mv,struct sbevent * sb)1769 static void MVVScroll(MetricsView *mv,struct sbevent *sb) {
1770     int newpos = mv->yoff;
1771     int32 min, max, page;
1772 
1773     GScrollBarGetBounds(mv->vsb,&min,&max,&page);
1774     switch( sb->type ) {
1775       case et_sb_top:
1776         newpos = 0;
1777       break;
1778       case et_sb_uppage:
1779         newpos -= page;
1780       break;
1781       case et_sb_up:
1782         newpos -= (page)/15;
1783       break;
1784       case et_sb_down:
1785         newpos += (page)/15;
1786       break;
1787       case et_sb_downpage:
1788         newpos += page;
1789       break;
1790       case et_sb_bottom:
1791         newpos = max-page;
1792       break;
1793       case et_sb_thumb:
1794       case et_sb_thumbrelease:
1795         newpos = sb->pos;
1796       break;
1797     }
1798     if ( newpos>max-page )
1799         newpos = max-page;
1800     if ( newpos<min ) newpos = min;
1801     if ( newpos!=mv->yoff ) {
1802 	int diff = newpos-mv->yoff;
1803 	GRect charrect;
1804 
1805 	mv->yoff = newpos;
1806 	charrect.x = 0; charrect.width = mv->vwidth;
1807 	charrect.y = 0; charrect.height = mv->vheight;
1808 	GScrollBarSetPos(mv->vsb,mv->yoff);
1809 	GDrawScroll(mv->v,&charrect,0,diff);
1810     }
1811 }
1812 
MVFakeUnicodeOfSc(MetricsView * mv,SplineChar * sc)1813 static int MVFakeUnicodeOfSc(MetricsView *mv, SplineChar *sc) {
1814 
1815     if ( sc->unicodeenc!=-1 )
1816 return( sc->unicodeenc );
1817 
1818     if ( mv->fake_unicode_base==0 ) {		/* Not set */
1819 	/* If they have nothing in Supplementary Private Use Area-A use it */
1820 	/* If they have nothing in Supplementary Private Use Area-B use it */
1821 	/* else just use 0xfffd */
1822 	int a, al, ah, b, bl, bh;
1823 	int gid,k,max;
1824 	SplineChar *test;
1825 	SplineFont *_sf, *sf;
1826 	sf = mv->sf;
1827 	if ( sf->cidmaster ) sf = sf->cidmaster;
1828 	k=0;
1829 	a = al = ah = b = bl = bh = 0;
1830 	max = 0;
1831 	do {
1832 	    _sf =  ( sf->subfontcnt==0 ) ? sf : sf->subfonts[k];
1833 	    for ( gid=0; gid<_sf->glyphcnt; ++gid ) if ( (test=_sf->glyphs[gid])!=NULL ) {
1834 		if ( test->unicodeenc>=0xf0000 && test->unicodeenc<=0xfffff ) {
1835 		    a = true;
1836 		    if ( test->unicodeenc<0xf8000 )
1837 			al = true;
1838 		    else
1839 			ah = true;
1840 		} else if ( test->unicodeenc>=0x100000 && test->unicodeenc<=0x10ffff ) {
1841 		    b = true;
1842 		    if ( test->unicodeenc<0x108000 )
1843 			bl = true;
1844 		    else
1845 			bh = true;
1846 		}
1847 	    }
1848 	    if ( gid>max ) max = gid;
1849 	    ++k;
1850 	} while ( k<sf->subfontcnt );
1851 	if ( !a )		/* Nothing in SPUA-A */
1852 	    mv->fake_unicode_base = 0xf0000;
1853 	else if ( !b )
1854 	    mv->fake_unicode_base = 0x100000;
1855 	else if ( max<0x8000 ) {
1856 	    if ( !al )
1857 		mv->fake_unicode_base = 0xf0000;
1858 	    else if ( !ah )
1859 		mv->fake_unicode_base = 0xf8000;
1860 	    else if ( !bl )
1861 		mv->fake_unicode_base = 0x100000;
1862 	    else if ( !bh )
1863 		mv->fake_unicode_base = 0x108000;
1864 	}
1865 	if ( mv->fake_unicode_base==0 )
1866 	    mv->fake_unicode_base = -1;
1867     }
1868 
1869     if ( mv->fake_unicode_base==-1 )
1870 return( 0xfffd );
1871     else
1872 return( mv->fake_unicode_base+sc->orig_pos );
1873 }
1874 
MVOddMatch(MetricsView * mv,int uni,SplineChar * sc)1875 static int MVOddMatch(MetricsView *mv,int uni,SplineChar *sc) {
1876     if ( sc->unicodeenc!=-1 )
1877 return( false );
1878     else if ( mv->fake_unicode_base<=0 )
1879 return( uni==0xfffd );
1880     else
1881 return( uni>=mv->fake_unicode_base && sc->orig_pos == uni-mv->fake_unicode_base );
1882 }
1883 
MVSetSCs(MetricsView * mv,SplineChar ** scs)1884 void MVSetSCs(MetricsView *mv, SplineChar **scs) {
1885     /* set the list of characters being displayed to those in scs */
1886     int len;
1887     unichar_t *ustr;
1888 
1889     for ( len=0; scs[len]!=NULL; ++len );
1890     if ( len>=mv->cmax )
1891 	mv->chars = realloc(mv->chars,(mv->cmax=len+10)*sizeof(SplineChar *));
1892     memcpy(mv->chars,scs,(len+1)*sizeof(SplineChar *));
1893     mv->clen = len;
1894 
1895     ustr = malloc((len+1)*sizeof(unichar_t));
1896     for ( len=0; scs[len]!=NULL; ++len )
1897 	if ( scs[len]->unicodeenc>0 )
1898 	    ustr[len] = scs[len]->unicodeenc;
1899 	else
1900 	    ustr[len] = MVFakeUnicodeOfSc(mv,scs[len]);
1901     ustr[len] = 0;
1902     GGadgetSetTitle(mv->text,ustr);
1903     free(ustr);
1904 
1905     MVRemetric(mv);
1906 
1907     GDrawRequestExpose(mv->v,NULL,false);
1908 }
1909 
1910 
WordlistEscapedInputStringToRealString_getFakeUnicodeAs_MVFakeUnicodeOfSc(SplineChar * sc,void * udata)1911 static int WordlistEscapedInputStringToRealString_getFakeUnicodeAs_MVFakeUnicodeOfSc( SplineChar *sc, void* udata )
1912 {
1913     MetricsView *mv = (MetricsView *)udata;
1914     int n = MVFakeUnicodeOfSc( mv, sc );
1915     return n;
1916 }
1917 
1918 
MVTextChanged(MetricsView * mv)1919 static void MVTextChanged(MetricsView *mv) {
1920     const unichar_t *ret = 0, *pt, *ept, *tpt;
1921     int i,ei, j, start=0, end=0;
1922     int missing;
1923     int direction_change = false;
1924     SplineChar **hold = NULL;
1925 
1926     ret = _GGadgetGetTitle(mv->text);
1927 
1928     // convert the slash escpae codes and the like to the real string we will use
1929     // for the metrics window
1930     WordListLine wll = WordlistEscapedInputStringToParsedDataComplex(
1931     	mv->sf, _GGadgetGetTitle(mv->text),
1932     	WordlistEscapedInputStringToRealString_getFakeUnicodeAs_MVFakeUnicodeOfSc, mv );
1933     ret = WordListLine_toustr( wll );
1934 
1935     if (( ret[0]<0x10000 && isrighttoleft(ret[0]) && !mv->right_to_left ) ||
1936 	    ( ret[0]<0x10000 && !isrighttoleft(ret[0]) && mv->right_to_left )) {
1937 	direction_change = true;
1938 	mv->right_to_left = !mv->right_to_left;
1939     }
1940     for ( pt=ret, i=0; i<mv->clen && *pt!='\0'; ++i, ++pt )
1941 	if ( *pt!=mv->chars[i]->unicodeenc &&
1942 		!MVOddMatch(mv,*pt,mv->chars[i]))
1943     break;
1944     if ( i==mv->clen && *pt=='\0' )
1945 return;					/* Nothing changed */
1946     for ( ept=ret+u_strlen(ret)-1, ei=mv->clen-1; ; --ei, --ept )
1947 	if ( ei<0 || ept<ret || (*ept!=mv->chars[ei]->unicodeenc &&
1948 		!MVOddMatch(mv,*ept,mv->chars[ei]))) {
1949 	    ++ei; ++ept;
1950     break;
1951 	} else if ( ei<i || ept<pt ) {
1952 	    ++ei; ++ept;
1953     break;
1954 	}
1955     if ( ei==i && ept==pt )
1956 	IError("No change when there should have been one in MV_TextChanged");
1957     if ( u_strlen(ret)>=mv->cmax ) {
1958 	int oldmax=mv->cmax;
1959 	mv->cmax = u_strlen(ret)+10;
1960 	mv->chars = realloc(mv->chars,mv->cmax*sizeof(SplineChar *));
1961 	memset(mv->chars+oldmax,'\0',(mv->cmax-oldmax)*sizeof(SplineChar *));
1962     }
1963 
1964     missing = 0;
1965     for ( tpt=pt; tpt<ept; ++tpt )
1966 	if ( mv->fake_unicode_base>0 && *tpt>=mv->fake_unicode_base &&
1967 		*tpt<=mv->fake_unicode_base+mv->sf->glyphcnt )
1968 	    /* That's ok */;
1969 	else if ( SFFindSlot(mv->sf,mv->fv->b.map,*tpt,NULL)==-1 )
1970 	    ++missing;
1971 
1972     if ( ept-pt-missing > ei-i ) {
1973 	if ( i<mv->clen ) {
1974 	    int diff = (ept-pt-missing) - (ei-i);
1975 	    hold = malloc((mv->clen+diff+6)*sizeof(SplineChar *));
1976 	    for ( j=mv->clen-1; j>=ei; --j )
1977 		hold[j+diff] = mv->chars[j];
1978 	    start = ei+diff; end = mv->clen+diff;
1979 	}
1980     } else if ( ept-pt-missing != ei-i ) {
1981 	int diff = (ept-pt-missing) - (ei-i);
1982 	for ( j=ei; j<mv->clen; ++j )
1983 	    if ( j+diff>=0 )
1984 		mv->chars[j+diff] = mv->chars[j];
1985     }
1986     for ( j=i; pt<ept; ++pt ) {
1987 	SplineChar *sc;
1988 	sc = MVSCFromUnicode(mv,mv->sf,mv->fv->b.map,*pt,mv->bdf);
1989 	if ( sc!=NULL )
1990 	    mv->chars[j++] = sc;
1991     }
1992     if ( hold!=NULL ) {
1993 	/* We had to figure out what sc's there were before we wrote over them*/
1994 	/*  but we couldn't put them where they belonged until everything before*/
1995 	/*  them was set properly */
1996 	for ( j=start; j<end; ++j )
1997 	    mv->chars[j] = hold[j];
1998 	free(hold);
1999     }
2000     mv->clen = u_strlen(ret)-missing;
2001     mv->chars[mv->clen] = NULL;
2002     MVRemetric(mv);
2003 
2004     // handle selecting the default glyph if desired this is slightly
2005     // complex because we need to handle when there is no selected
2006     // entry, which is the case just after loading a word list, and
2007     // then the first line is the right line.
2008     GTextInfo* gt = GGadgetGetListItemSelected(mv->text);
2009     if( !gt )
2010     {
2011 	GTextInfo **ti=NULL;
2012 	int32 len;
2013 	ti = GGadgetGetList(mv->text,&len);
2014 	if( len )
2015 	    gt = ti[0];
2016     }
2017 
2018     selectUserChosenWordListGlyphs( mv, wll );
2019     GDrawRequestExpose(mv->v,NULL,false);
2020 }
2021 
2022 GTextInfo mv_text_init[] = {
2023     { (unichar_t *) "", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 1, 0, 1, 0, 0, '\0'},
2024     { NULL, NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 1, 0, 0, 0, '\0'},
2025     { (unichar_t *) N_("Load Word List..."), NULL, 0, 0, (void *) -1, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
2026     { (unichar_t *) N_("Load Glyph Name List..."), NULL, 0, 0, (void *) -2, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
2027     GTEXTINFO_EMPTY
2028 };
2029 
MVFigureGlyphNames(MetricsView * mv,const unichar_t * names)2030 static void MVFigureGlyphNames(MetricsView *mv,const unichar_t *names) {
2031     char buffer[400], *pt, *start;
2032     SplineChar *founds[40];
2033     int i,cnt,ch;
2034     unichar_t *newtext;
2035 
2036     u2utf8_strcpy(buffer,names);
2037     start = buffer;
2038     for ( i=0; *start; ) {
2039 	while ( *start==' ' ) ++start;
2040 	if ( *start=='\0' )
2041     break;
2042 	for ( pt=start; *pt && *pt!=' '; ++pt );
2043 	ch = *pt; *pt = '\0';
2044 	if ( i>=40 )
2045     break;
2046 	if ( (founds[i]=SFGetChar(mv->sf,-1,start))!=NULL )
2047 	    ++i;
2048 	*pt = ch;
2049 	start = pt;
2050     }
2051     cnt = i;
2052 
2053     if ( cnt>=mv->cmax ) {
2054 	mv->cmax = mv->clen+cnt+10;
2055 	mv->chars = realloc(mv->chars,mv->cmax*sizeof(SplineChar *));
2056     }
2057     newtext = malloc((cnt+1)*sizeof(unichar_t));
2058     for ( i=0; i<cnt; ++i ) {
2059 	newtext[i] = founds[i]->unicodeenc==-1 ?
2060 						MVFakeUnicodeOfSc(mv,founds[i]) :
2061 						founds[i]->unicodeenc;
2062 	mv->chars[i] = founds[i];
2063     }
2064     newtext[i] = 0;
2065     mv->chars[i] = NULL;
2066     mv->clen = cnt;
2067     MVRemetric(mv);
2068 
2069     GGadgetSetTitle(mv->text,newtext);
2070     free(newtext);
2071 
2072     GDrawRequestExpose(mv->v,NULL,false);
2073 }
2074 
MVLoadWordList(MetricsView * mv,int type)2075 static void MVLoadWordList(MetricsView *mv, int type) {
2076     int words_max = 1024*128;
2077     GTextInfo** words = WordlistLoadFileToGTextInfo( type, words_max );
2078     if ( !words ) {
2079 	    GGadgetSetTitle8(mv->text,"");
2080 	    return;
2081     }
2082 
2083     if ( words[0] ) {
2084 	    GGadgetSetList(mv->text,words,true);
2085 	    GGadgetSetTitle8(mv->text,(char *) (words[0]->text));
2086 	    if ( type==-2 )
2087 	        MVFigureGlyphNames(mv,_GGadgetGetTitle(mv->text)+1);
2088 	    mv->word_index = 0;
2089     }
2090     GTextInfoArrayFree(words);
2091 }
2092 
MV_TextChanged(GGadget * g,GEvent * e)2093 static int MV_TextChanged(GGadget *g, GEvent *e) {
2094 
2095     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
2096 	MetricsView *mv = GGadgetGetUserData(g);
2097 	int pos = e->u.control.u.tf_changed.from_pulldown;
2098 	if ( pos!=-1 ) {
2099 	    int32 len;
2100 	    GTextInfo **ti = GGadgetGetList(g,&len);
2101 	    GTextInfo *cur = ti[pos];
2102 	    int type = (intpt) cur->userdata;
2103 	    if ( type < 0 )
2104 		MVLoadWordList(mv,type);
2105 	    else if ( cur->text!=NULL ) {
2106 		mv->word_index = pos;
2107 		if ( cur->text[0]==0x200b )	/* Zero width space, flag for glyph names */
2108 		    MVFigureGlyphNames(mv,cur->text+1);
2109 	    }
2110 	}
2111 	MVTextChanged(mv);
2112     }
2113 return( true );
2114 }
2115 
MV_ScriptLangChanged(GGadget * g,GEvent * e)2116 static int MV_ScriptLangChanged(GGadget *g, GEvent *e) {
2117 
2118     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
2119 	const unichar_t *sstr = _GGadgetGetTitle(g);
2120 	MetricsView *mv = GGadgetGetUserData(g);
2121 	if ( e->u.control.u.tf_changed.from_pulldown!=-1 ) {
2122 	    GGadgetSetTitle8(g,mv->scriptlangs[e->u.control.u.tf_changed.from_pulldown].userdata );
2123 	    sstr = _GGadgetGetTitle(g);
2124 	} else {
2125 	    if ( u_strlen(sstr)<4 || !isalpha(sstr[0]) || !isalnum(sstr[1]) /*|| !isalnum(sstr[2]) || !isalnum(sstr[3])*/ )
2126 return( true );
2127 	    if ( u_strlen(sstr)==4 )
2128 		/* No language, we'll use default */;
2129 	    else if ( u_strlen(sstr)!=10 || sstr[4]!='{' || sstr[9]!='}' ||
2130 		    !isalpha(sstr[5]) || !isalpha(sstr[6]) || !isalpha(sstr[7])  )
2131 return( true );
2132 	}
2133 	MVSetFeatures(mv);
2134 	if ( mv->clen!=0 )/* if there are no chars, remetricking will set the script field to DFLT */
2135 	    MVRemetric(mv);
2136 	GDrawRequestExpose(mv->v,NULL,false);
2137     }
2138 return( true );
2139 }
2140 
MV_FeaturesChanged(GGadget * g,GEvent * e)2141 static int MV_FeaturesChanged(GGadget *g, GEvent *e) {
2142 
2143     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
2144 	MetricsView *mv = GGadgetGetUserData(g);
2145 	MVRemetric(mv);
2146 	GDrawRequestExpose(mv->v,NULL,false);
2147     }
2148 return( true );
2149 }
2150 
MV_FriendlyFeatures(GGadget * g,int pos)2151 void MV_FriendlyFeatures(GGadget *g, int pos) {
2152     int32 len;
2153     GTextInfo **ti = GGadgetGetList(g,&len);
2154 
2155     if ( pos<0 || pos>=len )
2156 	GGadgetEndPopup();
2157     else {
2158 	const unichar_t *pt = ti[pos]->text;
2159 	uint32 tag;
2160 	int i;
2161 	tag = (pt[0]<<24) | (pt[1]<<16) | (pt[2]<<8) | pt[3];
2162 	LookupUIInit();
2163 	for ( i=0; friendlies[i].friendlyname!=NULL; ++i )
2164 	    if ( friendlies[i].tag==tag )
2165 	break;
2166 	if ( friendlies[i].friendlyname!=NULL )
2167 	    GGadgetPreparePopup8(GGadgetGetWindow(g),friendlies[i].friendlyname);
2168     }
2169 }
2170 
MV_SubtableChanged(GGadget * g,GEvent * e)2171 static int MV_SubtableChanged(GGadget *g, GEvent *e) {
2172 
2173     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
2174 	MetricsView *mv = GGadgetGetUserData(g);
2175 	int32 len;
2176 	GTextInfo **ti = GGadgetGetList(g,&len);
2177 	int i;
2178 	KernPair *kp;
2179 	struct lookup_subtable *sub;
2180 	SplineFont *sf = mv->sf;
2181 
2182 	if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
2183 
2184 	if ( ti[len-1]->selected ) {/* New lookup subtable */
2185 	    struct subtable_data sd;
2186 	    memset(&sd,0,sizeof(sd));
2187 	    sd.flags = (mv->vertical ? sdf_verticalkern : sdf_horizontalkern ) |
2188 		    sdf_kernpair | sdf_dontedit;
2189 	    sub = SFNewLookupSubtableOfType(sf,gpos_pair,&sd,mv->layer);
2190 	    if ( sub==NULL )
2191 return( true );
2192 	    mv->cur_subtable = sub;
2193 	    MVSetSubtables(mv->sf);
2194 	    MVSetFeatures(mv);		/* Is this needed? */
2195 	} else if ( ti[len-2]->selected ) {	/* Idiots. They selected the line, can't have that */
2196 	    MVSetSubtables(mv->sf);
2197 	    sub = mv->cur_subtable;
2198 	} else
2199 	    mv->cur_subtable = GGadgetGetListItemSelected(mv->subtable_list)->userdata;
2200 
2201 	for ( i=0; i<mv->glyphcnt; ++i ) {
2202 	    if ( mv->perchar[i].selected )
2203 	break;
2204 	}
2205 	kp = mv->glyphs[i].kp;
2206 	if ( kp!=NULL )
2207 	    kp->subtable = mv->cur_subtable;
2208     }
2209 return( true );
2210 }
2211 
2212 #define MID_ZoomIn	2002
2213 #define MID_ZoomOut	2003
2214 #define MID_Next	2005
2215 #define MID_Prev	2006
2216 #define MID_Outline	2007
2217 #define MID_ShowGrid	2008
2218 #define MID_HideGrid	2009
2219 #define MID_PartialGrid 2010
2220 #define MID_HideGridWhenMoving	2011
2221 #define MID_NextDef	2012
2222 #define MID_PrevDef	2013
2223 #define MID_AntiAlias	2014
2224 #define MID_FindInFontView	2015
2225 #define MID_Ligatures	2020
2226 #define MID_KernPairs	2021
2227 #define MID_AnchorPairs	2022
2228 #define MID_Vertical	2023
2229 #define MID_ReplaceChar	2024
2230 #define MID_InsertCharB	2025
2231 #define MID_InsertCharA	2026
2232 #define MID_Layers	2027
2233 #define MID_PointSize	2028
2234 #define MID_Bigger	2029
2235 #define MID_Smaller	2030
2236 #define MID_SizeWindow	2031
2237 #define MID_CharInfo	2201
2238 #define MID_FindProblems 2216
2239 #define MID_Transform	2202
2240 #define MID_Stroke	2203
2241 #define MID_RmOverlap	2204
2242 #define MID_Simplify	2205
2243 #define MID_Correct	2206
2244 #define MID_BuildAccent	2208
2245 #define MID_AvailBitmaps	2210
2246 #define MID_RegenBitmaps	2211
2247 #define MID_Autotrace	2212
2248 #define MID_Round	2213
2249 #define MID_ShowDependents	2222
2250 #define MID_AddExtrema	2224
2251 #define MID_CleanupGlyph	2225
2252 #define MID_TilePath	2226
2253 #define MID_BuildComposite	2227
2254 #define MID_Intersection	2229
2255 #define MID_FindInter	2230
2256 #define MID_Effects	2231
2257 #define MID_SimplifyMore	2232
2258 #define MID_Center	2600
2259 #define MID_OpenBitmap	2700
2260 #define MID_OpenOutline	2701
2261 #define MID_Cut		2101
2262 #define MID_Copy	2102
2263 #define MID_Paste	2103
2264 #define MID_Clear	2104
2265 #define MID_SelAll	2106
2266 #define MID_ClearSel	2105
2267 #define MID_UnlinkRef	2108
2268 #define MID_Undo	2109
2269 #define MID_Redo	2110
2270 #define MID_CopyRef	2107
2271 #define MID_CopyWidth	2111
2272 #define MID_CopyLBearing	2125
2273 #define MID_CopyRBearing	2126
2274 #define MID_CopyVWidth	2127
2275 #define MID_Join	2128
2276 #define MID_Center	2600
2277 #define MID_SetWidth	2601
2278 #define MID_SetLBearing	2602
2279 #define MID_SetRBearing	2603
2280 #define MID_Thirds	2604
2281 #define MID_VKernClass	2605
2282 #define MID_VKernFromHKern	2606
2283 #define MID_KernOnly	2607
2284 #define MID_WidthOnly	2608
2285 #define MID_BothKernWidth	2609
2286 #define MID_SetBearings	2610
2287 #define MID_Recent	2703
2288 #define MID_SetVWidth	2705
2289 #define MID_RemoveKerns	2707
2290 #define MID_RemoveVKerns 2709
2291 
2292 #define  MID_NextLineInWordList 2720
2293 #define  MID_PrevLineInWordList 2721
2294 #define MID_RenderUsingHinting	2722
2295 
2296 
2297 #define MID_Warnings	3000
2298 
MVMenuOpen(GWindow gw,struct gmenuitem * mi,GEvent * g)2299 static void MVMenuOpen(GWindow gw, struct gmenuitem *mi, GEvent *g) {
2300     MetricsView *d = (MetricsView*)GDrawGetUserData(gw);
2301     FontView *fv = NULL;
2302     if (d) {
2303         fv = (FontView*)d->fv;
2304     }
2305     _FVMenuOpen(fv);
2306 }
2307 
MVMenuClose(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2308 static void MVMenuClose(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2309     GDrawDestroyWindow(gw);
2310 }
2311 
MVMenuOpenBitmap(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2312 static void MVMenuOpenBitmap(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2313     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2314     EncMap *map;
2315     int i;
2316 
2317     if ( mv->sf->bitmaps==NULL )
2318 return;
2319     for ( i=0; i<mv->glyphcnt; ++i )
2320         if ( mv->perchar[i].selected )
2321     break;
2322     map = mv->fv->b.map;
2323     if ( i!=mv->glyphcnt )
2324         BitmapViewCreatePick(map->backmap[mv->glyphs[i].sc->orig_pos],mv->fv);
2325 }
2326 
MVMenuMergeKern(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2327 static void MVMenuMergeKern(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2328     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2329     MergeKernInfo(mv->sf,mv->fv->b.map);
2330 }
2331 
MVMenuAddWordList(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2332 static void MVMenuAddWordList(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e))
2333 {
2334     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2335     MVLoadWordList(mv,-1);
2336     GWidgetIndicateFocusGadget( mv->text );
2337 
2338     GEvent e;
2339     e.type = et_controlevent;
2340     e.u.control.subtype = et_textchanged;
2341     e.u.control.u.tf_changed.from_pulldown = 0;
2342     MV_TextChanged(mv->text, &e );
2343 }
2344 
2345 
MVMenuOpenOutline(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2346 static void MVMenuOpenOutline(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2347     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2348     int i;
2349 
2350     for ( i=0; i<mv->glyphcnt; ++i )
2351         if ( mv->perchar[i].selected )
2352     break;
2353     if ( i!=mv->glyphcnt )
2354         CharViewCreate(mv->glyphs[i].sc, mv->fv, -1);
2355 }
2356 
MVMenuSave(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2357 static void MVMenuSave(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2358     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2359     _FVMenuSave(mv->fv);
2360 }
2361 
MVMenuSaveAs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2362 static void MVMenuSaveAs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2363     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2364     _FVMenuSaveAs(mv->fv);
2365 }
2366 
MVMenuGenerate(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2367 static void MVMenuGenerate(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2368     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2369     _FVMenuGenerate(mv->fv, false);
2370 }
2371 
MVMenuGenerateFamily(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2372 static void MVMenuGenerateFamily(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2373     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2374     _FVMenuGenerate(mv->fv, gf_macfamily);
2375 }
2376 
MVMenuGenerateTTC(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2377 static void MVMenuGenerateTTC(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2378     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2379     _FVMenuGenerate(mv->fv, gf_ttc);
2380 }
2381 
MVMenuPrint(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2382 static void MVMenuPrint(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2383     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2384     PrintFFDlg(NULL, NULL, mv);
2385 }
2386 
MVUndo(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2387 static void MVUndo(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2388     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2389     int i;
2390 
2391     if ( GGadgetActiveGadgetEditCmd(mv->gw,ec_undo) )
2392         /* MVTextChanged(mv) */ ;
2393     else {
2394         for ( i=mv->glyphcnt-1; i>=0; --i )
2395             if ( mv->perchar[i].selected )
2396         break;
2397         if ( i==-1 )
2398 return;
2399         if ( mv->glyphs[i].sc->layers[mv->layer].undoes!=NULL )
2400             SCDoUndo(mv->glyphs[i].sc, mv->layer);
2401     }
2402 }
2403 
MVRedo(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2404 static void MVRedo(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2405     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2406     int i;
2407 
2408     if ( GGadgetActiveGadgetEditCmd(mv->gw,ec_redo) )
2409         /* MVTextChanged(mv) */ ;
2410     else {
2411         for ( i=mv->glyphcnt-1; i>=0; --i )
2412             if ( mv->perchar[i].selected )
2413         break;
2414         if ( i==-1 )
2415 return;
2416         if ( mv->glyphs[i].sc->layers[mv->layer].redoes!=NULL )
2417             SCDoRedo(mv->glyphs[i].sc, mv->layer);
2418     }
2419 }
2420 
MVClear(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2421 static void MVClear(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2422     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2423     int i;
2424     SplineChar *sc;
2425     BDFFont *bdf;
2426     extern int onlycopydisplayed;
2427 
2428     if ( GGadgetActiveGadgetEditCmd(mv->gw, ec_clear) )
2429         /* MVTextChanged(mv) */;
2430     else {
2431         for ( i=mv->glyphcnt-1; i>=0; --i )
2432             if ( mv->perchar[i].selected )
2433         break;
2434         if ( i==-1 )
2435 return;
2436         sc = mv->glyphs[i].sc;
2437         if ( sc->dependents!=NULL ) {
2438             int yes;
2439             char *buts[4];
2440             buts[1] = _("_Unlink");
2441             buts[0] = _("_Yes");
2442             buts[2] = _("_Cancel");
2443             buts[3] = NULL;
2444             yes = gwwv_ask(_("Bad Reference"), (const char **) buts, 1, 2, _("You are attempting to clear %.30s which is referred to by\nanother character. Are you sure you want to clear it?"), sc->name);
2445             if ( yes==2 )
2446 return;
2447             if ( yes==1 )
2448                 UnlinkThisReference(NULL, sc, mv->layer);
2449         }
2450 
2451         if ( onlycopydisplayed && mv->bdf==NULL ) {
2452             SCClearAll(sc, mv->layer);
2453         } else if ( onlycopydisplayed ) {
2454             BCClearAll(mv->bdf->glyphs[sc->orig_pos]);
2455         } else {
2456             SCClearAll(sc,mv->layer);
2457             for ( bdf=mv->sf->bitmaps; bdf!=NULL; bdf = bdf->next )
2458                 BCClearAll(bdf->glyphs[sc->orig_pos]);
2459         }
2460     }
2461 }
2462 
MVCut(GWindow gw,struct gmenuitem * mi,GEvent * e)2463 static void MVCut(GWindow gw, struct gmenuitem *mi, GEvent *e) {
2464     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2465     int i;
2466 
2467     if ( GGadgetActiveGadgetEditCmd(mv->gw, ec_cut) )
2468         /* MVTextChanged(mv) */ ;
2469     else {
2470         for ( i=mv->glyphcnt-1; i>=0; --i )
2471             if ( mv->perchar[i].selected )
2472         break;
2473         if ( i==-1 )
2474 return;
2475         MVCopyChar(&mv->fv->b,mv->bdf,mv->glyphs[i].sc,ct_fullcopy);
2476         MVClear(gw, mi, e); /* mi & e are actually not used */
2477     }
2478 }
2479 
MVCopy(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2480 static void MVCopy(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2481     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2482     int i;
2483 
2484     if ( GGadgetActiveGadgetEditCmd(mv->gw, ec_copy) )
2485         /* MVTextChanged(mv) */ ;
2486     else {
2487         for ( i=mv->glyphcnt-1; i>=0; --i )
2488             if ( mv->perchar[i].selected )
2489         break;
2490         if ( i==-1 )
2491 return;
2492         MVCopyChar(&mv->fv->b, mv->bdf, mv->glyphs[i].sc, ct_fullcopy);
2493     }
2494 }
2495 
MVMenuCopyRef(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2496 static void MVMenuCopyRef(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2497     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2498     int i;
2499 
2500     if ( GWindowGetFocusGadgetOfWindow(gw)!=NULL )
2501 return;
2502     for ( i=mv->glyphcnt-1; i>=0; --i )
2503         if ( mv->perchar[i].selected )
2504     break;
2505     if ( i==-1 )
2506 return;
2507     MVCopyChar(&mv->fv->b, mv->bdf, mv->glyphs[i].sc, ct_reference);
2508 }
2509 
MVMenuCopyWidth(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))2510 static void MVMenuCopyWidth(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
2511     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2512     int i;
2513 
2514     if ( GWindowGetFocusGadgetOfWindow(gw)!=NULL )
2515 return;
2516     for ( i=mv->glyphcnt-1; i>=0; --i )
2517         if ( mv->perchar[i].selected )
2518     break;
2519     if ( i==-1 )
2520 return;
2521     SCCopyWidth(mv->glyphs[i].sc,
2522 		   mi->mid==MID_CopyWidth?ut_width:
2523 		   mi->mid==MID_CopyVWidth?ut_vwidth:
2524 		   mi->mid==MID_CopyLBearing?ut_lbearing:
2525 					 ut_rbearing);
2526 }
2527 
MVMenuJoin(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2528 static void MVMenuJoin(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2529     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2530     int i, changed;
2531     extern float joinsnap;
2532 
2533     if ( GWindowGetFocusGadgetOfWindow(gw)!=NULL )
2534 return;
2535     for ( i=mv->glyphcnt-1; i>=0; --i )
2536         if ( mv->perchar[i].selected )
2537     break;
2538     if ( i==-1 )
2539 return;
2540     SCPreserveLayer(mv->glyphs[i].sc, mv->layer, false);
2541     mv->glyphs[i].sc->layers[mv->layer].splines =
2542         SplineSetJoin(mv->glyphs[i].sc->layers[mv->layer].splines, true, joinsnap, &changed);
2543     if ( changed )
2544         SCCharChangedUpdate(mv->glyphs[i].sc, mv->layer);
2545 }
2546 
MVPaste(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2547 static void MVPaste(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2548     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2549     int i;
2550 
2551     if ( GGadgetActiveGadgetEditCmd(mv->gw, ec_paste) )
2552         /*MVTextChanged(mv)*/ ;         /* Should get an event now */
2553     else {
2554         for ( i=mv->glyphcnt-1; i>=0; --i )
2555             if ( mv->perchar[i].selected )
2556         break;
2557         if ( i==-1 )
2558 return;
2559         PasteIntoMV(&mv->fv->b, mv->bdf, mv->glyphs[i].sc, true);
2560     }
2561 }
2562 
MVUnlinkRef(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2563 static void MVUnlinkRef(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2564     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2565     int i;
2566     SplineChar *sc;
2567     RefChar *rf, *next;
2568 
2569     for ( i=mv->glyphcnt-1; i>=0; --i )
2570         if ( mv->perchar[i].selected )
2571     break;
2572     if ( i==-1 )
2573 return;
2574     sc = mv->glyphs[i].sc;
2575     SCPreserveLayer(sc, mv->layer,false);
2576     for ( rf=sc->layers[mv->layer].refs; rf!=NULL ; rf=next ) {
2577         next = rf->next;
2578         SCRefToSplines(sc, rf, mv->layer);
2579     }
2580     SCCharChangedUpdate(sc, mv->layer);
2581 }
2582 
MVSelectAll(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2583 static void MVSelectAll(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2584     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2585     GGadgetActiveGadgetEditCmd(mv->gw, ec_selectall);
2586 }
2587 
MVClearSelection(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2588 static void MVClearSelection(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2589     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2590     int i;
2591 
2592     GWindowClearFocusGadgetOfWindow(mv->gw);
2593     for ( i=0; i<mv->glyphcnt; ++i )
2594         if ( mv->perchar[i].selected )
2595             MVDeselectChar(mv,i);
2596 }
2597 
MVMenuFontInfo(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2598 static void MVMenuFontInfo(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2599     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2600     DelayEvent(FontMenuFontInfo, mv->fv);
2601 }
2602 
MVMenuCharInfo(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2603 static void MVMenuCharInfo(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2604     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2605     int i;
2606 
2607     for ( i=mv->glyphcnt-1; i>=0; --i )
2608         if ( mv->perchar[i].selected )
2609     break;
2610     if ( i!=-1 )
2611         SCCharInfo(mv->glyphs[i].sc, mv->layer, mv->fv->b.map, -1);
2612 }
2613 
MVMenuShowDependents(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2614 static void MVMenuShowDependents(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2615     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2616     int i;
2617 
2618     for ( i=mv->glyphcnt-1; i>=0; --i )
2619         if ( mv->perchar[i].selected )
2620     break;
2621     if ( i!=-1 )
2622 return;
2623     if ( mv->glyphs[i].sc->dependents==NULL )
2624 return;
2625     SCRefBy(mv->glyphs[i].sc);
2626 }
2627 
MVMenuFindProblems(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2628 static void MVMenuFindProblems(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2629     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2630     int i;
2631 
2632     for ( i=mv->glyphcnt-1; i>=0; --i )
2633         if ( mv->perchar[i].selected )
2634     break;
2635     if ( i!=-1 )
2636         FindProblems(mv->fv, NULL, mv->glyphs[i].sc);
2637 }
2638 
MVMenuBitmaps(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))2639 static void MVMenuBitmaps(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
2640     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2641     int i;
2642 
2643     for ( i=0; i<mv->glyphcnt; ++i )
2644         if ( mv->perchar[i].selected )
2645     break;
2646     if ( i!=mv->glyphcnt )
2647         BitmapDlg(mv->fv, mv->glyphs[i].sc, mi->mid==MID_AvailBitmaps );
2648     else if ( mi->mid==MID_AvailBitmaps )
2649         BitmapDlg(mv->fv, NULL, true );
2650 }
2651 
getorigin(void * d,BasePoint * base,int index)2652 static int getorigin(void *d, BasePoint *base, int index) {
2653     SplineChar *sc = (SplineChar *) d;
2654     DBounds bb;
2655 
2656     base->x = base->y = 0;
2657     switch ( index ) {
2658       case 0:		/* Character origin */
2659 	/* all done */
2660       break;
2661       case 1:		/* Center of selection */
2662 	SplineCharFindBounds(sc,&bb);
2663 	base->x = (bb.minx+bb.maxx)/2;
2664 	base->y = (bb.miny+bb.maxy)/2;
2665       break;
2666       default:
2667 return( false );
2668     }
2669 return( true );
2670 }
2671 
MVTransFunc(void * _sc,real transform[6],int UNUSED (otype),BVTFunc * UNUSED (bvts),enum fvtrans_flags flags)2672 static void MVTransFunc(void *_sc, real transform[6], int UNUSED(otype),
2673         BVTFunc *UNUSED(bvts), enum fvtrans_flags flags ) {
2674     SplineChar *sc = _sc;
2675     FVTrans( (FontViewBase *)sc->parent->fv, sc, transform, NULL, flags);
2676 }
2677 
MVMenuTransform(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2678 static void MVMenuTransform(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2679     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2680     int i;
2681 
2682     for ( i=mv->glyphcnt-1; i>=0; --i )
2683         if ( mv->perchar[i].selected )
2684     break;
2685     if ( i!=-1 )
2686         TransformDlgCreate( mv->glyphs[i].sc, MVTransFunc, getorigin, true, cvt_none );
2687 }
2688 
2689 #ifdef FONTFORGE_CONFIG_TILEPATH
MVMenuTilePath(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2690 static void MVMenuTilePath(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2691     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2692     int i;
2693 
2694     for ( i=mv->glyphcnt-1; i>=0; --i )
2695         if ( mv->perchar[i].selected )
2696     break;
2697     if ( i!=-1 )
2698         SCTile(mv->glyphs[i].sc, mv->layer);
2699 }
2700 #endif
2701 
_MVMenuOverlap(MetricsView * mv,enum overlap_type ot)2702 static void _MVMenuOverlap(MetricsView *mv, enum overlap_type ot) {
2703     int i;
2704 
2705     for ( i=mv->glyphcnt-1; i>=0; --i )
2706 	if ( mv->perchar[i].selected )
2707     break;
2708     if ( i!=-1 ) {
2709 	SplineChar *sc = mv->glyphs[i].sc;
2710 	if ( !SCRoundToCluster(sc, mv->layer, false, 0.03, 0.12))
2711 	    SCPreserveLayer(sc, mv->layer, false);
2712 	MinimumDistancesFree(sc->md);
2713 	sc->md = NULL;
2714 	sc->layers[mv->layer].splines = SplineSetRemoveOverlap(sc, sc->layers[mv->layer].splines, ot);
2715 	SCCharChangedUpdate(sc, mv->layer);
2716     }
2717 }
2718 
MVMenuOverlap(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))2719 static void MVMenuOverlap(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
2720     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2721     _MVMenuOverlap(mv, mi->mid==MID_RmOverlap ? over_remove :
2722 		       mi->mid==MID_Intersection ? over_intersect :
2723 			   over_findinter);
2724 }
2725 
MVMenuInline(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2726 static void MVMenuInline(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2727     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2728     OutlineDlg(NULL, NULL, mv, true);
2729 }
2730 
MVMenuOutline(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2731 static void MVMenuOutline(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2732     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2733     OutlineDlg(NULL, NULL, mv, false);
2734 }
2735 
MVMenuShadow(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2736 static void MVMenuShadow(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2737     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2738     ShadowDlg(NULL, NULL, mv, false);
2739 }
2740 
MVMenuWireframe(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2741 static void MVMenuWireframe(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2742     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2743     ShadowDlg(NULL, NULL, mv, true);
2744 }
2745 
MVSimplify(MetricsView * mv,int type)2746 static void MVSimplify( MetricsView *mv,int type ) {
2747     int i;
2748     static struct simplifyinfo smpls[] = {
2749 	    { sf_normal, 0, 0, 0, 0, 0, 0 },
2750 	    { sf_normal,.75,.05,0,-1, 0, 0 },
2751 	    { sf_normal,.75,.05,0,-1, 0, 0 }};
2752     struct simplifyinfo *smpl = &smpls[type+1];
2753 
2754     if ( smpl->linelenmax==-1 ) {
2755 	smpl->err = (mv->sf->ascent+mv->sf->descent)/1000.;
2756 	smpl->linelenmax = (mv->sf->ascent+mv->sf->descent)/100.;
2757     }
2758 
2759     if ( type==1 ) {
2760 	if ( !SimplifyDlg(mv->sf,smpl))
2761 return;
2762 	if ( smpl->set_as_default )
2763 	    smpls[1] = *smpl;
2764     }
2765 
2766     for ( i=mv->glyphcnt-1; i>=0; --i )
2767 	if ( mv->perchar[i].selected )
2768     break;
2769     if ( i!=-1 ) {
2770 	SplineChar *sc = mv->glyphs[i].sc;
2771 	SCPreserveLayer(sc,mv->layer,false);
2772 	sc->layers[mv->layer].splines = SplineCharSimplify(sc,sc->layers[mv->layer].splines,smpl);
2773 	SCCharChangedUpdate(sc,mv->layer);
2774     }
2775 }
2776 
MVMenuSimplify(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2777 static void MVMenuSimplify(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2778     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2779     MVSimplify(mv, false);
2780 }
2781 
MVMenuSimplifyMore(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2782 static void MVMenuSimplifyMore(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2783     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2784     MVSimplify(mv, true);
2785 }
2786 
MVMenuCleanup(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2787 static void MVMenuCleanup(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2788     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2789     MVSimplify(mv, -1);
2790 }
2791 
MVMenuAddExtrema(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2792 static void MVMenuAddExtrema(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2793     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2794     int i;
2795     SplineFont *sf = mv->sf;
2796     int emsize = sf->ascent+sf->descent;
2797 
2798     for ( i=mv->glyphcnt-1; i>=0; --i )
2799         if ( mv->perchar[i].selected )
2800     break;
2801     if ( i!=-1 ) {
2802         SplineChar *sc = mv->glyphs[i].sc;
2803         SCPreserveLayer(sc, mv->layer, false);
2804         SplineCharAddExtrema(sc, sc->layers[mv->layer].splines, ae_only_good, emsize);
2805         SCCharChangedUpdate(sc, mv->layer);
2806     }
2807 }
2808 
MVMenuRound2Int(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2809 static void MVMenuRound2Int(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2810     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2811     int i;
2812 
2813     for ( i=mv->glyphcnt-1; i>=0; --i )
2814         if ( mv->perchar[i].selected )
2815     break;
2816     if ( i!=-1 ) {
2817         SCPreserveLayer(mv->glyphs[i].sc, mv->layer, false);
2818         SCRound2Int( mv->glyphs[i].sc, mv->layer, 1.0);
2819     }
2820 }
2821 
MVMenuAutotrace(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * e)2822 static void MVMenuAutotrace(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *e) {
2823     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2824     int i;
2825     GCursor ct;
2826 
2827     for ( i=mv->glyphcnt-1; i>=0; --i )
2828         if ( mv->perchar[i].selected )
2829     break;
2830     if ( i!=-1 ) {
2831         ct = GDrawGetCursor(mv->gw);
2832         GDrawSetCursor(mv->gw, ct_watch);
2833         ff_progress_allow_events();
2834         SCAutoTrace(mv->glyphs[i].sc, mv->layer, e!=NULL && (e->u.mouse.state&ksm_shift));
2835         GDrawSetCursor(mv->gw, ct);
2836     }
2837 }
2838 
MVMenuCorrectDir(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2839 static void MVMenuCorrectDir(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2840     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2841     int i;
2842 
2843     for ( i=mv->glyphcnt-1; i>=0; --i )
2844 	if ( mv->perchar[i].selected )
2845     break;
2846     if ( i!=-1 ) {
2847 	SplineChar *sc = mv->glyphs[i].sc;
2848 	int changed = false, refchanged=false;
2849 	RefChar *ref;
2850 	int asked=-1;
2851 
2852 	for ( ref=sc->layers[mv->layer].refs; ref!=NULL; ref=ref->next ) {
2853 	    if ( ref->transform[0]*ref->transform[3]<0 ||
2854 		    (ref->transform[0]==0 && ref->transform[1]*ref->transform[2]>0)) {
2855 		if ( asked==-1 ) {
2856 		    char *buts[4];
2857 		    buts[0] = _("_Unlink");
2858 		    buts[1] = _("_No");
2859 		    buts[2] = _("_Cancel");
2860 		    buts[3] = NULL;
2861 		    asked = gwwv_ask(_("Flipped Reference"),(const char **) buts,0,2,_("%.50s contains a flipped reference. This cannot be corrected as is. Would you like me to unlink it and then correct it?"), sc->name );
2862 		    if ( asked==2 )
2863 return;
2864 		    else if ( asked==1 )
2865 	break;
2866 		}
2867 		if ( asked==0 ) {
2868 		    if ( !refchanged ) {
2869 			refchanged = true;
2870 			SCPreserveLayer(sc,mv->layer,false);
2871 		    }
2872 		    SCRefToSplines(sc,ref,mv->layer);
2873 		}
2874 	    }
2875 	}
2876 
2877 	if ( !refchanged )
2878 	    SCPreserveLayer(sc,mv->layer,false);
2879 	sc->layers[mv->layer].splines = SplineSetsCorrect(sc->layers[mv->layer].splines,&changed);
2880 	if ( changed || refchanged )
2881 	    SCCharChangedUpdate(sc,mv->layer);
2882     }
2883 }
2884 
_MVMenuBuildAccent(MetricsView * mv,int onlyaccents)2885 static void _MVMenuBuildAccent(MetricsView *mv,int onlyaccents) {
2886     int i;
2887     extern int onlycopydisplayed;
2888 
2889     for ( i=mv->glyphcnt-1; i>=0; --i )
2890 	if ( mv->perchar[i].selected )
2891     break;
2892     if ( i!=-1 ) {
2893 	SplineChar *sc = mv->glyphs[i].sc;
2894 	if ( SFIsSomethingBuildable(mv->sf,sc,mv->layer,onlyaccents) )
2895 	    SCBuildComposit(mv->sf,sc,mv->layer,NULL,onlycopydisplayed,onlyaccents);
2896     }
2897 }
2898 
MVMenuBuildAccent(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2899 static void MVMenuBuildAccent(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2900     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2901     _MVMenuBuildAccent(mv, false);
2902 }
2903 
MVMenuBuildComposite(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2904 static void MVMenuBuildComposite(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2905     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2906     _MVMenuBuildAccent(mv, true);
2907 }
2908 
MVResetText(MetricsView * mv)2909 static void MVResetText(MetricsView *mv) {
2910     unichar_t *new, *pt;
2911     int i;
2912 
2913     new = malloc((mv->clen+1)*sizeof(unichar_t));
2914     for ( pt=new, i=0; i<mv->clen; ++i ) {
2915 	if ( mv->chars[i]->unicodeenc==-1 )
2916 	    *pt++ = MVFakeUnicodeOfSc(mv,mv->chars[i]);
2917 	else
2918 	    *pt++ = mv->chars[i]->unicodeenc;
2919     }
2920     *pt = '\0';
2921     GGadgetSetTitle(mv->text,new);
2922     free(new );
2923 }
2924 
MVMenuLigatures(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2925 static void MVMenuLigatures(GWindow gw,struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2926     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2927     SFShowLigatures(mv->sf, NULL);
2928 }
2929 
MVMenuKernPairs(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))2930 static void MVMenuKernPairs(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
2931     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2932     SFShowKernPairs(mv->sf, NULL, NULL, mv->layer);
2933 }
2934 
MVMenuAnchorPairs(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))2935 static void MVMenuAnchorPairs(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
2936     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2937     SFShowKernPairs(mv->sf, NULL, mi->ti.userdata, mv->layer);
2938 }
2939 
_MVMenuScale(MetricsView * mv,int mid)2940 static void _MVMenuScale( MetricsView *mv, int mid ) {
2941 
2942     if ( mid==MID_ZoomIn ) {
2943 	if ( --mv->scale_index<0 ) mv->scale_index = 0;
2944     } else {
2945 	if ( ++mv->scale_index >= sizeof(mv_scales)/sizeof(mv_scales[0]) )
2946 	    mv->scale_index = sizeof(mv_scales)/sizeof(mv_scales[0])-1;
2947     }
2948 
2949     if ( mv->pixelsize_set_by_window ) {
2950 	mv->pixelsize = mv_scales[mv->scale_index]*(mv->vheight - 2);
2951 	if ( mv->bdf==NULL ) {
2952 	    BDFFontFree(mv->show);
2953 	    mv->show = SplineFontPieceMeal(mv->sf,mv->layer,mv->pixelsize,72,
2954 					   MVGetSplineFontPieceMealFlags( mv ), NULL );
2955 	} else
2956 	    mv->pixelsize_set_by_window = false;
2957     }
2958     MVReKern(mv);
2959     MVSetVSb(mv);
2960 }
2961 
MVMenuScale(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))2962 static void MVMenuScale(GWindow gw,struct gmenuitem *mi, GEvent *UNUSED(e)) {
2963     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2964     _MVMenuScale(mv, mi->mid);
2965 }
2966 
MVMenuInsertChar(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))2967 static void MVMenuInsertChar(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
2968     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
2969     SplineFont *sf = mv->sf;
2970     int i, j, pos = GotoChar(sf,mv->fv->b.map,NULL);
2971 
2972     if ( pos==-1 || pos>=mv->fv->b.map->enccount )
2973 return;
2974 
2975     for ( i=0; i<mv->glyphcnt; ++i )
2976 	if ( mv->perchar[i].selected )
2977     break;
2978     if ( i!=mv->glyphcnt )	/* Something selected */
2979 	/* Ok... */;
2980     else if ( mi->mid==MID_InsertCharA )
2981 	i = mv->glyphcnt;
2982     else
2983 	i = 0;
2984     if ( mi->mid==MID_InsertCharA ) {
2985 	if ( i!=mv->glyphcnt )
2986 	    ++i;
2987     } else {
2988 	if ( i==mv->glyphcnt ) i = 0;
2989     }
2990     if ( i==mv->glyphcnt )
2991 	i = mv->clen;
2992     else
2993 	i = mv->glyphs[i].orig_index;		/* Index in the string of chars, not glyphs */
2994 
2995     if ( mv->clen+1>=mv->cmax ) {
2996 	int oldmax=mv->cmax;
2997 	mv->cmax = mv->clen+10;
2998 	mv->chars = realloc(mv->chars,mv->cmax*sizeof(SplineChar *));
2999 	memset(mv->chars+oldmax,'\0',(mv->cmax-oldmax)*sizeof(SplineChar *));
3000     }
3001     for ( j=mv->clen; j>i; --j )
3002 	mv->chars[j] = mv->chars[j-1];
3003     mv->chars[i] = SFMakeChar(sf,mv->fv->b.map,pos);
3004     ++mv->clen;
3005     MVRemetric(mv);
3006     for ( j=0; j<mv->glyphcnt; ++j )
3007 	if ( mv->glyphs[j].orig_index==i ) {
3008 	    MVDoSelect(mv,j);
3009     break;
3010 	}
3011     GDrawRequestExpose(mv->v,NULL,false);
3012     MVResetText(mv);
3013 }
3014 
MVMenuChangeChar(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3015 static void MVMenuChangeChar(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3016     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3017     SplineFont *sf = mv->sf;
3018     SplineChar *sc;
3019     EncMap *map = mv->fv->b.map;
3020     int i, pos, gid;
3021 
3022     for ( i=0; i<mv->glyphcnt; ++i )
3023 	if ( mv->perchar[i].selected )
3024     break;
3025     if ( i!=mv->glyphcnt ) {
3026 	pos = -1;
3027 	i = mv->glyphs[i].orig_index;
3028 	sc = mv->chars[ i ];
3029 	if ( mi->mid == MID_Next ) {
3030 	    pos = map->backmap[sc->orig_pos]+1;
3031 	} else if ( mi->mid==MID_Prev ) {
3032 	    pos = map->backmap[sc->orig_pos]-1;
3033 	} else if ( mi->mid==MID_NextDef ) {
3034 	    for ( pos = map->backmap[sc->orig_pos]+1;
3035 		    pos<map->enccount && ((gid=map->map[pos])==-1 || sf->glyphs[gid]==NULL); ++pos );
3036 	    if ( pos>=map->enccount )
3037 return;
3038 	} else if ( mi->mid==MID_PrevDef ) {
3039 	    for ( pos = map->backmap[sc->orig_pos]-1;
3040 		    pos<map->enccount && ((gid=map->map[pos])==-1 || sf->glyphs[gid]==NULL); --pos );
3041 	    if ( pos<0 )
3042 return;
3043 	} else if ( mi->mid==MID_ReplaceChar ) {
3044 	    pos = GotoChar(sf,mv->fv->b.map,NULL);
3045 	    if ( pos<0 || pos>=mv->fv->b.map->enccount)
3046 return;
3047 	}
3048 	if ( pos>=0 && pos<map->enccount ) {
3049 	    mv->chars[i] = SFMakeChar(sf,mv->fv->b.map,pos);
3050 	    MVRemetric(mv);
3051 	    MVResetText(mv);
3052 	    GDrawRequestExpose(mv->v,NULL,false);
3053 	}
3054     }
3055 }
3056 
MVMenuFindInFontView(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3057 static void MVMenuFindInFontView(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3058     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3059     int i;
3060 
3061     for ( i=0; i<mv->glyphcnt; ++i ) {
3062         if ( mv->perchar[i].selected ) {
3063             FVChangeChar(mv->fv, mv->fv->b.map->backmap[mv->glyphs[i].sc->orig_pos]);
3064             GDrawSetVisible(mv->fv->gw, true);
3065             GDrawRaise(mv->fv->gw);
3066     break;
3067         }
3068     }
3069 }
3070 
MVMenuShowGrid(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3071 static void MVMenuShowGrid(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3072     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3073     if ( mi->mid == MID_ShowGrid )
3074         mv->showgrid = mv_showgrid;
3075     else if ( mi->mid == MID_HideGrid )
3076         mv->showgrid = mv_hidegrid;
3077     else if ( mi->mid == MID_PartialGrid )
3078         mv->showgrid = mv_partialgrid;
3079     else if ( mi->mid == MID_HideGridWhenMoving )
3080         mv->showgrid = mv_hidemovinggrid;
3081     mvshowgrid = mv->showgrid;
3082     SavePrefs(true);
3083     GDrawRequestExpose(mv->v, NULL, false);
3084 }
3085 
MVMenuAA(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3086 static void MVMenuAA(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3087     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3088 
3089     mv_antialias = mv->antialias = !mv->antialias;
3090     mv->bdf = NULL;
3091     BDFFontFree(mv->show);
3092     mv->show = SplineFontPieceMeal(mv->sf, mv->layer, mv->ptsize, mv->dpi,
3093                     MVGetSplineFontPieceMealFlags( mv ),
3094                     NULL);
3095     GDrawRequestExpose(mv->v,NULL,false);
3096 }
3097 
3098 
MVMenuRenderUsingHinting(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3099 static void MVMenuRenderUsingHinting(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3100     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3101 
3102     mv->usehinting = !mv->usehinting;
3103     mv->bdf = NULL;
3104     BDFFontFree(mv->show);
3105     mv->show = SplineFontPieceMeal(mv->sf, mv->layer, mv->ptsize, mv->dpi,
3106 				   MVGetSplineFontPieceMealFlags( mv ),
3107 				   NULL);
3108     GDrawRequestExpose(mv->v,NULL,false);
3109 }
3110 
MVWindowTitle(MetricsView * mv)3111 static char* MVWindowTitle(MetricsView *mv) {
3112     return smprintf(mv->type == mv_kernonly ?  _("Kerning Metrics For %.50s") :
3113 	                mv->type == mv_widthonly ? _("Advance Width Metrics For %.50s") :
3114 	                                           _("Metrics For %.50s"), mv->sf->fontname);
3115 }
3116 
MVMenuWindowType(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3117 static void MVMenuWindowType(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3118     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3119 
3120     mv_type = mv->type = mi->mid==MID_KernOnly  ? mv_kernonly :
3121 			 mi->mid==MID_WidthOnly ? mv_widthonly :
3122 			 mv_kernwidth;
3123     char* buf = MVWindowTitle(mv);
3124     GDrawSetWindowTitles8(mv->gw, buf, buf);
3125     free(buf); // GGDKDrawSetWindowTitles8 does a copy
3126     GDrawRequestExpose(mv->v, NULL, false);
3127     GDrawRequestExpose(mv->gw, NULL, false);
3128 }
3129 
MVMenuVertical(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3130 static void MVMenuVertical(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3131     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3132 
3133     if ( !mv->sf->hasvmetrics ) {
3134         if ( mv->vertical )
3135             MVToggleVertical(mv);
3136     } else
3137         MVToggleVertical(mv);
3138     GDrawRequestExpose(mv->gw, NULL, false);
3139     GDrawRequestExpose(mv->v, NULL, false);
3140 }
3141 
MVMenuShowBitmap(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3142 static void MVMenuShowBitmap(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3143     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3144     BDFFont *bdf = mi->ti.userdata;
3145 
3146     if ( mv->bdf!=bdf ) {
3147         mv->pixelsize_set_by_window = bdf==NULL;
3148         if ( bdf!=NULL ) {
3149             mv->pixelsize = mv->ptsize = bdf->pixelsize;
3150             mv->dpi = 72;
3151         }
3152         MVChangeDisplayFont(mv, bdf);
3153         GDrawRequestExpose(mv->v, NULL, false);
3154     }
3155 }
3156 
MVMoveInWordListByOffset(MetricsView * mv,int offset)3157 static void MVMoveInWordListByOffset( MetricsView *mv, int offset )
3158 {
3159     if ( mv->word_index!=-1 ) {
3160 	int32 len;
3161 	GTextInfo **ti = GGadgetGetList(mv->text,&len);
3162 	/* We subtract 3 because: There are two lines saying "load * list" */
3163 	/*  and then a line with a rule on it which we don't want access to */
3164 	if ( mv->word_index+offset >=0 && mv->word_index+offset<len-3 ) {
3165 	    const unichar_t *tit;
3166 	    mv->word_index += offset;
3167 	    GGadgetSelectOneListItem(mv->text,mv->word_index);
3168 	    tit = _GGadgetGetTitle(mv->text);
3169 	    if ( tit!=NULL && tit[0]==0x200b )
3170 		MVFigureGlyphNames(mv,tit+1);
3171 	    else
3172 		MVTextChanged(mv);
3173 	    ti = NULL;
3174 	}
3175     }
3176 }
3177 
MVMenuNextLineInWordList(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3178 static void MVMenuNextLineInWordList(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3179     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3180     MVMoveInWordListByOffset( mv, 1 );
3181 }
MVMenuPrevLineInWordList(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3182 static void MVMenuPrevLineInWordList(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3183     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3184     MVMoveInWordListByOffset( mv, -1 );
3185 }
3186 
3187 
3188 #define CID_DPI		1002
3189 #define CID_Size	1003
3190 
3191 struct pxsz {
3192     MetricsView *mv;
3193     GWindow gw;
3194     int done;
3195 };
3196 
PXSZ_OK(GGadget * g,GEvent * e)3197 static int PXSZ_OK(GGadget *g, GEvent *e) {
3198 
3199     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3200 	struct pxsz *pxsz = GDrawGetUserData(GGadgetGetWindow(g));
3201 	MetricsView *mv = pxsz->mv;
3202 	int ptsize, dpi, err=0;
3203 
3204 	ptsize = GetInt8( pxsz->gw, CID_Size, _("Point Size"), &err );
3205 	dpi = GetInt8( pxsz->gw, CID_DPI, _("DPI"), &err );
3206 	if ( err )
3207 return(true);
3208 	if ( ptsize<3 || ptsize>1500 || dpi<10 || dpi > 2000 ) {
3209 	    ff_post_error(_("Number out of range"),_("Number out of range"));
3210 return( true );
3211 	}
3212 	mv->pixelsize_set_by_window = false;
3213 	mv->ptsize = ptsize;
3214 	mv->dpi = dpi;
3215 	mv->pixelsize = rint( (ptsize*dpi)/72.0 );
3216 	if ( mv->bdf==NULL )
3217 	    BDFFontFree(mv->show);
3218 	mv->bdf = NULL;
3219 	mv->show = SplineFontPieceMeal(mv->sf,mv->layer,mv->ptsize,mv->dpi,
3220 				       MVGetSplineFontPieceMealFlags( mv ), NULL );
3221 
3222 	MVReKern(mv);
3223 	MVSetVSb(mv);
3224 	pxsz->done = 2;
3225     }
3226 return( true );
3227 }
3228 
PXSZ_Cancel(GGadget * g,GEvent * e)3229 static int PXSZ_Cancel(GGadget *g, GEvent *e) {
3230 
3231     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
3232 	struct pxsz *pxsz = GDrawGetUserData(GGadgetGetWindow(g));
3233 	pxsz->done = true;
3234     }
3235 return( true );
3236 }
3237 
pxsz_e_h(GWindow gw,GEvent * event)3238 static int pxsz_e_h(GWindow gw, GEvent *event) {
3239     struct pxsz *pxsz = GDrawGetUserData(gw);
3240 
3241     switch ( event->type ) {
3242       case et_char:
3243 return( false );
3244       case et_close:
3245 	pxsz->done = true;
3246       break;
3247     }
3248 return( true );
3249 }
3250 
MVMenuPointSize(GWindow mgw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3251 static void MVMenuPointSize(GWindow mgw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3252     MetricsView *mv = (MetricsView *) GDrawGetUserData(mgw);
3253     struct pxsz pxsz;
3254     GRect pos;
3255     GWindow gw;
3256     GWindowAttrs wattrs;
3257     GGadgetCreateData gcd[7], *hvarray[5][3], *barray[8], boxes[3];
3258     GTextInfo label[7];
3259     int i,k;
3260     double iscale = mv->pixelsize_set_by_window ? 1.0 : mv_scales[mv->scale_index];
3261     char buffer[20], dbuffer[20];
3262 
3263     memset(&pxsz,0,sizeof(pxsz));
3264     pxsz.mv = mv;
3265 
3266     memset(&wattrs,0,sizeof(wattrs));
3267     memset(&gcd,0,sizeof(gcd));
3268     memset(&label,0,sizeof(label));
3269     memset(&boxes,0,sizeof(boxes));
3270 
3271     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
3272     wattrs.event_masks = ~(1<<et_charup);
3273     wattrs.restrict_input_to_me = true;
3274     wattrs.undercursor = 1;
3275     wattrs.cursor = ct_pointer;
3276     wattrs.utf8_window_title = _("Set Point Size");
3277     wattrs.is_dlg = true;
3278     pos.x = pos.y = 0;
3279     pos.width = 100;
3280     pos.height = 100;
3281     pxsz.gw = gw = GDrawCreateTopWindow(NULL,&pos,pxsz_e_h,&pxsz,&wattrs);
3282 
3283     k = i = 0;
3284 
3285     label[k].text = (unichar_t *) _("Point Size:");
3286     label[k].text_is_1byte = true;
3287     gcd[k].gd.label = &label[k];
3288     gcd[k].gd.flags = gg_visible|gg_enabled ;
3289     /*gcd[k].gd.handle_controlevent = SS_ScriptChanged;*/
3290     gcd[k++].creator = GLabelCreate;
3291     hvarray[i][0] = &gcd[k-1];
3292 
3293     sprintf( buffer, "%d", (int) rint( mv->ptsize/iscale ));
3294     label[k].text = (unichar_t *) buffer;
3295     label[k].text_is_1byte = true;
3296     gcd[k].gd.label = &label[k];
3297     gcd[k].gd.flags = gg_visible|gg_enabled;
3298     gcd[k].gd.cid = CID_Size;
3299     gcd[k++].creator = GTextFieldCreate;
3300     hvarray[i][1] = &gcd[k-1]; hvarray[i++][2] = NULL;
3301 
3302     label[k].text = (unichar_t *) _("DPI:");
3303     label[k].text_is_1byte = true;
3304     gcd[k].gd.label = &label[k];
3305     gcd[k].gd.flags = gg_visible|gg_enabled ;
3306     /*gcd[k].gd.handle_controlevent = SS_ScriptChanged;*/
3307     gcd[k++].creator = GLabelCreate;
3308     hvarray[i][0] = &gcd[k-1];
3309 
3310     sprintf( dbuffer, "%d", mv->dpi );
3311     label[k].text = (unichar_t *) dbuffer;
3312     label[k].text_is_1byte = true;
3313     gcd[k].gd.label = &label[k];
3314     gcd[k].gd.flags = gg_visible|gg_enabled;
3315     gcd[k].gd.cid = CID_DPI;
3316     gcd[k++].creator = GTextFieldCreate;
3317     hvarray[i][1] = &gcd[k-1]; hvarray[i++][2] = NULL;
3318 
3319     label[k].text = (unichar_t *) _("_OK");
3320     label[k].text_is_1byte = true;
3321     label[k].text_in_resource = true;
3322     gcd[k].gd.label = &label[k];
3323     gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_default;
3324     gcd[k].gd.handle_controlevent = PXSZ_OK;
3325     gcd[k++].creator = GButtonCreate;
3326 
3327     label[k].text = (unichar_t *) _("_Cancel");
3328     label[k].text_is_1byte = true;
3329     label[k].text_in_resource = true;
3330     gcd[k].gd.label = &label[k];
3331     gcd[k].gd.flags = gg_visible|gg_enabled | gg_but_cancel;
3332     gcd[k].gd.handle_controlevent = PXSZ_Cancel;
3333     gcd[k++].creator = GButtonCreate;
3334 
3335     barray[0] = barray[2] = barray[3] = barray[4] = barray[6] = GCD_Glue; barray[7] = NULL;
3336     barray[1] = &gcd[k-2]; barray[5] = &gcd[k-1];
3337     hvarray[i][0] = &boxes[2]; hvarray[i][1] = GCD_ColSpan; hvarray[i++][2] = NULL;
3338     hvarray[i][0] = NULL;
3339 
3340     memset(boxes,0,sizeof(boxes));
3341     boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
3342     boxes[0].gd.flags = gg_enabled|gg_visible;
3343     boxes[0].gd.u.boxelements = hvarray[0];
3344     boxes[0].creator = GHVGroupCreate;
3345 
3346     boxes[2].gd.flags = gg_enabled|gg_visible;
3347     boxes[2].gd.u.boxelements = barray;
3348     boxes[2].creator = GHBoxCreate;
3349 
3350     GGadgetsCreate(gw,boxes);
3351     GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
3352     GHVBoxSetExpandableRow(boxes[0].ret,gb_expandglue);
3353 
3354     GHVBoxFitWindow(boxes[0].ret);
3355 
3356     GDrawSetVisible(gw,true);
3357     while ( !pxsz.done )
3358         GDrawProcessOneEvent(NULL);
3359     GDrawDestroyWindow(gw);
3360 }
3361 
MVMenuSizeWindow(GWindow mgw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3362 static void MVMenuSizeWindow(GWindow mgw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3363     MetricsView *mv = (MetricsView *) GDrawGetUserData(mgw);
3364     mv->pixelsize_set_by_window = true;
3365     mv->pixelsize = mv_scales[mv->scale_index]*(mv->vheight - 2);
3366     mv->dpi = 72;
3367     mv->ptsize = mv->pixelsize;
3368     if ( mv->bdf==NULL ) {
3369         BDFFontFree(mv->show);
3370         mv->show = SplineFontPieceMeal(
3371                         mv->sf, mv->layer, mv->pixelsize, 72,
3372 			MVGetSplineFontPieceMealFlags( mv ),
3373                         NULL);
3374     }
3375     MVReKern(mv);
3376     MVSetVSb(mv);
3377 }
3378 
MVMenuChangePointSize(GWindow mgw,struct gmenuitem * mi,GEvent * UNUSED (e))3379 static void MVMenuChangePointSize(GWindow mgw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3380     MetricsView *mv = (MetricsView *) GDrawGetUserData(mgw);
3381 
3382     if ( mv->pixelsize_set_by_window )
3383 return;
3384     if ( mi->mid==MID_Bigger )
3385         ++(mv->ptsize);
3386     else
3387         --(mv->ptsize);
3388     mv->pixelsize = rint( (mv->ptsize*mv->dpi)/72.0 );
3389     if ( mv->bdf==NULL )
3390         BDFFontFree(mv->show);
3391     mv->bdf = NULL;
3392     mv->show = SplineFontPieceMeal(mv->sf, mv->layer, mv->ptsize, mv->dpi,
3393 				   MVGetSplineFontPieceMealFlags( mv ), NULL);
3394 
3395     MVReKern(mv);
3396     MVSetVSb(mv);
3397 }
3398 
MVMenuChangeLayer(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3399 static void MVMenuChangeLayer(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3400     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3401 
3402     mv->layer = mi->mid;
3403     BDFFontFree(mv->show);
3404     mv->show = SplineFontPieceMeal(mv->sf, mv->layer, mv->ptsize, mv->dpi,
3405 				   MVGetSplineFontPieceMealFlags( mv ), NULL);
3406     MVRemetric(mv);
3407     GDrawRequestExpose(mv->v,NULL,false);
3408 }
3409 
MVMenuCenter(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3410 static void MVMenuCenter(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e))
3411 {
3412     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3413     int i;
3414     DBounds bb;
3415     real transform[6];
3416     SplineChar *sc;
3417 
3418     for ( i=0; i<mv->glyphcnt; ++i )
3419 	if ( mv->perchar[i].selected )
3420     break;
3421     if ( i!=mv->glyphcnt ) {
3422 	sc = mv->glyphs[i].sc;
3423 	transform[0] = transform[3] = 1.0;
3424 	transform[1] = transform[2] = transform[5] = 0.0;
3425 	SplineCharFindBounds(sc,&bb);
3426 	if ( mi->mid==MID_Center )
3427 	    transform[4] = (sc->width-(bb.maxx-bb.minx))/2 - bb.minx;
3428 	else
3429 	    transform[4] = (sc->width-(bb.maxx-bb.minx))/3 - bb.minx;
3430 	if ( transform[4]!=0 )
3431 	    FVTrans( (FontViewBase *)mv->fv,sc,transform,NULL, fvt_dontmovewidth| fvt_alllayers );
3432     }
3433 }
3434 
MVMenuKernByClasses(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3435 static void MVMenuKernByClasses(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3436     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3437     ShowKernClasses(mv->sf, mv, mv->layer, false);
3438 }
3439 
MVMenuVKernByClasses(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3440 static void MVMenuVKernByClasses(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3441     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3442     ShowKernClasses(mv->sf, mv, mv->layer, true);
3443 }
3444 
MVMenuVKernFromHKern(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3445 static void MVMenuVKernFromHKern(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3446     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3447     FVVKernFromHKern((FontViewBase *) mv->fv);
3448 }
3449 
MVMenuKPCloseup(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3450 static void MVMenuKPCloseup(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3451     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3452     SplineChar *sc1=NULL, *sc2=NULL;
3453     int i;
3454 
3455     for ( i=0; i<mv->glyphcnt; ++i ) {
3456         if ( mv->perchar[i].selected ) {
3457             sc1 = mv->glyphs[i].sc;
3458             if ( i+1<mv->glyphcnt )
3459                 sc2 = mv->glyphs[i+1].sc;
3460     break;
3461         }
3462     }
3463     KernPairD(mv->sf,sc1,sc2,mv->layer,mv->vertical);
3464 }
3465 
3466 static GMenuItem2 wnmenu[] = {
3467     { { (unichar_t *) N_("New O_utline Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'u' }, H_("New Outline Window|No Shortcut"), NULL, NULL, MVMenuOpenOutline, MID_OpenOutline },
3468     { { (unichar_t *) N_("New _Bitmap Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("New Bitmap Window|No Shortcut"), NULL, NULL, MVMenuOpenBitmap, MID_OpenBitmap },
3469     { { (unichar_t *) N_("New _Metrics Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 1, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("New Metrics Window|No Shortcut"), NULL, NULL, /* No function, never avail */NULL, 0 },
3470     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3471     { { (unichar_t *) N_("Warnings"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Warnings|No Shortcut"), NULL, NULL, _MenuWarnings, MID_Warnings },
3472     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3473     GMENUITEM2_EMPTY
3474 };
3475 
MVWindowMenuBuild(GWindow gw,struct gmenuitem * mi,GEvent * e)3476 static void MVWindowMenuBuild(GWindow gw,struct gmenuitem *mi,GEvent *e) {
3477     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3478     int i;
3479     SplineChar *sc;
3480     struct gmenuitem *wmi;
3481 
3482     WindowMenuBuild(gw,mi,e);
3483 
3484     for ( i=mv->glyphcnt-1; i>=0; --i )
3485 	if ( mv->perchar[i].selected )
3486     break;
3487     if ( i==-1 ) sc = NULL; else sc = mv->glyphs[i].sc;
3488 
3489     for ( wmi = mi->sub; wmi->ti.text!=NULL || wmi->ti.line ; ++wmi ) {
3490 	switch ( wmi->mid ) {
3491 	  case MID_OpenOutline:
3492 	    wmi->ti.disabled = sc==NULL;
3493 	  break;
3494 	  case MID_OpenBitmap:
3495 	    mi->ti.disabled = mv->sf->bitmaps==NULL || sc==NULL;
3496 	  break;
3497 	  case MID_Warnings:
3498 	    wmi->ti.disabled = ErrorWindowExists();
3499 	  break;
3500 	}
3501     }
3502 }
3503 
3504 static GMenuItem2 dummyitem[] = {
3505     { { (unichar_t *) N_("Font|_New"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'N' }, NULL, NULL, NULL, NULL, 0 },
3506     GMENUITEM2_EMPTY
3507 };
3508 static GMenuItem2 fllist[] = {
3509     { { (unichar_t *) N_("Font|_New"), (GImage *) "filenew.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'N' }, H_("New|No Shortcut"), NULL, NULL, MenuNew, 0 },
3510     { { (unichar_t *) N_("_Open"), (GImage *) "fileopen.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'O' }, H_("Open|No Shortcut"), NULL, NULL, MVMenuOpen, 0 },
3511     { { (unichar_t *) N_("Recen_t"), (GImage *) "filerecent.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 't' }, NULL, dummyitem, MenuRecentBuild, NULL, MID_Recent },
3512     { { (unichar_t *) N_("_Close"), (GImage *) "fileclose.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'C' }, H_("Close|No Shortcut"), NULL, NULL, MVMenuClose, 0 },
3513     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3514     { { (unichar_t *) N_("_Save"), (GImage *) "filesave.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Save|No Shortcut"), NULL, NULL, MVMenuSave, 0 },
3515     { { (unichar_t *) N_("S_ave as..."), (GImage *) "filesaveas.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'a' }, H_("Save as...|No Shortcut"), NULL, NULL, MVMenuSaveAs, 0 },
3516     { { (unichar_t *) N_("_Generate Fonts..."), (GImage *) "filegenerate.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'G' }, H_("Generate Fonts...|No Shortcut"), NULL, NULL, MVMenuGenerate, 0 },
3517     { { (unichar_t *) N_("Generate Mac _Family..."), (GImage *) "filegeneratefamily.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("Generate Mac Family...|No Shortcut"), NULL, NULL, MVMenuGenerateFamily, 0 },
3518     { { (unichar_t *) N_("Generate TTC..."), (GImage *) "filegeneratefamily.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("Generate TTC...|No Shortcut"), NULL, NULL, MVMenuGenerateTTC, 0 },
3519     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3520     { { (unichar_t *) N_("_Merge Feature Info..."), (GImage *) "filemergefeature.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Merge Feature Info...|No Shortcut"), NULL, NULL, MVMenuMergeKern, 0 },
3521     { { (unichar_t *) N_("Load _Word List..."), (GImage *) 0, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Load Word List...|No Shortcut"), NULL, NULL, MVMenuAddWordList, 0 },
3522     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3523     { { (unichar_t *) N_("_Print..."), (GImage *) "fileprint.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Print...|No Shortcut"), NULL, NULL, MVMenuPrint, 0 },
3524     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3525     { { (unichar_t *) N_("Pr_eferences..."), (GImage *) "fileprefs.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'e' }, H_("Preferences...|No Shortcut"), NULL, NULL, MenuPrefs, 0 },
3526     { { (unichar_t *) N_("_X Resource Editor..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'e' }, H_("X Resource Editor...|No Shortcut"), NULL, NULL, MenuXRes, 0 },
3527     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3528     { { (unichar_t *) N_("_Quit"), (GImage *) "filequit.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'Q' }, H_("Quit|No Shortcut"), NULL, NULL, MenuExit, 0 },
3529     GMENUITEM2_EMPTY
3530 };
3531 
3532 static GMenuItem2 edlist[] = {
3533     { { (unichar_t *) N_("_Undo"), (GImage *) "editundo.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'U' }, H_("Undo|No Shortcut"), NULL, NULL, MVUndo, MID_Undo },
3534     { { (unichar_t *) N_("_Redo"), (GImage *) "editredo.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Redo|No Shortcut"), NULL, NULL, MVRedo, MID_Redo },
3535     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3536     { { (unichar_t *) N_("Cu_t"), (GImage *) "editcut.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 't' }, H_("Cut|No Shortcut"), NULL, NULL, MVCut, MID_Cut },
3537     { { (unichar_t *) N_("_Copy"), (GImage *) "editcopy.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'C' }, H_("Copy|No Shortcut"), NULL, NULL, MVCopy, MID_Copy },
3538     { { (unichar_t *) N_("C_opy Reference"), (GImage *) "editcopyref.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'o' }, H_("Copy Reference|No Shortcut"), NULL, NULL, MVMenuCopyRef, MID_CopyRef },
3539     { { (unichar_t *) N_("Copy _Width"), (GImage *) "editcopywidth.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'W' }, H_("Copy Width|No Shortcut"), NULL, NULL, MVMenuCopyWidth, MID_CopyWidth },
3540     { { (unichar_t *) N_("Copy _VWidth"), (GImage *) "editcopyvwidth.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("Copy VWidth|No Shortcut"), NULL, NULL, MVMenuCopyWidth, MID_CopyVWidth },
3541     { { (unichar_t *) N_("Co_py LBearing"), (GImage *) "editcopylbearing.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'p' }, H_("Copy LBearing|No Shortcut"), NULL, NULL, MVMenuCopyWidth, MID_CopyLBearing },
3542     { { (unichar_t *) N_("Copy RBearin_g"), (GImage *) "editcopyrbearing.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'g' }, H_("Copy RBearing|No Shortcut"), NULL, NULL, MVMenuCopyWidth, MID_CopyRBearing },
3543     { { (unichar_t *) N_("_Paste"), (GImage *) "editpaste.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Paste|No Shortcut"), NULL, NULL, MVPaste, MID_Paste },
3544     { { (unichar_t *) N_("C_lear"), (GImage *) "editclear.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 1, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Clear|No Shortcut"), NULL, NULL, MVClear, MID_Clear },
3545     { { (unichar_t *) N_("_Join"), (GImage *) "editjoin.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'J' }, H_("Join|No Shortcut"), NULL, NULL, MVMenuJoin, MID_Join },
3546     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3547     { { (unichar_t *) N_("Select _All"), (GImage *) "editselect.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 1, 1, 0, 0, 0, 0, 1, 1, 0, 'A' }, H_("Select All|No Shortcut"), NULL, NULL, MVSelectAll, MID_SelAll },
3548     { { (unichar_t *) N_("_Deselect All"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'D' }, H_("Deselect All|Escape"), NULL, NULL, MVClearSelection, MID_ClearSel },
3549     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3550     { { (unichar_t *) N_("U_nlink Reference"), (GImage *) "editunlink.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'U' }, H_("Unlink Reference|No Shortcut"), NULL, NULL, MVUnlinkRef, MID_UnlinkRef },
3551     GMENUITEM2_EMPTY
3552 };
3553 
3554 static GMenuItem2 smlist[] = {
3555     { { (unichar_t *) N_("_Simplify"), (GImage *) "elementsimplify.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Simplify|No Shortcut"), NULL, NULL, MVMenuSimplify, MID_Simplify },
3556     { { (unichar_t *) N_("Simplify More..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Simplify More...|No Shortcut"), NULL, NULL, MVMenuSimplifyMore, MID_SimplifyMore },
3557     { { (unichar_t *) N_("Clea_nup Glyph"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'n' }, H_("Cleanup Glyph|No Shortcut"), NULL, NULL, MVMenuCleanup, MID_CleanupGlyph },
3558     GMENUITEM2_EMPTY
3559 };
3560 
3561 static GMenuItem2 rmlist[] = {
3562     { { (unichar_t *) N_("_Remove Overlap"), (GImage *) "overlaprm.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'O' }, H_("Remove Overlap|No Shortcut"), NULL, NULL, MVMenuOverlap, MID_RmOverlap },
3563     { { (unichar_t *) N_("_Intersect"), (GImage *) "overlapintersection.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Intersect|No Shortcut"), NULL, NULL, MVMenuOverlap, MID_Intersection },
3564     { { (unichar_t *) N_("_Find Intersections"), (GImage *) "overlapfindinter.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'O' }, H_("Find Intersections|No Shortcut"), NULL, NULL, MVMenuOverlap, MID_FindInter },
3565     GMENUITEM2_EMPTY
3566 };
3567 
3568 static GMenuItem2 eflist[] = {
3569     { { (unichar_t *) N_("_Inline"), (GImage *) "stylesinline.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'O' }, H_("Inline|No Shortcut"), NULL, NULL, MVMenuInline, 0 },
3570     { { (unichar_t *) N_("_Outline"), (GImage *) "stylesoutline.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Outline|No Shortcut"), NULL, NULL, MVMenuOutline, 0 },
3571     { { (unichar_t *) N_("_Shadow"), (GImage *) "stylesshadow.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Shadow|No Shortcut"), NULL, NULL, MVMenuShadow, 0 },
3572     { { (unichar_t *) N_("_Wireframe"), (GImage *) "styleswireframe.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, true, 0, 0, 0, 0, 1, 1, 0, 'W' }, H_("Wireframe|No Shortcut"), NULL, NULL, MVMenuWireframe, 0 },
3573     GMENUITEM2_EMPTY
3574 };
3575 
3576 static GMenuItem2 balist[] = {
3577     { { (unichar_t *) N_("_Build Accented Glyph"), (GImage *) "elementbuildaccent.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Build Accented Glyph|No Shortcut"), NULL, NULL, MVMenuBuildAccent, MID_BuildAccent },
3578     { { (unichar_t *) N_("Build _Composite Glyph"), (GImage *) "elementbuildcomposite.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Build Composite Glyph|No Shortcut"), NULL, NULL, MVMenuBuildComposite, MID_BuildComposite },
3579     GMENUITEM2_EMPTY
3580 };
3581 
balistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3582 static void balistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3583     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3584     int i;
3585     SplineChar *sc;
3586 
3587     for ( i=mv->glyphcnt-1; i>=0; --i )
3588         if ( mv->perchar[i].selected )
3589     break;
3590     if ( i==-1 ) sc = NULL; else sc = mv->glyphs[i].sc;
3591 
3592     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
3593         switch ( mi->mid ) {
3594           case MID_BuildAccent:
3595             mi->ti.disabled = sc==NULL || !SFIsSomethingBuildable(sc->parent, sc, mv->layer, true);
3596           break;
3597           case MID_BuildComposite:
3598             mi->ti.disabled = sc==NULL || !SFIsSomethingBuildable(sc->parent, sc, mv->layer, false);
3599           break;
3600         }
3601     }
3602 }
3603 
3604 static GMenuItem2 ellist[] = {
3605     { { (unichar_t *) N_("_Font Info..."), (GImage *) "elementfontinfo.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("Font Info...|No Shortcut"), NULL, NULL, MVMenuFontInfo, 0 },
3606     { { (unichar_t *) N_("Glyph _Info..."), (GImage *) "elementglyphinfo.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("Glyph Info...|No Shortcut"), NULL, NULL, MVMenuCharInfo, MID_CharInfo },
3607     { { (unichar_t *) N_("S_how Dependent"), (GImage *) "elementshowdep.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'D' }, H_("Show Dependent|No Shortcut"), NULL, NULL, MVMenuShowDependents, MID_ShowDependents },
3608     { { (unichar_t *) N_("Find Pr_oblems..."), (GImage *) "elementfindprobs.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'o' }, H_("Find Problems...|No Shortcut"), NULL, NULL, MVMenuFindProblems, MID_FindProblems },
3609     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3610     { { (unichar_t *) N_("Bitm_ap Strikes Available..."), (GImage *) "elementbitmapsavail.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'A' }, H_("Bitmap Strikes Available...|No Shortcut"), NULL, NULL, MVMenuBitmaps, MID_AvailBitmaps },
3611     { { (unichar_t *) N_("Regenerate _Bitmap Glyphs..."), (GImage *) "elementregenbitmaps.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Regenerate Bitmap Glyphs...|No Shortcut"), NULL, NULL, MVMenuBitmaps, MID_RegenBitmaps },
3612     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3613     { { (unichar_t *) N_("_Transform..."), (GImage *) "elementtransform.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Transform...|No Shortcut"), NULL, NULL, MVMenuTransform, MID_Transform },
3614 #ifdef FONTFORGE_CONFIG_TILEPATH
3615     { { (unichar_t *) N_("Tile _Path..."), (GImage *) "elementtilepath.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Tile Path...|No Shortcut"), NULL, NULL, MVMenuTilePath, MID_TilePath },
3616 #endif
3617     { { (unichar_t *) N_("_Remove Overlap"), (GImage *) "rmoverlap.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'O' }, H_("Remove Overlap|No Shortcut"), rmlist, NULL, NULL, MID_RmOverlap },
3618     { { (unichar_t *) N_("_Simplify"), (GImage *) "elementsimplify.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Simplify|No Shortcut"), smlist, NULL, NULL, MID_Simplify },
3619     { { (unichar_t *) N_("Add E_xtrema"), (GImage *) "elementaddextrema.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'x' }, H_("Add Extrema|No Shortcut"), NULL, NULL, MVMenuAddExtrema, MID_AddExtrema },
3620     { { (unichar_t *) N_("To _Int"), (GImage *) "elementround.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'I' }, H_("To Int|No Shortcut"), NULL, NULL, MVMenuRound2Int, MID_Round },
3621     { { (unichar_t *) N_("Effects"), (GImage *) "elementstyles.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Effects|No Shortcut"), eflist, NULL, NULL, MID_Effects },
3622     { { (unichar_t *) N_("Autot_race"), (GImage *) "elementautotrace.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'r' }, H_("Autotrace|No Shortcut"), NULL, NULL, MVMenuAutotrace, MID_Autotrace },
3623     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3624     { { (unichar_t *) N_("_Correct Direction"), (GImage *) "elementcorrectdir.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'D' }, H_("Correct Direction|No Shortcut"), NULL, NULL, MVMenuCorrectDir, MID_Correct },
3625     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3626     { { (unichar_t *) N_("B_uild"), (GImage *) "elementbuildaccent.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Build|No Shortcut"), balist, balistcheck, NULL, MID_BuildAccent },
3627     GMENUITEM2_EMPTY
3628 };
3629 
3630 static GMenuItem2 dummyall[] = {
3631     { { (unichar_t *) N_("All"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 1, 1, 0, 0, 0, 0, 1, 1, 0, 'K' }, H_("All|No Shortcut"), NULL, NULL, NULL, 0 },
3632     GMENUITEM2_EMPTY
3633 };
3634 
3635 /* Builds up a menu containing all the anchor classes */
aplistbuild(GWindow base,struct gmenuitem * mi,GEvent * UNUSED (e))3636 static void aplistbuild(GWindow base, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3637     MetricsView *mv = (MetricsView *) GDrawGetUserData(base);
3638 
3639     GMenuItemArrayFree(mi->sub);
3640     mi->sub = NULL;
3641 
3642     _aplistbuild(mi, mv->sf, MVMenuAnchorPairs);
3643 }
3644 
3645 static GMenuItem2 cblist[] = {
3646     { { (unichar_t *) N_("_Kern Pairs"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'K' }, H_("Kern Pairs|No Shortcut"), NULL, NULL, MVMenuKernPairs, MID_KernPairs },
3647     { { (unichar_t *) N_("_Anchored Pairs"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'K' }, H_("Anchored Pairs|No Shortcut"), dummyall, aplistbuild, MVMenuAnchorPairs, MID_AnchorPairs },
3648     { { (unichar_t *) N_("_Ligatures"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'L' }, H_("Ligatures|No Shortcut"), NULL, NULL, MVMenuLigatures, MID_Ligatures },
3649     GMENUITEM2_EMPTY
3650 };
3651 
cblistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3652 static void cblistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3653     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3654     SplineFont *sf = mv->sf;
3655     int i, anyligs=0, anykerns=0;
3656     PST *pst;
3657 
3658     for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
3659 	for ( pst=sf->glyphs[i]->possub; pst!=NULL; pst=pst->next ) {
3660 	    if ( pst->type==pst_ligature ) {
3661 		anyligs = true;
3662 		if ( anykerns )
3663     break;
3664 	    }
3665 	}
3666 	if ( (mv->vertical ? sf->glyphs[i]->vkerns : sf->glyphs[i]->kerns)!=NULL ) {
3667 	    anykerns = true;
3668 	    if ( anyligs )
3669     break;
3670 	}
3671     }
3672 
3673     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
3674 	switch ( mi->mid ) {
3675 	  case MID_Ligatures:
3676 	    mi->ti.disabled = !anyligs;
3677 	  break;
3678 	  case MID_KernPairs:
3679 	    mi->ti.disabled = !anykerns;
3680 	  break;
3681 	  case MID_AnchorPairs:
3682 	    mi->ti.disabled = sf->anchor==NULL;
3683 	  break;
3684 	}
3685     }
3686 }
3687 
3688 static GMenuItem2 lylist[] = {
3689     { { (unichar_t *) N_("Layer|Foreground"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 1, 1, 1, 1, 0, 0, 1, 1, 0, '\0' }, NULL, NULL, NULL, MVMenuChangeLayer, ly_fore },
3690     GMENUITEM2_EMPTY
3691 };
3692 
lylistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3693 static void lylistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3694     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3695     SplineFont *sf = mv->fv->b.sf;
3696     int ly;
3697     GMenuItem *sub;
3698 
3699     sub = calloc(sf->layer_cnt+1,sizeof(GMenuItem));
3700     for ( ly=ly_fore; ly<sf->layer_cnt; ++ly ) {
3701 	sub[ly-1].ti.text = utf82u_copy(sf->layers[ly].name);
3702 	sub[ly-1].ti.checkable = true;
3703 	sub[ly-1].ti.checked = ly == mv->layer;
3704 	sub[ly-1].invoke = MVMenuChangeLayer;
3705 	sub[ly-1].mid = ly;
3706 	sub[ly-1].ti.fg = sub[ly-1].ti.bg = COLOR_DEFAULT;
3707     }
3708     GMenuItemArrayFree(mi->sub);
3709     mi->sub = sub;
3710 }
3711 
3712 static GMenuItem2 gdlist[] = {
3713     { { (unichar_t *) N_("_Show"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Show|No Shortcut"), NULL, NULL, MVMenuShowGrid, MID_ShowGrid },
3714     { { (unichar_t *) N_("_Partial"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Partial|No Shortcut"), NULL, NULL, MVMenuShowGrid, MID_PartialGrid },
3715     { { (unichar_t *) N_("Hide when _Moving"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Hide when Moving|No Shortcut"), NULL, NULL, MVMenuShowGrid, MID_HideGridWhenMoving },
3716     { { (unichar_t *) N_("_Hide"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Hide|No Shortcut"), NULL, NULL, MVMenuShowGrid, MID_HideGrid },
3717     GMENUITEM2_EMPTY
3718 };
3719 
gdlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3720 static void gdlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3721     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3722 
3723     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
3724 	switch ( mi->mid ) {
3725 	  case MID_ShowGrid:
3726 	    mi->ti.checked = mv->showgrid == mv_showgrid;
3727 	  break;
3728 	  case MID_HideGrid:
3729 	    mi->ti.checked = mv->showgrid == mv_hidegrid;
3730 	  break;
3731 	  case MID_PartialGrid:
3732 	    mi->ti.checked = mv->showgrid == mv_partialgrid;
3733 	  break;
3734 	  case MID_HideGridWhenMoving:
3735 	    mi->ti.checked = mv->showgrid == mv_hidemovinggrid;
3736 	  break;
3737 	}
3738     }
3739 }
3740 
3741 static GMenuItem2 vwlist[] = {
3742     { { (unichar_t *) N_("Z_oom out"), (GImage *) "viewzoomout.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'o' }, H_("Zoom out|No Shortcut"), NULL, NULL, MVMenuScale, MID_ZoomOut },
3743     { { (unichar_t *) N_("Zoom _in"), (GImage *) "viewzoomin.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'i' }, H_("Zoom in|No Shortcut"), NULL, NULL, MVMenuScale, MID_ZoomIn },
3744     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3745     { { (unichar_t *) N_("Insert Glyph _After..."), (GImage *) "viewinsertafter.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'C' }, H_("Insert Glyph After...|No Shortcut"), NULL, NULL, MVMenuInsertChar, MID_InsertCharA },
3746     { { (unichar_t *) N_("Insert Glyph _Before..."), (GImage *) "viewinsertbefore.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Insert Glyph Before...|No Shortcut"), NULL, NULL, MVMenuInsertChar, MID_InsertCharB },
3747     { { (unichar_t *) N_("_Replace Glyph..."), (GImage *) "viewreplace.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Replace Glyph...|No Shortcut"), NULL, NULL, MVMenuChangeChar, MID_ReplaceChar },
3748     { { (unichar_t *) N_("_Next Glyph"), (GImage *) "viewnext.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'N' }, H_("Next Glyph|No Shortcut"), NULL, NULL, MVMenuChangeChar, MID_Next },
3749     { { (unichar_t *) N_("_Prev Glyph"), (GImage *) "viewprev.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Prev Glyph|No Shortcut"), NULL, NULL, MVMenuChangeChar, MID_Prev },
3750     { { (unichar_t *) N_("Next _Defined Glyph"), (GImage *) "viewnextdef.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'D' }, H_("Next Defined Glyph|No Shortcut"), NULL, NULL, MVMenuChangeChar, MID_NextDef },
3751     { { (unichar_t *) N_("Prev Defined Gl_yph"), (GImage *) "viewprevdef.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'a' }, H_("Prev Defined Glyph|No Shortcut"), NULL, NULL, MVMenuChangeChar, MID_PrevDef },
3752     { { (unichar_t *) N_("Find In Font _View"), (GImage *) "viewfindinfont.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("Find In Font View|No Shortcut"), NULL, NULL, MVMenuFindInFontView, MID_FindInFontView },
3753     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3754     { { (unichar_t *) N_("_Layers"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, '\0' }, H_("Layers|No Shortcut"), lylist, lylistcheck, NULL, 0 },
3755     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3756     { { (unichar_t *) N_("Com_binations"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'b' }, H_("Combinations|No Shortcut"), cblist, cblistcheck, NULL, 0 },
3757     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3758     { { (unichar_t *) N_("Show _Grid"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'G' }, H_("Show Grid|No Shortcut"), gdlist, gdlistcheck, MVMenuShowGrid, MID_ShowGrid },
3759     { { (unichar_t *) N_("_Anti Alias"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'A' }, H_("Anti Alias|No Shortcut"), NULL, NULL, MVMenuAA, MID_AntiAlias },
3760     { { (unichar_t *) N_("Render using Hinting"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'A' }, H_("Render using Hinting|No Shortcut"), NULL, NULL, MVMenuRenderUsingHinting, MID_RenderUsingHinting },
3761     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3762     { { (unichar_t *) N_("_Vertical"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, '\0' }, H_("Vertical|No Shortcut"), NULL, NULL, MVMenuVertical, MID_Vertical },
3763     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3764     { { (unichar_t *) N_("Size set from _Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'O' }, H_("Size set from Window|No Shortcut"), NULL, NULL, MVMenuSizeWindow, MID_SizeWindow },
3765     { { (unichar_t *) N_("Set Point _Size"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'O' }, H_("Set Point Size|No Shortcut"), NULL, NULL, MVMenuPointSize, MID_PointSize },
3766     { { (unichar_t *) N_("_Bigger Point Size"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'B' }, H_("Bigger Point Size|No Shortcut"), NULL, NULL, MVMenuChangePointSize, MID_Bigger },
3767     { { (unichar_t *) N_("_Smaller Point Size"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'S' }, H_("Smaller Point Size|No Shortcut"), NULL, NULL, MVMenuChangePointSize, MID_Smaller },
3768     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3769     { { (unichar_t *) N_("Next _Line in Word List"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'O' }, H_("Next Line in Word List|No Shortcut"), NULL, NULL, MVMenuNextLineInWordList, MID_NextLineInWordList },
3770     { { (unichar_t *) N_("Previous Line in _Word List"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'O' }, H_("Previous Line in Word List|No Shortcut"), NULL, NULL, MVMenuPrevLineInWordList, MID_PrevLineInWordList },
3771 
3772     { { NULL, NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 1, 0, 0, 0, '\0' }, NULL, NULL, NULL, NULL, 0 }, /* line */
3773     { { (unichar_t *) N_("_Outline"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'O' }, H_("Outline|No Shortcut"), NULL, NULL, MVMenuShowBitmap, MID_Outline },
3774     GMENUITEM2_EMPTY,
3775     /* Some extra room to show bitmaps */
3776     GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY,
3777     GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY,
3778     GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY, GMENUITEM2_EMPTY,
3779     GMENUITEM2_EMPTY
3780 };
3781 
MVMenuContextualHelp(GWindow UNUSED (base),struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3782 static void MVMenuContextualHelp(GWindow UNUSED(base), struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3783     help("ui/mainviews/metricsview.html", NULL);
3784 }
3785 
3786 static GMenuItem2 tylist[] = {
3787     { { (unichar_t *) N_("_Kerning only"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'C' }, H_("Kerning only|No Shortcut"), NULL, NULL, MVMenuWindowType, MID_KernOnly },
3788     { { (unichar_t *) N_("_Advance Width only"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Advance Width only|No Shortcut"), NULL, NULL, MVMenuWindowType, MID_WidthOnly },
3789     { { (unichar_t *) N_("_Both"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 1, 0, 0, 0, 1, 1, 0, 'T' }, H_("Both|No Shortcut"), NULL, NULL, MVMenuWindowType, MID_BothKernWidth },
3790     GMENUITEM2_EMPTY
3791 };
3792 
tylistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3793 static void tylistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3794     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3795 
3796     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
3797 	switch ( mi->mid ) {
3798 	  case MID_KernOnly:
3799 	    mi->ti.checked = mv->type == mv_kernonly;
3800 	  break;
3801 	  case MID_WidthOnly:
3802 	    mi->ti.checked = mv->type == mv_widthonly;
3803 	  break;
3804 	  case MID_BothKernWidth:
3805 	    mi->ti.checked = mv->type == mv_kernwidth;
3806 	  break;
3807 	}
3808     }
3809 }
3810 
3811 
3812 
MVMenuSetWidth(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3813 static void MVMenuSetWidth(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3814     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3815     if ( mi->mid == MID_SetVWidth && !mv->sf->hasvmetrics )
3816 return;
3817     SplineChar* sc = getSelectedChar(mv);
3818 //    printf("MVMenuSetWidth() sc:%p\n",sc);
3819     if(!sc)
3820  	return;
3821 
3822     GenericVSetWidth(mv->fv,sc,
3823 		     mi->mid==MID_SetWidth?wt_width:
3824 		     mi->mid==MID_SetLBearing?wt_lbearing:
3825 		     mi->mid==MID_SetRBearing?wt_rbearing:
3826 		     mi->mid==MID_SetBearings?wt_bearings:
3827 		     wt_vwidth);
3828 }
3829 
MVMenuRemoveKern(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3830 static void MVMenuRemoveKern(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3831     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3832     SplineChar* sc = getSelectedChar(mv);
3833     if(!sc)
3834  	return;
3835     SCRemoveKern(sc);
3836 }
MVMenuRemoveVKern(GWindow gw,struct gmenuitem * UNUSED (mi),GEvent * UNUSED (e))3837 static void MVMenuRemoveVKern(GWindow gw, struct gmenuitem *UNUSED(mi), GEvent *UNUSED(e)) {
3838     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3839     SplineChar* sc = getSelectedChar(mv);
3840     if(!sc)
3841  	return;
3842     SCRemoveVKern(sc);
3843 }
3844 
3845 static GMenuItem2 mtlist[] = {
3846     { { (unichar_t *) N_("_Center in Width"), (GImage *) "metricscenter.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'C' }, H_("Center in Width|No Shortcut"), NULL, NULL, MVMenuCenter, MID_Center },
3847     { { (unichar_t *) N_("_Thirds in Width"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Thirds in Width|No Shortcut"), NULL, NULL, MVMenuCenter, MID_Thirds },
3848     { { (unichar_t *) N_("Set _Width..."), (GImage *) "metricssetwidth.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'W' }, H_("Set Width...|No Shortcut"), NULL, NULL, MVMenuSetWidth, MID_SetWidth },
3849     { { (unichar_t *) N_("Set _LBearing..."), (GImage *) "metricssetlbearing.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'L' }, H_("Set LBearing...|No Shortcut"), NULL, NULL, MVMenuSetWidth, MID_SetLBearing },
3850     { { (unichar_t *) N_("Set _RBearing..."), (GImage *) "metricssetrbearing.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Set RBearing...|No Shortcut"), NULL, NULL, MVMenuSetWidth, MID_SetRBearing },
3851     { { (unichar_t *) N_("Set Both Bearings..."), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'R' }, H_("Set Both Bearings...|No Shortcut"), NULL, NULL, MVMenuSetWidth, MID_SetBearings },
3852     GMENUITEM2_LINE,
3853     { { (unichar_t *) N_("Set _Vertical Advance..."), (GImage *) "metricssetvwidth.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("Set Vertical Advance...|No Shortcut"), NULL, NULL, MVMenuSetWidth, MID_SetVWidth },
3854     GMENUITEM2_LINE,
3855     { { (unichar_t *) N_("_Window Type"), (GImage *) "menuempty.png", COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Window Type|No Shortcut"), tylist, tylistcheck, NULL, 0 },
3856     GMENUITEM2_LINE,
3857     { { (unichar_t *) N_("Ker_n By Classes..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Kern By Classes...|No Shortcut"), NULL, NULL, MVMenuKernByClasses, 0 },
3858     { { (unichar_t *) N_("VKern By Classes..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("VKern By Classes...|No Shortcut"), NULL, NULL, MVMenuVKernByClasses, MID_VKernClass },
3859     { { (unichar_t *) N_("VKern From HKern"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("VKern From HKern|No Shortcut"), NULL, NULL, MVMenuVKernFromHKern, MID_VKernFromHKern },
3860     { { (unichar_t *) N_("Remove Kern _Pairs"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Remove Kern Pairs|No Shortcut"), NULL, NULL, MVMenuRemoveKern, MID_RemoveKerns },
3861     { { (unichar_t *) N_("Remove VKern Pairs"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'P' }, H_("Remove VKern Pairs|No Shortcut"), NULL, NULL, MVMenuRemoveVKern, MID_RemoveVKerns },
3862     { { (unichar_t *) N_("Kern Pair Closeup..."), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'T' }, H_("Kern Pair Closeup...|No Shortcut"), NULL, NULL, MVMenuKPCloseup, 0 },
3863     GMENUITEM2_EMPTY
3864 };
3865 
fllistcheck(GWindow UNUSED (gw),struct gmenuitem * mi,GEvent * UNUSED (e))3866 static void fllistcheck(GWindow UNUSED(gw), struct gmenuitem *mi, GEvent *UNUSED(e)) {
3867     /*MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);*/
3868 
3869     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
3870 	switch ( mi->mid ) {
3871 	  case MID_Recent:
3872 	    mi->ti.disabled = !RecentFilesAny();
3873 	  break;
3874 	}
3875     }
3876 }
3877 
edlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3878 static void edlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3879     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3880     int i;
3881 
3882     if ( GWindowGetFocusGadgetOfWindow(gw)!=NULL )
3883 	i = -1;
3884     else
3885 	for ( i=mv->glyphcnt-1; i>=0; --i )
3886 	    if ( mv->perchar[i].selected )
3887 	break;
3888 
3889     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
3890 	switch ( mi->mid ) {
3891 	  case MID_Cut: case MID_Copy:
3892 	  break;
3893 	  case MID_Join:
3894 	  case MID_CopyRef: case MID_CopyWidth:
3895 	  case MID_CopyLBearing: case MID_CopyRBearing:
3896 	  case MID_Clear:
3897 	    mi->ti.disabled = i==-1;
3898 	  break;
3899 	  case MID_CopyVWidth:
3900 	    mi->ti.disabled = i==-1 || !mv->sf->hasvmetrics;
3901 	  break;
3902 	  case MID_UnlinkRef:
3903 	    mi->ti.disabled = i==-1 || mv->glyphs[i].sc->layers[mv->layer].refs==NULL;
3904 	  break;
3905 	  case MID_Paste:
3906 	  break;
3907 	}
3908     }
3909 }
3910 
ellistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3911 static void ellistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3912     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3913     int i, anybuildable;
3914     SplineChar *sc;
3915     int order2 = mv->sf->layers[mv->layer].order2;
3916 
3917     for ( i=mv->glyphcnt-1; i>=0; --i )
3918 	if ( mv->perchar[i].selected )
3919     break;
3920     if ( i==-1 ) sc = NULL; else sc = mv->glyphs[i].sc;
3921 
3922     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
3923 	switch ( mi->mid ) {
3924 	  case MID_RegenBitmaps:
3925 	    mi->ti.disabled = mv->sf->bitmaps==NULL;
3926 	  break;
3927 	  case MID_CharInfo:
3928 	    mi->ti.disabled = sc==NULL /*|| mv->fv->b.cidmaster!=NULL*/;
3929 	  break;
3930 	  case MID_ShowDependents:
3931 	    mi->ti.disabled = sc==NULL || sc->dependents == NULL;
3932 	  break;
3933 	  case MID_FindProblems:
3934 	  case MID_Transform:
3935 	    mi->ti.disabled = sc==NULL;
3936 	  break;
3937 	  case MID_Effects:
3938 	    mi->ti.disabled = sc==NULL || mv->sf->onlybitmaps || order2;
3939 	  break;
3940 	  case MID_RmOverlap: case MID_Stroke:
3941 	    mi->ti.disabled = sc==NULL || mv->sf->onlybitmaps;
3942 	  break;
3943 	  case MID_AddExtrema:
3944 	  case MID_Round: case MID_Correct:
3945 	    mi->ti.disabled = sc==NULL || mv->sf->onlybitmaps;
3946 	  break;
3947 #ifdef FONTFORGE_CONFIG_TILEPATH
3948 	  case MID_TilePath:
3949 	    mi->ti.disabled = sc==NULL || mv->sf->onlybitmaps || ClipBoardToSplineSet()==NULL || order2;
3950 	  break;
3951 #endif
3952 	  case MID_Simplify:
3953 	    mi->ti.disabled = sc==NULL || mv->sf->onlybitmaps;
3954 	  break;
3955 	  case MID_BuildAccent:
3956 	    anybuildable = false;
3957 	    if ( sc!=NULL && SFIsSomethingBuildable(mv->sf,sc,mv->layer,false) )
3958 		anybuildable = true;
3959 	    mi->ti.disabled = !anybuildable;
3960 	  break;
3961 	  case MID_Autotrace:
3962 	    mi->ti.disabled = !(FindAutoTraceName()!=NULL && sc!=NULL &&
3963 		    sc->layers[ly_back].images!=NULL );
3964 	  break;
3965 	}
3966     }
3967 }
3968 
vwlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))3969 static void vwlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
3970     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
3971     int i, j, base, aselection;
3972     BDFFont *bdf;
3973     char buffer[60];
3974 
3975     aselection = false;
3976     for ( j=0; j<mv->glyphcnt; ++j )
3977 	if ( mv->perchar[j].selected ) {
3978 	    aselection = true;
3979     break;
3980 	}
3981 
3982     for ( i=0; vwlist[i].mid!=MID_Outline; ++i )
3983 	switch ( vwlist[i].mid ) {
3984 	  case MID_ZoomIn:
3985 	    vwlist[i].ti.disabled = mv->scale_index==0;
3986 	  break;
3987 	  case MID_ZoomOut:
3988 	    vwlist[i].ti.disabled = mv->scale_index>=sizeof(mv_scales)/sizeof(mv_scales[0])-1;
3989 	  break;
3990 	  case MID_AntiAlias:
3991 	    vwlist[i].ti.checked = mv->antialias;
3992 	    vwlist[i].ti.disabled = mv->bdf!=NULL;
3993 	  break;
3994 	  case MID_RenderUsingHinting:
3995 	    vwlist[i].ti.checked = mv->usehinting;
3996 	    vwlist[i].ti.disabled = mv->bdf!=NULL;
3997 	  break;
3998 	  case MID_SizeWindow:
3999 	    vwlist[i].ti.disabled = mv->pixelsize_set_by_window;
4000 	    vwlist[i].ti.checked = mv->pixelsize_set_by_window;
4001 	  break;
4002 	  case MID_Bigger:
4003 	  case MID_Smaller:
4004 	    vwlist[i].ti.disabled = mv->pixelsize_set_by_window;
4005 	  break;
4006 	  case MID_ReplaceChar:
4007 	  case MID_FindInFontView:
4008 	  case MID_Next:
4009 	  case MID_Prev:
4010 	  case MID_NextDef:
4011 	  case MID_PrevDef:
4012 	    vwlist[i].ti.disabled = !aselection;
4013 	  break;
4014 	  case MID_Vertical:
4015 	    vwlist[i].ti.checked = mv->vertical;
4016 	    vwlist[i].ti.disabled = !mv->sf->hasvmetrics;
4017 	  break;
4018 	  case MID_Layers:
4019 	    vwlist[i].ti.disabled = mv->sf->layer_cnt<=2 || mv->sf->multilayer;
4020 	  break;
4021 	}
4022     vwlist[i].ti.checked = mv->bdf==NULL;
4023     base = i+1;
4024     for ( i=base; vwlist[i].ti.text!=NULL || vwlist[i].ti.line; ++i ) {
4025 	free( vwlist[i].ti.text);
4026 	vwlist[i].ti.text = NULL;
4027     }
4028 
4029     if ( mv->sf->bitmaps!=NULL ) {
4030 	for ( bdf = mv->sf->bitmaps, i=base;
4031 		i<sizeof(vwlist)/sizeof(vwlist[0])-1 && bdf!=NULL;
4032 		++i, bdf = bdf->next ) {
4033 	    if ( BDFDepth(bdf)==1 )
4034 		sprintf( buffer, _("%d pixel bitmap"), bdf->pixelsize );
4035 	    else
4036 		sprintf( buffer, _("%d@%d pixel bitmap"),
4037 			bdf->pixelsize, BDFDepth(bdf) );
4038 	    vwlist[i].ti.text = utf82u_copy(buffer);
4039 	    vwlist[i].ti.checkable = true;
4040 	    vwlist[i].ti.checked = bdf==mv->bdf;
4041 	    vwlist[i].ti.userdata = bdf;
4042 	    vwlist[i].invoke = MVMenuShowBitmap;
4043 	    vwlist[i].ti.fg = vwlist[i].ti.bg = COLOR_DEFAULT;
4044 	}
4045     }
4046     GMenuItemArrayFree(mi->sub);
4047     mi->sub = GMenuItem2ArrayCopy(vwlist,NULL);
4048 }
4049 
mtlistcheck(GWindow gw,struct gmenuitem * mi,GEvent * UNUSED (e))4050 static void mtlistcheck(GWindow gw, struct gmenuitem *mi, GEvent *UNUSED(e)) {
4051     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
4052     SplineChar* sc = getSelectedChar(mv);
4053 
4054     for ( mi = mi->sub; mi->ti.text!=NULL || mi->ti.line ; ++mi ) {
4055         switch ( mi->mid ) {
4056 	  case MID_VKernClass:
4057 	  case MID_VKernFromHKern:
4058 	  case MID_SetVWidth:
4059 	    mi->ti.disabled = !mv->sf->hasvmetrics;
4060 	  break;
4061 	case MID_RemoveKerns:
4062 	    mi->ti.disabled = sc ? sc->kerns==NULL : 1;
4063 	  break;
4064 	case MID_RemoveVKerns:
4065 	    mi->ti.disabled = sc ? sc->vkerns==NULL : 1;
4066 	  break;
4067 
4068 	}
4069     }
4070 }
4071 
4072 static GMenuItem2 mblist[] = {
4073     { { (unichar_t *) N_("_File"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'F' }, H_("File|No Shortcut"), fllist, fllistcheck, NULL, 0  },
4074     { { (unichar_t *) N_("_Edit"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'E' }, H_("Edit|No Shortcut"), edlist, edlistcheck, NULL, 0  },
4075     { { (unichar_t *) N_("E_lement"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'l' }, H_("Element|No Shortcut"), ellist, ellistcheck, NULL, 0  },
4076     { { (unichar_t *) N_("_View"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'V' }, H_("View|No Shortcut"), vwlist, vwlistcheck, NULL, 0  },
4077     { { (unichar_t *) N_("_Metrics"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'M' }, H_("Metrics|No Shortcut"), mtlist, mtlistcheck, NULL, 0  },
4078     { { (unichar_t *) N_("_Window"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'W' }, H_("Window|No Shortcut"), wnmenu, MVWindowMenuBuild, NULL, 0 },
4079     { { (unichar_t *) N_("_Help"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 1, 0, 0, 0, 0, 1, 1, 0, 'H' }, H_("Help|No Shortcut"), helplist, NULL, NULL, 0 },
4080     GMENUITEM2_EMPTY
4081 };
4082 
MVResize(MetricsView * mv)4083 static void MVResize(MetricsView *mv) {
4084     GRect pos, wsize;
4085     int i;
4086     int size;
4087 
4088     GDrawGetSize(mv->gw,&wsize);
4089     if ( wsize.height < mv->topend+20 + mv->height-mv->displayend ||
4090 	    wsize.width < 30 ) {
4091 	int width= wsize.width < 30 ? 30 : wsize.width;
4092 	int height;
4093 
4094 	if ( wsize.height < mv->topend+20 + mv->height-mv->displayend )
4095 	    height = mv->topend+20 + mv->height-mv->displayend;
4096 	else
4097 	    height = wsize.height;
4098 	GDrawResize(mv->gw,width,height);
4099 return;
4100     }
4101 
4102     mv->width = wsize.width;
4103     mv->displayend = wsize.height - (mv->height-mv->displayend);
4104     mv->height = wsize.height;
4105 
4106     mv_width = wsize.width; mv_height = wsize.height;
4107     SavePrefs(true);
4108 
4109     pos.width = wsize.width;
4110     pos.height = mv->sbh;
4111     pos.y = wsize.height - pos.height; pos.x = 0;
4112     GGadgetResize(mv->hsb,pos.width,pos.height);
4113     GGadgetMove(mv->hsb,pos.x,pos.y);
4114 
4115     mv->dwidth = mv->width - mv->sbh;
4116     GGadgetResize(mv->vsb,mv->sbh,mv->displayend-mv->topend);
4117     GGadgetMove(mv->vsb,wsize.width-mv->sbh,mv->topend);
4118 
4119     GGadgetResize(mv->features,mv->xstart,mv->displayend - mv->topend);
4120 
4121     size = (mv->displayend - mv->topend - 4);
4122     if ( mv->dwidth-20<size )
4123 	size = mv->dwidth-20;
4124     if ( mv->pixelsize_set_by_window ) {
4125 	mv->ptsize = mv->pixelsize = mv_scales[mv->scale_index]*size;
4126 	mv->dpi = 72;
4127 	if ( mv->bdf==NULL ) {
4128 	    BDFFontFree(mv->show);
4129 	    mv->show = SplineFontPieceMeal(mv->sf,mv->layer,mv->ptsize,mv->dpi,
4130 					   MVGetSplineFontPieceMealFlags( mv ), NULL );
4131 	}
4132     }
4133 
4134     for ( i=0; i<mv->max; ++i ) if ( mv->perchar[i].width!=NULL ) {
4135 	GGadgetMove(mv->perchar[i].name,mv->perchar[i].mx,mv->displayend+2);
4136 	GGadgetMove(mv->perchar[i].width,mv->perchar[i].mx,mv->displayend+2+mv->fh+4);
4137 	GGadgetMove(mv->perchar[i].lbearing,mv->perchar[i].mx,mv->displayend+2+2*(mv->fh+4));
4138 	GGadgetMove(mv->perchar[i].rbearing,mv->perchar[i].mx,mv->displayend+2+3*(mv->fh+4));
4139 	if ( mv->perchar[i].kern!=NULL )
4140 	    GGadgetMove(mv->perchar[i].kern,mv->perchar[i].mx-mv->perchar[i].mwidth/2,mv->displayend+2+4*(mv->fh+4));
4141     }
4142     GGadgetMove(mv->namelab,2,mv->displayend+2);
4143     GGadgetMove(mv->widthlab,2,mv->displayend+2+mv->fh+4);
4144     GGadgetMove(mv->lbearinglab,2,mv->displayend+2+2*(mv->fh+4));
4145     GGadgetMove(mv->rbearinglab,2,mv->displayend+2+3*(mv->fh+4));
4146     GGadgetMove(mv->kernlab,2,mv->displayend+2+4*(mv->fh+4));
4147 
4148     {
4149       int newwidth = mv->width;
4150       GRect scriptselector_size;
4151       GRect charselector_size;
4152       GRect charselectorNext_size;
4153       GRect charselectorPrev_size;
4154       GRect subtable_list_size;
4155       GGadgetGetSize(mv->script, &scriptselector_size);
4156       GGadgetGetSize(mv->text, &charselector_size);
4157       GGadgetGetSize(mv->textPrev, &charselectorPrev_size);
4158       GGadgetGetSize(mv->textNext, &charselectorNext_size);
4159       GGadgetGetSize(mv->subtable_list, &subtable_list_size);
4160       int new_charselector_width = newwidth - charselector_size.x -       charselectorNext_size.width - 2 - charselectorPrev_size.width - 2 - subtable_list_size.width - 10 - 10;
4161       if (new_charselector_width < GDrawPointsToPixels(mv->gw,100))
4162         new_charselector_width = GDrawPointsToPixels(mv->gw,100);
4163       int new_charselectorPrev_x = charselector_size.x + new_charselector_width + 4;
4164       int new_charselectorNext_x = new_charselectorPrev_x + charselectorPrev_size.width + 4;
4165       int new_subtableselector_x = new_charselectorNext_x + charselectorNext_size.width + 10;
4166 
4167       GGadgetResize(mv->text, new_charselector_width, charselector_size.height);
4168       GGadgetMove(mv->textPrev, new_charselectorPrev_x, charselectorPrev_size.y);
4169       GGadgetMove(mv->textNext, new_charselectorNext_x, charselectorNext_size.y);
4170       GGadgetMove(mv->subtable_list, new_subtableselector_x, subtable_list_size.y);
4171     }
4172 
4173     mv->vwidth = mv->dwidth-mv->xstart;
4174     mv->vheight = mv->displayend-mv->topend-2;
4175     GDrawResize(mv->v,mv->vwidth, mv->vheight);
4176     MVRemetric(mv);
4177     GDrawRequestExpose(mv->gw,NULL,true);
4178     GDrawRequestExpose(mv->v,NULL,true);
4179 }
4180 
MVChar(MetricsView * mv,GEvent * event)4181 static void MVChar(MetricsView *mv,GEvent *event)
4182 {
4183     if ( event->u.chr.keysym=='s' &&
4184 	    (event->u.chr.state&ksm_control) &&
4185 	    (event->u.chr.state&ksm_meta) )
4186 	MenuSaveAll(NULL,NULL,NULL);
4187     else if ( event->u.chr.keysym=='I' &&
4188 	    (event->u.chr.state&ksm_shift) &&
4189 	    (event->u.chr.state&ksm_meta) )
4190 	MVMenuCharInfo(mv->gw,NULL,NULL);
4191     else if ( event->u.chr.keysym == GK_Help ) {
4192 	MenuHelp(NULL,NULL,NULL);	/* Menu does F1 */
4193     }
4194     if ( event->u.chr.keysym == GK_Up || event->u.chr.keysym==GK_KP_Up
4195 	 || event->u.chr.keysym == GK_Down || event->u.chr.keysym==GK_KP_Down
4196 	 || event->u.chr.keysym == GK_Left || event->u.chr.keysym==GK_KP_Left
4197 	 || event->u.chr.keysym == GK_Right || event->u.chr.keysym==GK_KP_Right ) {
4198 	if( event->u.chr.state&ksm_meta ) {
4199 	    int dir = ( event->u.chr.keysym == GK_Up || event->u.chr.keysym==GK_KP_Up ) ? -1
4200 		: ( ( event->u.chr.keysym == GK_Down || event->u.chr.keysym==GK_KP_Down ) ? 1 : 0);
4201 	    GGadget *active = GWindowGetFocusGadgetOfWindow(mv->gw);
4202 	    GGadget *toSelect = 0;
4203 
4204 	    if( active ) {
4205 		int i=0, j=0;
4206 		for ( i=0; i<mv->glyphcnt; ++i ) {
4207 		    // remember here that j=0 is NULL because updownkparray is NULL terminated
4208 		    // at both ends.
4209 		    for ( j=1; j<10 && mv->perchar[i].updownkparray[j]; ++j ) {
4210 			if ( active == mv->perchar[i].updownkparray[j] ) {
4211 			    if( dir != 0 ) {
4212 				toSelect =  mv->perchar[i].updownkparray[j+dir];
4213 			    } else {
4214 				int newidx = i;
4215 				if( event->u.chr.keysym == GK_Left || event->u.chr.keysym==GK_KP_Left )
4216 				    newidx--;
4217 				if( event->u.chr.keysym == GK_Right || event->u.chr.keysym==GK_KP_Right )
4218 				    newidx++;
4219 				if( newidx < 0 || newidx >= mv->glyphcnt )
4220 				    return;
4221 				toSelect =  mv->perchar[newidx].updownkparray[j];
4222 			    }
4223 			}
4224 		    }
4225 		}
4226 	    }
4227 	    if( toSelect ) {
4228 		GWidgetIndicateFocusGadget(toSelect);
4229 	    }
4230 	    return;
4231 	}
4232     }
4233     if ( event->u.chr.keysym == GK_Up || event->u.chr.keysym==GK_KP_Up ||
4234 	    event->u.chr.keysym == GK_Down || event->u.chr.keysym==GK_KP_Down ) {
4235 	    GGadget *active = GWindowGetFocusGadgetOfWindow(mv->gw);
4236 	    if(!active)
4237 		return;
4238 
4239 	    // MIQ: We do not want to increment and decrement the integer
4240 	    //      value of the kerning word on up/down now, instead we
4241 	    //      should always move up/down in the list of kerning words.
4242 	    if( active != mv->text )
4243 	    {
4244 		unichar_t *end;
4245 		double val = u_strtod(_GGadgetGetTitle(active),&end);
4246 		if (isValidInt(end)) {
4247 		    int dir = ( event->u.chr.keysym == GK_Up || event->u.chr.keysym==GK_KP_Up ) ? 1 : -1;
4248 		    if( event->u.chr.state&ksm_control && event->u.chr.state&ksm_shift ) {
4249 			dir *= pref_mv_control_shift_and_arrow_skip;
4250 		    }
4251 		    else if( event->u.chr.state&ksm_shift ) {
4252 			dir *= pref_mv_shift_and_arrow_skip;
4253 		    }
4254 		    val += dir;
4255 		    char buf[100];
4256 		    snprintf(buf,99,"%.0f",val);
4257 		    GGadgetSetTitle8(active, buf);
4258 
4259 		    event->u.control.u.tf_changed.from_pulldown=-1;
4260 		    event->type=et_controlevent;
4261 		    event->u.control.subtype = et_textchanged;
4262 		    GGadgetDispatchEvent(active,event);
4263 
4264 		    if( haveClassBasedKerningInView(mv) )
4265 		    {
4266 			MVRemetric(mv);
4267 			GDrawRequestExpose(mv->v,NULL,false);
4268 		    }
4269 		}
4270 	    }
4271     }
4272     if ( event->u.chr.keysym == GK_Up || event->u.chr.keysym==GK_KP_Up ||
4273 	 event->u.chr.keysym == GK_Down || event->u.chr.keysym==GK_KP_Down )
4274     {
4275 	int dir = ( event->u.chr.keysym == GK_Up || event->u.chr.keysym==GK_KP_Up ) ? -1 : 1;
4276 	MVMoveInWordListByOffset( mv, dir );
4277     }
4278 }
4279 
hitsbit(BDFChar * bc,int x,int y)4280 static int hitsbit(BDFChar *bc, int x, int y) {
4281     if ( bc->byte_data )
4282 return( bc->bitmap[y*bc->bytes_per_line+x] );
4283     else
4284 return( bc->bitmap[y*bc->bytes_per_line+(x>>3)]&(1<<(7-(x&7))) );
4285 }
4286 
_MVSubVMouse(MetricsView * mv,GEvent * event)4287 static void _MVSubVMouse(MetricsView *mv,GEvent *event) {
4288     int i, x, y, j, within, xbase;
4289     SplineChar *sc;
4290     int diff;
4291     int onwidth, onkern;
4292     SplineFont *sf = mv->sf;
4293     double iscale = mv->pixelsize_set_by_window ? 1.0 : mv_scales[mv->scale_index];
4294     double scale = iscale*mv->pixelsize/(double) (sf->ascent+sf->descent);
4295     int as = rint(sf->ascent*scale);
4296 
4297     xbase = mv->vwidth/2;
4298     within = -1;
4299     for ( i=0; i<mv->glyphcnt; ++i ) {
4300 	y = mv->perchar[i].dy + mv->perchar[i].yoff;
4301 	x = xbase - mv->pixelsize*iscale/2 - mv->perchar[i].xoff;
4302 	if ( mv->bdf==NULL ) {
4303 	    BDFChar *bdfc = BDFPieceMealCheck(mv->show,mv->glyphs[i].sc->orig_pos);
4304 	    if ( event->u.mouse.x >= x+bdfc->xmin &&
4305 		event->u.mouse.x <= x+bdfc->xmax &&
4306 		event->u.mouse.y <= (y+as)-bdfc->ymin &&
4307 		event->u.mouse.y >= (y+as)-bdfc->ymax &&
4308 		hitsbit(bdfc,rint(iscale*(event->u.mouse.x-x-bdfc->xmin)),
4309 			rint(iscale*(event->u.mouse.y-(y+as-bdfc->ymax)))) )
4310     break;
4311 	}
4312 	y += -mv->perchar[i].yoff;
4313 	if ( event->u.mouse.y >= y && event->u.mouse.y < y+mv->perchar[i].dheight+ mv->perchar[i].kernafter )
4314 	    within = i;
4315     }
4316     if ( i==mv->glyphcnt )
4317 	sc = NULL;
4318     else
4319 	sc = mv->glyphs[i].sc;
4320 
4321     diff = event->u.mouse.y-mv->pressed_y;
4322     onwidth = onkern = false;
4323     if ( sc==NULL ) {
4324 	if ( mv->type == mv_kernonly ) {
4325 	    if ( within!=-1 && within+1<mv->glyphcnt &&
4326 		    event->u.mouse.y>mv->perchar[within+1].dy-3 ) {
4327 		onkern = true;			/* subsequent char */
4328 		++within;
4329 	    } else if ( within>0 &&
4330 		    event->u.mouse.y<mv->perchar[within].dy+3 )
4331 		onkern = true;
4332 	} else if ( mv->type == mv_widthonly ) {
4333 	    if ( within!=-1 && within+1<mv->glyphcnt &&
4334 		    event->u.mouse.y>mv->perchar[within+1].dy-3 )
4335 		onwidth = true;			/* subsequent char */
4336 	    else if ( within>=0 &&
4337 		    event->u.mouse.y>mv->perchar[within].dy+mv->perchar[within].dheight+mv->perchar[within].kernafter-3 ) {
4338 		onwidth = true;
4339 	    }
4340 	} else {
4341 	    if ( within>0 && mv->perchar[within-1].selected &&
4342 		    event->u.mouse.y<mv->perchar[within].dy+3 )
4343 		onwidth = true;		/* previous char */
4344 	    else if ( within!=-1 && within+1<mv->glyphcnt &&
4345 		    mv->perchar[within+1].selected &&
4346 		    event->u.mouse.y>mv->perchar[within+1].dy-3 ) {
4347 		onkern = true;			/* subsequent char */
4348 		++within;
4349 	    } else if ( within>0 && mv->perchar[within].selected &&
4350 		    event->u.mouse.y<mv->perchar[within].dy+3 )
4351 		onkern = true;
4352 	    else if ( within>=0 &&
4353 		    event->u.mouse.y>mv->perchar[within].dy+mv->perchar[within].dheight+mv->perchar[within].kernafter-3 ) {
4354 		onwidth = true;
4355 	    }
4356 	}
4357     }
4358 
4359     if ( event->type != et_mousemove || !mv->pressed ) {
4360 	int ct = -1;
4361 	if ( mv->bdf!=NULL ||
4362 		( mv->type==mv_kernonly && !onkern ) ||
4363 		( mv->type==mv_widthonly && !onwidth )) {
4364 	    if ( mv->cursor!=ct_mypointer )
4365 		ct = ct_mypointer;
4366 	} else if ( sc!=NULL ) {
4367 	    if ( mv->cursor!=ct_lbearing )
4368 		ct = ct_lbearing;
4369 	} else if ( onwidth ) {
4370 	    if ( mv->cursor!=ct_rbearing )
4371 		ct = ct_rbearing;
4372 	} else if ( onkern ) {
4373 	    if ( mv->cursor!=ct_kerning )
4374 		ct = ct_kerning;
4375 	} else {
4376 	    if ( mv->cursor!=ct_mypointer )
4377 		ct = ct_mypointer;
4378 	}
4379 	if ( ct!=-1 ) {
4380 	    GDrawSetCursor(mv->gw,ct);
4381 	    mv->cursor = ct;
4382 	}
4383     }
4384 
4385     if ( event->type == et_mousemove && !mv->pressed ) {
4386 	if ( sc==NULL && within!=-1 )
4387 	    sc = mv->glyphs[within].sc;
4388 	if ( sc!=NULL )
4389 	    SCPreparePopup(mv->gw,sc,mv->fv->b.map->remap,mv->fv->b.map->backmap[sc->orig_pos],sc->unicodeenc);
4390 /* Don't allow any editing when displaying a bitmap font */
4391     } else if ( event->type == et_mousedown && mv->bdf==NULL ) {
4392 	CVPaletteDeactivate();
4393 	if ( sc!=NULL ) {
4394 	    for ( j=0; j<mv->glyphcnt; ++j )
4395 		if ( j!=i && mv->perchar[j].selected )
4396 		    MVDeselectChar(mv,j);
4397 	    MVSelectChar(mv,i);
4398 	    GWindowClearFocusGadgetOfWindow(mv->gw);
4399 	    mv->pressed = true;
4400 	} else if ( within!=-1 ) {
4401 	    mv->pressedwidth = onwidth;
4402 	    mv->pressedkern = onkern;
4403 	    if ( mv->pressedwidth || mv->pressedkern ) {
4404 		mv->pressed = true;
4405 		if ( !mv->perchar[within].selected ) {
4406 		    MVDoSelect(mv,within);
4407 		}
4408 	    }
4409 	}
4410 	mv->pressed_y = event->u.mouse.y;
4411     } else if ( event->type == et_mousemove && mv->pressed ) {
4412 	for ( i=0; i<mv->glyphcnt && !mv->perchar[i].selected; ++i );
4413 	if ( mv->pressedwidth ) {
4414 	    int ow = mv->perchar[i].dwidth;
4415 	    mv->perchar[i].dwidth = rint(mv->glyphs[i].sc->vwidth*scale) + diff;
4416 	    if ( ow!=mv->perchar[i].dwidth ) {
4417 		for ( j=i+1; j<mv->glyphcnt; ++j )
4418 		    mv->perchar[j].dy = mv->perchar[j-1].dy+mv->perchar[j-1].dheight+
4419 			    mv->perchar[j-1].kernafter;
4420 		GDrawRequestExpose(mv->v,NULL,false);
4421 	    }
4422 	} else if ( mv->pressedkern ) {
4423 	    int ow = mv->perchar[i-1].kernafter;
4424 	    KernPair *kp;
4425 	    int kpoff;
4426 	    KernClass *kc;
4427 	    kp = mv->glyphs[i-1].kp;
4428 	    if ( kp!=NULL )
4429 		kpoff = kp->off;
4430 	    else if ((kc=mv->glyphs[i-1].kc)!=NULL )
4431 		kpoff = kc->offsets[mv->glyphs[i-1].kc_index];
4432 	    else
4433 		kpoff = 0;
4434 	    kpoff = kpoff * mv->pixelsize*iscale /
4435 			(mv->sf->descent+mv->sf->ascent);
4436 	    mv->perchar[i-1].kernafter = kpoff + diff;
4437 	    if ( ow!=mv->perchar[i-1].kernafter ) {
4438 		for ( j=i; j<mv->glyphcnt; ++j )
4439 		    mv->perchar[j].dy = mv->perchar[j-1].dy+mv->perchar[j-1].dheight+
4440 			    mv->perchar[j-1].kernafter;
4441 		GDrawRequestExpose(mv->v,NULL,false);
4442 	    }
4443 	} else if ( mv->type!=mv_kernonly ) {
4444 	    int olda = mv->activeoff;
4445 	    BDFChar *bdfc = BDFPieceMealCheck(mv->show,mv->glyphs[i].sc->orig_pos);
4446 	    mv->activeoff = diff;
4447 	    MVRedrawI(mv,i,bdfc->xmin+olda,bdfc->xmax+olda);
4448 	}
4449     } else if ( event->type == et_mouseup && event->u.mouse.clicks>1 &&
4450 	    (within!=-1 || sc!=NULL)) {
4451 	mv->pressed = false; mv->activeoff = 0;
4452 	mv->pressedwidth = mv->pressedkern = false;
4453 	if ( within==-1 ) within = i;
4454 	if ( mv->bdf==NULL )
4455 	    CharViewCreate(mv->glyphs[within].sc,mv->fv,-1);
4456 	else
4457 	    BitmapViewCreate(mv->bdf->glyphs[mv->glyphs[within].sc->orig_pos],mv->bdf,mv->fv,-1);
4458 	if ( mv->showgrid==mv_hidemovinggrid )
4459 	    GDrawRequestExpose(mv->v,NULL,false);
4460     } else if ( event->type == et_mouseup && mv->pressed ) {
4461 	for ( i=0; i<mv->glyphcnt && !mv->perchar[i].selected; ++i );
4462 	mv->pressed = false;
4463 	mv->activeoff = 0;
4464 	sc = mv->glyphs[i].sc;
4465 	if ( mv->pressedwidth ) {
4466 	    mv->pressedwidth = false;
4467 	    diff = diff*(mv->sf->ascent+mv->sf->descent)/(mv->pixelsize*iscale);
4468 	    if ( diff!=0 ) {
4469 		SCPreserveWidth(sc);
4470 		sc->vwidth += diff;
4471 		SCCharChangedUpdate(sc,ly_none);
4472 		for ( ; i<mv->glyphcnt; ++i )
4473 		    mv->perchar[i].dy = mv->perchar[i-1].dy+mv->perchar[i-1].dheight +
4474 			    mv->perchar[i-1].kernafter ;
4475 		GDrawRequestExpose(mv->v,NULL,false);
4476 	    } else if ( mv->showgrid==mv_hidemovinggrid )
4477 		GDrawRequestExpose(mv->v,NULL,false);
4478 	} else if ( mv->pressedkern ) {
4479 	    mv->pressedkern = false;
4480 	    diff = diff*(mv->sf->ascent+mv->sf->descent)/(mv->pixelsize*iscale);
4481 	    if ( diff!=0 )
4482 		MV_ChangeKerning(mv, i, diff, true);
4483 	    MVRefreshValues(mv,i-1);
4484 	} else if ( mv->type!=mv_kernonly ) {
4485 	    real transform[6];
4486 	    DBounds bb;
4487 	    SplineCharFindBounds(sc,&bb);
4488 	    transform[0] = transform[3] = 1.0;
4489 	    transform[1] = transform[2] = transform[4] = 0;
4490 	    transform[5] = -diff*
4491 		    (mv->sf->ascent+mv->sf->descent)/(mv->pixelsize*iscale);
4492 	    if ( transform[5]!=0 )
4493 		FVTrans( (FontViewBase *)mv->fv,sc,transform,NULL,false);
4494 	}
4495 	mv->pressedwidth = false;
4496 	mv->pressedkern = false;
4497     } else if ( event->type == et_mouseup && mv->bdf!=NULL && within!=-1 ) {
4498 	for ( j=0; j<mv->glyphcnt; ++j )
4499 	    if ( j!=within && mv->perchar[j].selected )
4500 		MVDeselectChar(mv,j);
4501 	MVSelectChar(mv,within);
4502 	if ( mv->showgrid==mv_hidemovinggrid )
4503 	    GDrawRequestExpose(mv->v,NULL,false);
4504     }
4505 }
4506 
MVSubMouse(MetricsView * mv,GEvent * event)4507 static void MVSubMouse(MetricsView *mv,GEvent *event) {
4508     // This handles mouse events in the preview area.
4509     int i, x, y, j, within, ybase;
4510     SplineChar *sc;
4511     int diff;
4512     int onwidth, onkern;
4513     BDFChar *bdfc;
4514     double iscale = mv->pixelsize_set_by_window ? 1.0 : mv_scales[mv->scale_index];
4515 
4516     GGadgetEndPopup();
4517 
4518     if ( event->type==et_mouseup ) {
4519 	event->type = et_mousemove;
4520 	MVSubMouse(mv,event);
4521 	event->u.mouse.x -= mv->xoff;
4522 	event->u.mouse.y -= mv->yoff;
4523 	event->type = et_mouseup;
4524     }
4525 
4526     event->u.mouse.x += mv->xoff;
4527     event->u.mouse.y += mv->yoff;
4528     if ( mv->vertical ) {
4529 	_MVSubVMouse(mv,event);
4530 return;
4531     }
4532 
4533     ybase = mv->ybaseline - mv->yoff;
4534     within = -1;
4535     for ( i=0; i<mv->glyphcnt; ++i ) {
4536 	x = mv->perchar[i].dx + mv->perchar[i].xoff;
4537 	if ( mv->right_to_left )
4538 	    x = mv->vwidth - x - mv->perchar[i].dwidth - mv->perchar[i].kernafter;
4539 	y = ybase - mv->perchar[i].yoff;
4540 	if ( mv->bdf==NULL ) {
4541 	    bdfc = BDFPieceMealCheck(mv->show,mv->glyphs[i].sc->orig_pos);
4542 	    if ( event->u.mouse.x >= x+bdfc->xmin &&
4543 		event->u.mouse.x <= x+bdfc->xmax &&
4544 		event->u.mouse.y <= y-bdfc->ymin &&
4545 		event->u.mouse.y >= y-bdfc->ymax &&
4546 		hitsbit(bdfc,rint(iscale*(event->u.mouse.x-x-bdfc->xmin)),
4547 			rint(iscale*(bdfc->ymax-(y-event->u.mouse.y)))) )
4548     break;
4549 	}
4550 	x += mv->right_to_left ? mv->perchar[i].xoff : -mv->perchar[i].xoff;
4551 	if ( event->u.mouse.x >= x && event->u.mouse.x < x+mv->perchar[i].dwidth+ mv->perchar[i].kernafter )
4552 	    within = i;
4553     }
4554     if ( i==mv->glyphcnt )
4555 	sc = NULL;
4556     else
4557 	sc = mv->glyphs[i].sc;
4558 
4559     diff = event->u.mouse.x-mv->pressed_x;
4560     /*if ( mv->right_to_left ) diff = -diff;*/
4561     onwidth = onkern = false;
4562     if ( sc==NULL ) {
4563 	if ( !mv->right_to_left ) {
4564 	    if ( mv->type == mv_kernonly ) {
4565 		if ( within>=0 && within+1<mv->glyphcnt &&
4566 			event->u.mouse.x>mv->perchar[within+1].dx-3 ) {
4567 		    onkern = true;			/* subsequent char */
4568 		    ++within;
4569 		} else if ( within>0 &&
4570 			event->u.mouse.x<mv->perchar[within].dx+3 )
4571 		    onkern = true;
4572 	    } else if ( mv->type == mv_widthonly ) {
4573 		if ( within>=0 && within+1<mv->glyphcnt &&
4574 			event->u.mouse.x>mv->perchar[within+1].dx-3 )
4575 		    onwidth = true;			/* subsequent char */
4576 		else if ( within>=0 &&
4577 			event->u.mouse.x>mv->perchar[within].dx+mv->perchar[within].dwidth+mv->perchar[within].kernafter-3 ) {
4578 		    onwidth = true;
4579 		}
4580 	    } else {
4581 		if ( within>0 && mv->perchar[within-1].selected &&
4582 			event->u.mouse.x<mv->perchar[within].dx+3 )
4583 		    onwidth = true;		/* previous char */
4584 		else if ( within!=-1 && within+1<mv->glyphcnt &&
4585 			mv->perchar[within+1].selected &&
4586 			event->u.mouse.x>mv->perchar[within+1].dx-3 ) {
4587 		    onkern = true;			/* subsequent char */
4588 		    ++within;
4589 		} else if ( within>0 && mv->perchar[within].selected &&
4590 			event->u.mouse.x<mv->perchar[within].dx+3 )
4591 		    onkern = true;
4592 		else if ( within>=0 &&
4593 			event->u.mouse.x>mv->perchar[within].dx+mv->perchar[within].dwidth+mv->perchar[within].kernafter-3 ) {
4594 		    onwidth = true;
4595 		}
4596 	    }
4597 	} else {
4598 	    if ( mv->type == mv_kernonly ) {
4599 		if ( within>=0 && within+1<mv->glyphcnt &&
4600 			event->u.mouse.x<mv->dwidth-(mv->perchar[within+1].dx-3) ) {
4601 		    onkern = true;			/* subsequent char */
4602 		    ++within;
4603 		} else if ( within>0 &&
4604 			event->u.mouse.x>mv->dwidth-(mv->perchar[within].dx+3) )
4605 		    onkern = true;
4606 	    } else if ( mv->type == mv_widthonly ) {
4607 		if ( within>=0 && within+1<mv->glyphcnt &&
4608 			event->u.mouse.x<mv->dwidth-(mv->perchar[within+1].dx-3) )
4609 		    onwidth = true;			/* subsequent char */
4610 		else if ( within>=0 &&
4611 			event->u.mouse.x<mv->dwidth-(mv->perchar[within].dx+mv->perchar[within].dwidth+mv->perchar[within].kernafter-3) ) {
4612 		    onwidth = true;
4613 		}
4614 	    } else {
4615 		if ( within>0 && mv->perchar[within-1].selected &&
4616 			event->u.mouse.x>mv->dwidth-(mv->perchar[within].dx+3) )
4617 		    onwidth = true;		/* previous char */
4618 		else if ( within!=-1 && within+1<mv->glyphcnt &&
4619 			mv->perchar[within+1].selected &&
4620 			event->u.mouse.x<mv->dwidth-(mv->perchar[within+1].dx-3) ) {
4621 		    onkern = true;			/* subsequent char */
4622 		    ++within;
4623 		} else if ( within>0 && mv->perchar[within].selected &&
4624 			event->u.mouse.x>mv->dwidth-(mv->perchar[within].dx+3) )
4625 		    onkern = true;
4626 		else if ( within>=0 &&
4627 			event->u.mouse.x<mv->dwidth-(mv->perchar[within].dx+mv->perchar[within].dwidth+mv->perchar[within].kernafter-3) ) {
4628 		    onwidth = true;
4629 		}
4630 	    }
4631 	}
4632     }
4633 
4634     if ( event->type != et_mousemove || !mv->pressed ) {
4635 	int ct = -1;
4636 	if ( mv->bdf!=NULL ||
4637 		( mv->type==mv_kernonly && !onkern ) ||
4638 		( mv->type==mv_widthonly && !onwidth )) {
4639 	    if ( mv->cursor!=ct_mypointer )
4640 		ct = ct_mypointer;
4641 	} else if ( sc!=NULL ) {
4642 	    if ( mv->cursor!=ct_lbearing )
4643 		ct = ct_lbearing;
4644 	} else if ( onwidth ) {
4645 	    if ( mv->cursor!=ct_rbearing )
4646 		ct = ct_rbearing;
4647 	} else if ( onkern ) {
4648 	    if ( mv->cursor!=ct_kerning )
4649 		ct = ct_kerning;
4650 	} else {
4651 	    if ( mv->cursor!=ct_mypointer )
4652 		ct = ct_mypointer;
4653 	}
4654 	if ( ct!=-1 ) {
4655 	    GDrawSetCursor(mv->gw,ct);
4656 	    mv->cursor = ct;
4657 	}
4658     }
4659 
4660     if ( event->type == et_mousemove && !mv->pressed ) {
4661 	if ( sc==NULL && within!=-1 )
4662 	    sc = mv->glyphs[within].sc;
4663 	if ( sc!=NULL )
4664 	    SCPreparePopup(mv->gw,sc,mv->fv->b.map->remap,mv->fv->b.map->backmap[sc->orig_pos],sc->unicodeenc);
4665 /* Don't allow any editing when displaying a bitmap font */
4666     } else if ( event->type == et_mousedown && mv->bdf==NULL ) {
4667 	CVPaletteDeactivate();
4668 	if ( sc!=NULL ) {
4669 	    for ( j=0; j<mv->glyphcnt; ++j )
4670 		if ( j!=i && mv->perchar[j].selected )
4671 		    MVDeselectChar(mv,j);
4672 	    MVSelectChar(mv,i);
4673 	    GWindowClearFocusGadgetOfWindow(mv->gw);
4674 	    mv->pressed = true;
4675 	} else if ( within!=-1 ) {
4676 	    mv->pressedwidth = onwidth;
4677 	    mv->pressedkern = onkern;
4678 	    if ( mv->pressedwidth || mv->pressedkern ) {
4679 		mv->pressed = true;
4680 		if ( !mv->perchar[within].selected ) {
4681 		    MVDoSelect(mv,within);
4682 		}
4683 	    }
4684 	}
4685 	mv->pressed_x = event->u.mouse.x;
4686     } else if ( event->type == et_mousemove && mv->pressed ) {
4687 //	printf("move & pressed pressedwidth:%d pressedkern:%d type!=mv_kernonly:%d\n",mv->pressedwidth,mv->pressedkern,(mv->type!=mv_kernonly));
4688 
4689 	for ( i=0; i<mv->glyphcnt && !mv->perchar[i].selected; ++i )
4690 	{
4691 	    // nothing
4692 	}
4693 
4694 	if ( mv->pressedwidth ) {
4695 	    int ow = mv->perchar[i].dwidth;
4696 	    if ( mv->right_to_left ) diff = -diff;
4697 	    bdfc = BDFPieceMealCheck(mv->show,mv->glyphs[i].sc->orig_pos);
4698 	    mv->perchar[i].dwidth = bdfc->width + diff;
4699 	    if ( ow!=mv->perchar[i].dwidth ) {
4700 		for ( j=i+1; j<mv->glyphcnt; ++j )
4701 		    mv->perchar[j].dx = mv->perchar[j-1].dx+mv->perchar[j-1].dwidth+ mv->perchar[j-1].kernafter;
4702 		GDrawRequestExpose(mv->v,NULL,false);
4703 	    }
4704 	} else if ( mv->pressedkern ) {
4705 	    int ow = mv->perchar[i-1].kernafter;
4706 	    KernPair *kp;
4707 	    int kpoff;
4708 	    KernClass *kc;
4709 	    int index;
4710 	    for ( kp = mv->glyphs[i-1].sc->kerns; kp!=NULL && kp->sc!=mv->glyphs[i].sc; kp = kp->next );
4711 	    if ( kp!=NULL )
4712 		kpoff = kp->off;
4713 	    else if ((kc=SFFindKernClass(mv->sf,mv->glyphs[i-1].sc,mv->glyphs[i].sc,&index,false))!=NULL )
4714 		kpoff = kc->offsets[index];
4715 	    else
4716 		kpoff = 0;
4717 	    kpoff = kpoff * mv->pixelsize*iscale /
4718 			(mv->sf->descent+mv->sf->ascent);
4719 	    if ( mv->right_to_left ) diff = -diff;
4720 	    mv->perchar[i-1].kernafter = kpoff + diff;
4721 	    if ( ow!=mv->perchar[i-1].kernafter ) {
4722 		for ( j=i; j<mv->glyphcnt; ++j )
4723 		    mv->perchar[j].dx = mv->perchar[j-1].dx+mv->perchar[j-1].dwidth+ mv->perchar[j-1].kernafter;
4724 		GDrawRequestExpose(mv->v,NULL,false);
4725 	    }
4726 	} else if ( mv->type!=mv_kernonly ) {
4727 	    int olda = mv->activeoff;
4728 	    bdfc = BDFPieceMealCheck(mv->show,mv->glyphs[i].sc->orig_pos);
4729 	    mv->activeoff = diff;
4730 	    MVRedrawI(mv,i,bdfc->xmin+olda,bdfc->xmax+olda);
4731 	}
4732     } else if ( event->type == et_mouseup && event->u.mouse.clicks>1 &&
4733 	    (within!=-1 || sc!=NULL)) {
4734 	mv->pressed = false; mv->activeoff = 0;
4735 	mv->pressedwidth = mv->pressedkern = false;
4736 	if ( within==-1 ) within = i;
4737 	if ( mv->bdf==NULL )
4738 	    CharViewCreate(mv->glyphs[within].sc,mv->fv,-1);
4739 	else
4740 	    BitmapViewCreate(mv->bdf->glyphs[mv->glyphs[within].sc->orig_pos],mv->bdf,mv->fv,-1);
4741 	if ( mv->showgrid==mv_hidemovinggrid )
4742 	    GDrawRequestExpose(mv->v,NULL,false);
4743     } else if ( event->type == et_mouseup && mv->pressed ) {
4744 	for ( i=0; i<mv->glyphcnt && !mv->perchar[i].selected; ++i )
4745 	{
4746 	    // nothing
4747 	}
4748 
4749 //	printf("mvsubmouse() mv->pressedwidth:%d \n", mv->pressedwidth );
4750 	mv->pressed = false;
4751 	mv->activeoff = 0;
4752 	sc = mv->glyphs[i].sc;
4753 	if ( mv->pressedwidth ) {
4754 	    mv->pressedwidth = false;
4755 	    if ( mv->right_to_left ) diff = -diff;
4756 	    diff = diff*(mv->sf->ascent+mv->sf->descent)/(mv->pixelsize*iscale);
4757 //	    printf("mvsubmouse() diff:%d \n", diff );
4758 	    if ( diff!=0 ) {
4759 		SCPreserveWidth(sc);
4760 		SCSynchronizeWidth(sc,sc->width+diff,sc->width,NULL);
4761 		SCCharChangedUpdate(sc,ly_none);
4762 	    }
4763 	} else if ( mv->pressedkern ) {
4764 	    mv->pressedkern = false;
4765 	    diff = diff*(mv->sf->ascent+mv->sf->descent)/(mv->pixelsize*iscale);
4766 	    if ( diff!=0 ) {
4767 		if ( mv->right_to_left ) diff = -diff;
4768 		MV_ChangeKerning(mv, i, diff, true);
4769 		MVRefreshValues(mv,i-1);
4770 	    }
4771 	} else if ( mv->type!=mv_kernonly ) {
4772 	    real transform[6];
4773 	    transform[0] = transform[3] = 1.0;
4774 	    transform[1] = transform[2] = transform[5] = 0;
4775 	    transform[4] = diff*
4776 		    (mv->sf->ascent+mv->sf->descent)/(mv->pixelsize*iscale);
4777 	    if ( transform[4]!=0 )
4778 		FVTrans( (FontViewBase *)mv->fv,sc,transform,NULL, 0 | fvt_alllayers );
4779 	}
4780 	mv->pressedwidth = false;
4781 	mv->pressedkern = false;
4782 	if ( mv->showgrid==mv_hidemovinggrid )
4783 	    GDrawRequestExpose(mv->v,NULL,false);
4784     } else if ( event->type == et_mouseup && mv->bdf!=NULL && within!=-1 ) {
4785 	for ( j=0; j<mv->glyphcnt; ++j )
4786 	    if ( j!=within && mv->perchar[j].selected )
4787 		MVDeselectChar(mv,j);
4788 	MVSelectChar(mv,within);
4789 	if ( mv->showgrid==mv_hidemovinggrid )
4790 	    GDrawRequestExpose(mv->v,NULL,false);
4791     }
4792 }
4793 
MVMouse(MetricsView * mv,GEvent * event)4794 static void MVMouse(MetricsView *mv,GEvent *event) {
4795     int i;
4796 
4797     if ( event->u.mouse.y< mv->topend || event->u.mouse.y >= mv->displayend ) {
4798     // mv->displayend > mv->topend
4799     // This triggers when the mouse is in the data entry grid.
4800 	if ( event->u.mouse.y >= mv->displayend &&
4801 		event->u.mouse.y<mv->height-mv->sbh ) {
4802 	    // This excludes the scroll bar.
4803 	    event->u.mouse.x += (mv->coff*mv->mwidth);
4804 	    for ( i=0; i<mv->glyphcnt; ++i ) {
4805 		if ( event->u.mouse.x >= mv->perchar[i].mx &&
4806 			event->u.mouse.x < mv->perchar[i].mx+mv->perchar[i].mwidth )
4807 	    break; // This triggers only if the column has an associated character.
4808 	    }
4809 	    if ( i<mv->glyphcnt )
4810 		SCPreparePopup(mv->gw,mv->glyphs[i].sc,mv->fv->b.map->remap,
4811 			mv->fv->b.map->backmap[mv->glyphs[i].sc->orig_pos],
4812 			mv->glyphs[i].sc->unicodeenc);
4813 	}
4814 	if ( mv->cursor!=ct_mypointer ) {
4815 	    GDrawSetCursor(mv->gw,ct_mypointer);
4816 	    mv->cursor = ct_mypointer;
4817 	}
4818 return;
4819     }
4820 }
4821 
MVDrop(MetricsView * mv,GEvent * event)4822 static void MVDrop(MetricsView *mv,GEvent *event) {
4823     int x,ex = event->u.drag_drop.x + mv->xoff;
4824     int y,ey = event->u.drag_drop.y + mv->yoff;
4825     int within, i, cnt, ch;
4826     int32 len;
4827     char *cnames, *start, *pt;
4828     unichar_t *newtext;
4829     const unichar_t *oldtext;
4830     SplineChar **founds;
4831     /* We should get a list of character names. Add them before the character */
4832     /*  on which they are dropped */
4833 
4834     if ( !GDrawSelectionHasType(mv->gw,sn_drag_and_drop,"STRING"))
4835 return;
4836     cnames = GDrawRequestSelection(mv->gw,sn_drag_and_drop,"STRING",&len);
4837     if ( cnames==NULL )
4838 return;
4839 
4840     within = mv->glyphcnt;
4841     if ( !mv->vertical ) {
4842 	for ( i=0; i<mv->glyphcnt; ++i ) {
4843 	    x = mv->perchar[i].dx;
4844 	    if ( mv->right_to_left )
4845 		x = mv->dwidth - x - mv->perchar[i].dwidth - mv->perchar[i].kernafter ;
4846 	    if ( ex >= x && ex < x+mv->perchar[i].dwidth+ mv->perchar[i].kernafter ) {
4847 		within = i;
4848 	break;
4849 	    }
4850 	}
4851     } else {
4852 	for ( i=0; i<mv->glyphcnt; ++i ) {
4853 	    y = mv->perchar[i].dy;
4854 	    if ( ey >= y && ey < y+mv->perchar[i].dheight+
4855 		    mv->perchar[i].kernafter ) {
4856 		within = i;
4857 	break;
4858 	    }
4859 	}
4860     }
4861 
4862     founds = malloc(len*sizeof(SplineChar *));	/* Will be a vast over-estimate */
4863     start = cnames;
4864     for ( i=0; *start; ) {
4865 	while ( *start==' ' ) ++start;
4866 	if ( *start=='\0' )
4867     break;
4868 	for ( pt=start; *pt && *pt!=' '; ++pt );
4869 	ch = *pt; *pt = '\0';
4870 	if ( (founds[i]=SFGetChar(mv->sf,-1,start))!=NULL )
4871 	    ++i;
4872 	*pt = ch;
4873 	start = pt;
4874     }
4875     cnt = i;
4876     free( cnames );
4877     if ( cnt==0 ) {
4878         free(founds);
4879 return;
4880     }
4881     if ( within<mv->glyphcnt )
4882 	within = mv->glyphs[within].orig_index;
4883     else
4884 	within = mv->clen;
4885 
4886     if ( mv->clen+cnt+1>=mv->cmax ) {
4887 	mv->cmax = mv->clen+cnt+10;
4888 	mv->chars = realloc(mv->chars,mv->cmax*sizeof(SplineChar *));
4889     }
4890     oldtext = _GGadgetGetTitle(mv->text);
4891     newtext = malloc((mv->clen+cnt+1)*sizeof(unichar_t));
4892     u_strcpy(newtext,oldtext);
4893     newtext[mv->clen+cnt]='\0';
4894     for ( i=mv->clen+cnt; i>=within+cnt; --i ) {
4895 	newtext[i] = newtext[i-cnt];
4896 	mv->chars[i] = mv->chars[i-cnt];
4897     }
4898     for ( i=within; i<within+cnt; ++i ) {
4899 	mv->chars[i] = founds[i-within];
4900 	newtext[i] = founds[i-within]->unicodeenc>=0 ?
4901 		founds[i-within]->unicodeenc : MVFakeUnicodeOfSc(mv,founds[i-within]);
4902     }
4903     mv->clen += cnt;
4904     MVRemetric(mv);
4905     free(founds);
4906 
4907     GGadgetSetTitle(mv->text,newtext);
4908     free(newtext);
4909 
4910     GDrawRequestExpose(mv->v,NULL,false);
4911 }
4912 
mv_v_e_h(GWindow gw,GEvent * event)4913 static int mv_v_e_h(GWindow gw, GEvent *event) {
4914     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
4915 
4916     switch ( event->type ) {
4917       case et_expose:
4918 	GDrawSetLineWidth(gw,0);
4919 	MVSubExpose(mv,gw,event);
4920       break;
4921       case et_char:
4922 	MVChar(mv,event);
4923       break;
4924       case et_charup:
4925 	  if ( event->u.chr.keysym == GK_Left || event->u.chr.keysym==GK_KP_Left
4926 	       || event->u.chr.keysym == GK_Right || event->u.chr.keysym==GK_KP_Right ) {
4927 	      if( event->u.chr.state&ksm_meta ) {
4928 		  MVChar(mv,event);
4929 	      }
4930 	  }
4931       break;
4932       case et_mouseup: case et_mousemove: case et_mousedown:
4933 	if (( event->type==et_mouseup || event->type==et_mousedown ) &&
4934 		(event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
4935 	    int ish = event->u.mouse.button>5;
4936 	    if ( event->u.mouse.state&ksm_shift ) ish = !ish;
4937 	    if ( event->u.mouse.state&ksm_control ) {	/* bind control to magnify/minify */
4938 		if ( event->type==et_mousedown ) {
4939 		    if ( event->u.mouse.button==4 || event->u.mouse.button==6 )
4940 			_MVMenuScale(mv,MID_ZoomIn);
4941 		    else
4942 			_MVMenuScale(mv,MID_ZoomOut);
4943 		}
4944 	    } else if ( ish ) {		/* bind shift to horizontal scroll */
4945 return( GGadgetDispatchEvent(mv->hsb,event));
4946 	    } else {
4947 return( GGadgetDispatchEvent(mv->vsb,event));
4948 	    }
4949 return( true );
4950 	}
4951 	if ( mv->gwgic!=NULL && event->type==et_mousedown)
4952 	    GDrawSetGIC(mv->gw,mv->gwgic,0,20);
4953 	MVSubMouse(mv,event);
4954       break;
4955       case et_drop:
4956 	MVDrop(mv,event);
4957       break;
4958     }
4959 return( true );
4960 }
4961 
mv_e_h(GWindow gw,GEvent * event)4962 static int mv_e_h(GWindow gw, GEvent *event) {
4963     MetricsView *mv = (MetricsView *) GDrawGetUserData(gw);
4964     SplineFont *sf;
4965     GGadget *active = 0;
4966 //    printf("mv_e_h()  event->type:%d\n", event->type );
4967 
4968     switch ( event->type ) {
4969       case et_selclear:
4970 	ClipboardClear();
4971       break;
4972       case et_expose:
4973 	GDrawSetLineWidth(gw,0);
4974 	MVExpose(mv,gw,event);
4975       break;
4976       case et_resize:
4977 	if ( event->u.resize.sized )
4978 	    MVResize(mv);
4979       break;
4980       case et_char:
4981 	if ((event->u.chr.keysym == GK_Tab || event->u.chr.keysym == GK_BackTab) && (!(event->u.chr.state&ksm_meta))) {
4982 	  // We want to allow somebody to move the cursor position
4983 	  // forwards with tab and backwards with shift + tab.
4984 	  // GGadget *active = GWindowGetFocusGadgetOfWindow(mv->gw); if (event->u.chr.state&ksm_shift) return 0;
4985 	  // For now, we just return 0 so that the default event handler takes care.
4986 	  return 0;
4987 	}
4988 	// MVChar(mv,event);
4989       break;
4990       case et_charup:
4991 	if ((event->u.chr.keysym == GK_Tab || event->u.chr.keysym == GK_BackTab) && (!(event->u.chr.state&ksm_meta))) {
4992 	  // We want to allow somebody to move the cursor position
4993 	  // forwards with tab and backwards with shift + tab.
4994 	  // GGadget *active = GWindowGetFocusGadgetOfWindow(mv->gw); if (event->u.chr.state&ksm_shift) return 0;
4995 	  // For now, we just return 0 so that the default event handler takes care.
4996 	  return 0;
4997 	} else if ((event->u.chr.keysym == GK_Return) && (!(event->u.chr.state&ksm_meta))) {
4998 		MVMoveInTableByColumnByOffset(mv, (event->u.chr.state&ksm_shift) ? -1 : 1);
4999 	} else {
5000 		MVChar(mv,event);
5001 	}
5002 #if 0
5003 	  // It is unclear to Frank why we were being so selective.
5004 	  if ( event->u.chr.keysym == GK_Left || event->u.chr.keysym==GK_KP_Left
5005 	       || event->u.chr.keysym == GK_Right || event->u.chr.keysym==GK_KP_Right ) {
5006 	      if( event->u.chr.state&ksm_meta ) {
5007 		  MVChar(mv,event);
5008 	      }
5009 	  }
5010 #endif // 0
5011       break;
5012       case et_mouseup: case et_mousemove: case et_mousedown:
5013           active = GWindowGetFocusGadgetOfWindow(mv->gw);
5014           if( GGadgetContainsEventLocation( mv->textPrev, event ))
5015           {
5016               GGadgetPreparePopup(mv->gw,c_to_u("Show the previous word in the current word list\n"
5017                                                 "Select the menu File / Load Word List... to load a wordlist."));
5018           }
5019           else if( GGadgetContainsEventLocation( mv->textNext, event ))
5020           {
5021               GGadgetPreparePopup(mv->gw,c_to_u("Show the next word in the current word list\n"
5022                                                 "Select the menu File / Load Word List... to load a wordlist."));
5023           }
5024           else if( GGadgetContainsEventLocation( mv->text, event ))
5025           {
5026               GGadgetPreparePopup(mv->gw,c_to_u("This is a word list that you can step through to quickly see your glyphs in context\n"
5027                                                 "Select the menu File / Load Word List... to load a wordlist."));
5028           }
5029           else
5030           {
5031               GGadgetPreparePopup(mv->gw, 0);
5032           }
5033 
5034 
5035 	if (( event->type==et_mouseup || event->type==et_mousedown ) &&
5036 		(event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
5037 	    int ish = event->u.mouse.button>5;
5038 	    if ( event->u.mouse.state&ksm_shift ) ish = !ish;
5039 	    if ( event->u.mouse.state&ksm_control ) {	/* bind control to magnify/minify */
5040 		if ( event->type==et_mousedown ) {
5041 		    if ( event->u.mouse.button==4 || event->u.mouse.button==6 )
5042 			_MVMenuScale(mv,MID_ZoomIn);
5043 		    else
5044 			_MVMenuScale(mv,MID_ZoomOut);
5045 		}
5046 	    } else if ( ish ) {	/* bind shift to horizontal scroll */
5047 return( GGadgetDispatchEvent(mv->hsb,event));
5048 	    } else {
5049 return( GGadgetDispatchEvent(mv->vsb,event));
5050 	    }
5051 return( true );
5052 	}
5053 	if ( mv->gwgic!=NULL && event->type==et_mousedown)
5054 	    GDrawSetGIC(mv->gw,mv->gwgic,0,20);
5055 	MVMouse(mv,event);
5056       break;
5057       case et_drop:
5058 	MVDrop(mv,event);
5059       break;
5060       case et_controlevent:
5061 	switch ( event->u.control.subtype ) {
5062 	  case et_scrollbarchange:
5063 	    if ( event->u.control.g==mv->hsb )
5064 		MVHScroll(mv,&event->u.control.u.sb);
5065 	    else
5066 		MVVScroll(mv,&event->u.control.u.sb);
5067 	  break;
5068 	}
5069       break;
5070       case et_close:
5071 	MVMenuClose(gw,NULL,NULL);
5072       break;
5073       case et_destroy:
5074 	sf = mv->sf;
5075 	if ( sf->cidmaster ) sf = sf->cidmaster;
5076 	if ( sf->metrics==mv )
5077 	    sf->metrics = mv->next;
5078 	else {
5079 	    MetricsView *n;
5080 	    for ( n=sf->metrics; n->next!=mv; n=n->next );
5081 	    n->next = mv->next;
5082 	}
5083 	KCLD_MvDetach(sf->kcld,mv);
5084 	MetricsViewFree(mv);
5085       break;
5086       case et_focus:
5087       break;
5088     }
5089 return( true );
5090 }
5091 
SLOfFont(SplineFont * sf)5092 GTextInfo *SLOfFont(SplineFont *sf) {
5093     uint32 *scripttags, *langtags;
5094     int s, l, i, k, cnt;
5095     extern GTextInfo scripts[], languages[];
5096     GTextInfo *ret = NULL;
5097     char *sname, *lname, *temp;
5098     char sbuf[8], lbuf[8];
5099 
5100     LookupUIInit();
5101     scripttags = SFScriptsInLookups(sf,-1);
5102     if ( scripttags==NULL )
5103 return( NULL );
5104 
5105     for ( k=0; k<2; ++k ) {
5106 	cnt = 0;
5107 	for ( s=0; scripttags[s]!=0; ++s ) {
5108 	    if ( k ) {
5109 		for ( i=0; scripts[i].text!=NULL; ++i )
5110 		    if ( scripttags[s] == (intpt) (scripts[i].userdata))
5111 		break;
5112 		sname = (char *) (scripts[i].text);
5113 		sbuf[0] = scripttags[s]>>24;
5114 		sbuf[1] = scripttags[s]>>16;
5115 		sbuf[2] = scripttags[s]>>8;
5116 		sbuf[3] = scripttags[s];
5117 		sbuf[4] = 0;
5118 		if ( sname==NULL )
5119 		    sname = sbuf;
5120 	    }
5121 	    langtags = SFLangsInScript(sf,-1,scripttags[s]);
5122 	    /* This one can't be NULL */
5123 	    for ( l=0; langtags[l]!=0; ++l ) {
5124 		if ( k ) {
5125 		    for ( i=0; languages[i].text!=NULL; ++i )
5126 			if ( langtags[l] == (intpt) (languages[i].userdata))
5127 		    break;
5128 		    lname = (char *) (languages[i].text);
5129 		    lbuf[0] = langtags[l]>>24;
5130 		    lbuf[1] = langtags[l]>>16;
5131 		    lbuf[2] = langtags[l]>>8;
5132 		    lbuf[3] = langtags[l];
5133 		    lbuf[4] = 0;
5134 		    if ( lname==NULL )
5135 			lname = lbuf;
5136 		    temp = malloc(strlen(sname)+strlen(lname)+3);
5137 		    strcpy(temp,sname); strcat(temp,"{"); strcat(temp,lname); strcat(temp,"}");
5138 		    ret[cnt].text = (unichar_t *) temp;
5139 		    ret[cnt].text_is_1byte = true;
5140 		    temp = malloc(11);
5141 		    strcpy(temp,sbuf); temp[4] = '{'; strcpy(temp+5,lbuf); temp[9]='}'; temp[10] = 0;
5142 		    ret[cnt].userdata = temp;
5143 		}
5144 		++cnt;
5145 	    }
5146 	    free(langtags);
5147 	}
5148 	if ( !k )
5149 	    ret = calloc((cnt+1),sizeof(GTextInfo));
5150     }
5151     free(scripttags);
5152 return( ret );
5153 }
5154 
5155 #define metricsicon_width 16
5156 #define metricsicon_height 16
5157 static unsigned char metricsicon_bits[] = {
5158    0x04, 0x10, 0xf0, 0x03, 0x24, 0x12, 0x20, 0x00, 0x24, 0x10, 0xe0, 0x00,
5159    0x24, 0x10, 0x20, 0x00, 0x24, 0x10, 0x20, 0x00, 0x74, 0x10, 0x00, 0x00,
5160    0x55, 0x55, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00};
5161 
5162 static int metricsview_ready = false;
5163 
MetricsViewFinish()5164 static void MetricsViewFinish() {
5165   if (!metricsview_ready) return;
5166   mb2FreeGetText(mblist);
5167 }
5168 
MetricsViewFinishNonStatic()5169 void MetricsViewFinishNonStatic() {
5170   MetricsViewFinish();
5171 }
5172 
MetricsViewInit(void)5173 static void MetricsViewInit(void ) {
5174     // static int inited = false; // superseded by metricsview_ready.
5175     if (metricsview_ready) return;
5176 	mv_text_init[2].text = (unichar_t *) _((char *) mv_text_init[2].text);
5177 	mb2DoGetText(mblist);
5178 	MVColInit();
5179     atexit(&MetricsViewFinishNonStatic);
5180 }
5181 
MetricsViewCreate(FontView * fv,SplineChar * sc,BDFFont * bdf)5182 MetricsView *MetricsViewCreate(FontView *fv,SplineChar *sc,BDFFont *bdf) {
5183     GRect pos;
5184     GWindow gw;
5185     GWindowAttrs wattrs;
5186     GGadgetData gd;
5187     GRect gsize;
5188     MetricsView *mv = calloc(1,sizeof(MetricsView));
5189     FontRequest rq;
5190     static GWindow icon = NULL;
5191     extern int _GScrollBar_Width;
5192     // The maximum length of a glyph's name is 31 chars:
5193     // https://docs.microsoft.com/en-us/typography/opentype/spec/recom#post-table
5194 #define MAXGLYPHNAME_LEN 31
5195     unsigned int selectmax = fvmv_selectmax < 0 ? fv->b.sf->glyphcnt : fvmv_selectmax;
5196     char *buf = malloc(selectmax * (MAXGLYPHNAME_LEN + 1) + 1);
5197     char *pt;
5198     GTextInfo label;
5199     int i,j,cnt;
5200     int as,ds,ld;
5201     static GFont *mvfont=NULL;
5202     SplineFont *master = fv->b.sf->cidmaster ? fv->b.sf->cidmaster : fv->b.sf;
5203 
5204     MetricsViewInit();
5205 
5206     if ( icon==NULL )
5207 	icon = GDrawCreateBitmap(NULL,metricsicon_width,metricsicon_height,metricsicon_bits);
5208 
5209     mv->fv = fv;
5210     mv->sf = fv->b.sf;
5211     mv->bdf = bdf;
5212     mv->showgrid = mvshowgrid;
5213     mv->antialias = mv_antialias;
5214     mv->scale_index = SCALE_INDEX_NORMAL;
5215     mv->next = master->metrics;
5216     master->metrics = mv;
5217     mv->layer = fv->b.active_layer;
5218     mv->type = mv_type;
5219     mv->pixelsize_set_by_window = true;
5220     mv->dpi = 72;
5221 
5222     memset(&wattrs,0,sizeof(wattrs));
5223     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_icon;
5224     wattrs.event_masks = ~(0);
5225     wattrs.cursor = ct_mypointer;
5226     char* titlebuf = MVWindowTitle(mv);
5227     wattrs.utf8_window_title = titlebuf;
5228     wattrs.icon = icon;
5229     pos.x = pos.y = 0;
5230     pos.width = mv_width;
5231     pos.height = mv_height;
5232     mv->gw = gw = GDrawCreateTopWindow(NULL,&pos,mv_e_h,mv,&wattrs);
5233     free(titlebuf);
5234     mv->width = pos.width; mv->height = pos.height;
5235     mv->gwgic = GDrawCreateInputContext(mv->gw,gic_root|gic_orlesser);
5236     GDrawSetGIC(gw,mv->gwgic,0,20);
5237     GDrawSetWindowTypeName(mv->gw, "MetricsView");
5238 
5239     memset(&gd,0,sizeof(gd));
5240     gd.flags = gg_visible | gg_enabled;
5241     helplist[0].invoke = MVMenuContextualHelp;
5242     gd.u.menu2 = mblist;
5243     mv->mb = GMenu2BarCreate( gw, &gd, NULL);
5244     GGadgetGetSize(mv->mb,&gsize);
5245     mv->mbh = gsize.height;
5246 
5247     gd.pos.height = GDrawPointsToPixels(gw,_GScrollBar_Width);
5248     gd.pos.y = pos.height-gd.pos.height;
5249     gd.pos.x = 0; gd.pos.width = pos.width;
5250     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
5251     mv->hsb = GScrollBarCreate(gw,&gd,mv);
5252     GGadgetGetSize(mv->hsb,&gsize);
5253     mv->sbh = gsize.height;
5254     mv->dwidth = mv->width-mv->sbh;
5255 
5256     gd.pos.width = mv->sbh;
5257     gd.pos.y = 0; gd.pos.height = pos.height;	/* we'll fix these later */
5258     gd.pos.x = pos.width-gd.pos.width;
5259     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_sb_vert;
5260     mv->vsb = GScrollBarCreate(gw,&gd,mv);
5261 
5262     if ( mvfont==NULL ) {
5263 	memset(&rq,0,sizeof(rq));
5264 	rq.utf8_family_name = SANS_UI_FAMILIES;
5265 	rq.point_size = -12;
5266 	rq.weight = 400;
5267 	mvfont = GDrawInstanciateFont(gw,&rq);
5268 	mvfont = GResourceFindFont("MetricsView.Font",mvfont);
5269     }
5270     mv->font = mvfont;
5271     GDrawWindowFontMetrics(gw,mv->font,&as,&ds,&ld);
5272     mv->fh = as+ds; mv->as = as;
5273 
5274     pt = buf;
5275     // +1 because mv->chars is 0 terminated
5276     mv->chars = calloc(mv->cmax=selectmax+1,sizeof(SplineChar *));
5277     if ( sc!=NULL ) {
5278 	mv->chars[mv->clen++] = sc;
5279     } else {
5280 	EncMap *map = fv->b.map;
5281 	for ( j=1; (j<=fv->sel_index || j<1) && mv->clen<selectmax; ++j ) {
5282 	    for ( i=0; i<map->enccount && mv->clen<selectmax; ++i ) {
5283 		int gid = map->map[i];
5284 		if ( gid!=-1 && fv->b.selected[i]==j && fv->b.sf->glyphs[gid]!=NULL ) {
5285 		    mv->chars[mv->clen++] = fv->b.sf->glyphs[gid];
5286 		}
5287 	    }
5288 	}
5289     }
5290     mv->chars[mv->clen] = NULL;
5291 
5292     for ( cnt=0; cnt<mv->clen; ++cnt ) {
5293         int cp = mv->chars[cnt]->unicodeenc;
5294         if ( cp != -1 && strchr("#[]/\\", cp) == NULL)
5295 	    pt = utf8_idpb(pt,cp,0);
5296         else {
5297             *pt = '/'; pt++;
5298             strcpy(pt, mv->chars[cnt]->name);
5299             pt += strlen(mv->chars[cnt]->name);
5300         }
5301     }
5302     *pt = '\0';
5303 
5304     memset(&gd,0,sizeof(gd));
5305     memset(&label,0,sizeof(label));
5306     gd.pos.y = mv->mbh+2; gd.pos.x = 10;
5307     gd.pos.width = GDrawPointsToPixels(mv->gw,100);
5308     gd.label = &label;
5309     label.text = (unichar_t *) "DFLT{dflt}";
5310     label.text_is_1byte = true;
5311     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
5312     gd.u.list = mv->scriptlangs = SLOfFont(mv->sf);
5313     gd.handle_controlevent = MV_ScriptLangChanged;
5314     mv->script = GListFieldCreate(gw,&gd,mv);
5315     GGadgetGetSize(mv->script,&gsize);
5316     mv->topend = gsize.y + gsize.height + 2;
5317 
5318     gd.pos.x = gd.pos.x+gd.pos.width+10;
5319     gd.pos.width = GDrawPointsToPixels(mv->gw,200);
5320     gd.pos.height = gsize.height;
5321     label.text = (unichar_t *) buf;
5322     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_text_xim;
5323     gd.handle_controlevent = MV_TextChanged;
5324     gd.u.list = mv_text_init;
5325     mv->text = GListFieldCreate(gw,&gd,mv);
5326 
5327     // Up and Down buttons for moving through the word list.
5328     {
5329         GTextInfo label[9];
5330         GGadgetData xgd = gd;
5331         gd.pos.width += 2 * xgd.pos.height + 4;
5332         memset(label, '\0', sizeof(GTextInfo));
5333         xgd.pos.x += xgd.pos.width + 2;
5334         xgd.pos.width = xgd.pos.height;
5335         xgd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
5336         xgd.handle_controlevent = MVMoveToPrevInWordList;
5337         xgd.label = &label[0];
5338         label[0].text = (unichar_t *) "⇞";
5339         label[0].text_is_1byte = true;
5340         mv->textPrev = GButtonCreate(mv->gw,&xgd,mv);
5341         memset(label, '\0', sizeof(GTextInfo));
5342         xgd.pos.x += xgd.pos.width + 2;
5343         xgd.pos.width = xgd.pos.height;
5344         xgd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
5345         xgd.handle_controlevent = MVMoveToNextInWordList;
5346         xgd.label = &label[0];
5347         label[0].text = (unichar_t *) "⇟";
5348         label[0].text_is_1byte = true;
5349         mv->textNext = GButtonCreate(mv->gw,&xgd,mv);
5350     }
5351 
5352 
5353     gd.pos.x = gd.pos.x+gd.pos.width+10; --gd.pos.y;
5354     gd.pos.width += 30;
5355     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels;
5356     gd.handle_controlevent = MV_SubtableChanged;
5357     gd.label = NULL;
5358     gd.u.list = NULL;
5359     mv->subtable_list = GListButtonCreate(gw,&gd,mv);
5360     MVSetSubtables(master);
5361 
5362     gd.pos.y = mv->topend; gd.pos.x = 0;
5363     gd.flags = gg_visible|gg_enabled|gg_pos_in_pixels|gg_pos_use0|gg_list_multiplesel|gg_list_alphabetic;
5364     gd.pos.width = GDrawPointsToPixels(mv->gw,50);
5365     gd.handle_controlevent = MV_FeaturesChanged;
5366     mv->features = GListCreate(gw,&gd,mv);
5367     GListSetSBAlwaysVisible(mv->features,true);
5368     GListSetPopupCallback(mv->features,MV_FriendlyFeatures);
5369     mv->xstart = gd.pos.width;
5370 
5371     pos.x = mv->xstart; pos.width = mv->dwidth - mv->xstart;
5372     pos.y = mv->topend+2; pos.height = mv->displayend - mv->topend - 2;
5373     memset(&wattrs,0,sizeof(wattrs));
5374     wattrs.mask = wam_events|wam_backcol;
5375     wattrs.background_color = view_bgcol;
5376     wattrs.event_masks = -1;
5377     wattrs.cursor = ct_mypointer;
5378     mv->v = GWidgetCreateSubWindow(mv->gw,&pos,mv_v_e_h,mv,&wattrs);
5379     GDrawSetWindowTypeName(mv->v, "MetricsView");
5380 
5381     MVSetFeatures(mv);
5382     MVMakeLabels(mv);
5383     MVResize(mv);
5384     GWidgetIndicateFocusGadget(mv->text);
5385 
5386     GDrawSetVisible(mv->v,true);
5387     GDrawSetVisible(gw,true);
5388     /*GWidgetHidePalettes();*/
5389     free(buf);
5390 return( mv );
5391 }
5392 
MetricsViewFree(MetricsView * mv)5393 void MetricsViewFree(MetricsView *mv) {
5394 
5395     if ( mv->scriptlangs!=NULL ) {
5396 	int i;
5397 	for ( i=0; mv->scriptlangs[i].text!=NULL ; ++i )
5398 	    free(mv->scriptlangs[i].userdata );
5399 	GTextInfoListFree(mv->scriptlangs);
5400     }
5401     BDFFontFree(mv->show);
5402     /* the fields will free themselves */
5403     free(mv->chars);
5404     free(mv->glyphs);
5405     free(mv->perchar);
5406     free(mv);
5407 }
5408 
MVRefreshAll(MetricsView * mv)5409 void MVRefreshAll(MetricsView *mv) {
5410 
5411     if ( mv!=NULL ) {
5412 	MVRemetric(mv);
5413 	GDrawRequestExpose(mv->v,NULL,false);
5414     }
5415 }
5416 
5417 /******************************************************************************/
MV_GlyphCnt(struct metricsview * mv)5418 static int MV_GlyphCnt(struct metricsview *mv) {
5419 return( mv->glyphcnt );
5420 }
5421 
MV_Glyph(struct metricsview * mv,int i)5422 static SplineChar *MV_Glyph(struct metricsview *mv,int i) {
5423     if ( i<0 || i>=mv->glyphcnt )
5424 return( NULL );
5425 
5426 return( mv->glyphs[i].sc );
5427 }
5428 
MV_ReKernAll(struct splinefont * sf)5429 static void MV_ReKernAll(struct splinefont *sf) {
5430     MetricsView *mv;
5431 
5432     for ( mv=sf->metrics; mv!=NULL; mv=mv->next )
5433 	MVReKern(mv);
5434 }
5435 
MV_ReFeatureAll(struct splinefont * sf)5436 static void MV_ReFeatureAll(struct splinefont *sf) {
5437     MetricsView *mv;
5438 
5439     MVSetSubtables(sf);
5440     for ( mv=sf->metrics; mv!=NULL; mv=mv->next )
5441 	MVSetFeatures(mv);
5442 }
5443 
MV_CloseAll(struct splinefont * sf)5444 static void MV_CloseAll(struct splinefont *sf) {
5445     MetricsView *mv, *mvnext;
5446     for ( mv=sf->metrics; mv!=NULL; mv=mvnext ) {
5447 	mvnext = mv->next;
5448 	GDrawDestroyWindow(mv->gw);
5449     }
5450     GDrawSync(NULL);
5451     GDrawProcessPendingEvents(NULL);
5452 }
5453 
5454 struct mv_interface gdraw_mv_interface = {
5455     MV_GlyphCnt,
5456     MV_Glyph,
5457     MV_ReKernAll,
5458     MV_ReFeatureAll,
5459     MV_CloseAll
5460 };
5461 
5462 static struct resed metricsview_re[] = {
5463     {N_("Advance Width Col"), "AdvanceWidthColor", rt_color, &widthcol, N_("Color used to draw the advance width line of a glyph"), NULL, { 0 }, 0, 0 },
5464     {N_("Italic Advance Col"), "ItalicAdvanceColor", rt_color, &widthcol, N_("Color used to draw the italic advance width line of a glyph"), NULL, { 0 }, 0, 0 },
5465     {N_("Kern Line Color"), "KernLineColor", rt_color, &kernlinecol, N_("Color used to draw the kerning line"), NULL, { 0 }, 0, 0 },
5466     {N_("Side Bearing Color"), "SideBearingLineColor", rt_color, &rbearinglinecol, N_("Color used to draw the left side bearing"), NULL, { 0 }, 0, 0 },
5467     {N_("Selected Glyph Col"), "SelectedGlyphColor", rt_color, &selglyphcol, N_("Color used to mark the selected glyph"), NULL, { 0 }, 0, 0 },
5468     RESED_EMPTY
5469 };
5470 extern GResInfo view_ri;
5471 GResInfo metricsview_ri = {
5472     &view_ri, NULL,NULL, NULL,
5473     NULL,
5474     NULL,
5475     NULL,
5476     metricsview_re,
5477     N_("MetricsView"),
5478     N_("This window displays metrics information about a font"),
5479     "MetricsView",
5480     "fontforge",
5481     false,
5482     0,
5483     NULL,
5484     GBOX_EMPTY,
5485     NULL,
5486     NULL,
5487     NULL
5488 };
5489 
5490 
MVSelectFirstKerningTable(struct metricsview * mv)5491 void MVSelectFirstKerningTable(struct metricsview *mv)
5492 {
5493     /* SplineFont *sf = mv->sf; */
5494     /* printf("MVSelectFirstKerningTable() kerns:%p\n", sf->kerns ); */
5495     /* if( sf->kerns ) */
5496     /* { */
5497     /* 	printf("MVSelectFirstKerningTable() kerns.next:%p\n", sf->kerns->next ); */
5498     /* 	printf("MVSelectFirstKerningTable() kerns.subt:%p\n", sf->kerns->subtable ); */
5499     /* } */
5500 
5501     //
5502     // if nothing selected, then select the first entry.
5503     //
5504     if( GGadgetGetFirstListSelectedItem(mv->features) >= 0 )
5505     {
5506 	return;
5507     }
5508 
5509     GTextInfo **ti=NULL;
5510     int32 len;
5511     ti = GGadgetGetList(mv->features,&len);
5512     GGadgetSelectOneListItem(mv->features,0);
5513     MVRemetric(mv);
5514     GDrawRequestExpose(mv->v,NULL,false);
5515 }
5516 
5517 
5518