LCOV - code coverage report
Current view: top level - fontforgeexe - groupsdlg.c (source / functions) Hit Total Coverage
Test: FontForge coverage report 2017-08-04 01:21:11+02:00 (commit d35f7e4107a9e1db65cce47c468fcc914cecb8fd) Lines: 0 1095 0.0 %
Date: 2017-08-04 Functions: 0 43 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2005-2012 by George Williams */
       2             : /*
       3             :  * Redistribution and use in source and binary forms, with or without
       4             :  * modification, are permitted provided that the following conditions are met:
       5             : 
       6             :  * Redistributions of source code must retain the above copyright notice, this
       7             :  * list of conditions and the following disclaimer.
       8             : 
       9             :  * Redistributions in binary form must reproduce the above copyright notice,
      10             :  * this list of conditions and the following disclaimer in the documentation
      11             :  * and/or other materials provided with the distribution.
      12             : 
      13             :  * The name of the author may not be used to endorse or promote products
      14             :  * derived from this software without specific prior written permission.
      15             : 
      16             :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
      17             :  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
      18             :  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
      19             :  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      20             :  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      21             :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
      22             :  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
      23             :  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      24             :  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
      25             :  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      26             :  */
      27             : #include "fontforgeui.h"
      28             : #include "fvfonts.h"
      29             : #include "groups.h"
      30             : #include "namelist.h"
      31             : #include "splineutil.h"
      32             : #include <unistd.h>
      33             : #include <ustring.h>
      34             : #include <utype.h>
      35             : #include <gkeysym.h>
      36             : #include <math.h>
      37             : 
      38             : 
      39             : 
      40             : /******************************************************************************/
      41             : /******************************** Group Widget ********************************/
      42             : /******************************************************************************/
      43             : 
      44             : #define COLOR_CHOOSE    (-10)
      45             : static GTextInfo std_colors[] = {
      46             :     { (unichar_t *) N_("Select by Color"), NULL, 0, 0, (void *) COLOR_DEFAULT, NULL, 0, 1, 0, 0, 0, 0, 1, 0, 0, '\0' },
      47             :     { (unichar_t *) N_("Color|Choose..."), NULL, 0, 0, (void *) COLOR_CHOOSE, NULL, 0, 1, 0, 0, 0, 0, 1, 0, 0, '\0' },
      48             :     { (unichar_t *) N_("Color|Default"), &def_image, 0, 0, (void *) COLOR_DEFAULT, NULL, 0, 1, 0, 0, 0, 0, 1, 0, 0, '\0' },
      49             :     { NULL, &white_image, 0, 0, (void *) 0xffffff, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
      50             :     { NULL, &red_image, 0, 0, (void *) 0xff0000, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
      51             :     { NULL, &green_image, 0, 0, (void *) 0x00ff00, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
      52             :     { NULL, &blue_image, 0, 0, (void *) 0x0000ff, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
      53             :     { NULL, &yellow_image, 0, 0, (void *) 0xffff00, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
      54             :     { NULL, &cyan_image, 0, 0, (void *) 0x00ffff, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
      55             :     { NULL, &magenta_image, 0, 0, (void *) 0xff00ff, NULL, 0, 1, 0, 0, 0, 0, 0, 0, 0, '\0' },
      56             :     GTEXTINFO_EMPTY
      57             : };
      58             : 
      59             : struct groupdlg {
      60             :     unsigned int oked: 1;
      61             :     unsigned int done: 1;
      62             :     unsigned int select_many: 1;
      63             :         /* define groups can only select one group at a time, select/restrict */
      64             :         /*  to groups can select multiple things */
      65             :     unsigned int select_kids_too: 1;
      66             :         /* When we select a parent group do we want to select all the kids? */
      67             :     Group *root;
      68             :     Group *oldsel;
      69             :     int open_cnt, lines_page, off_top, off_left, page_width, bmargin;
      70             :     int maxl;
      71             :     GWindow gw,v;
      72             :     GGadget *vsb, *hsb, *cancel, *ok, *compact;
      73             :     GGadget *newsub, *delete, *line1, *gpnamelab, *gpname, *glyphslab, *glyphs;
      74             :     GGadget *idlab, *idname, *iduni, *set, *select, *unique, *colour, *line2;
      75             :     int fh, as;
      76             :     GFont *font;
      77             :     FontView *fv;
      78             :     void (*select_callback)(struct groupdlg *);
      79             :     GTimer *showchange;
      80             : };
      81             : 
      82             : extern int _GScrollBar_Width;
      83             : 
      84           0 : static Group *GroupFindLPos(Group *group,int lpos,int *depth) {
      85             :     int i;
      86             : 
      87             :     for (;;) {
      88           0 :         if ( group->lpos==lpos )
      89           0 : return( group );
      90           0 :         if ( !group->open )
      91           0 : return( NULL );
      92           0 :         for ( i=0; i<group->kid_cnt-1; ++i ) {
      93           0 :             if ( lpos<group->kids[i+1]->lpos )
      94           0 :         break;
      95             :         }
      96           0 :         group = group->kids[i];
      97           0 :         ++*depth;
      98           0 :     }
      99             : }
     100             : 
     101           0 : static int GroupPosInParent(Group *group) {
     102           0 :     Group *parent = group->parent;
     103             :     int i;
     104             : 
     105           0 :     if ( parent==NULL )
     106           0 : return( 0 );
     107           0 :     for ( i=0; i<parent->kid_cnt; ++i )
     108           0 :         if ( parent->kids[i]==group )
     109           0 : return( i );
     110             : 
     111           0 : return( -1 );
     112             : }
     113             : 
     114           0 : static Group *GroupNext(Group *group,int *depth) {
     115           0 :     if ( group->open && group->kids ) {
     116           0 :         ++*depth;
     117           0 : return( group->kids[0] );
     118             :     }
     119             :     for (;;) {
     120             :         int pos;
     121           0 :         if ( group->parent==NULL )
     122           0 : return( NULL );
     123           0 :         pos = GroupPosInParent(group);
     124           0 :         if ( pos+1<group->parent->kid_cnt )
     125           0 : return( group->parent->kids[pos+1] );
     126           0 :         group = group->parent;
     127           0 :         --*depth;
     128           0 :     }
     129             : }
     130             : 
     131           0 : static Group *GroupPrev(struct groupdlg *grp, Group *group,int *depth) {
     132             :     int pos;
     133             : 
     134           0 :     while ( group->parent!=NULL && group==group->parent->kids[0] ) {
     135           0 :         group = group->parent;
     136           0 :         --*depth;
     137             :     }
     138           0 :     if ( group->parent==NULL )
     139           0 : return( NULL );
     140           0 :     pos = GroupPosInParent(group);
     141           0 :     group = group->parent->kids[pos-1];
     142           0 :     while ( group->open ) {
     143           0 :         group = group->kids[group->kid_cnt-1];
     144           0 :         ++*depth;
     145             :     }
     146           0 : return( group );
     147             : }
     148             : 
     149           0 : static int _GroupSBSizes(struct groupdlg *grp, Group *group, int lpos, int depth) {
     150             :     int i, len;
     151             : 
     152           0 :     group->lpos = lpos++;
     153             : 
     154           0 :     len = 5+8*depth+ grp->as + 5 + GDrawGetText8Width(grp->v,group->name,-1);
     155           0 :     if ( group->glyphs!=NULL )
     156           0 :         len += 5 + GDrawGetText8Width(grp->v,group->glyphs,-1);
     157           0 :     if ( len > grp->maxl )
     158           0 :         grp->maxl = len;
     159             : 
     160           0 :     if ( group->open ) {
     161           0 :         for ( i=0; i< group->kid_cnt; ++i )
     162           0 :             lpos = _GroupSBSizes(grp,group->kids[i],lpos,depth+1);
     163             :     }
     164           0 : return( lpos );
     165             : }
     166             : 
     167           0 : static int GroupSBSizes(struct groupdlg *grp) {
     168             :     int lpos;
     169             : 
     170           0 :     grp->maxl = 0;
     171           0 :     GDrawSetFont(grp->v,grp->font);
     172           0 :     lpos = _GroupSBSizes(grp,grp->root,0,0);
     173           0 :     grp->maxl += 5;          /* margin */
     174             : 
     175           0 :     GScrollBarSetBounds(grp->vsb,0,lpos,grp->lines_page);
     176           0 :     GScrollBarSetBounds(grp->hsb,0,grp->maxl,grp->page_width);
     177           0 :     grp->open_cnt = lpos;
     178           0 : return( lpos );
     179             : }
     180             : 
     181           0 : static void GroupSelectKids(Group *group,int sel) {
     182             :     int i;
     183             : 
     184           0 :     group->selected = sel;
     185           0 :     for ( i=0; i<group->kid_cnt; ++i )
     186           0 :         GroupSelectKids(group->kids[i],sel);
     187           0 : }
     188             : 
     189           0 : static void GroupDeselectAllBut(Group *root,Group *group) {
     190             :     int i;
     191             : 
     192           0 :     if ( root!=group )
     193           0 :         root->selected = false;
     194           0 :     for ( i=0; i<root->kid_cnt; ++i )
     195           0 :         GroupDeselectAllBut(root->kids[i],group);
     196           0 : }
     197             : 
     198           0 : static Group *_GroupCurrentlySelected(Group *group) {
     199             :     int i;
     200             :     Group *sel;
     201             : 
     202           0 :     if ( group->selected )
     203           0 : return( group );
     204           0 :     for ( i=0; i<group->kid_cnt; ++i ) {
     205           0 :         sel = _GroupCurrentlySelected(group->kids[i]);
     206           0 :         if ( sel!=NULL )
     207           0 : return( sel );
     208             :     }
     209           0 : return( NULL );
     210             : }
     211             : 
     212           0 : static Group *GroupCurrentlySelected(struct groupdlg *grp) {
     213             : 
     214           0 :     if ( grp->select_many )
     215           0 : return( NULL );
     216           0 : return( _GroupCurrentlySelected(grp->root));
     217             : }
     218             : 
     219           0 : static void GroupWExpose(struct groupdlg *grp,GWindow pixmap,GRect *rect) {
     220             :     int depth, y, len;
     221             :     Group *group;
     222             :     GRect r;
     223             :     Color fg;
     224             : 
     225           0 :     GDrawFillRect(pixmap,rect,GDrawGetDefaultBackground(NULL));
     226           0 :     GDrawSetLineWidth(pixmap,0);
     227             : 
     228           0 :     r.height = r.width = grp->as;
     229           0 :     y = (rect->y/grp->fh) * grp->fh + grp->as;
     230           0 :     depth=0;
     231           0 :     group = GroupFindLPos(grp->root,rect->y/grp->fh+grp->off_top,&depth);
     232           0 :     GDrawSetFont(pixmap,grp->font);
     233           0 :     while ( group!=NULL ) {
     234           0 :         r.y = y-grp->as+1;
     235           0 :         r.x = 5+8*depth - grp->off_left;
     236           0 :         fg = group->selected ? 0xff0000 : 0x000000;
     237           0 :         if ( group->glyphs==NULL ) {
     238           0 :             GDrawDrawRect(pixmap,&r,fg);
     239           0 :             GDrawDrawLine(pixmap,r.x+2,r.y+grp->as/2,r.x+grp->as-2,r.y+grp->as/2,
     240             :                     fg);
     241           0 :             if ( !group->open )
     242           0 :                 GDrawDrawLine(pixmap,r.x+grp->as/2,r.y+2,r.x+grp->as/2,r.y+grp->as-2,
     243             :                         fg);
     244             :         }
     245           0 :         len = GDrawDrawText8(pixmap,r.x+r.width+5,y,group->name,-1,fg);
     246           0 :         if ( group->glyphs )
     247           0 :             GDrawDrawText8(pixmap,r.x+r.width+5+len+5,y,group->glyphs,-1,fg);
     248           0 :         group = GroupNext(group,&depth);
     249           0 :         y += grp->fh;
     250           0 :         if ( y-grp->fh>rect->y+rect->height )
     251           0 :     break;
     252             :     }
     253           0 : }
     254             : 
     255           0 : static void GroupWMouse(struct groupdlg *grp,GEvent *event) {
     256             :     int x;
     257           0 :     int depth=0;
     258             :     Group *group;
     259             : 
     260           0 :     group = GroupFindLPos(grp->root,event->u.mouse.y/grp->fh+grp->off_top,&depth);
     261           0 :     if ( group==NULL )
     262           0 : return;
     263             : 
     264           0 :     x = 5+8*depth - grp->off_left;
     265           0 :     if ( event->u.mouse.x<x )
     266           0 : return;
     267           0 :     if ( event->u.mouse.x<=x+grp->as ) {
     268           0 :         if ( group->glyphs != NULL )
     269           0 : return;
     270           0 :         group->open = !group->open;
     271           0 :         GroupSBSizes(grp);
     272             :     } else {
     273           0 :         group->selected = !group->selected;
     274           0 :         if ( grp->select_kids_too )
     275           0 :             GroupSelectKids(group,group->selected);
     276           0 :         else if ( group->selected && !grp->select_many )
     277           0 :             GroupDeselectAllBut(grp->root,group);
     278           0 :         if ( grp->select_callback!=NULL )
     279           0 :             (grp->select_callback)(grp);
     280             :     }
     281           0 :     GDrawRequestExpose(grp->v,NULL,false);
     282             : }
     283             : 
     284           0 : static void GroupScroll(struct groupdlg *grp,struct sbevent *sb) {
     285           0 :     int newpos = grp->off_top;
     286             : 
     287           0 :     switch( sb->type ) {
     288             :       case et_sb_top:
     289           0 :         newpos = 0;
     290           0 :       break;
     291             :       case et_sb_uppage:
     292           0 :         newpos -= grp->lines_page;
     293           0 :       break;
     294             :       case et_sb_up:
     295           0 :         --newpos;
     296           0 :       break;
     297             :       case et_sb_down:
     298           0 :         ++newpos;
     299           0 :       break;
     300             :       case et_sb_downpage:
     301           0 :         newpos += grp->lines_page;
     302           0 :       break;
     303             :       case et_sb_bottom:
     304           0 :         newpos = grp->open_cnt-grp->lines_page;
     305           0 :       break;
     306             :       case et_sb_thumb:
     307             :       case et_sb_thumbrelease:
     308           0 :         newpos = sb->pos;
     309           0 :       break;
     310             :     }
     311           0 :     if ( newpos>grp->open_cnt-grp->lines_page )
     312           0 :         newpos = grp->open_cnt-grp->lines_page;
     313           0 :     if ( newpos<0 ) newpos =0;
     314           0 :     if ( newpos!=grp->off_top ) {
     315           0 :         int diff = newpos-grp->off_top;
     316           0 :         grp->off_top = newpos;
     317           0 :         GScrollBarSetPos(grp->vsb,grp->off_top);
     318           0 :         GDrawScroll(grp->v,NULL,0,diff*grp->fh);
     319             :     }
     320           0 : }
     321             : 
     322             : 
     323           0 : static void GroupHScroll(struct groupdlg *grp,struct sbevent *sb) {
     324           0 :     int newpos = grp->off_left;
     325             : 
     326           0 :     switch( sb->type ) {
     327             :       case et_sb_top:
     328           0 :         newpos = 0;
     329           0 :       break;
     330             :       case et_sb_uppage:
     331           0 :         newpos -= grp->page_width;
     332           0 :       break;
     333             :       case et_sb_up:
     334           0 :         --newpos;
     335           0 :       break;
     336             :       case et_sb_down:
     337           0 :         ++newpos;
     338           0 :       break;
     339             :       case et_sb_downpage:
     340           0 :         newpos += grp->page_width;
     341           0 :       break;
     342             :       case et_sb_bottom:
     343           0 :         newpos = grp->maxl-grp->page_width;
     344           0 :       break;
     345             :       case et_sb_thumb:
     346             :       case et_sb_thumbrelease:
     347           0 :         newpos = sb->pos;
     348           0 :       break;
     349             :     }
     350           0 :     if ( newpos>grp->maxl-grp->page_width )
     351           0 :         newpos = grp->maxl-grp->page_width;
     352           0 :     if ( newpos<0 ) newpos =0;
     353           0 :     if ( newpos!=grp->off_left ) {
     354           0 :         int diff = newpos-grp->off_left;
     355           0 :         grp->off_left = newpos;
     356           0 :         GScrollBarSetPos(grp->hsb,grp->off_left);
     357           0 :         GDrawScroll(grp->v,NULL,-diff,0);
     358             :     }
     359           0 : }
     360             : 
     361           0 : static void GroupResize(struct groupdlg *grp,GEvent *event) {
     362             :     GRect size, wsize;
     363             :     int lcnt, offy;
     364           0 :     int sbsize = GDrawPointsToPixels(grp->gw,_GScrollBar_Width);
     365             : 
     366           0 :     GDrawGetSize(grp->gw,&size);
     367           0 :     lcnt = (size.height-grp->bmargin)/grp->fh;
     368           0 :     GGadgetResize(grp->vsb,sbsize,lcnt*grp->fh);
     369           0 :     GGadgetMove(grp->vsb,size.width-sbsize,0);
     370           0 :     GGadgetResize(grp->hsb,size.width-sbsize,sbsize);
     371           0 :     GGadgetMove(grp->hsb,0,lcnt*grp->fh);
     372           0 :     GDrawResize(grp->v,size.width-sbsize,lcnt*grp->fh);
     373           0 :     grp->page_width = size.width-sbsize;
     374           0 :     grp->lines_page = lcnt;
     375           0 :     GScrollBarSetBounds(grp->vsb,0,grp->open_cnt,grp->lines_page);
     376           0 :     GScrollBarSetBounds(grp->hsb,0,grp->maxl,grp->page_width);
     377             : 
     378           0 :     GGadgetGetSize(grp->cancel,&wsize);
     379           0 :     offy = size.height-wsize.height-6 - wsize.y;
     380           0 :     GGadgetMove(grp->cancel,size.width-wsize.width-30,  wsize.y+offy);
     381           0 :     GGadgetMove(grp->ok    ,                       30-3,wsize.y+offy-3);
     382           0 :     if ( grp->newsub!=NULL ) {
     383           0 :         GGadgetGetSize(grp->newsub,&wsize);
     384           0 :         GGadgetMove(grp->newsub,wsize.x,wsize.y+offy);
     385           0 :         GGadgetGetSize(grp->delete,&wsize);
     386           0 :         GGadgetMove(grp->delete,wsize.x,wsize.y+offy);
     387           0 :         GGadgetGetSize(grp->line1,&wsize);
     388           0 :         GGadgetMove(grp->line1,wsize.x,wsize.y+offy);
     389           0 :         GGadgetGetSize(grp->gpnamelab,&wsize);
     390           0 :         GGadgetMove(grp->gpnamelab,wsize.x,wsize.y+offy);
     391           0 :         GGadgetGetSize(grp->gpname,&wsize);
     392           0 :         GGadgetMove(grp->gpname,wsize.x,wsize.y+offy);
     393           0 :         GGadgetGetSize(grp->glyphslab,&wsize);
     394           0 :         GGadgetMove(grp->glyphslab,wsize.x,wsize.y+offy);
     395           0 :         GGadgetGetSize(grp->glyphs,&wsize);
     396           0 :         GGadgetMove(grp->glyphs,wsize.x,wsize.y+offy);
     397           0 :         GGadgetGetSize(grp->idlab,&wsize);
     398           0 :         GGadgetMove(grp->idlab,wsize.x,wsize.y+offy);
     399           0 :         GGadgetGetSize(grp->idname,&wsize);
     400           0 :         GGadgetMove(grp->idname,wsize.x,wsize.y+offy);
     401           0 :         GGadgetGetSize(grp->iduni,&wsize);
     402           0 :         GGadgetMove(grp->iduni,wsize.x,wsize.y+offy);
     403           0 :         GGadgetGetSize(grp->set,&wsize);
     404           0 :         GGadgetMove(grp->set,wsize.x,wsize.y+offy);
     405           0 :         GGadgetGetSize(grp->select,&wsize);
     406           0 :         GGadgetMove(grp->select,wsize.x,wsize.y+offy);
     407           0 :         GGadgetGetSize(grp->unique,&wsize);
     408           0 :         GGadgetMove(grp->unique,wsize.x,wsize.y+offy);
     409           0 :         GGadgetGetSize(grp->colour,&wsize);
     410           0 :         GGadgetMove(grp->colour,wsize.x,wsize.y+offy);
     411           0 :         GGadgetGetSize(grp->line2,&wsize);
     412           0 :         GGadgetMove(grp->line2,wsize.x,wsize.y+offy);
     413             :     } else {
     414           0 :         GGadgetGetSize(grp->compact,&wsize);
     415           0 :         GGadgetMove(grp->compact,wsize.x,wsize.y+offy);
     416             :     }
     417           0 :     GDrawRequestExpose(grp->v,NULL,true);
     418           0 :     GDrawRequestExpose(grp->gw,NULL,true);
     419           0 : }
     420             : 
     421           0 : static void GroupWChangeCurrent(struct groupdlg *grp,Group *current,Group *next ) {
     422           0 :     if ( current!=NULL )
     423           0 :         current->selected = false;
     424           0 :     next->selected = true;
     425           0 :     if ( next->lpos<grp->off_top || next->lpos>=grp->off_top+grp->lines_page ) {
     426           0 :         if ( next->lpos>=grp->off_top+grp->lines_page )
     427           0 :             grp->off_top = next->lpos;
     428             :         else {
     429           0 :             grp->off_top = next->lpos-grp->lines_page-1;
     430           0 :             if ( grp->off_top<0 ) grp->off_top = 0;
     431             :         }
     432           0 :         GScrollBarSetPos(grp->vsb,grp->off_top);
     433           0 :         GDrawRequestExpose(grp->v,NULL,false);
     434             :     }
     435           0 : }
     436             : 
     437           0 : static int GroupChar(struct groupdlg *grp,GEvent *event) {
     438           0 :     int depth = 0;
     439             :     int pos;
     440           0 :     Group *current = GroupCurrentlySelected(grp);
     441             : 
     442           0 :     switch (event->u.chr.keysym) {
     443             :       case GK_F1: case GK_Help:
     444           0 :         help("groups.html");
     445           0 : return( true );
     446             :       case GK_Return: case GK_KP_Enter:
     447           0 :         if ( current!=NULL ) {
     448           0 :             current->open = !current->open;
     449           0 :             GroupSBSizes(grp);
     450           0 :             GDrawRequestExpose(grp->v,NULL,false);
     451             :         }
     452           0 : return( true );
     453             :       case GK_Page_Down: case GK_KP_Page_Down:
     454           0 :         pos = grp->off_top+(grp->lines_page<=1?1:grp->lines_page-1);
     455           0 :         if ( pos >= grp->open_cnt-grp->lines_page )
     456           0 :             pos = grp->open_cnt-grp->lines_page;
     457           0 :         if ( pos<0 ) pos = 0;
     458           0 :         grp->off_top = pos;
     459           0 :         GScrollBarSetPos(grp->vsb,pos);
     460           0 :         GDrawRequestExpose(grp->v,NULL,false);
     461           0 : return( true );
     462             :       case GK_Down: case GK_KP_Down:
     463           0 :         if ( current==NULL || (event->u.chr.state&ksm_control)) {
     464           0 :             if ( grp->off_top<grp->open_cnt-1 ) {
     465           0 :                 ++grp->off_top;
     466           0 :                 GScrollBarSetPos(grp->vsb,grp->off_top);
     467           0 :                 GDrawScroll(grp->v,NULL,0,grp->fh);
     468             :             }
     469             :         } else
     470           0 :             GroupWChangeCurrent(grp,current,GroupNext(current,&depth));
     471           0 : return( true );
     472             :       case GK_Up: case GK_KP_Up:
     473           0 :         if ( current==NULL || (event->u.chr.state&ksm_control)) {
     474           0 :             if (grp->off_top!=0 ) {
     475           0 :                 --grp->off_top;
     476           0 :                 GScrollBarSetPos(grp->vsb,grp->off_top);
     477           0 :                 GDrawScroll(grp->v,NULL,0,-grp->fh);
     478             :             }
     479             :         } else
     480           0 :             GroupWChangeCurrent(grp,current,GroupPrev(grp,current,&depth));
     481           0 : return( true );
     482             :       case GK_Page_Up: case GK_KP_Page_Up:
     483           0 :         pos = grp->off_top-(grp->lines_page<=1?1:grp->lines_page-1);
     484           0 :         if ( pos<0 ) pos = 0;
     485           0 :         grp->off_top = pos;
     486           0 :         GScrollBarSetPos(grp->vsb,pos);
     487           0 :         GDrawRequestExpose(grp->v,NULL,false);
     488           0 : return( true );
     489             :       case GK_Left: case GK_KP_Left:
     490           0 :         if ( !grp->select_many && current!=NULL )
     491           0 :             GroupWChangeCurrent(grp,current,current->parent);
     492           0 : return( true );
     493             :       case GK_Right: case GK_KP_Right:
     494           0 :         if ( !grp->select_many && current != NULL && current->kid_cnt!=0 ) {
     495           0 :             if ( !current->open ) {
     496           0 :                 current->open = !current->open;
     497           0 :                 GroupSBSizes(grp);
     498             :             }
     499           0 :             GroupWChangeCurrent(grp,current,current->kids[0]);
     500             :         }
     501           0 : return( true );
     502             :       case GK_Home: case GK_KP_Home:
     503           0 :         if ( grp->off_top!=0 ) {
     504           0 :             grp->off_top = 0;
     505           0 :             GScrollBarSetPos(grp->vsb,0);
     506           0 :             GDrawRequestExpose(grp->v,NULL,false);
     507             :         }
     508           0 :         if ( !grp->select_many )
     509           0 :             GroupWChangeCurrent(grp,current,grp->root);
     510           0 : return( true );
     511             :       case GK_End: case GK_KP_End:
     512           0 :         pos = grp->open_cnt-grp->lines_page;
     513           0 :         if ( pos<0 ) pos = 0;
     514           0 :         if ( pos!=grp->off_top ) {
     515           0 :             grp->off_top = pos;
     516           0 :             GScrollBarSetPos(grp->vsb,pos);
     517           0 :             GDrawRequestExpose(grp->v,NULL,false);
     518             :         }
     519           0 :         if ( !grp->select_many )
     520           0 :             GroupWChangeCurrent(grp,current,GroupFindLPos(grp->root,grp->open_cnt-1,&depth));
     521           0 : return( true );
     522             :     }
     523           0 : return( false );
     524             : }
     525             : 
     526           0 : static int grpv_e_h(GWindow gw, GEvent *event) {
     527           0 :     struct groupdlg *grp = (struct groupdlg *) GDrawGetUserData(gw);
     528             : 
     529           0 :     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
     530           0 :             (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
     531           0 : return( GGadgetDispatchEvent(grp->vsb,event));
     532             :     }
     533             : 
     534           0 :     switch ( event->type ) {
     535             :       case et_expose:
     536           0 :         GroupWExpose(grp,gw,&event->u.expose.rect);
     537           0 :       break;
     538             :       case et_char:
     539           0 : return( GroupChar(grp,event));
     540             :       case et_mouseup:
     541           0 :         GroupWMouse(grp,event);
     542           0 :       break;
     543             :     }
     544           0 : return( true );
     545             : }
     546             : 
     547           0 : static void GroupWCreate(struct groupdlg *grp,GRect *pos) {
     548             :     FontRequest rq;
     549             :     int as, ds, ld;
     550             :     GGadgetCreateData gcd[5];
     551             :     GTextInfo label[4];
     552           0 :     int sbsize = GDrawPointsToPixels(NULL,_GScrollBar_Width);
     553             :     GWindowAttrs wattrs;
     554             :     static GFont *font=NULL;
     555             : 
     556           0 :     if ( font==NULL ) {
     557           0 :         memset(&rq,'\0',sizeof(rq));
     558           0 :         rq.utf8_family_name = SANS_UI_FAMILIES;
     559           0 :         rq.point_size = 12;
     560           0 :         rq.weight = 400;
     561           0 :         font = GDrawInstanciateFont(grp->gw,&rq);
     562           0 :         font = GResourceFindFont("Groups.Font",font);
     563             :     }
     564           0 :     grp->font = font;
     565           0 :     GDrawWindowFontMetrics(grp->gw,grp->font,&as,&ds,&ld);
     566           0 :     grp->fh = as+ds; grp->as = as;
     567             : 
     568           0 :     grp->lines_page = (pos->height-grp->bmargin)/grp->fh;
     569           0 :     grp->page_width = pos->width-sbsize;
     570           0 :     wattrs.mask = wam_events|wam_cursor/*|wam_bordwidth|wam_bordcol*/;
     571           0 :     wattrs.event_masks = ~0;
     572           0 :     wattrs.border_width = 1;
     573           0 :     wattrs.border_color = 0x000000;
     574           0 :     wattrs.cursor = ct_pointer;
     575           0 :     pos->x = 0; pos->y = 0;
     576           0 :     pos->width -= sbsize; pos->height = grp->lines_page*grp->fh;
     577           0 :     grp->v = GWidgetCreateSubWindow(grp->gw,pos,grpv_e_h,grp,&wattrs);
     578           0 :     GDrawSetVisible(grp->v,true);
     579             : 
     580           0 :     memset(&label,0,sizeof(label));
     581           0 :     memset(&gcd,0,sizeof(gcd));
     582             : 
     583           0 :     gcd[0].gd.pos.x = pos->width; gcd[0].gd.pos.y = 0;
     584           0 :     gcd[0].gd.pos.width = sbsize;
     585           0 :     gcd[0].gd.pos.height = pos->height;
     586           0 :     gcd[0].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels | gg_sb_vert;
     587           0 :     gcd[0].creator = GScrollBarCreate;
     588             : 
     589           0 :     gcd[1].gd.pos.x = 0; gcd[1].gd.pos.y = pos->height;
     590           0 :     gcd[1].gd.pos.height = sbsize;
     591           0 :     gcd[1].gd.pos.width = pos->width;
     592           0 :     gcd[1].gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels;
     593           0 :     gcd[1].creator = GScrollBarCreate;
     594             : 
     595           0 :     GGadgetsCreate(grp->gw,gcd);
     596           0 :     grp->vsb = gcd[0].ret;
     597           0 :     grp->hsb = gcd[1].ret;
     598           0 : }
     599             : 
     600             : /******************************************************************************/
     601             : /******************************** Group Dialogs *******************************/
     602             : /******************************************************************************/
     603             : 
     604           0 : static int FindDuplicateNumberInString(int seek,char *str) {
     605             :     char *start;
     606             : 
     607           0 :     if ( str==NULL )
     608           0 : return( false );
     609             : 
     610           0 :     while ( *str!='\0' ) {
     611           0 :         while ( *str==' ' ) ++str;
     612           0 :         start = str;
     613           0 :         while ( *str!=' ' && *str!='\0' ) ++str;
     614           0 :         if ( start==str )
     615           0 :     break;
     616           0 :         if ( (start[0]=='U' || start[0]=='u') && start[1]=='+' ) {
     617             :             char *end;
     618           0 :             int val = strtol(start+2,&end,16), val2=val;
     619           0 :             if ( *end=='-' ) {
     620           0 :                 if ( (end[1]=='u' || end[1]=='U') && end[2]=='+' )
     621           0 :                     end+=2;
     622           0 :                 val2 = strtol(end+1,NULL,16);
     623             :             }
     624           0 :             if ( seek>=val && seek<=val2 )
     625           0 : return( true );
     626             :         }
     627             :     }
     628           0 : return( false );
     629             : }
     630             : 
     631           0 : static int FindDuplicateNameInString(char *name,char *str) {
     632             :     char *start;
     633             : 
     634           0 :     if ( str==NULL )
     635           0 : return( false );
     636             : 
     637           0 :     while ( *str!='\0' ) {
     638           0 :         while ( *str==' ' ) ++str;
     639           0 :         start = str;
     640           0 :         while ( *str!=' ' && *str!='\0' ) ++str;
     641           0 :         if ( start==str )
     642           0 :     break;
     643           0 :         if ( (start[0]=='U' || start[0]=='u') && start[1]=='+' )
     644             :             /* Skip it */;
     645             :         else {
     646           0 :             int ch = *str;
     647           0 :             *str = '\0';
     648           0 :             if ( strcmp(name,start)==0 ) {
     649           0 :                 *str = ch;
     650           0 : return( true );
     651             :             }
     652           0 :             *str = ch;
     653             :         }
     654             :     }
     655           0 : return( false );
     656             : }
     657             : 
     658           0 : static Group *FindDuplicateNumber(Group *top,int val,Group *cur,char *str) {
     659             :     int i;
     660             :     Group *grp;
     661             : 
     662           0 :     if ( FindDuplicateNumberInString(val,str))
     663           0 : return( cur );
     664           0 :     if ( top==cur )
     665           0 : return( NULL );
     666           0 :     if ( FindDuplicateNumberInString(val,top->glyphs))
     667           0 : return( top );
     668           0 :     for ( i=0; i<top->kid_cnt; ++i )
     669           0 :         if ( (grp = FindDuplicateNumber(top->kids[i],val,cur,NULL))!=NULL )
     670           0 : return( grp );
     671             : 
     672           0 : return( NULL );
     673             : }
     674             : 
     675           0 : static Group *FindDuplicateName(Group *top,char *name,Group *cur,char *str) {
     676             :     int i;
     677             :     Group *grp;
     678             : 
     679           0 :     if ( FindDuplicateNameInString(name,str))
     680           0 : return( cur );
     681           0 :     if ( top==cur )
     682           0 : return( NULL );
     683           0 :     if ( FindDuplicateNameInString(name,top->glyphs))
     684           0 : return( top );
     685           0 :     for ( i=0; i<top->kid_cnt; ++i )
     686           0 :         if ( (grp = FindDuplicateName(top->kids[i],name,cur,NULL))!=NULL )
     687           0 : return( grp );
     688             : 
     689           0 : return( NULL );
     690             : }
     691             : 
     692           0 : static int GroupValidateGlyphs(Group *cur,char *g,const unichar_t *gu,int unique) {
     693             :     char *gpt, *start;
     694             :     Group *top, *grp;
     695             : 
     696           0 :     if ( gu!=NULL ) {
     697           0 :         for ( ; *gu!='\0'; ++gu ) {
     698           0 :             if ( *gu<' ' || *gu>=0x7f || *gu=='(' || *gu==')' ||
     699           0 :                     *gu=='[' || *gu==']' || *gu=='{' || *gu=='}' ||
     700           0 :                     *gu=='<' || *gu=='>' || *gu=='%' || *gu=='/' ) {
     701           0 :                 ff_post_error(_("Glyph names must be valid postscript names"),_("Glyph names must be valid postscript names"));
     702           0 : return( false );
     703             :             }
     704             :         }
     705             :     }
     706           0 :     if ( unique ) {             /* Can't use cur->unique because it hasn't been set yet */
     707           0 :         top = cur;
     708           0 :         while ( top->parent!=NULL && top->parent->unique )
     709           0 :             top = top->parent;
     710           0 :         for ( gpt=g; *gpt!='\0' ; ) {
     711           0 :             while ( *gpt==' ' ) ++gpt;
     712           0 :             start = gpt;
     713           0 :             while ( *gpt!=' ' && *gpt!='\0' ) ++gpt;
     714           0 :             if ( start==gpt )
     715           0 :         break;
     716           0 :             if ( (start[0]=='U' || start[0]=='u') && start[1]=='+' ) {
     717             :                 char *end;
     718           0 :                 int val = strtol(start+2,&end,16), val2=val;
     719           0 :                 if ( *end=='-' ) {
     720           0 :                     if ( (end[1]=='u' || end[1]=='U') && end[2]=='+' )
     721           0 :                         end+=2;
     722           0 :                     val2 = strtol(end+1,NULL,16);
     723             :                 }
     724           0 :                 if ( val2<val ) {
     725           0 :                     ff_post_error(_("Bad Range"),_("Bad Range, start (%1$04X) is greater than end (%2$04X)"), val, val2 );
     726           0 : return( false );
     727             :                 }
     728           0 :                 for ( ; val<=val2; ++val )
     729           0 :                     if ( (grp=FindDuplicateNumber(top,val,cur,gpt))!=NULL ) {
     730           0 :                         ff_post_error(_("Duplicate Name"),_("The code point U+%1$04X occurs in groups %2$.30s and %3$.30s"), val, cur->name, grp->name);
     731           0 : return( false );
     732             :                     }
     733             :             } else {
     734           0 :                 int ch = *gpt;
     735           0 :                 *gpt = '\0';
     736           0 :                 if ( (grp=FindDuplicateName(top,start,cur,ch!='\0'?gpt+1:NULL))!=NULL ) {
     737           0 :                     ff_post_error(_("Duplicate Name"),_("The glyph name \"%1$.30s\" occurs in groups %2$.30s and %3$.30s"), start, cur->name, grp->name);
     738           0 :                     *gpt = ch;
     739           0 : return( false );
     740             :                 }
     741           0 :                 *gpt = ch;
     742             :             }
     743             :         }
     744             :     }
     745           0 : return( true );
     746             : }
     747             : 
     748           0 : static int GroupSetKidsUnique(Group *group) {
     749             :     int i;
     750             : 
     751           0 :     group->unique = true;
     752           0 :     for ( i=0; i<group->kid_cnt; ++i )
     753           0 :         if ( !GroupSetKidsUnique(group->kids[i]))
     754           0 : return( false );
     755           0 :     if ( group->glyphs!=NULL ) {
     756           0 :         if ( !GroupValidateGlyphs(group,group->glyphs,NULL,true))
     757           0 : return( false );
     758             :     }
     759           0 : return( true );
     760             : }
     761             : 
     762           0 : static int GroupFinishOld(struct groupdlg *grp) {
     763           0 :     if ( grp->oldsel!=NULL ) {
     764           0 :         const unichar_t *gu = _GGadgetGetTitle(grp->glyphs);
     765           0 :         char *g = cu_copy(gu);
     766           0 :         int oldunique = grp->oldsel->unique;
     767             : 
     768           0 :         if ( !GroupValidateGlyphs(grp->oldsel,g,gu,GGadgetIsChecked(grp->unique))) {
     769           0 :             free(g);
     770           0 : return( false );
     771             :         }
     772             : 
     773           0 :         free(grp->oldsel->name);
     774           0 :         grp->oldsel->name = GGadgetGetTitle8(grp->gpname);
     775           0 :         free(grp->oldsel->glyphs);
     776           0 :         if ( *g=='\0' ) {
     777           0 :             grp->oldsel->glyphs = NULL;
     778           0 :             free(g);
     779             :         } else
     780           0 :             grp->oldsel->glyphs = g;
     781             :         
     782           0 :         grp->oldsel->unique = GGadgetIsChecked(grp->unique);
     783           0 :         if ( grp->oldsel->unique && !oldunique ) {
     784             :             /* The just set the unique bit. We must force it set in all */
     785             :             /*  kids. We really should check for uniqueness too!!!!! */
     786           0 :             if ( !GroupSetKidsUnique(grp->oldsel))
     787           0 : return( false );
     788             :         }
     789             :     }
     790           0 : return( true );
     791             : }
     792             : 
     793           0 : static void GroupSelected(struct groupdlg *grp) {
     794           0 :     Group *current = GroupCurrentlySelected(grp);
     795             : 
     796           0 :     if ( !GroupFinishOld(grp)) {
     797           0 :         if ( current!=NULL )
     798           0 :             current->selected=false;
     799           0 :         if ( grp->oldsel!=NULL )
     800           0 :             grp->oldsel->selected = true;
     801           0 : return;
     802             :     }
     803           0 :     grp->oldsel = current;
     804           0 :     if ( current == NULL ) {
     805           0 :         GGadgetSetEnabled(grp->newsub,false);
     806           0 :         GGadgetSetEnabled(grp->delete,false);
     807           0 :         GGadgetSetEnabled(grp->gpnamelab,false);
     808           0 :         GGadgetSetEnabled(grp->gpname,false);
     809           0 :         GGadgetSetEnabled(grp->glyphslab,false);
     810           0 :         GGadgetSetEnabled(grp->glyphs,false);
     811           0 :         GGadgetSetEnabled(grp->set,false);
     812           0 :         GGadgetSetEnabled(grp->select,false);
     813           0 :         GGadgetSetEnabled(grp->unique,false);
     814           0 :         GGadgetSetEnabled(grp->colour,false);
     815             :     } else {
     816           0 :         unichar_t *glyphs = uc_copy(current->glyphs);
     817           0 :         GGadgetSetTitle8(grp->gpname,current->name);
     818           0 :         if ( glyphs==NULL ) glyphs = uc_copy("");
     819           0 :         GGadgetSetTitle(grp->glyphs,glyphs);
     820           0 :         free(glyphs);
     821           0 :         GGadgetSetChecked(grp->unique,current->unique);
     822           0 :         GGadgetSetEnabled(grp->newsub,current->glyphs==NULL || *current->glyphs=='\0');
     823           0 :         GGadgetSetEnabled(grp->delete,current->parent!=NULL);
     824           0 :         GGadgetSetEnabled(grp->gpnamelab,true);
     825           0 :         GGadgetSetEnabled(grp->gpname,true);
     826           0 :         GGadgetSetEnabled(grp->glyphslab,current->kid_cnt==0);
     827           0 :         GGadgetSetEnabled(grp->glyphs,current->kid_cnt==0);
     828           0 :         GGadgetSetEnabled(grp->set,current->kid_cnt==0);
     829           0 :         GGadgetSetEnabled(grp->select,current->kid_cnt==0);
     830           0 :         GGadgetSetEnabled(grp->unique,current->parent==NULL || !current->parent->unique);
     831           0 :         GGadgetSetEnabled(grp->colour,current->kid_cnt==0);
     832             :     }
     833             : }
     834             : 
     835           0 : static void GroupShowChange(struct groupdlg *grp) {
     836           0 :     if ( GroupFinishOld(grp))
     837           0 :         GDrawRequestExpose(grp->v,NULL,false);
     838           0 :     grp->showchange = NULL;
     839           0 : }
     840             : 
     841           0 : static int Group_GlyphListChanged(GGadget *g, GEvent *e) {
     842           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_textchanged ) {
     843           0 :         struct groupdlg *grp = GDrawGetUserData(GGadgetGetWindow(g));
     844           0 :         const unichar_t *glyphs = _GGadgetGetTitle(g);
     845           0 :         GGadgetSetEnabled(grp->newsub,*glyphs=='\0');
     846           0 :         if ( grp->showchange!=NULL )
     847           0 :             GDrawCancelTimer(grp->showchange);
     848           0 :         grp->showchange = GDrawRequestTimer(grp->gw,500,0,NULL);
     849             :     }
     850           0 : return( true );
     851             : }
     852             : 
     853           0 : static int Group_ToSelection(GGadget *g, GEvent *e) {
     854           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
     855           0 :         struct groupdlg *grp = GDrawGetUserData(GGadgetGetWindow(g));
     856           0 :         const unichar_t *ret = _GGadgetGetTitle(grp->glyphs);
     857           0 :         SplineFont *sf = grp->fv->b.sf;
     858           0 :         FontView *fv = grp->fv;
     859             :         const unichar_t *end;
     860           0 :         int pos, found=-1;
     861             :         char *nm;
     862             : 
     863           0 :         GDrawSetVisible(fv->gw,true);
     864           0 :         GDrawRaise(fv->gw);
     865           0 :         memset(fv->b.selected,0,fv->b.map->enccount);
     866           0 :         while ( *ret ) {
     867           0 :             end = u_strchr(ret,' ');
     868           0 :             if ( end==NULL ) end = ret+u_strlen(ret);
     869           0 :             nm = cu_copybetween(ret,end);
     870           0 :             for ( ret = end; isspace(*ret); ++ret);
     871           0 :             if ( (nm[0]=='U' || nm[0]=='u') && nm[1]=='+' ) {
     872             :                 char *end;
     873           0 :                 int val = strtol(nm+2,&end,16), val2=val;
     874           0 :                 if ( *end=='-' ) {
     875           0 :                     if ( (end[1]=='u' || end[1]=='U') && end[2]=='+' )
     876           0 :                         end+=2;
     877           0 :                     val2 = strtol(end+1,NULL,16);
     878             :                 }
     879           0 :                 for ( ; val<=val2; ++val ) {
     880           0 :                     if (( pos = SFFindSlot(sf,fv->b.map,val,NULL))!=-1 ) {
     881           0 :                         if ( found==-1 ) found = pos;
     882           0 :                         if ( pos!=-1 )
     883           0 :                             fv->b.selected[pos] = true;
     884             :                     }
     885             :                 }
     886           0 :             } else if ( strncasecmp(nm,"color=#",strlen("color=#"))==0 ) {
     887           0 :                 Color col = strtoul(nm+strlen("color=#"),NULL,16);
     888             :                 int gid; SplineChar *sc;
     889             : 
     890           0 :                 for ( pos=0; pos<fv->b.map->enccount; ++pos )
     891           0 :                     if ( (gid=fv->b.map->map[pos])!=-1 && 
     892           0 :                             (sc = sf->glyphs[gid])!=NULL &&
     893           0 :                             sc->color == col )
     894           0 :                         fv->b.selected[pos] = true;
     895             :             } else {
     896           0 :                 if (( pos = SFFindSlot(sf,fv->b.map,-1,nm))!=-1 ) {
     897           0 :                     if ( found==-1 ) found = pos;
     898           0 :                     if ( pos!=-1 )
     899           0 :                         fv->b.selected[pos] = true;
     900             :                 }
     901             :             }
     902           0 :             free(nm);
     903             :         }
     904             : 
     905           0 :         if ( found!=-1 )
     906           0 :             FVScrollToChar(fv,found);
     907           0 :         GDrawRequestExpose(fv->v,NULL,false);
     908             :     }
     909           0 : return( true );
     910             : }
     911             : 
     912           0 : static int Group_FromSelection(GGadget *g, GEvent *e) {
     913           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
     914           0 :         struct groupdlg *grp = GDrawGetUserData(GGadgetGetWindow(g));
     915           0 :         SplineFont *sf = grp->fv->b.sf;
     916           0 :         FontView *fv = grp->fv;
     917             :         unichar_t *vals, *pt;
     918             :         int i, len, max, gid, k;
     919             :         SplineChar *sc, dummy;
     920             :         char buffer[20];
     921             : 
     922           0 :         if ( GGadgetIsChecked(grp->idname) ) {
     923           0 :             for ( i=len=max=0; i<fv->b.map->enccount; ++i ) if ( fv->b.selected[i]) {
     924           0 :                 gid = fv->b.map->map[i];
     925           0 :                 if ( gid!=-1 && sf->glyphs[gid]!=NULL )
     926           0 :                     sc = sf->glyphs[gid];
     927             :                 else
     928           0 :                     sc = SCBuildDummy(&dummy,sf,fv->b.map,i);
     929           0 :                 len += strlen(sc->name)+1;
     930           0 :                 if ( fv->b.selected[i]>max ) max = fv->b.selected[i];
     931             :             }
     932           0 :             pt = vals = malloc((len+1)*sizeof(unichar_t));
     933           0 :             *pt = '\0';
     934           0 :             for ( i=len=max=0; i<fv->b.map->enccount; ++i ) if ( fv->b.selected[i]) {
     935           0 :                 gid = fv->b.map->map[i];
     936           0 :                 if ( gid!=-1 && sf->glyphs[gid]!=NULL )
     937           0 :                     sc = sf->glyphs[gid];
     938             :                 else
     939           0 :                     sc = SCBuildDummy(&dummy,sf,fv->b.map,i);
     940           0 :                 uc_strcpy(pt,sc->name);
     941           0 :                 pt += u_strlen(pt);
     942           0 :                 *pt++ = ' ';
     943             :             }
     944           0 :             if ( pt>vals ) pt[-1]='\0';
     945             :         } else {
     946           0 :             vals = NULL;
     947           0 :             for ( k=0; k<2; ++k ) {
     948           0 :                 int last=-2, start=-2;
     949           0 :                 len = 0;
     950           0 :                 for ( i=len=max=0; i<fv->b.map->enccount; ++i ) if ( fv->b.selected[i]) {
     951           0 :                     gid = fv->b.map->map[i];
     952           0 :                     if ( gid!=-1 && sf->glyphs[gid]!=NULL )
     953           0 :                         sc = sf->glyphs[gid];
     954             :                     else
     955           0 :                         sc = SCBuildDummy(&dummy,sf,fv->b.map,i);
     956           0 :                     if ( sc->unicodeenc==-1 )
     957           0 :                 continue;
     958           0 :                     if ( sc->unicodeenc==last+1 )
     959           0 :                         last = sc->unicodeenc;
     960             :                     else {
     961           0 :                         if ( last!=-2 ) {
     962           0 :                             if ( start!=last )
     963           0 :                                 sprintf( buffer, "U+%04X-U+%04X ", start, last );
     964             :                             else
     965           0 :                                 sprintf( buffer, "U+%04X ", start );
     966           0 :                             if ( vals!=NULL )
     967           0 :                                 uc_strcpy(vals+len,buffer);
     968           0 :                             len += strlen(buffer);
     969             :                         }
     970           0 :                         start = last = sc->unicodeenc;
     971             :                     }
     972             :                 }
     973           0 :                 if ( last!=-2 ) {
     974           0 :                     if ( start!=last )
     975           0 :                         sprintf( buffer, "U+%04X-U+%04X ", start, last );
     976             :                     else
     977           0 :                         sprintf( buffer, "U+%04X ", start );
     978           0 :                     if ( vals!=NULL )
     979           0 :                         uc_strcpy(vals+len,buffer);
     980           0 :                     len += strlen(buffer);
     981             :                 }
     982           0 :                 if ( !k )
     983           0 :                     vals = malloc((len+1)*sizeof(unichar_t));
     984           0 :                 else if ( len!=0 )
     985           0 :                     vals[len-1] = '\0';
     986             :                 else
     987           0 :                     *vals = '\0';
     988             :             }
     989             :         }
     990             : 
     991           0 :         GGadgetSetTitle(grp->glyphs,vals);
     992           0 :         free(vals);
     993             :     }
     994           0 : return( true );
     995             : }
     996             : 
     997           0 : static int Group_AddColor(GGadget *g, GEvent *e) {
     998           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
     999           0 :         struct groupdlg *grp = GDrawGetUserData(GGadgetGetWindow(g));
    1000           0 :         GTextInfo *ti = GGadgetGetListItemSelected(g);
    1001           0 :         int set=false;
    1002           0 :         Color xcol=0;
    1003             : 
    1004           0 :         if ( ti==NULL )
    1005             :             /* Can't happen */;
    1006           0 :         else if ( ti->userdata == (void *) COLOR_CHOOSE ) {
    1007             :             struct hslrgb col, font_cols[6];
    1008           0 :             memset(&col,0,sizeof(col));
    1009           0 :             col = GWidgetColor(_("Pick a color"),&col,SFFontCols(grp->fv->b.sf,font_cols));
    1010           0 :             if ( col.rgb ) {
    1011           0 :                 xcol = (((int) rint(255.*col.r))<<16 ) |
    1012           0 :                             (((int) rint(255.*col.g))<<8 ) |
    1013           0 :                             (((int) rint(255.*col.b)) );
    1014           0 :                 set = true;
    1015             :             }
    1016             :         } else {
    1017           0 :             xcol = (intpt) ti->userdata;
    1018           0 :             set = true;
    1019             :         }
    1020             : 
    1021           0 :         if ( set ) {
    1022             :             char buffer[40]; unichar_t ubuf[40];
    1023           0 :             sprintf(buffer," color=#%06x", xcol );
    1024           0 :             uc_strcpy(ubuf,buffer);
    1025           0 :             GTextFieldReplace(grp->glyphs,ubuf);
    1026           0 :             if ( grp->showchange==NULL )
    1027           0 :                 GroupShowChange(grp);
    1028             :         }
    1029           0 :         GGadgetSelectOneListItem(g,0);
    1030             :     }
    1031           0 : return( true );
    1032             : }
    1033             : 
    1034           0 : static int Group_NewSubGroup(GGadget *g, GEvent *e) {
    1035           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
    1036           0 :         struct groupdlg *grp = GDrawGetUserData(GGadgetGetWindow(g));
    1037             :         Group *new_grp;
    1038           0 :         if ( !GroupFinishOld(grp))
    1039           0 : return( true );
    1040           0 :         GDrawRequestExpose(grp->v,NULL,false);
    1041           0 :         if ( grp->oldsel==NULL )
    1042           0 : return( true );
    1043           0 :         if ( grp->oldsel->glyphs!=NULL && grp->oldsel->glyphs[0]!='\0' ) {
    1044           0 :             GGadgetSetEnabled(grp->newsub,false);
    1045           0 : return( true );
    1046             :         }
    1047           0 :         grp->oldsel->kids = realloc(grp->oldsel->kids,(++grp->oldsel->kid_cnt)*sizeof(Group *));
    1048           0 :         grp->oldsel->kids[grp->oldsel->kid_cnt-1] = new_grp = chunkalloc(sizeof(Group));
    1049           0 :         new_grp->parent = grp->oldsel;
    1050           0 :         new_grp->unique = grp->oldsel->unique;
    1051           0 :         new_grp->name = copy(_("UntitledGroup"));
    1052           0 :         grp->oldsel->selected = false;
    1053           0 :         grp->oldsel->open = true;
    1054           0 :         new_grp->selected = true;
    1055           0 :         GroupSBSizes(grp);
    1056           0 :         GroupSelected(grp);
    1057           0 :         GDrawRequestExpose(grp->v,NULL,false);
    1058             :     }
    1059           0 : return( true );
    1060             : }
    1061             : 
    1062           0 : static int Group_Delete(GGadget *g, GEvent *e) {
    1063           0 :     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
    1064           0 :         struct groupdlg *grp = GDrawGetUserData(GGadgetGetWindow(g));
    1065             :         Group *parent;
    1066             :         int pos, i;
    1067           0 :         if ( grp->oldsel==NULL || grp->oldsel->parent==NULL )
    1068           0 : return( true );
    1069           0 :         parent = grp->oldsel->parent;
    1070           0 :         pos = GroupPosInParent(grp->oldsel);
    1071           0 :         if ( pos==-1 )
    1072           0 : return( true );
    1073           0 :         for ( i=pos; i<parent->kid_cnt-1; ++i )
    1074           0 :             parent->kids[i] = parent->kids[i+1];
    1075           0 :         --parent->kid_cnt;
    1076           0 :         GroupFree(grp->oldsel);
    1077           0 :         grp->oldsel = NULL;
    1078           0 :         GroupSBSizes(grp);
    1079           0 :         GroupSelected(grp);
    1080           0 :         GDrawRequestExpose(grp->v,NULL,false);
    1081             :     }
    1082           0 : return( true );
    1083             : }
    1084             : 
    1085           0 : static int displaygrp_e_h(GWindow gw, GEvent *event) {
    1086           0 :     struct groupdlg *grp = (struct groupdlg *) GDrawGetUserData(gw);
    1087             : 
    1088           0 :     if (( event->type==et_mouseup || event->type==et_mousedown ) &&
    1089           0 :             (event->u.mouse.button>=4 && event->u.mouse.button<=7) ) {
    1090           0 : return( GGadgetDispatchEvent(grp->vsb,event));
    1091             :     }
    1092             : 
    1093           0 :     if ( grp==NULL )
    1094           0 : return( true );
    1095             : 
    1096           0 :     switch ( event->type ) {
    1097             :       case et_expose:
    1098           0 :       break;
    1099             :       case et_char:
    1100           0 : return( GroupChar(grp,event));
    1101             :       break;
    1102             :       case et_timer:
    1103           0 :         GroupShowChange(grp);
    1104           0 :       break;
    1105             :       case et_resize:
    1106           0 :         if ( event->u.resize.sized )
    1107           0 :             GroupResize(grp,event);
    1108           0 :       break;
    1109             :       case et_controlevent:
    1110           0 :         switch ( event->u.control.subtype ) {
    1111             :           case et_scrollbarchange:
    1112           0 :             if ( event->u.control.g == grp->vsb )
    1113           0 :                 GroupScroll(grp,&event->u.control.u.sb);
    1114             :             else
    1115           0 :                 GroupHScroll(grp,&event->u.control.u.sb);
    1116           0 :           break;
    1117             :           case et_buttonactivate:
    1118           0 :             grp->done = true;
    1119           0 :             grp->oked = event->u.control.g == grp->ok;
    1120           0 :           break;
    1121             :         }
    1122           0 :       break;
    1123             :       case et_close:
    1124           0 :         grp->done = true;
    1125           0 :       break;
    1126             :       case et_destroy:
    1127           0 :         if ( grp->newsub!=NULL )
    1128           0 :             free(grp);
    1129           0 : return( true );
    1130             :     }
    1131           0 :     if ( grp->done && grp->newsub!=NULL ) {
    1132           0 :         if ( grp->oked ) {
    1133           0 :             if ( !GroupFinishOld(grp)) {
    1134           0 :                 grp->done = grp->oked = false;
    1135           0 : return( true );
    1136             :             }
    1137           0 :             GroupFree(group_root);
    1138           0 :             if ( grp->root->kid_cnt==0 && grp->root->glyphs==NULL ) {
    1139           0 :                 group_root = NULL;
    1140           0 :                 GroupFree(grp->root);
    1141             :             } else
    1142           0 :                 group_root = grp->root;
    1143           0 :             SaveGroupList();
    1144             :         } else
    1145           0 :             GroupFree(grp->root);
    1146           0 :         GDrawDestroyWindow(grp->gw);
    1147             :     }
    1148           0 : return( true );
    1149             : }
    1150             : 
    1151           0 : void DefineGroups(FontView *fv) {
    1152             :     struct groupdlg *grp;
    1153             :     GRect pos;
    1154             :     GWindowAttrs wattrs;
    1155             :     GGadgetCreateData gcd[20];
    1156             :     GTextInfo label[19];
    1157             :     int h, k,kk;
    1158             : 
    1159           0 :     grp = calloc(1,sizeof(*grp));
    1160           0 :     grp->fv = fv;
    1161           0 :     grp->select_many = grp->select_kids_too = false;
    1162           0 :     grp->select_callback = GroupSelected;
    1163             : 
    1164           0 :     if ( group_root==NULL ) {
    1165           0 :         grp->root = chunkalloc(sizeof(Group));
    1166           0 :         grp->root->name = copy(_("Groups"));
    1167             :     } else
    1168           0 :         grp->root = GroupCopy(group_root);
    1169             : 
    1170           0 :     memset(&wattrs,0,sizeof(wattrs));
    1171           0 :     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
    1172           0 :     wattrs.event_masks = ~(1<<et_charup);
    1173           0 :     wattrs.is_dlg = true;
    1174           0 :     wattrs.restrict_input_to_me = false;
    1175           0 :     wattrs.undercursor = 1;
    1176           0 :     wattrs.cursor = ct_pointer;
    1177           0 :     wattrs.utf8_window_title = _("Define Groups");
    1178           0 :     pos.x = pos.y = 0;
    1179           0 :     pos.width =GDrawPointsToPixels(NULL,GGadgetScale(200));
    1180           0 :     pos.height = h = GDrawPointsToPixels(NULL,482);
    1181           0 :     grp->gw = GDrawCreateTopWindow(NULL,&pos,displaygrp_e_h,grp,&wattrs);
    1182             : 
    1183           0 :     grp->bmargin = GDrawPointsToPixels(NULL,248)+GDrawPointsToPixels(grp->gw,_GScrollBar_Width);
    1184             : 
    1185           0 :     GroupWCreate(grp,&pos);
    1186             : 
    1187           0 :     memset(&label,0,sizeof(label));
    1188           0 :     memset(&gcd,0,sizeof(gcd));
    1189             : 
    1190           0 :     k = 0;
    1191             : 
    1192           0 :     gcd[k].gd.pos.x = 20;
    1193           0 :     gcd[k].gd.pos.y = GDrawPixelsToPoints(NULL,h-grp->bmargin)+12;
    1194           0 :     gcd[k].gd.flags = gg_visible;
    1195           0 :     label[k].text = (unichar_t *) _("New Sub-Group");
    1196           0 :     label[k].text_is_1byte = true;
    1197           0 :     gcd[k].gd.label = &label[k];
    1198           0 :     gcd[k].gd.handle_controlevent = Group_NewSubGroup;
    1199           0 :     gcd[k++].creator = GButtonCreate;
    1200             : 
    1201           0 :     gcd[k].gd.pos.width = -1;
    1202           0 :     gcd[k].gd.pos.x = GDrawPixelsToPoints(NULL,(pos.width-30-GIntGetResource(_NUM_Buttonsize)*100/GIntGetResource(_NUM_ScaleFactor)));
    1203           0 :     gcd[k].gd.pos.y = gcd[k-1].gd.pos.y;
    1204           0 :     gcd[k].gd.flags = gg_visible;
    1205           0 :     label[k].text = (unichar_t *) _("_Delete");
    1206           0 :     label[k].text_is_1byte = true;
    1207           0 :     label[k].text_in_resource = true;
    1208           0 :     gcd[k].gd.label = &label[k];
    1209           0 :     gcd[k].gd.handle_controlevent = Group_Delete;
    1210           0 :     gcd[k++].creator = GButtonCreate;
    1211             : 
    1212           0 :     gcd[k].gd.pos.width = GDrawPixelsToPoints(NULL,pos.width)-20;
    1213           0 :     gcd[k].gd.pos.x = 10;
    1214           0 :     gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+26;
    1215           0 :     gcd[k].gd.flags = gg_visible | gg_enabled;
    1216           0 :     gcd[k++].creator = GLineCreate;
    1217             : 
    1218           0 :     gcd[k].gd.pos.x = 5;
    1219           0 :     gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+8;
    1220           0 :     gcd[k].gd.flags = gg_visible;
    1221           0 :     label[k].text = (unichar_t *) _("Group Name:");
    1222           0 :     label[k].text_is_1byte = true;
    1223           0 :     gcd[k].gd.label = &label[k];
    1224           0 :     gcd[k++].creator = GLabelCreate;
    1225             : 
    1226           0 :     gcd[k].gd.pos.x = 80; gcd[k].gd.pos.width = 115;
    1227           0 :     gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-3;
    1228           0 :     gcd[k].gd.flags = gg_visible;
    1229           0 :     gcd[k++].creator = GTextFieldCreate;
    1230             : 
    1231           0 :     gcd[k].gd.pos.x = 5;
    1232           0 :     gcd[k].gd.pos.y = gcd[k-2].gd.pos.y+16;
    1233           0 :     gcd[k].gd.flags = gg_visible;
    1234           0 :     label[k].text = (unichar_t *) _("Glyphs:");
    1235           0 :     label[k].text_is_1byte = true;
    1236           0 :     gcd[k].gd.label = &label[k];
    1237           0 :     gcd[k++].creator = GLabelCreate;
    1238             : 
    1239           0 :     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+14;
    1240           0 :     gcd[k].gd.pos.width = GDrawPixelsToPoints(NULL,pos.width)-10; gcd[k].gd.pos.height = 4*13+4;
    1241           0 :     gcd[k].gd.flags = gg_visible | gg_enabled | gg_textarea_wrap;
    1242           0 :     gcd[k].gd.handle_controlevent = Group_GlyphListChanged;
    1243           0 :     gcd[k++].creator = GTextAreaCreate;
    1244             : 
    1245           0 :     gcd[k].gd.pos.x = 5;
    1246           0 :     gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+gcd[k-1].gd.pos.height+5;
    1247           0 :     gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup;
    1248           0 :     label[k].text = (unichar_t *) _("Identify by");
    1249           0 :     label[k].text_is_1byte = true;
    1250           0 :     gcd[k].gd.label = &label[k];
    1251           0 :     gcd[k].gd.popup_msg = (unichar_t *) _("Glyphs may be either identified by name or by unicode code point.\nGenerally you control this by what you type in.\nTyping \"A\" would identify a glyph by name.\nTyping \"U+0041\" identifies a glyph by code point.\nWhen loading glyphs from the selection you must specify which format is desired.");
    1252           0 :     gcd[k++].creator = GLabelCreate;
    1253             : 
    1254           0 :     gcd[k].gd.pos.x = 90; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y-2;
    1255           0 :     label[k].text = (unichar_t *) _("Name");
    1256           0 :     label[k].text_is_1byte = true;
    1257           0 :     gcd[k].gd.label = &label[k];
    1258           0 :     gcd[k].gd.flags = (gg_visible | gg_enabled | gg_cb_on | gg_utf8_popup);
    1259           0 :     gcd[k].gd.popup_msg = (unichar_t *) _("Glyphs may be either identified by name or by unicode code point.\nGenerally you control this by what you type in.\nTyping \"A\" would identify a glyph by name.\nTyping \"U+0041\" identifies a glyph by code point.\nWhen loading glyphs from the selection you must specify which format is desired.");
    1260           0 :     gcd[k++].creator = GRadioCreate;
    1261             : 
    1262           0 :     gcd[k].gd.pos.x = 140; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y;
    1263           0 :     label[k].text = (unichar_t *) _("Unicode");
    1264           0 :     label[k].text_is_1byte = true;
    1265           0 :     gcd[k].gd.label = &label[k];
    1266           0 :     gcd[k].gd.flags = gg_visible | gg_enabled | gg_utf8_popup;
    1267           0 :     gcd[k].gd.popup_msg = (unichar_t *) _("Glyphs may be either identified by name or by unicode code point.\nGenerally you control this by what you type in.\nTyping \"A\" would identify a glyph by name.\nTyping \"U+0041\" identifies a glyph by code point.\nWhen loading glyphs from the selection you must specify which format is desired.");
    1268           0 :     gcd[k++].creator = GRadioCreate;
    1269             : 
    1270           0 :     label[k].text = (unichar_t *) _("Set From Font");
    1271           0 :     label[k].text_is_1byte = true;
    1272           0 :     gcd[k].gd.label = &label[k];
    1273           0 :     gcd[k].gd.pos.x = 5; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+18;
    1274           0 :     gcd[k].gd.popup_msg = (unichar_t *) _("Set this glyph list to be the glyphs selected in the fontview");
    1275           0 :     gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    1276           0 :     gcd[k].gd.handle_controlevent = Group_FromSelection;
    1277           0 :     gcd[k++].creator = GButtonCreate;
    1278             : 
    1279           0 :     label[k].text = (unichar_t *) _("Select In Font");
    1280           0 :     label[k].text_is_1byte = true;
    1281           0 :     gcd[k].gd.label = &label[k];
    1282           0 :     gcd[k].gd.pos.x = 110; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y;
    1283           0 :     gcd[k].gd.popup_msg = (unichar_t *) _("Set the fontview's selection to be the glyphs named here");
    1284           0 :     gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    1285           0 :     gcd[k].gd.handle_controlevent = Group_ToSelection;
    1286           0 :     gcd[k++].creator = GButtonCreate;
    1287             : 
    1288           0 :     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+26;
    1289           0 :     label[k].text = (unichar_t *) _("No Glyph Duplicates");
    1290           0 :     label[k].text_is_1byte = true;
    1291           0 :     gcd[k].gd.label = &label[k];
    1292           0 :     gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    1293           0 :     gcd[k].gd.popup_msg = (unichar_t *) _("Glyph names (or unicode code points) may occur at most once in this group and any of its sub-groups");
    1294           0 :     gcd[k++].creator = GCheckBoxCreate;
    1295             : 
    1296           0 :     for ( kk=0; kk<3; ++kk )
    1297           0 :         std_colors[kk].text = (unichar_t *) S_((char *) std_colors[kk].text);
    1298           0 :     std_colors[1].image = GGadgetImageCache("colorwheel.png");
    1299           0 :     std_colors[0].selected = true;
    1300           0 :     gcd[k].gd.pos.x = 10; gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+15;
    1301           0 :     gcd[k].gd.label = &std_colors[0];
    1302           0 :     gcd[k].gd.u.list = std_colors;
    1303           0 :     gcd[k].gd.handle_controlevent = Group_AddColor;
    1304           0 :     gcd[k].gd.flags = gg_visible | gg_utf8_popup;
    1305           0 :     gcd[k++].creator = GListButtonCreate;
    1306             : 
    1307           0 :     gcd[k].gd.pos.width = GDrawPixelsToPoints(NULL,pos.width)-20;
    1308           0 :     gcd[k].gd.pos.x = 10;
    1309           0 :     gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+28;
    1310           0 :     gcd[k].gd.flags = gg_visible | gg_enabled;
    1311           0 :     gcd[k++].creator = GLineCreate;
    1312             : 
    1313           0 :     gcd[k].gd.pos.width = -1;
    1314           0 :     gcd[k].gd.pos.x = 30;
    1315           0 :     gcd[k].gd.pos.y = h-GDrawPointsToPixels(NULL,32);
    1316           0 :     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default | gg_pos_in_pixels;
    1317           0 :     label[k].text = (unichar_t *) _("_OK");
    1318           0 :     label[k].text_is_1byte = true;
    1319           0 :     label[k].text_in_resource = true;
    1320           0 :     gcd[k].gd.label = &label[k];
    1321           0 :     gcd[k++].creator = GButtonCreate;
    1322             : 
    1323           0 :     gcd[k].gd.pos.width = -1;
    1324           0 :     gcd[k].gd.pos.x = (pos.width-30-GIntGetResource(_NUM_Buttonsize)*100/GIntGetResource(_NUM_ScaleFactor));
    1325           0 :     gcd[k].gd.pos.y = gcd[k-1].gd.pos.y+3;
    1326           0 :     gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel | gg_pos_in_pixels;
    1327           0 :     label[k].text = (unichar_t *) _("_Cancel");
    1328           0 :     label[k].text_is_1byte = true;
    1329           0 :     label[k].text_in_resource = true;
    1330           0 :     gcd[k].gd.label = &label[k];
    1331           0 :     gcd[k++].creator = GButtonCreate;
    1332             : 
    1333           0 :     GGadgetsCreate(grp->gw,gcd);
    1334           0 :     grp->newsub = gcd[0].ret;
    1335           0 :     grp->delete = gcd[1].ret;
    1336           0 :     grp->line1 = gcd[2].ret;
    1337           0 :     grp->gpnamelab = gcd[3].ret;
    1338           0 :     grp->gpname = gcd[4].ret;
    1339           0 :     grp->glyphslab = gcd[5].ret;
    1340           0 :     grp->glyphs = gcd[6].ret;
    1341           0 :     grp->idlab = gcd[7].ret;
    1342           0 :     grp->idname = gcd[8].ret;
    1343           0 :     grp->iduni = gcd[9].ret;
    1344           0 :     grp->set = gcd[10].ret;
    1345           0 :     grp->select = gcd[11].ret;
    1346           0 :     grp->unique = gcd[12].ret;
    1347           0 :     grp->colour = gcd[13].ret;
    1348           0 :     grp->line2 = gcd[14].ret;
    1349           0 :     grp->ok = gcd[15].ret;
    1350           0 :     grp->cancel = gcd[16].ret;
    1351             : 
    1352           0 :     GroupSBSizes(grp);
    1353           0 :     GroupResize(grp,NULL);
    1354             : 
    1355           0 :     GDrawSetVisible(grp->gw,true);
    1356           0 : }
    1357             : 
    1358           0 : static void MapEncAddGid(EncMap *map,SplineFont *sf, int compacted,
    1359             :         int gid, int uni, char *name) {
    1360             : 
    1361           0 :     if ( compacted && gid==-1 )
    1362           0 : return;
    1363             : 
    1364           0 :     if ( gid!=-1 && map->backmap[gid]==-1 )
    1365           0 :         map->backmap[gid] = map->enccount;
    1366           0 :     if ( map->enccount>=map->encmax )
    1367           0 :         map->map = realloc(map->map,(map->encmax+=100)*sizeof(int));
    1368           0 :     map->map[map->enccount++] = gid;
    1369           0 :     if ( !compacted ) {
    1370           0 :         Encoding *enc = map->enc;
    1371           0 :         if ( enc->char_cnt>=enc->char_max ) {
    1372           0 :             enc->unicode = realloc(enc->unicode,(enc->char_max+=256)*sizeof(int));
    1373           0 :             enc->psnames = realloc(enc->psnames,enc->char_max*sizeof(char *));
    1374             :         }
    1375           0 :         if ( uni==-1 && name!=NULL ) {
    1376           0 :             if ( gid!=-1 && sf->glyphs[gid]!=NULL )
    1377           0 :                 uni = sf->glyphs[gid]->unicodeenc;
    1378             :             else
    1379           0 :                 uni = UniFromName(name,ui_none,&custom);
    1380             :         }
    1381           0 :         enc->unicode[enc->char_cnt] = uni;
    1382           0 :         enc->psnames[enc->char_cnt++] = copy( name );
    1383             :     }
    1384             : }
    1385             : 
    1386           0 : static void MapAddGroupGlyph(EncMap *map,SplineFont *sf,char *name, int compacted) {
    1387             :     int gid;
    1388             : 
    1389           0 :     if ( (name[0]=='u' || name[0]=='U') && name[1]=='+' && ishexdigit(name[2])) {
    1390             :         char *end;
    1391           0 :         int val = strtol(name+2,&end,16), val2=val;
    1392           0 :         if ( *end=='-' ) {
    1393           0 :             if ( (end[1]=='u' || end[1]=='U') && end[2]=='+' )
    1394           0 :                 end+=2;
    1395           0 :             val2 = strtol(end+1,NULL,16);
    1396             :         }
    1397           0 :         for ( ; val<=val2; ++val ) {
    1398           0 :             gid = SFFindExistingSlot(sf,val,NULL);
    1399           0 :             MapEncAddGid(map,sf,compacted,gid,val,NULL);
    1400             :         }
    1401           0 :     } else if ( strncasecmp(name,"color=#",strlen("color=#"))==0 ) {
    1402           0 :         Color col = strtoul(name+strlen("color=#"),NULL,16);
    1403             :         int gid; SplineChar *sc;
    1404             : 
    1405           0 :         for ( gid=0; gid<sf->glyphcnt; ++gid )
    1406           0 :             if ( (sc = sf->glyphs[gid])!=NULL &&
    1407           0 :                     sc->color == col )
    1408           0 :                 MapEncAddGid(map,sf,compacted,gid,sc->unicodeenc,NULL);
    1409             :     } else {
    1410           0 :         gid = SFFindExistingSlot(sf,-1,name);
    1411           0 :         MapEncAddGid(map,sf,compacted,gid,-1,name);
    1412             :     }
    1413           0 : }
    1414             : 
    1415           0 : static int MapAddSelectedGroups(EncMap *map,SplineFont *sf,Group *group, int compacted) {
    1416           0 :     int i, cnt=0;
    1417             :     char *start, *pt;
    1418             :     int ch;
    1419             : 
    1420           0 :     if ( group->glyphs==NULL ) {
    1421           0 :         for ( i=0; i<group->kid_cnt; ++i )
    1422           0 :             cnt += MapAddSelectedGroups(map,sf,group->kids[i], compacted);
    1423           0 :     } else if ( group->selected ) {
    1424           0 :         for ( pt=group->glyphs; *pt!='\0'; ) {
    1425           0 :             while ( *pt==' ' ) ++pt;
    1426           0 :             start = pt;
    1427           0 :             while ( *pt!=' ' && *pt!='\0' ) ++pt;
    1428           0 :             ch = *pt; *pt='\0';
    1429           0 :             if ( *start!='\0' )
    1430           0 :                 MapAddGroupGlyph(map,sf,start, compacted);
    1431           0 :             *pt=ch;
    1432             :         }
    1433           0 :         ++cnt;
    1434             :     }
    1435           0 : return( cnt );
    1436             : }
    1437             : 
    1438           0 : static int GroupSelCnt(Group *group, Group **first, Group **second) {
    1439           0 :     int cnt = 0, i;
    1440             : 
    1441           0 :     if ( group->glyphs==NULL ) {
    1442           0 :         for ( i=0; i<group->kid_cnt; ++i )
    1443           0 :             cnt += GroupSelCnt(group->kids[i],first,second);
    1444           0 :     } else if ( group->selected ) {
    1445           0 :         if ( *first==NULL )
    1446           0 :             *first = group;
    1447           0 :         else if ( *second==NULL )
    1448           0 :             *second = group;
    1449           0 :         ++cnt;
    1450             :     }
    1451           0 : return( cnt );
    1452             : }
    1453             : 
    1454           0 : static char *EncNameFromGroups(Group *group) {
    1455           0 :     Group *first = NULL, *second = NULL;
    1456           0 :     int cnt = GroupSelCnt(group,&first,&second);
    1457           0 :     char *prefix = P_("Group","Groups",cnt);
    1458             :     char *ret;
    1459             : 
    1460           0 :     switch ( cnt ) {
    1461             :       case 0:
    1462           0 : return( copy( _("No Groups")) );
    1463             :       case 1:
    1464           0 :         ret = malloc(strlen(prefix) + strlen(first->name) + 3 );
    1465           0 :         sprintf( ret, "%s: %s", prefix, first->name);
    1466           0 :       break;
    1467             :       case 2:
    1468           0 :         ret = malloc(strlen(prefix) + strlen(first->name) + strlen(second->name) + 5 );
    1469           0 :         sprintf( ret, "%s: %s, %s", prefix, first->name, second->name );
    1470           0 :       break;
    1471             :       default:
    1472           0 :         ret = malloc(strlen(prefix) + strlen(first->name) + strlen(second->name) + 9 );
    1473           0 :         sprintf( ret, "%s: %s, %s ...", prefix, first->name, second->name );
    1474           0 :       break;
    1475             :     }
    1476           0 : return( ret );
    1477             : }
    1478             : 
    1479           0 : static void EncodeToGroups(FontView *fv,Group *group, int compacted) {
    1480           0 :     SplineFont *sf = fv->b.sf;
    1481             :     EncMap *map;
    1482           0 :     if ( compacted )
    1483           0 :         map = EncMapNew(0,sf->glyphcnt,&custom);
    1484             :     else {
    1485           0 :         Encoding *enc = calloc(1,sizeof(Encoding));
    1486           0 :         enc->enc_name = EncNameFromGroups(group);
    1487           0 :         enc->is_temporary = true;
    1488           0 :         enc->char_max = 256;
    1489           0 :         enc->unicode = malloc(256*sizeof(int32));
    1490           0 :         enc->psnames = malloc(256*sizeof(char *));
    1491           0 :         map = EncMapNew(0,sf->glyphcnt,enc);
    1492             :     }
    1493             : 
    1494           0 :     if ( MapAddSelectedGroups(map,sf,group,compacted)==0 ) {
    1495           0 :         ff_post_error(_("Nothing Selected"),_("Nothing Selected"));
    1496           0 :         EncMapFree(map);
    1497           0 :     } else if ( map->enccount==0 ) {
    1498           0 :         ff_post_error(_("Nothing Selected"),_("None of the glyphs in the current font match any names or code points in the selected groups"));
    1499           0 :         EncMapFree(map);
    1500             :     } else {
    1501           0 :         fv->b.selected = realloc(fv->b.selected,map->enccount);
    1502           0 :         memset(fv->b.selected,0,map->enccount);
    1503           0 :         EncMapFree(fv->b.map);
    1504           0 :         fv->b.map = map;
    1505           0 :         FVSetTitle((FontViewBase *) fv);
    1506           0 :         FontViewReformatOne((FontViewBase *) fv);
    1507             :     }
    1508           0 : }
    1509             : 
    1510           0 : void DisplayGroups(FontView *fv) {
    1511             :     struct groupdlg grp;
    1512             :     GRect pos;
    1513             :     GWindowAttrs wattrs;
    1514             :     GGadgetCreateData gcd[6];
    1515             :     GTextInfo label[5];
    1516             :     int h;
    1517             : 
    1518           0 :     memset( &grp,0,sizeof(grp));
    1519           0 :     grp.fv = fv;
    1520           0 :     grp.select_many = grp.select_kids_too = true;
    1521           0 :     grp.root = group_root;
    1522             : 
    1523           0 :     if ( grp.root==NULL ) {
    1524           0 :         grp.root = chunkalloc(sizeof(Group));
    1525           0 :         grp.root->name = copy(_("Groups"));
    1526             :     }
    1527             : 
    1528           0 :     memset(&wattrs,0,sizeof(wattrs));
    1529           0 :     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
    1530           0 :     wattrs.event_masks = ~(1<<et_charup);
    1531           0 :     wattrs.is_dlg = true;
    1532           0 :     wattrs.restrict_input_to_me = 1;
    1533           0 :     wattrs.undercursor = 1;
    1534           0 :     wattrs.cursor = ct_pointer;
    1535           0 :     wattrs.utf8_window_title = _("Display By Groups");
    1536           0 :     pos.x = pos.y = 0;
    1537           0 :     pos.width =GDrawPointsToPixels(NULL,GGadgetScale(200));
    1538           0 :     pos.height = h = GDrawPointsToPixels(NULL,317);
    1539           0 :     grp.gw = GDrawCreateTopWindow(NULL,&pos,displaygrp_e_h,&grp,&wattrs);
    1540             : 
    1541           0 :     grp.bmargin = GDrawPointsToPixels(NULL,50)+GDrawPointsToPixels(grp.gw,_GScrollBar_Width);
    1542             : 
    1543           0 :     GroupWCreate(&grp,&pos);
    1544             : 
    1545           0 :     memset(&label,0,sizeof(label));
    1546           0 :     memset(&gcd,0,sizeof(gcd));
    1547             : 
    1548           0 :     gcd[0].gd.pos.width = -1;
    1549           0 :     gcd[0].gd.pos.x = 30;
    1550           0 :     gcd[0].gd.pos.y = h-GDrawPointsToPixels(NULL,30);
    1551           0 :     gcd[0].gd.flags = gg_visible | gg_enabled | gg_but_default | gg_pos_in_pixels;
    1552           0 :     label[0].text = (unichar_t *) _("_OK");
    1553           0 :     label[0].text_is_1byte = true;
    1554           0 :     label[0].text_in_resource = true;
    1555           0 :     gcd[0].gd.label = &label[0];
    1556           0 :     gcd[0].creator = GButtonCreate;
    1557             : 
    1558           0 :     gcd[1].gd.pos.width = -1;
    1559           0 :     gcd[1].gd.pos.x = (pos.width-30-GIntGetResource(_NUM_Buttonsize)*100/GIntGetResource(_NUM_ScaleFactor));
    1560           0 :     gcd[1].gd.pos.y = gcd[0].gd.pos.y+3;
    1561           0 :     gcd[1].gd.flags = gg_visible | gg_enabled | gg_but_cancel | gg_pos_in_pixels;
    1562           0 :     label[1].text = (unichar_t *) _("_Cancel");
    1563           0 :     label[1].text_is_1byte = true;
    1564           0 :     label[1].text_in_resource = true;
    1565           0 :     gcd[1].gd.label = &label[1];
    1566           0 :     gcd[1].creator = GButtonCreate;
    1567             : 
    1568           0 :     gcd[2].gd.pos.width = -1;
    1569           0 :     gcd[2].gd.pos.x = 10;
    1570           0 :     gcd[2].gd.pos.y = gcd[0].gd.pos.y-GDrawPointsToPixels(NULL,17);
    1571           0 :     gcd[2].gd.flags = gg_visible | gg_enabled | gg_cb_on | gg_pos_in_pixels;
    1572           0 :     label[2].text = (unichar_t *) _("Compacted");
    1573           0 :     label[2].text_is_1byte = true;
    1574           0 :     label[2].text_in_resource = true;
    1575           0 :     gcd[2].gd.label = &label[2];
    1576           0 :     gcd[2].creator = GCheckBoxCreate;    
    1577             : 
    1578           0 :     GGadgetsCreate(grp.gw,gcd);
    1579           0 :     grp.ok = gcd[0].ret;
    1580           0 :     grp.cancel = gcd[1].ret;
    1581           0 :     grp.compact = gcd[2].ret;
    1582             : 
    1583           0 :     GroupSBSizes(&grp);
    1584             : 
    1585           0 :     GDrawSetVisible(grp.gw,true);
    1586             : 
    1587           0 :     while ( !grp.done )
    1588           0 :         GDrawProcessOneEvent(NULL);
    1589           0 :     GDrawSetUserData(grp.gw,NULL);
    1590           0 :     if ( grp.oked )
    1591           0 :         EncodeToGroups(fv,grp.root, GGadgetIsChecked(gcd[2].ret));
    1592           0 :     if ( grp.root!=group_root )
    1593           0 :         GroupFree(grp.root);
    1594           0 :     GDrawDestroyWindow(grp.gw);
    1595           0 : }

Generated by: LCOV version 1.10