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