1 /* Copyright (C) 2003-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "autohint.h"
31 #include "dumppfa.h"
32 #include "ffglib.h"
33 #include "fontforgeui.h"
34 #include "gkeysym.h"
35 #include "psfont.h"
36 #include "splineutil.h"
37 #include "ustring.h"
38 #include "utype.h"
39 
40 #include <math.h>
41 
42 /* This operations are designed to work on a single font. NOT a CID collection*/
43 /*  A CID collection must be treated one sub-font at a time */
44 
45 struct hentry {
46     int cnt, sum;
47     int char_cnt, max;
48     SplineChar **chars;
49 };
50 
51 typedef struct histdata {
52     int low, high;
53     struct hentry *hist;	/* array of high-low+1 elements */
54     int tot, max;
55 } HistData;
56 
HistDataFree(HistData * h)57 static void HistDataFree(HistData *h) {
58     int i;
59 
60     for ( i=h->low; i<=h->high; ++i )
61 	free(h->hist[i-h->low].chars);
62     free(h->hist);
63     free(h);
64 }
65 
HistFindBlues(SplineFont * sf,int layer,uint8 * selected,EncMap * map)66 static HistData *HistFindBlues(SplineFont *sf,int layer, uint8 *selected, EncMap *map) {
67     int i, gid, low,high, top,bottom;
68     SplineChar *sc;
69     DBounds b;
70     HistData *hist;
71     struct hentry *h;
72 
73     hist = calloc(1,sizeof(HistData));
74     hist->hist = calloc(sf->ascent+sf->descent+1,sizeof(struct hentry));
75     hist->low = sf->ascent; hist->high = -sf->descent;
76     low = -sf->descent; high = sf->ascent;
77 
78     for ( i=0; i<(selected==NULL?sf->glyphcnt:map->enccount); ++i ) {
79 	gid = selected==NULL ? i : map->map[i];
80 	if ( gid!=-1 && (sc = sf->glyphs[gid])!=NULL &&
81 		sc->layers[ly_fore].splines!=NULL &&
82 		sc->layers[ly_fore].refs==NULL &&
83 		(selected==NULL || selected[i])) {
84 	    SplineCharLayerFindBounds(sc,layer,&b);
85 	    bottom = rint(b.miny);
86 	    top = rint(b.maxy);
87 	    if ( top==bottom )
88 	continue;
89 	    if ( top>hist->high ) {
90 		hist->high = top;
91 		if ( top>high ) {
92 		    hist->hist = realloc(hist->hist,(top+10-low)*sizeof(struct hentry));
93 		    memset(hist->hist + high-low+1,0,(top+10-high-1)*sizeof(struct hentry));
94 		    high = top+10 -1;
95 		}
96 	    }
97 	    ++ hist->hist[top-low].cnt;
98 	    if ( hist->hist[top-low].char_cnt >= hist->hist[top-low].max ) {
99 		if ( hist->hist[top-low].max==0 )
100 		    hist->hist[top-low].chars = malloc(10*sizeof(SplineChar *));
101 		else
102 		    hist->hist[top-low].chars = realloc(hist->hist[top-low].chars,(hist->hist[top-low].max+10)*sizeof(SplineChar *));
103 		hist->hist[top-low].max += 10;
104 	    }
105 	    hist->hist[top-low].chars[hist->hist[top-low].char_cnt++] = sc;
106 
107 	    if ( bottom<hist->low ) {
108 		hist->low = bottom;
109 		if ( bottom<low ) {
110 		    h = calloc((high-bottom+10),sizeof( struct hentry ));
111 		    memcpy(h+low-(bottom-10+1),hist->hist,(high+1-low)*sizeof(struct hentry));
112 		    low = bottom-10+1;
113 		    free( hist->hist );
114 		    hist->hist = h;
115 		}
116 	    }
117 	    ++ hist->hist[bottom-low].cnt;
118 	    if ( hist->hist[bottom-low].char_cnt >= hist->hist[bottom-low].max ) {
119 		if ( hist->hist[bottom-low].max==0 )
120 		    hist->hist[bottom-low].chars = malloc(10*sizeof(SplineChar *));
121 		else
122 		    hist->hist[bottom-low].chars = realloc(hist->hist[bottom-low].chars,(hist->hist[bottom-low].max+10)*sizeof(SplineChar *));
123 		hist->hist[bottom-low].max += 10;
124 	    }
125 	    hist->hist[bottom-low].chars[hist->hist[bottom-low].char_cnt++] = sc;
126 	}
127 	hist->tot += 2;
128     }
129     if ( hist->low>hist->high ) {		/* Found nothing */
130 	hist->low = hist->high = 0;
131     }
132     if ( low!=hist->low || high!=hist->high ) {
133 	h = malloc((hist->high-hist->low+1)*sizeof(struct hentry));
134 	memcpy(h,hist->hist + hist->low-low,(hist->high-hist->low+1)*sizeof(struct hentry));
135 	free(hist->hist);
136 	hist->hist = h;
137     }
138 return( hist );
139 }
140 
HistFindStemWidths(SplineFont * sf,int layer,uint8 * selected,EncMap * map,int hor)141 static HistData *HistFindStemWidths(SplineFont *sf,int layer, uint8 *selected,EncMap *map,int hor) {
142     int i, gid, low,high, val;
143     SplineChar *sc;
144     HistData *hist;
145     struct hentry *h;
146     StemInfo *stem;
147 
148     hist = calloc(1,sizeof(HistData));
149     hist->hist = calloc(sf->ascent+sf->descent+1,sizeof(struct hentry));
150     hist->low = sf->ascent+sf->descent;
151     low = 0; high = sf->ascent+sf->descent;
152 
153     for ( i=0; i<(selected==NULL?sf->glyphcnt:map->enccount); ++i ) {
154 	gid = selected==NULL ? i : map->map[i];
155 	if ( gid!=-1 && (sc = sf->glyphs[gid])!=NULL &&
156 		sc->layers[ly_fore].splines!=NULL &&
157 		sc->layers[ly_fore].refs==NULL &&
158 		(selected==NULL || selected[i])) {
159 	    if ( autohint_before_generate && sc->changedsincelasthinted && !sc->manualhints )
160 		SplineCharAutoHint(sc,layer,NULL);
161 	    for ( stem = hor ? sc->hstem : sc->vstem ; stem!=NULL; stem = stem->next ) {
162 		if ( stem->ghost )
163 	    continue;
164 		val = rint(stem->width);
165 		if ( val<=0 )
166 		    val = -val;
167 		if ( val>hist->high ) {
168 		    hist->high = val;
169 		    if ( val>high ) {
170 			hist->hist = realloc(hist->hist,(val+10-low)*sizeof(struct hentry));
171 			memset(hist->hist + high-low+1,0,(val+10-high-1)*sizeof(struct hentry));
172 			high = val+10 -1;
173 		    }
174 		}
175 		if ( val<hist->low )
176 		    hist->low = val;
177 		++ hist->hist[val-low].cnt;
178 		if ( hist->hist[val-low].char_cnt==0 ||
179 			hist->hist[val-low].chars[hist->hist[val-low].char_cnt-1]!=sc ) {
180 		    if ( hist->hist[val-low].char_cnt >= hist->hist[val-low].max ) {
181 			if ( hist->hist[val-low].max==0 )
182 			    hist->hist[val-low].chars = malloc(10*sizeof(SplineChar *));
183 			else
184 			    hist->hist[val-low].chars = realloc(hist->hist[val-low].chars,(hist->hist[val-low].max+10)*sizeof(SplineChar *));
185 			hist->hist[val-low].max += 10;
186 		    }
187 		    hist->hist[val-low].chars[hist->hist[val-low].char_cnt++] = sc;
188 		}
189 		++ hist->tot;
190 	    }
191 	}
192     }
193     if ( hist->low>hist->high ) {		/* Found nothing */
194 	hist->low = hist->high = 0;
195     }
196     if ( low!=hist->low || high!=hist->high ) {
197 	h = malloc((hist->high-hist->low+1)*sizeof(struct hentry));
198 	memcpy(h,hist->hist + hist->low-low,(hist->high-hist->low+1)*sizeof(struct hentry));
199 	free(hist->hist);
200 	hist->hist = h;
201     }
202 return( hist );
203 }
204 
HistFindHStemWidths(SplineFont * sf,int layer,uint8 * selected,EncMap * map)205 static HistData *HistFindHStemWidths(SplineFont *sf,int layer, uint8 *selected,EncMap *map) {
206 return( HistFindStemWidths(sf,layer,selected,map,true) );
207 }
208 
HistFindVStemWidths(SplineFont * sf,int layer,uint8 * selected,EncMap * map)209 static HistData *HistFindVStemWidths(SplineFont *sf,int layer, uint8 *selected,EncMap *map) {
210 return( HistFindStemWidths(sf,layer,selected,map,false) );
211 }
212 
HistFindMax(HistData * h,int sum_around)213 static void HistFindMax(HistData *h, int sum_around) {
214     int i, j, m=1;
215     int c;
216 
217     if ( sum_around<0 ) sum_around = 0;
218     for ( i = h->low; i<=h->high; ++i ) {
219 	c = 0;
220 	for ( j=i-sum_around; j<=i+sum_around; ++j )
221 	    if ( j>=h->low && j<=h->high )
222 		c += h->hist[j-h->low].cnt;
223 	h->hist[i-h->low].sum = c;
224 	if ( c>m )
225 	    m = c;
226     }
227     h->max = m;
228 }
229 
230 #define CID_ScrollBar		1000
231 #define CID_MainVal		1001
232 #define CID_SecondaryVal	1002
233 
234 #define CID_SumAround		1003
235 #define CID_BarWidth		1004
236 
237 #define CID_MainValL		2001
238 #define CID_SecondaryValL	2002
239 #define CID_SumAroundL		2003
240 #define CID_BarWidthL		2004
241 #define CID_Group		2005
242 #define CID_BlueMsg		2006
243 
244 #define CID_OK			3001
245 #define CID_Cancel		3002
246 
247 #define CID_LeftSide		4001
248 #define CID_Histogram		4002
249 #define CID_RightSide		4003
250 
251 struct hist_dlg {
252     enum hist_type which;
253     SplineFont *sf;
254     int layer;
255     struct psdict *private;
256     uint8 *selected;
257     HistData *h;
258 
259     int pending_blue;
260     int is_pending;
261 
262     int sum_around, barwidth;
263     int hoff;
264 
265     int x,y;
266     int hwidth, hheight;
267     int yoff;
268 
269     GWindow gw;
270     GFont *font;
271     int fh, as;
272     int done;
273 };
274 
HistPopup(struct hist_dlg * hist,GEvent * e)275 static void HistPopup(struct hist_dlg *hist,GEvent *e) {
276     int x = e->u.mouse.x;
277     struct hentry *h;
278     static char buffer[300];
279     char *end = buffer + sizeof(buffer)/sizeof(buffer[0]), *pt, *line;
280     int i;
281 
282     x /= hist->barwidth;
283     if ( x + hist->hoff > hist->h->high || x + hist->hoff - hist->h->low<0 )
284 return;
285 
286     h = &hist->h->hist[x + hist->hoff - hist->h->low];
287     if ( hist->sum_around==0 ) {
288 	if ( hist->which == hist_blues )
289 	    snprintf(buffer,end-buffer,
290 		    _("Position: %d\nCount: %d\n"),
291 		    x + hist->hoff,
292 		    h->sum);
293 	else
294 	    snprintf(buffer,end-buffer,
295 		    _("Width: %d\nCount: %d\nPercentage of Max: %d%%\n"),
296 		    x + hist->hoff,
297 		    h->sum, (int) rint(h->sum*100.0/hist->h->max));
298     } else {
299 	if ( hist->which == hist_blues )
300 	    snprintf(buffer,end-buffer,
301 		    _("Position: %d-%d (%d)\nCount: %d (%d)\n"),
302 		    x+hist->hoff-hist->sum_around, x+hist->hoff+hist->sum_around, x + hist->hoff,
303 		    h->sum, h->cnt);
304 	else
305 	    snprintf(buffer,end-buffer,
306 		    _("Width: %d-%d (%d)\nCount: %d (%d)\nPercentage of Max: %d%%\n"),
307 		    x+hist->hoff-hist->sum_around, x+hist->hoff+hist->sum_around, x + hist->hoff,
308 		    h->sum, h->cnt, (int) rint(h->sum*100.0/hist->h->max));
309     }
310     pt = buffer+strlen(buffer);
311     line = pt;
312     for ( i = 0; i<h->char_cnt; ++i ) {
313 	if ( pt+strlen(h->chars[i]->name)+4>end ) {
314 	    strcpy(pt,"...");
315     break;
316 	}
317 	strcpy(pt,h->chars[i]->name);
318 	pt += strlen(pt);
319 	if ( pt-line>70 ) {
320 	    *pt++ = '\n';
321 	    line = pt;
322 	} else
323 	    *pt++ = ' ';
324 	*pt = '\0';
325     }
326     GGadgetPreparePopup8(hist->gw,buffer);
327 }
328 
ArrayOrder(char * old,int args,int val1,int val2)329 static char *ArrayOrder(char *old,int args,int val1,int val2) {
330     char *end;
331     double array[40];
332     int i,j,k;
333     GString *new;
334 
335     if ( *old=='[' ) ++old;
336 
337     for ( i=0; i<40 && *old!=']' && *old!='\0'; ++i ) {
338 	array[i] = strtod(old,&end);
339 	if ( old==end )
340     break;
341 	old = end;
342 	while ( *old==' ' ) ++old;
343     }
344     if (i<40)
345         array[i++] = val1;
346     if (i<40) {
347         if ( args==2 )
348 	    array[i++] = val2;
349     }
350     for ( j=0; j<i; ++j ) for ( k=j+1; k<i; ++k ) if ( array[j]>array[k] ) {
351 	double temp = array[j];
352 	array[j] = array[k];
353 	array[k] = temp;
354     }
355 
356     new = g_string_new( "[" );
357     for ( k=0; k<i; ++k ) {
358 	if (k == i-1)
359 	    g_string_append_printf( new, "%g]", array[k] );
360 	else
361 	    g_string_append_printf( new, "%g ", array[k] );
362     }
363 
364 return( (char *) g_string_free( new, FALSE ) );
365 }
366 
367 /* Handle clicks on histogram chart and update text fields below accordingly */
HistPress(struct hist_dlg * hist,GEvent * e)368 static void HistPress(struct hist_dlg *hist,GEvent *e) {
369     char *old = NULL;
370     char *new = NULL;
371     int x = e->u.mouse.x;
372 
373     x /= hist->barwidth;
374     x += hist->hoff;
375     if ( x > hist->h->high || x<hist->h->low )
376 return;
377 
378     if ( hist->which==hist_blues ) {
379 	if ( hist->is_pending ) {
380 	    if ( x<hist->pending_blue )
381 		ff_post_error(_("Bad Value"),_("The smaller number must be selected first in a pair of bluevalues"));
382 	    else if ( x<0 ) {	/* OtherBlues */
383 		old = GGadgetGetTitle8( GWidgetGetControl( hist->gw, CID_SecondaryVal ));
384 		new = ArrayOrder( old, 2, hist->pending_blue, x );
385 		GGadgetSetTitle8( GWidgetGetControl( hist->gw, CID_SecondaryVal ), new );
386 	    } else {
387 		old = GGadgetGetTitle8( GWidgetGetControl( hist->gw, CID_MainVal ));
388 		new = ArrayOrder( old, 2, hist->pending_blue, x );
389 		GGadgetSetTitle8( GWidgetGetControl( hist->gw, CID_MainVal ), new );
390 	    }
391 	    GDrawSetCursor(hist->gw,ct_pointer);
392 	    hist->is_pending = false;
393 	} else {
394 	    hist->is_pending = true;
395 	    hist->pending_blue = x;
396 	    GDrawSetCursor(hist->gw,ct_eyedropper);
397 	}
398 	GGadgetSetVisible(GWidgetGetControl(hist->gw,CID_MainVal),!hist->is_pending);
399 	GGadgetSetVisible(GWidgetGetControl(hist->gw,CID_MainValL),!hist->is_pending);
400 	GGadgetSetVisible(GWidgetGetControl(hist->gw,CID_BlueMsg),hist->is_pending);
401     } else { /* HStem and VStem */
402 	if ( !( e->u.mouse.state&ksm_shift )) {
403 	    new = smprintf( "[%d]", x );
404 	    GGadgetSetTitle8( GWidgetGetControl( hist->gw, CID_MainVal ), new );
405 	    GGadgetSetTitle8( GWidgetGetControl( hist->gw, CID_SecondaryVal ), new );
406 	} else {
407 	    old = GGadgetGetTitle8( GWidgetGetControl( hist->gw, CID_SecondaryVal ));
408 	    new = ArrayOrder( old, 1, x, 0 );
409 	    GGadgetSetTitle8( GWidgetGetControl( hist->gw, CID_SecondaryVal ), new );
410 	}
411     }
412     free( old );
413     free( new );
414 }
415 
HistExpose(GWindow pixmap,struct hist_dlg * hist)416 static void HistExpose(GWindow pixmap, struct hist_dlg *hist) {
417     GRect r,old;
418     int height;
419     double yscale;
420     int i;
421     char buf[20];
422     GRect size;
423     GDrawGetSize(GDrawableGetWindow(GWidgetGetControl(hist->gw,CID_Histogram)),&size);
424 
425     height = size.height-hist->fh-2;
426     yscale = (4*height/5.0)/(hist->h->max-0);
427 
428     GDrawSetLineWidth(pixmap,0);
429     r.x = 0; r.y = 0;
430     r.width = size.width-1; r.height = height-1;
431     GDrawDrawRect(pixmap,&r,0x000000);
432 
433     ++r.x; r.width--;
434     ++r.y; r.height--;
435     GDrawPushClip(pixmap,&r,&old);
436 
437     for ( i=hist->hoff; (i-hist->hoff)*hist->barwidth<size.width-2 && i<=hist->h->high; ++i ) {
438 	r.x = (i-hist->hoff)*hist->barwidth+1; r.width = hist->barwidth;
439 	r.height = rint(hist->h->hist[i-hist->h->low].sum * yscale);
440 	if ( r.height>=0 ) {
441 	    r.y = height - r.height;
442 	    GDrawFillRect(pixmap,&r,0x2020ff);
443 	}
444     }
445 
446     GDrawPopClip(pixmap,&old);
447 
448     GDrawSetFont(pixmap,hist->font);
449     sprintf(buf,"%d",hist->hoff);
450     GDrawDrawText8(pixmap,0,height+2+hist->as, buf,-1,0x000000);
451     sprintf(buf,"%d",hist->hoff+hist->hwidth/hist->barwidth);
452     GDrawDrawText8(pixmap,size.width-GDrawGetText8Width(pixmap,buf,-1),height+2+hist->as,
453 	    buf,-1,0x000000);
454 }
455 
HistRExpose(GWindow pixmap,struct hist_dlg * hist)456 static void HistRExpose(GWindow pixmap, struct hist_dlg *hist) {
457     int height;
458     double yscale;
459     GRect size;
460     char buf[20];
461 
462     GDrawGetSize(GDrawableGetWindow(GWidgetGetControl(hist->gw,CID_RightSide)),&size);
463     height = size.height-hist->fh-2;
464     yscale = (4*height/5.0)/(hist->h->max-0);
465 
466     sprintf(buf,"%d",hist->h->max);
467     GDrawDrawText8(pixmap,1,height-rint(hist->h->max*yscale),
468 	    buf,-1,0x000000);
469 }
470 
HistLExpose(GWindow pixmap,struct hist_dlg * hist)471 static void HistLExpose(GWindow pixmap, struct hist_dlg *hist) {
472     int height;
473     double yscale;
474     GRect size;
475     char buf[20];
476 
477     GDrawGetSize(GDrawableGetWindow(GWidgetGetControl(hist->gw,CID_LeftSide)),&size);
478     height = size.height-hist->fh-2;
479     yscale = (4*height/5.0)/(hist->h->max-0);
480 
481     sprintf(buf,"%d",hist->h->max);
482     GDrawDrawText8(pixmap,size.width-GDrawGetText8Width(pixmap,buf,-1)-1,height-rint(hist->h->max*yscale),
483 	    buf,-1,0x000000);
484 }
485 
HistScroll(struct hist_dlg * hist,struct sbevent * sb)486 static void HistScroll(struct hist_dlg *hist,struct sbevent *sb) {
487     int newpos = hist->hoff;
488     int cols;
489     GRect size;
490     GGadget *g = GWidgetGetControl(hist->gw,CID_ScrollBar);
491 
492     GGadgetGetSize(g,&size);
493     cols = (size.width-2)/hist->barwidth;
494 
495     switch( sb->type ) {
496       case et_sb_top:
497         newpos = 0;
498       break;
499       case et_sb_uppage:
500         newpos -= cols;
501       break;
502       case et_sb_up:
503         --newpos;
504       break;
505       case et_sb_down:
506         ++newpos;
507       break;
508       case et_sb_downpage:
509         newpos += cols;
510       break;
511       case et_sb_bottom:
512         newpos = (hist->h->high+1-hist->h->low)-cols;
513       break;
514       case et_sb_thumb:
515       case et_sb_thumbrelease:
516         newpos = sb->pos;
517       break;
518     }
519     if ( newpos>(hist->h->high+1-hist->h->low)-cols + hist->h->low )
520         newpos = (hist->h->high+1-hist->h->low)-cols + hist->h->low;
521     if ( newpos<hist->h->low ) newpos = hist->h->low;
522     if ( newpos!=hist->hoff ) {
523 	/*int diff = newpos-hist->hoff;*/
524 	hist->hoff = newpos;
525 	GScrollBarSetPos(g,hist->hoff);
526 	GDrawRequestExpose(GDrawableGetWindow(GWidgetGetControl(hist->gw,CID_Histogram)),NULL,false);
527     }
528 }
529 
HistRefigureSB(struct hist_dlg * hist)530 static void HistRefigureSB(struct hist_dlg *hist) {
531     GGadget *g = GWidgetGetControl(hist->gw,CID_ScrollBar);
532     int width, hoff, cols;
533     GRect size;
534 
535     GGadgetGetSize(g,&size);
536     width = size.width-2;
537     cols = width/hist->barwidth;
538 
539     GScrollBarSetBounds(g,hist->h->low,hist->h->high+1,cols);
540     if ( hist->hoff+cols >hist->h->high ) {
541 	hoff = hist->h->high-cols;
542 	if ( hoff<0 ) hoff = 0;
543 	if ( hoff!=hist->hoff ) {
544 	    hist->hoff = hoff;
545 	    GScrollBarSetPos(g,hoff);
546 	}
547     }
548 }
549 
HistResize(struct hist_dlg * hist)550 static void HistResize(struct hist_dlg *hist) {
551 
552     HistRefigureSB(hist);
553     GDrawRequestExpose(hist->gw,NULL,false);
554 }
555 
HistSet(struct hist_dlg * hist)556 static void HistSet(struct hist_dlg *hist) {
557     char *primary, *secondary;
558     char *temp;
559     struct psdict *p = hist->private ? hist->private : hist->sf->private;
560     const unichar_t *ret1, *ret2;
561 
562     switch ( hist->which ) {
563       case hist_hstem:
564 	primary = "StdHW"; secondary = "StemSnapH";
565       break;
566       case hist_vstem:
567 	primary = "StdVW"; secondary = "StemSnapV";
568       break;
569       case hist_blues:
570 	primary = "BlueValues"; secondary = "OtherBlues";
571       break;
572     }
573     ret1 = GGadgetGetTitle(GWidgetGetControl(hist->gw,CID_MainVal));
574     ret2 = GGadgetGetTitle(GWidgetGetControl(hist->gw,CID_SecondaryVal));
575     hist->done = true;
576     if ( (*ret1=='\0' || uc_strcmp(ret1,"[]")==0 ) &&
577 	    (*ret2=='\0' || uc_strcmp(ret2,"[]")==0 ) && p==NULL )
578 return;
579     if ( p==NULL ) {
580 	hist->sf->private = p = calloc(1,sizeof(struct psdict));
581 	p->cnt = 10;
582 	p->keys = calloc(10,sizeof(char *));
583 	p->values = calloc(10,sizeof(char *));
584     }
585     PSDictChangeEntry(p,primary,temp=cu_copy(ret1)); free(temp);
586     PSDictChangeEntry(p,secondary,temp=cu_copy(ret2)); free(temp);
587 }
588 
leftside_e_h(GWindow gw,GEvent * event)589 static int leftside_e_h(GWindow gw, GEvent *event) {
590     struct hist_dlg *hist = GDrawGetUserData(gw);
591 
592     switch ( event->type ) {
593       case et_char:
594 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
595 	    help("ui/dialogs/histogram.html", NULL);
596 return( true );
597 	}
598 return( false );
599       break;
600       case et_expose:
601 	HistLExpose(gw,hist);
602       break;
603       case et_mousemove:
604       case et_mousedown:
605 	GGadgetEndPopup();
606       break;
607     }
608 return( true );
609 }
610 
rightside_e_h(GWindow gw,GEvent * event)611 static int rightside_e_h(GWindow gw, GEvent *event) {
612     struct hist_dlg *hist = GDrawGetUserData(gw);
613 
614     switch ( event->type ) {
615       case et_char:
616 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
617 	    help("ui/dialogs/histogram.html", NULL);
618 return( true );
619 	}
620 return( false );
621       break;
622       case et_expose:
623 	HistRExpose(gw,hist);
624       break;
625       case et_mousemove:
626       case et_mousedown:
627 	GGadgetEndPopup();
628       break;
629     }
630 return( true );
631 }
632 
histogram_e_h(GWindow gw,GEvent * event)633 static int histogram_e_h(GWindow gw, GEvent *event) {
634     struct hist_dlg *hist = GDrawGetUserData(gw);
635 
636     switch ( event->type ) {
637       case et_char:
638 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
639 	    help("ui/dialogs/histogram.html", NULL);
640 return( true );
641 	}
642 return( false );
643       break;
644       case et_expose:
645 	HistExpose(gw,hist);
646       break;
647       case et_mousemove:
648 	GGadgetEndPopup();
649 	HistPopup(hist,event);
650       break;
651       case et_mousedown:
652 	GGadgetEndPopup();
653 	HistPress(hist,event);
654       break;
655     }
656 return( true );
657 }
658 
hist_e_h(GWindow gw,GEvent * event)659 static int hist_e_h(GWindow gw, GEvent *event) {
660     struct hist_dlg *hist = GDrawGetUserData(gw);
661     int temp;
662     const unichar_t *ret;
663     unichar_t *end;
664 
665     if ( event->type==et_close ) {
666 	hist->done = true;
667     } else if ( event->type==et_char ) {
668 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
669 	    help("ui/dialogs/histogram.html", NULL);
670 return( true );
671 	}
672 return( false );
673     } else if ( event->type==et_resize ) {
674 	HistResize(hist);;
675     } else if ( event->type==et_mousemove ) {
676 	GGadgetEndPopup();
677     } else if ( event->type==et_mousedown ) {
678 	GGadgetEndPopup();
679     } else if ( event->type==et_controlevent ) {
680 	switch ( event->u.control.subtype ) {
681 	  case et_scrollbarchange:
682 	    HistScroll(hist,&event->u.control.u.sb);
683 	  break;
684 	  case et_textchanged:
685 	    switch( GGadgetGetCid(event->u.control.g)) {
686 	      case CID_SumAround: case CID_BarWidth:
687 		ret = _GGadgetGetTitle(event->u.control.g);
688 		temp = u_strtol(ret,&end,10);
689 		if ( temp<0 || *end )
690 	      break;
691 		if ( GGadgetGetCid(event->u.control.g)==CID_SumAround ) {
692 		    hist->sum_around = temp;
693 		    HistFindMax(hist->h,temp);
694 		} else if ( temp==0 )
695 	      break;
696 		else {
697 		    hist->barwidth = temp;
698 		    HistRefigureSB(hist);
699 		}
700 		GDrawRequestExpose(GDrawableGetWindow(GWidgetGetControl(gw,CID_Histogram)),NULL,false);
701 		GDrawRequestExpose(GDrawableGetWindow(GWidgetGetControl(gw,CID_LeftSide)),NULL,false);
702 		GDrawRequestExpose(GDrawableGetWindow(GWidgetGetControl(gw,CID_RightSide)),NULL,false);
703 	      break;
704 	    }
705 	  break;
706 	  case et_buttonactivate:
707 	    if ( GGadgetGetCid(event->u.control.g)==CID_OK ) {
708 		HistSet(hist);
709 	    } else
710 		hist->done = true;
711 	  break;
712 	}
713     }
714 return( true );
715 }
716 
CheckSmallSelection(uint8 * selected,EncMap * map,SplineFont * sf)717 static void CheckSmallSelection(uint8 *selected,EncMap *map,SplineFont *sf) {
718     int i, cnt, tot;
719 
720     for ( i=cnt=tot=0; i<map->enccount; ++i ) {
721 	int gid = map->map[i];
722 	if ( gid!=-1 && sf->glyphs[gid]!=NULL ) {
723 	    ++tot;
724 	    if ( selected[i] )
725 		++cnt;
726 	}
727     }
728     if ( (cnt==1 && tot>1) || (cnt<8 && tot>30) )
729 	ff_post_notice(_("Tiny Selection"),_("There are so few glyphs selected that it seems unlikely to me that you will get a representative sample of this aspect of your font. If you deselect everything the command will apply to all glyphs in the font"));
730 }
731 
SFHistogram(SplineFont * sf,int layer,struct psdict * private,uint8 * selected,EncMap * map,enum hist_type which)732 void SFHistogram(SplineFont *sf,int layer, struct psdict *private, uint8 *selected,
733 	EncMap *map,enum hist_type which) {
734     struct hist_dlg hist;
735     GWindow gw;
736     GRect pos;
737     GWindowAttrs wattrs;
738     GGadgetCreateData gcd[17], boxes[6], *hv[4][2], *butarray[9], *hvctls[5][5], *hvbody[3][4];
739     GTextInfo label[17];
740     int i,j;
741     char binsize[20], barwidth[20], *primary, *secondary;
742     FontRequest rq;
743     int as, ds, ld;
744     static unichar_t n9999[] = { '9', '9', '9', '9', 0 };
745     static GFont *font = NULL;
746 
747     memset(&hist,0,sizeof(hist));
748     hist.sf = sf;
749     hist.layer = layer;
750     hist.private = private;
751     if ( private==NULL ) private = sf->private;
752     hist.selected = selected;
753     hist.which = which;
754     hist.barwidth = 6;
755     hist.sum_around = 0;
756     switch ( which ) {
757       case hist_hstem:
758 	hist.h = HistFindHStemWidths(sf,layer,selected,map);
759       break;
760       case hist_vstem:
761 	hist.h = HistFindVStemWidths(sf,layer,selected,map);
762       break;
763       case hist_blues:
764 	hist.h = HistFindBlues(sf,layer,selected,map);
765       break;
766     }
767     HistFindMax(hist.h,hist.sum_around);
768 
769     if ( selected!=NULL )
770 	CheckSmallSelection(selected,map,sf);
771 
772     memset(&wattrs,0,sizeof(wattrs));
773     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
774     wattrs.event_masks = ~(1<<et_charup);
775     wattrs.restrict_input_to_me = 1;
776     wattrs.undercursor = 1;
777     wattrs.cursor = ct_pointer;
778     wattrs.utf8_window_title =  which==hist_hstem?_("HStem") :
779 					      which==hist_vstem?_("VStem"):
780 							  _("Blues");
781     wattrs.is_dlg = true;
782     pos.x = pos.y = 0;
783     pos.width = GGadgetScale(GDrawPointsToPixels(NULL,210));
784     hist.yoff = GDrawPointsToPixels(NULL,120);
785     pos.height = pos.width + hist.yoff;
786     hist.gw = gw = GDrawCreateTopWindow(NULL,&pos,hist_e_h,&hist,&wattrs);
787 
788     if ( font == NULL ) {
789 	memset(&rq,0,sizeof(rq));
790 	rq.utf8_family_name = SANS_UI_FAMILIES;
791 	rq.point_size = 10;
792 	rq.weight = 400;
793 	font = GDrawInstanciateFont(NULL,&rq);
794 	font = GResourceFindFont("Histogram.Font",font);
795     }
796     hist.font = font;
797     GDrawWindowFontMetrics(gw,hist.font,&as,&ds,&ld);
798     hist.fh = as+ds; hist.as = as;
799 
800     GDrawSetFont(gw,hist.font);
801     hist.x = 10+GDrawGetTextWidth(gw,n9999,-1);
802     hist.hwidth = pos.width - 2*hist.x;
803     hist.y = 10; hist.hheight = pos.width-20;
804 
805     memset(&gcd,0,sizeof(gcd));
806     memset(&label,0,sizeof(label));
807     memset(&boxes,0,sizeof(boxes));
808 
809     i=0;
810     gcd[i].gd.pos.width = hist.x; gcd[i].gd.pos.height = 200;
811     gcd[i].gd.flags = gg_enabled|gg_visible|gg_pos_in_pixels;
812     gcd[i].gd.cid = CID_LeftSide;
813     gcd[i].gd.u.drawable_e_h = leftside_e_h;
814     gcd[i++].creator = GDrawableCreate;
815     hvbody[0][0] = &gcd[i-1];
816 
817     gcd[i].gd.pos.width = hist.hwidth+1; gcd[i].gd.pos.height = 200;
818     gcd[i].gd.flags = gg_enabled|gg_visible|gg_pos_in_pixels;
819     gcd[i].gd.cid = CID_Histogram;
820     gcd[i].gd.u.drawable_e_h = histogram_e_h;
821     gcd[i++].creator = GDrawableCreate;
822     hvbody[0][1] = &gcd[i-1];
823 
824     gcd[i].gd.pos.width = hist.x; gcd[i].gd.pos.height = 200;
825     gcd[i].gd.flags = gg_enabled|gg_visible|gg_pos_in_pixels;
826     gcd[i].gd.cid = CID_RightSide;
827     gcd[i].gd.u.drawable_e_h = rightside_e_h;
828     gcd[i++].creator = GDrawableCreate;
829     hvbody[0][2] = &gcd[i-1];
830     hvbody[0][3] = NULL;
831 
832     hvbody[1][0] = GCD_Glue;
833     gcd[i].gd.pos.width = hist.hwidth+1;
834     gcd[i].gd.flags = gg_enabled|gg_visible|gg_pos_in_pixels;
835     gcd[i].gd.cid = CID_ScrollBar;
836     gcd[i++].creator = GScrollBarCreate;
837     hvbody[1][1] = &gcd[i-1];
838     hvbody[1][2] = GCD_Glue;
839     hvbody[1][3] = NULL;
840     hvbody[2][0] = NULL;
841 
842     boxes[2].gd.flags = gg_enabled|gg_visible;
843     boxes[2].gd.u.boxelements = &hvbody[0][0];
844     boxes[2].creator = GHVBoxCreate;
845 
846     label[i].text = (unichar_t *) _("Sum Around:");
847     label[i].text_is_1byte = true;
848     gcd[i].gd.label = &label[i];
849     gcd[i].gd.flags = gg_enabled|gg_visible;
850     gcd[i].gd.cid = CID_SumAroundL;
851     gcd[i++].creator = GLabelCreate;
852     hvctls[0][0] = &gcd[i-1];
853 
854     sprintf(binsize,"%d", hist.sum_around);
855     label[i].text = (unichar_t *) binsize;
856     label[i].text_is_1byte = true;
857     gcd[i].gd.label = &label[i];
858     gcd[i].gd.pos.width = 30;
859     gcd[i].gd.flags = gg_enabled|gg_visible;
860     gcd[i].gd.cid = CID_SumAround;
861     gcd[i++].creator = GTextFieldCreate;
862     hvctls[0][1] = &gcd[i-1];
863 
864     label[i].text = (unichar_t *) _("Bar Width:");
865     label[i].text_is_1byte = true;
866     gcd[i].gd.label = &label[i];
867     gcd[i].gd.flags = gg_enabled|gg_visible;
868     gcd[i].gd.cid = CID_BarWidthL;
869     gcd[i++].creator = GLabelCreate;
870     hvctls[0][2] = &gcd[i-1];
871 
872     sprintf(barwidth,"%d", hist.barwidth);
873     label[i].text = (unichar_t *) barwidth;
874     label[i].text_is_1byte = true;
875     gcd[i].gd.label = &label[i];
876     gcd[i].gd.pos.width = 30;
877     gcd[i].gd.flags = gg_enabled|gg_visible;
878     gcd[i].gd.cid = CID_BarWidth;
879     gcd[i++].creator = GTextFieldCreate;
880     hvctls[0][3] = &gcd[i-1];
881     hvctls[0][4] = NULL;
882 
883     label[i].text = (unichar_t *) _("BlueValues come in pairs. Select another.");
884     label[i].text_is_1byte = true;
885     label[i].fg = 0xff0000;
886     label[i].bg = GDrawGetDefaultBackground(NULL);
887     gcd[i].gd.label = &label[i];
888     gcd[i].gd.flags = gg_enabled;
889     gcd[i].gd.cid = CID_BlueMsg;
890     gcd[i++].creator = GLabelCreate;
891     hvctls[1][0] = &gcd[i-1];
892     hvctls[1][1] = hvctls[1][2] = hvctls[1][3] = GCD_ColSpan;
893     hvctls[1][4] = NULL;
894 
895     switch ( which ) {
896       case hist_hstem:
897 	label[i].text = (unichar_t *) "StdHW:";
898 	label[i+2].text = (unichar_t *) "StemSnapH:";
899 	primary = "StdHW"; secondary = "StemSnapH";
900       break;
901       case hist_vstem:
902 	label[i].text = (unichar_t *) "StdVW:";
903 	label[i+2].text = (unichar_t *) "StemSnapV:";
904 	primary = "StdVW"; secondary = "StemSnapV";
905       break;
906       case hist_blues:
907 	label[i].text = (unichar_t *) "BlueValues:";
908 	label[i+2].text = (unichar_t *) "OtherBlues:";
909 	primary = "BlueValues"; secondary = "OtherBlues";
910       break;
911     }
912     label[i].text_is_1byte = true;
913     gcd[i].gd.label = &label[i];
914     gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = gcd[i-2].gd.pos.y+28;
915     gcd[i].gd.flags = gg_enabled|gg_visible;
916     gcd[i].gd.cid = CID_MainValL;
917     gcd[i++].creator = GLabelCreate;
918     hvctls[2][0] = &gcd[i-1];
919 
920     if ( private!=NULL && (j=PSDictFindEntry(private,primary))!=-1 ) {
921 	label[i].text = (unichar_t *) private->values[j];
922 	label[i].text_is_1byte = true;
923 	gcd[i].gd.label = &label[i];
924     }
925     gcd[i].gd.pos.x = 64; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y-4;
926     gcd[i].gd.pos.width = 140;
927     gcd[i].gd.flags = gg_enabled|gg_visible;
928     gcd[i].gd.cid = CID_MainVal;
929     gcd[i++].creator = GTextFieldCreate;
930     hvctls[2][1] = &gcd[i-1];
931     hvctls[2][2] = hvctls[2][3] = GCD_ColSpan;
932     hvctls[2][4] = NULL;
933 
934     label[i].text_is_1byte = true;
935     gcd[i].gd.label = &label[i];
936     gcd[i].gd.pos.x = 5; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+28;
937     gcd[i].gd.flags = gg_enabled|gg_visible;
938     gcd[i].gd.cid = CID_SecondaryValL;
939     gcd[i++].creator = GLabelCreate;
940     hvctls[3][0] = &gcd[i-1];
941 
942     if ( private!=NULL && (j=PSDictFindEntry(private,secondary))!=-1 ) {
943 	label[i].text = (unichar_t *) private->values[j];
944 	label[i].text_is_1byte = true;
945 	gcd[i].gd.label = &label[i];
946     }
947     gcd[i].gd.pos.x = 64; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y-4;
948     gcd[i].gd.pos.width = 140;
949     gcd[i].gd.flags = gg_enabled|gg_visible;
950     gcd[i].gd.cid = CID_SecondaryVal;
951     gcd[i++].creator = GTextFieldCreate;
952     hvctls[3][1] = &gcd[i-1];
953     hvctls[3][2] = hvctls[3][3] = GCD_ColSpan;
954     hvctls[3][4] = NULL;
955     hvctls[4][0] = NULL;
956 
957     boxes[3].gd.flags = gg_enabled|gg_visible;
958     boxes[3].gd.u.boxelements = &hvctls[0][0];
959     boxes[3].creator = GHVBoxCreate;
960 
961     gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
962     label[i].text = (unichar_t *) _("_OK");
963     label[i].text_is_1byte = true;
964     label[i].text_in_resource = true;
965     gcd[i].gd.label = &label[i];
966     gcd[i].gd.cid = CID_OK;
967     gcd[i++].creator = GButtonCreate;
968     butarray[0] = GCD_Glue; butarray[1] = &gcd[i-1]; butarray[2] = GCD_Glue; butarray[3] = GCD_Glue;
969 
970     gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
971     label[i].text = (unichar_t *) _("_Cancel");
972     label[i].text_is_1byte = true;
973     label[i].text_in_resource = true;
974     gcd[i].gd.label = &label[i];
975     gcd[i].gd.mnemonic = 'C';
976     gcd[i].gd.cid = CID_Cancel;
977     gcd[i++].creator = GButtonCreate;
978     butarray[4] = GCD_Glue; butarray[5] = &gcd[i-1]; butarray[6] = GCD_Glue; butarray[7] = NULL;
979 
980     boxes[4].gd.flags = gg_enabled|gg_visible;
981     boxes[4].gd.u.boxelements = &butarray[0];
982     boxes[4].creator = GHBoxCreate;
983 
984     hv[0][0] = &boxes[2]; hv[0][1] = NULL;
985     hv[1][0] = &boxes[3]; hv[1][1] = NULL;
986     hv[2][0] = &boxes[4]; hv[2][1] = NULL; hv[3][0] = NULL;
987 
988     boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
989     boxes[0].gd.flags = gg_enabled|gg_visible;
990     boxes[0].gd.u.boxelements = &hv[0][0];
991     boxes[0].creator = GHVGroupCreate;
992 
993     GGadgetsCreate(gw,boxes);
994 
995     GHVBoxSetExpandableRow(boxes[0].ret,0);
996     GHVBoxSetExpandableCol(boxes[2].ret,1);
997     GHVBoxSetExpandableRow(boxes[2].ret,0);
998     GHVBoxSetExpandableCol(boxes[3].ret,1);
999     GHVBoxSetExpandableCol(boxes[4].ret,gb_expandglue);
1000 
1001     hist.hoff = 0;
1002     if ( hist.h->low>0 )
1003 	hist.hoff = hist.h->low;
1004     GScrollBarSetPos(GWidgetGetControl(hist.gw,CID_ScrollBar),hist.hoff);
1005     HistRefigureSB(&hist);
1006 
1007     GHVBoxFitWindow(boxes[0].ret);
1008     GDrawSetVisible(gw,true);
1009     while ( !hist.done )
1010 	GDrawProcessOneEvent(NULL);
1011     GDrawDestroyWindow(gw);
1012 
1013     HistDataFree(hist.h);
1014 }
1015