LCOV - code coverage report
Current view: top level - fontforgeexe - metricsview.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 3 3423 0.1 %
Date: 2017-08-04 Functions: 1 179 0.6 %

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

Generated by: LCOV version 1.10