1 /* -*- coding: utf-8 -*- */
2 /* Copyright (C) 2000-2012 by George Williams */
3 /*
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6 
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9 
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13 
14  * The name of the author may not be used to endorse or promote products
15  * derived from this software without specific prior written permission.
16 
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <fontforge-config.h>
30 
31 #include "fontforgeui.h"
32 #include "fvcomposite.h"
33 #include "fvfonts.h"
34 #include "gkeysym.h"
35 #include "lookups.h"
36 #include "psfont.h"
37 #include "splinefill.h"
38 #include "splineutil.h"
39 #include "tottfgpos.h"
40 #include "ustring.h"
41 #include "utype.h"
42 
43 #include <unistd.h>
44 
45 
46 GTextInfo sizes[] = {
47     { (unichar_t *) "24", NULL, 0, 0, (void *) 24, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
48     { (unichar_t *) "36", NULL, 0, 0, (void *) 36, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
49     { (unichar_t *) "48", NULL, 0, 0, (void *) 48, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
50     { (unichar_t *) "72", NULL, 0, 0, (void *) 72, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
51     { (unichar_t *) "96", NULL, 0, 0, (void *) 96, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
52     { (unichar_t *) "200", NULL, 0, 0, (void *) 200, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
53     GTEXTINFO_EMPTY
54 };
55 enum sortby { sb_first, sb_second, sb_kern };
56 GTextInfo sortby[] = {
57     { (unichar_t *) N_("First Char"), NULL, 0, 0, (void *) sb_first, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
58     { (unichar_t *) N_("Second Char"), NULL, 0, 0, (void *) sb_second, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
59     { (unichar_t *) N_("Kern Size"), NULL, 0, 0, (void *) sb_kern, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0'},
60     GTEXTINFO_EMPTY
61 };
62 
SFShowLigatures(SplineFont * sf,SplineChar * searchfor)63 void SFShowLigatures(SplineFont *sf,SplineChar *searchfor) {
64     int i, cnt;
65     char **choices=NULL;
66     int *where=NULL;
67     SplineChar *sc, *sc2;
68     char *pt, *line;
69     char *start, *end, ch;
70     PST *pst;
71 
72     while ( 1 ) {
73 	for ( i=cnt=0; i<sf->glyphcnt; ++i ) {
74 	    if ( (sc=sf->glyphs[i])!=NULL && SCDrawsSomething(sc) ) {
75 		for ( pst=sc->possub; pst!=NULL; pst=pst->next )
76 			if ( pst->type==pst_ligature &&
77 				(searchfor==NULL || PSTContains(pst->u.lig.components,searchfor->name))) {
78 		    if ( choices!=NULL ) {
79 			line = pt = malloc((strlen(sc->name)+13+3*strlen(pst->u.lig.components)));
80 			strcpy(pt,sc->name);
81 			pt += strlen(pt);
82 			if ( sc->unicodeenc!=-1 && sc->unicodeenc<0x10000 ) {
83 			    *pt++='(';
84 			    pt = utf8_idpb(pt,sc->unicodeenc,0);
85 			    *pt++=')';
86 			}
87 			/* *pt++ = 0x21d0;*/ /* left arrow */
88 			strcpy(pt," ⇐ "); pt += strlen(pt);
89 			for ( start= pst->u.lig.components; ; start=end ) {
90 			    while ( *start==' ' ) ++start;
91 			    if ( *start=='\0' )
92 			break;
93 			    for ( end=start+1; *end!='\0' && *end!=' '; ++end );
94 			    ch = *end;
95 			    *end = '\0';
96 			    strcpy( pt,start );
97 			    pt += strlen(pt);
98 			    sc2 = SFGetChar(sf,-1,start);
99 			    *end = ch;
100 			    if ( sc2!=NULL && sc2->unicodeenc!=-1 && sc2->unicodeenc<0x10000 ) {
101 				*pt++='(';
102 				*pt++ = sc2->unicodeenc;
103 				*pt++=')';
104 			    }
105 			    *pt++ = ' ';
106 			}
107 			pt[-1] = '\0';
108 			choices[cnt] = line;
109 			where[cnt] = i;
110 		    }
111 		    ++cnt;
112 		}
113 	    }
114 	}
115 	if ( choices!=NULL )
116     break;
117 	choices = malloc((cnt+2)*sizeof(unichar_t *));
118 	where = malloc((cnt+1)*sizeof(int));
119 	if ( cnt==0 ) {
120 	    choices[0] = copy("<No Ligatures>");
121 	    where[0] = -1;
122 	    choices[1] = NULL;
123     break;
124 	}
125     }
126     choices[cnt] = NULL;
127     i = gwwv_choose(_("Ligatures"),(const char **) choices,cnt,0,_("Select a ligature to view"));
128     if ( i!=-1 && where[i]!=-1 )
129 	CharViewCreate(sf->glyphs[where[i]],(FontView *) sf->fv,-1);
130     free(where);
131     for ( i=0; i<cnt; ++i )
132 	free(choices[i]);
133     free(choices);
134 }
135 
136 struct kerns {
137     SplineChar *first;
138     SplineChar *second;
139     int newoff, newyoff;
140     unsigned int r2l: 1;
141     KernPair *kp;
142     AnchorClass *ac;
143 };
144 
145 typedef struct kpdata {
146     GWindow gw,v;
147     int vwidth;
148     SplineFont *sf;
149     SplineChar *sc;		/* If set then restrict to kerns of this char */
150 				/*  in either position */
151     AnchorClass *ac;		/* If set then don't do kerns, but look for */
152 				/*  anchor combos with this class. If -1 */
153 			        /*  then all anchor combos with any class */
154     struct kerns *kerns;	/* All the kerns we care about */
155     int layer;
156     int kcnt, firstcnt;
157     BDFFont *bdf;
158     int header_height;
159     int sb_width;
160     GFont *font;
161     int fh, as;
162     int uh, wh, off_top, selected, last_index, vpad;
163     int pressed_x, old_val;
164     unsigned int done:1;
165     unsigned int first:1;
166     unsigned int pressed:1;
167     unsigned int movecursor:1;
168 } KPData;
169 
170 #define CID_Size	1001
171 #define CID_SortBy	1002
172 #define CID_ScrollBar	1003
173 #define CID_OK		1004
174 #define CID_Cancel	1005
175 
firstcmpr(const void * _k1,const void * _k2)176 static int firstcmpr(const void *_k1, const void *_k2) {
177     const struct kerns *k1 = _k1, *k2 = _k2;
178 
179     if ( k1->first==k2->first )		/* If same first char, use second char as tie breaker */
180 return( k1->second->unicodeenc-k2->second->unicodeenc );
181 
182 return( k1->first->unicodeenc-k2->first->unicodeenc );
183 }
184 
secondcmpr(const void * _k1,const void * _k2)185 static int secondcmpr(const void *_k1, const void *_k2) {
186     const struct kerns *k1 = _k1, *k2 = _k2;
187 
188     if ( k1->second==k2->second )		/* If same second char, use first char as tie breaker */
189 return( k1->first->unicodeenc-k2->first->unicodeenc );
190 
191 return( k1->second->unicodeenc-k2->second->unicodeenc );
192 }
193 
offcmpr(const void * _k1,const void * _k2)194 static int offcmpr(const void *_k1, const void *_k2) {
195     const struct kerns *k1 = _k1, *k2 = _k2;
196     int off1, off2;
197 
198     if ( (off1=k1->newoff)<0 ) off1 = -off1;
199     if ( (off2=k2->newoff)<0 ) off2 = -off2;
200 
201     if ( off1!=off2 )		/* If same offset, use first char as tie breaker */
202 return( off1-off2 );
203 
204     if ( k1->first!=k2->first )		/* If same first char, use second char as tie breaker */
205 return( k1->first->unicodeenc-k2->first->unicodeenc );
206 
207 return( k1->second->unicodeenc-k2->second->unicodeenc );
208 }
209 
KPSortEm(KPData * kpd,enum sortby sort_func)210 static void KPSortEm(KPData *kpd,enum sortby sort_func) {
211     int oldenc;
212 
213     if ( sort_func==sb_first || sort_func==sb_second ) {
214 	if ( kpd->sc!=NULL ) {
215 	    oldenc = kpd->sc->unicodeenc;
216 	    kpd->sc->unicodeenc = -1;
217 	}
218 	qsort(kpd->kerns,kpd->kcnt,sizeof(struct kerns),
219 		sort_func==sb_first ? firstcmpr : secondcmpr );
220 	if ( kpd->sc!=NULL )
221 	    kpd->sc->unicodeenc = oldenc;
222     } else
223 	qsort(kpd->kerns,kpd->kcnt,sizeof(struct kerns), offcmpr );
224 
225     if ( sort_func==sb_first ) {
226 	int cnt=1, i;
227 	for ( i=1; i<kpd->kcnt; ++i ) {
228 	    if ( kpd->kerns[i].first!=kpd->kerns[i-1].first )
229 		++cnt;
230 	}
231 	kpd->firstcnt = cnt;
232     }
233 }
234 
CheckLeftRight(struct kerns * k)235 static void CheckLeftRight(struct kerns *k) {
236     /* flag any hebrew/arabic entries */
237 
238     /* Figure that there won't be any mixed orientation kerns (no latin "A" with hebrew "Alef" kern) */
239     /*  but there might be some hebrew/arabic ligatures or something that */
240     /*  we don't recognize as right-to-left (ie. not in unicode) */
241     if ( SCRightToLeft(k->first) || SCRightToLeft(k->second) )
242 	k->r2l = true;
243     else
244         k->r2l = false;
245 }
246 
KPBuildKernList(KPData * kpd)247 static void KPBuildKernList(KPData *kpd) {
248     int i, cnt;
249     KernPair *kp;
250 
251     if ( kpd->sc!=NULL ) {
252 	while ( 1 ) {
253 	    for ( cnt=0, kp=kpd->sc->kerns; kp!=NULL; kp=kp->next ) {
254 		if ( kpd->kerns!=NULL ) {
255 		    kpd->kerns[cnt].first = kpd->sc;
256 		    kpd->kerns[cnt].second = kp->sc;
257 		    kpd->kerns[cnt].newoff = kp->off;
258 		    kpd->kerns[cnt].newyoff = 0;
259 		    kpd->kerns[cnt].kp = kp;
260 		    kpd->kerns[cnt].ac = NULL;
261 		    CheckLeftRight(&kpd->kerns[cnt]);
262 		}
263 		++cnt;
264 	    }
265 	    for ( i=0; i<kpd->sf->glyphcnt; ++i ) if ( kpd->sf->glyphs[i]!=NULL ) {
266 		for ( kp = kpd->sf->glyphs[i]->kerns; kp!=NULL; kp=kp->next ) {
267 		    if ( kp->sc == kpd->sc ) {
268 			if ( kpd->kerns!=NULL ) {
269 			    kpd->kerns[cnt].first = kpd->sf->glyphs[i];
270 			    kpd->kerns[cnt].second = kp->sc;
271 			    kpd->kerns[cnt].newoff = kp->off;
272 			    kpd->kerns[cnt].newyoff = 0;
273 			    kpd->kerns[cnt].kp = kp;
274 			    kpd->kerns[cnt].ac = NULL;
275 			    CheckLeftRight(&kpd->kerns[cnt]);
276 			}
277 			++cnt;
278 		break;
279 		    }
280 		}
281 	    }
282 	    if ( kpd->kerns!=NULL )
283 	break;
284 	    if ( cnt==0 )
285 return;
286 	    kpd->kerns = calloc(cnt+1, sizeof(struct kerns));
287 	    kpd->kcnt = cnt;
288 	}
289     } else {
290 	while ( 1 ) {
291 	    for ( cnt=i=0; i<kpd->sf->glyphcnt; ++i ) if ( kpd->sf->glyphs[i]!=NULL ) {
292 		for ( kp = kpd->sf->glyphs[i]->kerns; kp!=NULL; kp=kp->next ) {
293 		    if ( kpd->kerns!=NULL ) {
294 			kpd->kerns[cnt].first = kpd->sf->glyphs[i];
295 			kpd->kerns[cnt].second = kp->sc;
296 			kpd->kerns[cnt].newoff = kp->off;
297 			kpd->kerns[cnt].newyoff = 0;
298 			kpd->kerns[cnt].kp = kp;
299 			kpd->kerns[cnt].ac = NULL;
300 			CheckLeftRight(&kpd->kerns[cnt]);
301 		    }
302 		    ++cnt;
303 		}
304 	    }
305 	    if ( kpd->kerns!=NULL )
306 	break;
307 	    if ( cnt==0 )
308 return;
309 	    kpd->kerns = calloc(cnt+1, sizeof(struct kerns));
310 	    kpd->kcnt = cnt;
311 	}
312     }
313     KPSortEm(kpd,sb_first);
314 }
315 
AnchorRefigure(KPData * kpd)316 static void AnchorRefigure(KPData *kpd) {
317     AnchorPoint *ap1, *ap2;
318     DBounds bb;
319     int i;
320 
321     for ( i=0; i<kpd->kcnt; ++i ) {
322 	struct kerns *k= &kpd->kerns[i];
323 	for ( ap1=k->first->anchor; ap1!=NULL && ap1->anchor!=k->ac; ap1=ap1->next );
324 	for ( ap2=k->second->anchor; ap2!=NULL && ap2->anchor!=k->ac; ap2=ap2->next );
325 	if ( ap1!=NULL && ap2!=NULL ) {
326 	    if ( k->r2l ) {
327 		SplineCharQuickBounds(k->second,&bb);
328 		k->newoff = k->second->width-ap1->me.x + ap2->me.x;
329 	    } else
330 		k->newoff = -k->first->width+ap1->me.x-ap2->me.x;
331 	    k->newyoff = ap1->me.y-ap2->me.y;
332 	}
333     }
334 }
335 
KPBuildAnchorList(KPData * kpd)336 static void KPBuildAnchorList(KPData *kpd) {
337     int i, j, cnt;
338     AnchorClass *ac;
339     AnchorPoint *ap1, *ap2, *temp;
340     SplineFont *sf = kpd->sf;
341     DBounds bb;
342 
343     if ( kpd->sc!=NULL ) {
344 	while ( 1 ) {
345 	    cnt = 0;
346 	    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
347 		if ( (ac = AnchorClassMatch(kpd->sc,sf->glyphs[i],kpd->ac,&ap1,&ap2)) ||
348 			(ac = AnchorClassMatch(sf->glyphs[i],kpd->sc,kpd->ac,&ap1,&ap2)) ) {
349 		    if ( kpd->kerns!=NULL ) {
350 			struct kerns *k = &kpd->kerns[cnt];
351 			switch ( ap1->type ) {
352 			  case at_cexit: case at_basechar: case at_baselig: case at_basemark:
353 			    k->first = kpd->sc;
354 			    k->second = sf->glyphs[i];
355 			  break;
356 			  case at_centry: case at_mark:
357 			    k->first = sf->glyphs[i];
358 			    k->second = kpd->sc;
359 			    temp = ap1; ap1=ap2; ap2=temp;
360 			  break;
361 			}
362 			CheckLeftRight(k);
363 			if ( k->r2l ) {
364 			    SplineCharQuickBounds(k->second,&bb);
365 			    k->newoff = k->second->width-ap1->me.x + ap2->me.x;
366 			} else
367 			    k->newoff = -k->first->width+ap1->me.x-ap2->me.x;
368 			k->newyoff = ap1->me.y-ap2->me.y;
369 			k->ac = ac;
370 			k->kp = NULL;
371 		    }
372 		    ++cnt;
373 		}
374 	    }
375 	    if ( kpd->kerns!=NULL )
376 	break;
377 	    if ( cnt==0 )
378 return;
379 	    kpd->kerns = malloc((cnt+1)*sizeof(struct kerns));
380 	    kpd->kcnt = cnt;
381 	}
382     } else {
383 	while ( 1 ) {
384 	    cnt = 0;
385 	    for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL && sf->glyphs[i]->anchor ) {
386 		if ( kpd->ac!=(AnchorClass *) -1 ) {
387 		    for ( temp = sf->glyphs[i]->anchor; temp!=NULL && temp->anchor!=kpd->ac; temp=temp->next );
388 		    if ( temp==NULL )
389 	    continue;
390 		}
391 		for ( j=0; j<sf->glyphcnt; ++j ) if ( sf->glyphs[j]!=NULL ) {
392 		    if ( (ac = AnchorClassMatch(sf->glyphs[i],sf->glyphs[j],kpd->ac,&ap1,&ap2)) ) {
393 			if ( kpd->kerns!=NULL ) {
394 			    struct kerns *k = &kpd->kerns[cnt];
395 			    k->first = sf->glyphs[i];
396 			    k->second = sf->glyphs[j];
397 			    CheckLeftRight(k);
398 			    if ( k->r2l ) {
399 				SplineCharQuickBounds(k->second,&bb);
400 				k->newoff = k->second->width-ap1->me.x + ap2->me.x;
401 			    } else
402 				k->newoff = -k->first->width+ap1->me.x-ap2->me.x;
403 			    k->newyoff = ap1->me.y-ap2->me.y;
404 			    k->ac = ac;
405 			    k->kp = NULL;
406 			}
407 			++cnt;
408 		    }
409 		}
410 	    }
411 	    if ( kpd->kerns!=NULL )
412 	break;
413 	    if ( cnt==0 )
414 return;
415 	    kpd->kerns = malloc((cnt+1)*sizeof(struct kerns));
416 	    kpd->kcnt = cnt;
417 	}
418     }
419     KPSortEm(kpd,sb_first);
420     AnchorRefigure(kpd);
421 }
422 
KPScrollTo(KPData * kpd,unichar_t uch,enum sortby sort)423 static void KPScrollTo(KPData *kpd, unichar_t uch, enum sortby sort) {
424     int i;
425 
426     if ( sort==sb_first ) {
427 	for ( i=0; i<kpd->kcnt && kpd->kerns[i].first->unicodeenc<uch; ++i );
428     } else {
429 	for ( i=0; i<kpd->kcnt && kpd->kerns[i].second->unicodeenc<uch; ++i );
430     }
431     if ( kpd->wh<=2 )
432 	/* As is */;
433     else if ( kpd->wh<5 )
434 	--i;
435     else
436 	i -= kpd->wh/5;
437 
438     if ( i>kpd->kcnt-kpd->wh )
439 	i = kpd->kcnt-kpd->wh;
440     if ( i<0 )
441 	i = 0;
442     if ( i!=kpd->off_top ) {
443 	int off = i-kpd->off_top;
444 	kpd->off_top = i;
445 	GScrollBarSetPos(GWidgetGetControl(kpd->gw,CID_ScrollBar),kpd->off_top);
446 	GDrawScroll(kpd->v,NULL,0,off*kpd->uh);
447     }
448 }
449 
BaseFillFromBDFC(struct _GImage * base,BDFChar * bdfc)450 static void BaseFillFromBDFC(struct _GImage *base,BDFChar *bdfc) {
451     base->data = bdfc->bitmap;
452     base->bytes_per_line = bdfc->bytes_per_line;
453     base->width = bdfc->xmax-bdfc->xmin+1;
454     base->height = bdfc->ymax-bdfc->ymin+1;
455 }
456 
KP_ExposeKerns(KPData * kpd,GWindow pixmap,GRect * rect)457 static void KP_ExposeKerns(KPData *kpd,GWindow pixmap,GRect *rect) {
458     GRect old, subclip, subold, sel;
459     struct _GImage base;
460     GImage gi;
461     int index1, index2;
462     BDFChar *bdfc1, *bdfc2;
463     int i, as, x, em = kpd->sf->ascent+kpd->sf->descent, yoff;
464     int first, last;
465     struct kerns *kern;
466     char buffer[140];
467 
468     first = rect->y/kpd->uh;
469     last = (rect->y+rect->height+kpd->uh-1)/kpd->uh;
470 
471     for ( i=first; i<=last && i+kpd->off_top<kpd->kcnt; ++i ) {
472 	kern = &kpd->kerns[i+kpd->off_top];
473 	index1 = kern->first->orig_pos;
474 	if ( kpd->bdf->glyphs[index1]==NULL )
475 	    BDFPieceMeal(kpd->bdf,index1);
476 	index2 = kern->second->orig_pos;
477 	if ( kpd->bdf->glyphs[index2]==NULL )
478 	    BDFPieceMeal(kpd->bdf,index2);
479     }
480 
481     as = kpd->vpad + kpd->sf->ascent * kpd->bdf->pixelsize / em;
482 
483     memset(&gi,'\0',sizeof(gi));
484     memset(&base,'\0',sizeof(base));
485     gi.u.image = &base;
486     base.image_type = it_index;
487     base.clut = kpd->bdf->clut;
488     GDrawSetDither(NULL, false);
489 
490     GDrawPushClip(pixmap,rect,&old);
491     GDrawSetFont(pixmap,kpd->font);
492     GDrawSetLineWidth(pixmap,0);
493     GDrawFillRect(pixmap,rect,GDrawGetDefaultBackground(NULL));
494     subclip = *rect;
495     for ( i=first; i<=last && i+kpd->off_top<kpd->kcnt; ++i ) {
496 	subclip.y = i*kpd->uh; subclip.height = kpd->uh;
497 	GDrawPushClip(pixmap,&subclip,&subold);
498 
499 	kern = &kpd->kerns[i+kpd->off_top];
500 	index1 = kern->first->orig_pos;
501 	index2 = kern->second->orig_pos;
502 	bdfc1 = kpd->bdf->glyphs[index1];
503 	bdfc2 = kpd->bdf->glyphs[index2];
504 
505 	BaseFillFromBDFC(&base,bdfc1);
506 	base.trans = base.clut->trans_index = -1;
507 	/* the peculiar behavior concerning xmin/xmax is because the bitmaps */
508 	/*  don't contain the side-bearings, we have to add that spacing manually */
509 	if ( !kern->r2l ) {
510 	    GDrawDrawImage(pixmap,&gi,NULL, 10,subclip.y+as-bdfc1->ymax);
511 	    x = 10 + (bdfc1->width-bdfc1->xmin) + bdfc2->xmin +
512 		    (kern->newoff*kpd->bdf->pixelsize/em);
513 	} else {
514 	    x = kpd->vwidth-10-(bdfc1->xmax-bdfc1->xmin);
515 	    GDrawDrawImage(pixmap,&gi,NULL, x,subclip.y+as-bdfc1->ymax);
516 	    x -= bdfc1->xmin + (bdfc2->width-bdfc2->xmin) +
517 		    (kern->newoff*kpd->bdf->pixelsize/em);
518 	}
519 	BaseFillFromBDFC(&base,bdfc2);
520 #ifndef _BrokenBitmapImages
521 	base.trans = base.clut->trans_index = 0;
522 #endif
523 	yoff = (kern->newyoff*kpd->bdf->pixelsize/em);
524 	GDrawDrawImage(pixmap,&gi,NULL, x,subclip.y+as-bdfc2->ymax-yoff);
525 	GDrawDrawLine(pixmap,0,subclip.y+kpd->uh-1,
526 		subclip.x+subclip.width,subclip.y+kpd->uh-1,0x000000);
527 	if ( kern->kp!=NULL )
528 	    sprintf( buffer, "%d ", kern->newoff);
529 	else
530 	    sprintf( buffer, "%d,%d ", kern->newoff, kern->newyoff );
531 	if ( kern->ac!=NULL )
532 	    strncat(buffer,kern->ac->name,sizeof(buffer)-strlen(buffer)-1);
533 	GDrawDrawText8(pixmap,15,subclip.y+kpd->uh-kpd->fh+kpd->as,buffer,-1,
534 		kern->kp!=NULL && kern->newoff!=kern->kp->off ? 0xff0000 : 0x000000 );
535 	if ( i+kpd->off_top==kpd->selected ) {
536 	    sel.x = 0; sel.width = kpd->vwidth-1;
537 	    sel.y = subclip.y; sel.height = kpd->uh-2;
538 	    GDrawDrawRect(pixmap,&sel,0x000000);
539 	}
540 	GDrawPopClip(pixmap,&subold);
541     }
542 #ifndef _BrokenBitmapImages
543     base.clut->trans_index = -1;
544 #endif
545     GDrawPopClip(pixmap,&old);
546     GDrawSetDither(NULL, true);
547 }
548 
KP_RefreshSel(KPData * kpd,int index)549 static void KP_RefreshSel(KPData *kpd,int index) {
550     Color col = index==kpd->selected ? 0x000000 : GDrawGetDefaultBackground(NULL);
551     GRect sel;
552 
553     if ( index==-1 )
554 return;
555     sel.x = 0; sel.width = kpd->vwidth-1;
556     sel.y = (index-kpd->off_top)*kpd->uh; sel.height = kpd->uh-2;
557     GDrawSetLineWidth(kpd->v,0);
558     GDrawDrawRect(kpd->v,&sel,col);
559 }
560 
KP_RefreshKP(KPData * kpd,int index)561 static void KP_RefreshKP(KPData *kpd,int index) {
562     GRect sel;
563 
564     if ( index<kpd->off_top || index>kpd->off_top+kpd->wh )
565 return;
566     sel.x = 0; sel.width = kpd->vwidth;
567     sel.y = (index-kpd->off_top)*kpd->uh; sel.height = kpd->uh;
568     GDrawRequestExpose(kpd->v,&sel,false);
569 }
570 
KP_KernClassAlter(KPData * kpd,int index)571 static void KP_KernClassAlter(KPData *kpd,int index) {
572     KernPair *kp = kpd->kerns[index].kp;
573     int kc_pos, kc_pos2;
574     KernClass *kc = SFFindKernClass(kpd->sf,kpd->kerns[index].first,kpd->kerns[index].second,
575 	    &kc_pos,false);
576     int i;
577 
578     if ( kc==NULL )
579 return;
580 
581     for ( i=0; i<kpd->kcnt; ++i ) if ( i!=index &&
582 	    kpd->kerns[i].kp->kcid==kp->kcid &&
583 	    kpd->kerns[i].kp->off==kp->off ) {
584 	if ( SFFindKernClass(kpd->sf,kpd->kerns[i].first,kpd->kerns[i].second,
585 		&kc_pos2,false)==kc && kc_pos==kc_pos2 ) {
586 	    kpd->kerns[i].newoff = kpd->kerns[index].newoff;
587 	    KP_RefreshKP(kpd,i);
588 	}
589     }
590 }
591 
KP_Inside(KPData * kpd,GEvent * e)592 static BDFChar *KP_Inside(KPData *kpd, GEvent *e) {
593     struct kerns *kern;
594     int index1, index2, i;
595     int baseline, x, em = kpd->sf->ascent+kpd->sf->descent;
596     BDFChar *bdfc1, *bdfc2;
597 
598     i = e->u.mouse.y/kpd->uh + kpd->off_top;
599     if ( i>=kpd->kcnt )
600 return( NULL );
601 
602     kern = &kpd->kerns[i];
603     index1 = kern->first->orig_pos;
604     index2 = kern->second->orig_pos;
605     bdfc1 = kpd->bdf->glyphs[index1];
606     bdfc2 = kpd->bdf->glyphs[index2];
607     if ( bdfc1 ==NULL || bdfc2==NULL )
608 return( NULL );
609     if ( !kern->r2l )
610 	x = 10 + (bdfc1->width-bdfc1->xmin) + bdfc2->xmin +
611 		(kpd->kerns[i].newoff*kpd->bdf->pixelsize/em);
612     else
613 	x = kpd->vwidth-10- (bdfc1->xmax-bdfc1->xmin) - bdfc1->xmin -
614 		(bdfc2->width-bdfc2->xmin) -
615 		(kern->newoff*kpd->bdf->pixelsize/em);
616     if ( e->u.mouse.x < x || e->u.mouse.x>= x+bdfc2->xmax-bdfc2->xmin )
617 return( NULL );
618 
619     baseline = (i-kpd->off_top)*kpd->uh + kpd->sf->ascent * kpd->bdf->pixelsize / em + kpd->vpad;
620     if ( e->u.mouse.y < baseline-bdfc2->ymax || e->u.mouse.y >= baseline-bdfc2->ymin )
621 return( NULL );
622 
623 return( bdfc2 );
624 }
625 
KP_SetCursor(KPData * kpd,int ismove)626 static void KP_SetCursor(KPData *kpd, int ismove ) {
627 
628     if ( kpd->movecursor!=ismove ) {
629 	GDrawSetCursor(kpd->v,ismove ? ct_leftright : ct_mypointer );
630 	kpd->movecursor = ismove;
631     }
632 }
633 
KP_Cursor(KPData * kpd,GEvent * e)634 static BDFChar *KP_Cursor(KPData *kpd, GEvent *e) {
635     if ( kpd->ac==NULL ) {
636 	BDFChar *bdfc2 = KP_Inside(kpd,e);
637 
638 	KP_SetCursor(kpd,bdfc2!=NULL );
639 return( bdfc2 );
640     }
641 return( NULL );
642 }
643 
KP_ScrollTo(KPData * kpd,int where)644 static void KP_ScrollTo(KPData *kpd,int where) {
645     /* Make sure the line "where" is visible */
646 
647     if ( where<kpd->off_top || where>=kpd->off_top+kpd->wh ) {
648 	where -= kpd->wh/4;
649 	if ( where>kpd->kcnt-kpd->wh )
650 	    where = kpd->kcnt-kpd->wh;
651 	if ( where<0 ) where = 0;
652 	kpd->off_top = where;
653 	GScrollBarSetPos(GWidgetGetControl(kpd->gw,CID_ScrollBar),where);
654 	GDrawRequestExpose(kpd->v,NULL,false);
655     }
656 }
657 
KPRemove(KPData * kpd)658 static void KPRemove(KPData *kpd) {
659     if ( kpd->selected!=-1 ) {
660 	kpd->kerns[kpd->selected].newoff = 0;
661 	GDrawRequestExpose(kpd->v,NULL,false);
662     }
663 }
664 
KP_Commands(KPData * kpd,GEvent * e)665 static void KP_Commands(KPData *kpd, GEvent *e) {
666     int old_sel, amount;
667 
668     switch( e->u.chr.keysym ) {
669       case '\177':
670 	KPRemove(kpd);
671       break;
672       case 'z': case 'Z':
673 	if ( e->u.chr.state&ksm_control ) {
674 	    if ( kpd->last_index!=-1 ) {
675 		kpd->kerns[kpd->last_index].newoff = kpd->old_val;
676 		KP_RefreshKP(kpd,kpd->last_index);
677 		if ( kpd->kerns[kpd->last_index].kp->kcid!=0 )
678 		    KP_KernClassAlter(kpd,kpd->last_index);
679 		kpd->last_index = -1;
680 	    }
681 	} else if ( e->u.chr.state&ksm_meta ) {
682 	    if ( kpd->selected!=-1 ) {
683 		kpd->kerns[kpd->selected].newoff = kpd->kerns[kpd->selected].kp->off;
684 		KP_RefreshKP(kpd,kpd->selected);
685 		if ( kpd->kerns[kpd->selected].kp->kcid!=0 )
686 		    KP_KernClassAlter(kpd,kpd->selected);
687 		kpd->last_index = -1;
688 	    }
689 	}
690       break;
691       case GK_Up: case GK_KP_Up:
692 	old_sel = kpd->selected;
693 	if ( kpd->selected<=0 )
694 	    kpd->selected = kpd->kcnt-1;
695 	else
696 	    --kpd->selected;
697 	KP_RefreshSel(kpd,old_sel);
698 	KP_RefreshSel(kpd,kpd->selected);
699 	KP_ScrollTo(kpd,kpd->selected);
700       break;
701       case GK_Down: case GK_KP_Down:
702 	old_sel = kpd->selected;
703 	if ( kpd->selected==-1 || kpd->selected==kpd->kcnt-1 )
704 	    kpd->selected = 0;
705 	else
706 	    ++kpd->selected;
707 	KP_RefreshSel(kpd,old_sel);
708 	KP_RefreshSel(kpd,kpd->selected);
709 	KP_ScrollTo(kpd,kpd->selected);
710       break;
711       case GK_Left: case GK_KP_Left: case GK_Right: case GK_KP_Right:
712 	amount = e->u.chr.keysym==GK_Left || e->u.chr.keysym==GK_KP_Left? -1 : 1;
713 	if ( e->u.chr.state&(ksm_shift|ksm_control|ksm_meta) ) amount *= 10;
714 	if ( kpd->selected!=-1 ) {
715 	    KP_ScrollTo(kpd,kpd->selected);
716 	    kpd->last_index = kpd->selected;
717 	    kpd->old_val = kpd->kerns[kpd->selected].newoff;
718 	    kpd->kerns[kpd->selected].newoff += amount;
719 	    KP_RefreshKP(kpd,kpd->selected);
720 	    if ( kpd->kerns[kpd->selected].kp->kcid!=0 )
721 		KP_KernClassAlter(kpd,kpd->selected);
722 	}
723       break;
724     }
725 }
726 
KPV_Resize(KPData * kpd)727 static void KPV_Resize(KPData *kpd) {
728     GRect size;
729     GGadget *sb;
730 
731     GDrawGetSize(kpd->v,&size);
732     kpd->wh = size.height/kpd->uh;
733 
734     sb = GWidgetGetControl(kpd->gw,CID_ScrollBar);
735     GScrollBarSetBounds(sb,0,kpd->kcnt,kpd->wh);
736     if ( kpd->off_top>kpd->kcnt-kpd->wh )
737 	kpd->off_top = kpd->kcnt-kpd->wh;
738     if ( kpd->off_top<0 )
739 	kpd->off_top = 0;
740     GScrollBarSetPos(sb,kpd->off_top);
741     kpd->vwidth = size.width;
742     GDrawRequestExpose(kpd->v,NULL,false);
743     GDrawRequestExpose(kpd->gw,NULL,false);
744 }
745 
KP_Resize(KPData * kpd)746 static void KP_Resize(KPData *kpd) {
747 
748     kpd->uh = (4*kpd->bdf->pixelsize/3)+kpd->fh+6;
749     kpd->vpad = kpd->bdf->pixelsize/5 + 3;
750 }
751 
KP_ChangeSize(GGadget * g,GEvent * e)752 static int KP_ChangeSize(GGadget *g, GEvent *e) {
753     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
754 	KPData *kpd = GDrawGetUserData(GGadgetGetWindow(g));
755 	int newsize = (intpt) (GGadgetGetListItemSelected(g)->userdata);
756 	BDFFont *temp;
757 	if ( newsize==kpd->bdf->pixelsize )
758 return( true );
759 	temp = SplineFontPieceMeal(kpd->sf,kpd->layer,newsize,72,true,NULL);
760 	BDFFontFree(kpd->bdf);
761 	kpd->bdf = temp;
762 	KP_Resize(kpd);
763 	KPV_Resize(kpd);
764     }
765 return( true );
766 }
767 
KP_ChangeSort(GGadget * g,GEvent * e)768 static int KP_ChangeSort(GGadget *g, GEvent *e) {
769     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
770 	KPData *kpd = GDrawGetUserData(GGadgetGetWindow(g));
771 	KernPair *old = kpd->selected==-1 ? NULL : kpd->kerns[kpd->selected].kp;
772 	int i;
773 
774 	KPSortEm(kpd,GGadgetGetFirstListSelectedItem(g));
775 	for ( i=0 ; i<kpd->kcnt; ++i )
776 	    if ( kpd->kerns[i].kp==old ) {
777 		kpd->selected = i;
778 		KP_ScrollTo(kpd,i);
779 	break;
780 	    }
781 	GDrawRequestExpose(kpd->v,NULL,false);
782     }
783 return( true );
784 }
785 
KP_Scrolled(GGadget * g,GEvent * e)786 static int KP_Scrolled(GGadget *g, GEvent *e) {
787     if ( e->type==et_controlevent && e->u.control.subtype == et_scrollbarchange ) {
788 	KPData *kpd = GDrawGetUserData(GGadgetGetWindow(g));
789 	int newpos = kpd->off_top;
790 
791 	switch( e->u.control.u.sb.type ) {
792 	  case et_sb_top:
793 	    newpos = 0;
794 	  break;
795 	  case et_sb_halfup:
796 	  case et_sb_uppage:
797 	    newpos -= kpd->wh==1?1:kpd->wh-1;
798 	  break;
799 	  case et_sb_up:
800 	    newpos -= 1;
801 	  break;
802 	  case et_sb_halfdown:
803 	  case et_sb_down:
804 	    newpos += 1;
805 	  break;
806 	  case et_sb_downpage:
807 	    newpos += kpd->wh==1?1:kpd->wh-1;
808 	  break;
809 	  case et_sb_bottom:
810 	    newpos = kpd->kcnt-kpd->wh;
811 	  break;
812 	  case et_sb_thumb:
813 	  case et_sb_thumbrelease:
814 	    newpos = e->u.control.u.sb.pos;
815 	  break;
816 	}
817 	if ( newpos>kpd->kcnt-kpd->wh )
818 	    newpos = kpd->kcnt-kpd->wh;
819 	if ( newpos<0 )
820 	    newpos = 0;
821 	if ( newpos!=kpd->off_top ) {
822 	    int off = newpos-kpd->off_top;
823 	    kpd->off_top = newpos;
824 	    GScrollBarSetPos(g,kpd->off_top);
825 	    GDrawScroll(kpd->v,NULL,0,off*kpd->uh);
826 	}
827     }
828 return( true );
829 }
830 
KP_OK(GGadget * g,GEvent * e)831 static int KP_OK(GGadget *g, GEvent *e) {
832     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
833 	KPData *kpd = GDrawGetUserData(GGadgetGetWindow(g));
834 	int i;
835 	FontView *fv; MetricsView *mv;
836 
837 	for ( i=0; i<kpd->kcnt; ++i ) if ( kpd->kerns[i].kp!=NULL )
838 	    if ( kpd->kerns[i].newoff != kpd->kerns[i].kp->off ) {
839 		kpd->kerns[i].kp->off = kpd->kerns[i].newoff;
840 		kpd->sf->changed = true;
841 		for ( fv=(FontView *) kpd->sf->fv; fv!=NULL; fv = (FontView *) (fv->b.nextsame) ) {
842 		    for ( mv=fv->b.sf->metrics; mv!=NULL; mv=mv->next )
843 			MVRefreshChar(mv,kpd->kerns[i].first);
844 		}
845 	    }
846 	kpd->done = true;
847     }
848 return( true );
849 }
850 
KP_Cancel(GGadget * g,GEvent * e)851 static int KP_Cancel(GGadget *g, GEvent *e) {
852     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
853 	KPData *kpd = GDrawGetUserData(GGadgetGetWindow(g));
854 	kpd->done = true;
855     }
856 return( true );
857 }
858 
KPMenuRemove(GWindow gw,struct gmenuitem * mi,GEvent * e)859 static void KPMenuRemove(GWindow gw,struct gmenuitem *mi,GEvent *e) {
860     KPData *kpd = GDrawGetUserData(gw);
861     KPRemove(kpd);
862 }
863 
KPKPCloseup(KPData * kpd)864 static void KPKPCloseup(KPData *kpd) {
865     if ( kpd->selected!=-1 ) {
866 	struct kerns *k = &kpd->kerns[kpd->selected];
867 	int oldoff = k->kp->off;
868 	k->kp->off = k->newoff;
869 	KernPairD(k->first->parent,k->first,k->second,kpd->layer,false);
870 	k->newoff = k->kp->off;
871 	k->kp->off = oldoff;
872 	GDrawRequestExpose(kpd->v,NULL,false);
873 	kpd->selected = -1;
874     }
875 }
876 
KPAC(KPData * kpd,int base)877 static void KPAC(KPData *kpd, int base) {
878     if ( kpd->selected!=-1 ) {
879 	struct kerns *k = &kpd->kerns[kpd->selected];
880 	SplineChar *sc = base ? k->first : k->second;
881 	AnchorPoint *ap;
882 	for ( ap=sc->anchor; ap!=NULL && ap->anchor!=k->ac; ap=ap->next );
883 	if ( ap!=NULL ) {
884 	    /* There is currently no way to modify anchors in this dlg */
885 	    /* so the anchor will be right. On the other hand we might */
886 	    /* need to reinit all other combinations which use this point */
887 	    AnchorControl(sc,ap,kpd->layer);
888 	    AnchorRefigure(kpd);
889 	    GDrawRequestExpose(kpd->v,NULL,false);
890 	}
891     }
892 }
893 
KPMenuKPCloseup(GWindow gw,struct gmenuitem * mi,GEvent * e)894 static void KPMenuKPCloseup(GWindow gw,struct gmenuitem *mi,GEvent *e) {
895     KPData *kpd = GDrawGetUserData(gw);
896     KPKPCloseup(kpd);
897 }
898 
KPMenuACB(GWindow gw,struct gmenuitem * mi,GEvent * e)899 static void KPMenuACB(GWindow gw,struct gmenuitem *mi,GEvent *e) {
900     KPData *kpd = GDrawGetUserData(gw);
901     KPAC(kpd,true);
902 }
903 
KPMenuACM(GWindow gw,struct gmenuitem * mi,GEvent * e)904 static void KPMenuACM(GWindow gw,struct gmenuitem *mi,GEvent *e) {
905     KPData *kpd = GDrawGetUserData(gw);
906     KPAC(kpd,false);
907 }
908 
909 static GMenuItem kernmenu[] = {
910     { { (unichar_t *) N_("C_lear"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 'N' }, '\177', 0, NULL, NULL, KPMenuRemove, 0 },
911     { { (unichar_t *) N_("Kern Pair Closeup"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 'N' }, '\0', 0, NULL, NULL, KPMenuKPCloseup, 0 },
912     GMENUITEM_EMPTY
913 };
914 
915 static GMenuItem acmenu[] = {
916     { { (unichar_t *) N_("Anchor Control for Base"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 'N' }, '\0', 0, NULL, NULL, KPMenuACB, 0 },
917     { { (unichar_t *) N_("Anchor Control for Mark"), NULL, COLOR_DEFAULT, COLOR_DEFAULT, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, 'N' }, '\0', 0, NULL, NULL, KPMenuACM, 0 },
918     GMENUITEM_EMPTY
919 };
920 
921 static unichar_t upopupbuf[100];
922 
kpdv_e_h(GWindow gw,GEvent * event)923 static int kpdv_e_h(GWindow gw, GEvent *event) {
924     KPData *kpd = GDrawGetUserData(gw);
925     int index, old_sel, temp;
926     char buffer[100];
927     static int done=false;
928 
929     switch ( event->type ) {
930       case et_expose:
931 	KP_ExposeKerns(kpd,gw,&event->u.expose.rect);
932       break;
933       case et_char:
934 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
935 	    help("ui/dialogs/kernpairs.html", NULL);
936 return( true );
937 	}
938 	KP_Commands(kpd,event);
939       break;
940       case et_mousedown:
941 	GGadgetEndPopup();
942 	kpd->pressed = true;
943 	index = kpd->off_top + event->u.mouse.y/kpd->uh;
944 	if ( index>=kpd->kcnt )
945 	    index = -1;
946 	if ( index!=kpd->selected ) {
947 	    old_sel = kpd->selected;
948 	    kpd->selected = index;
949 	    KP_RefreshSel(kpd,old_sel);
950 	    KP_RefreshSel(kpd,index);
951 	}
952 	if ( event->u.mouse.button==3 && index>=0 ) {
953 	    if ( !done ) {
954 		int i;
955 		for ( i=0; kernmenu[i].ti.text!=NULL || kernmenu[i].ti.line; ++i )
956 		    if ( kernmenu[i].ti.text!=NULL )
957 			kernmenu[i].ti.text = (unichar_t *) _((char *) kernmenu[i].ti.text);
958 		for ( i=0; acmenu[i].ti.text!=NULL || acmenu[i].ti.line; ++i )
959 		    if ( acmenu[i].ti.text!=NULL )
960 			acmenu[i].ti.text = (unichar_t *) _((char *) acmenu[i].ti.text);
961 		done = true;
962 	    }
963 	    if ( kpd->ac==NULL )
964 		GMenuCreatePopupMenu(gw,event, kernmenu);
965 	    else
966 		GMenuCreatePopupMenu(gw,event, acmenu);
967 	} else if ( KP_Cursor(kpd,event)!=NULL ) {
968 	    kpd->pressed_x = event->u.mouse.x;
969 	    kpd->old_val = kpd->kerns[index].newoff;
970 	} else
971 	    kpd->pressed_x = -1;
972       break;
973       case et_mouseup:
974 	if ( kpd->pressed_x!=-1 )
975 	    kpd->last_index = kpd->selected;
976 	else
977 	    kpd->last_index = -1;
978 	if ( kpd->selected>=0 && event->u.mouse.clicks>1 ) {
979 	    if ( kpd->ac==NULL )
980 		KPKPCloseup(kpd);
981 	    else
982 		KPAC(kpd,true);
983 return( true );
984 	}
985       /* Fall through... */
986       case et_mousemove:
987 	GGadgetEndPopup();
988 	index = kpd->off_top + event->u.mouse.y/kpd->uh;
989 	if ( !kpd->pressed && index<kpd->kcnt ) {
990 	    sprintf( buffer, "%.20s %d U+%04x",
991 		    kpd->kerns[index].first->name,
992 		    kpd->kerns[index].first->orig_pos,
993 		    kpd->kerns[index].first->unicodeenc );
994 	    if ( kpd->kerns[index].first->unicodeenc==-1 )
995 		strcpy(buffer+strlen(buffer)-4, "????");
996 	    sprintf( buffer+strlen(buffer), " + %.20s %d U+%04x",
997 		    kpd->kerns[index].second->name,
998 		    kpd->kerns[index].second->orig_pos,
999 		    kpd->kerns[index].second->unicodeenc );
1000 	    if ( kpd->kerns[index].second->unicodeenc==-1 )
1001 		strcpy(buffer+strlen(buffer)-4, "????");
1002 	    uc_strcpy(upopupbuf,buffer);
1003 	    GGadgetPreparePopup(gw,upopupbuf);
1004 	    KP_Cursor(kpd,event);
1005 	} else if ( kpd->pressed && kpd->pressed_x!=-1 ) {
1006 	    if ( kpd->ac!=NULL ) {
1007 		/* Nothing to be done. That's what I find so wonderful. Happy Days */
1008 	    } else if ( index==kpd->selected ) {
1009 		KP_SetCursor(kpd,true);
1010 		temp = kpd->old_val + (event->u.mouse.x-kpd->pressed_x)*(kpd->sf->ascent+kpd->sf->descent)/kpd->bdf->pixelsize;
1011 		if ( temp!=kpd->kerns[index].newoff ) {
1012 		    kpd->kerns[index].newoff = temp;
1013 		    KP_RefreshKP(kpd,index);
1014 		}
1015 	    } else {
1016 		if ( kpd->movecursor ) {
1017 		    kpd->kerns[kpd->selected].newoff = kpd->old_val;
1018 		    KP_SetCursor(kpd,false);
1019 		    KP_RefreshKP(kpd,kpd->selected);
1020 		}
1021 	    }
1022 	    if ( kpd->ac==NULL && kpd->kerns[index].kp->kcid!=0 && event->type==et_mouseup )
1023 		KP_KernClassAlter(kpd,index);
1024 	}
1025 	if ( event->type==et_mouseup )
1026 	    kpd->pressed = false;
1027       break;
1028       case et_resize:
1029 	KPV_Resize(kpd);
1030       break;
1031     }
1032 return( true );
1033 }
1034 
kpdpopup(KPData * kpd)1035 static void kpdpopup(KPData *kpd) {
1036     char buffer[100];
1037 
1038     if ( kpd->ac==NULL ) {
1039 	sprintf( buffer, "total kern pairs=%d\nchars starting kerns=%d",
1040 		kpd->kcnt, kpd->firstcnt );
1041     } else {
1042 	sprintf( buffer, "total anchored pairs=%d\nbase char cnt=%d",
1043 		kpd->kcnt, kpd->firstcnt );
1044     }
1045     uc_strcpy(upopupbuf,buffer);
1046     GGadgetPreparePopup(kpd->gw,upopupbuf);
1047 }
1048 
kpd_e_h(GWindow gw,GEvent * event)1049 static int kpd_e_h(GWindow gw, GEvent *event) {
1050     if ( event->type==et_close ) {
1051 	KPData *kpd = GDrawGetUserData(gw);
1052 	kpd->done = true;
1053     } else if ( event->type == et_mousemove ) {
1054 	kpdpopup(GDrawGetUserData(gw));
1055     } else if ( event->type == et_expose ) {
1056 	KPData *kpd = GDrawGetUserData(gw);
1057 	GRect size, sbsize;
1058 	GDrawGetSize(kpd->v,&size);
1059 	GGadgetGetSize(GWidgetGetControl(kpd->gw,CID_ScrollBar),&sbsize);
1060 	GDrawSetLineWidth(gw,0);
1061 	GDrawDrawLine(gw,size.x,size.y-1,sbsize.x+sbsize.width-1,size.y-1,0x000000);
1062 	GDrawDrawLine(gw,size.x,size.y+size.height,sbsize.x+sbsize.width-1,size.y+size.height,0x000000);
1063 	GDrawDrawLine(gw,size.x-1,size.y-1,size.x-1,size.y+size.height,0x000000);
1064     } else if ( event->type == et_char ) {
1065 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
1066 	    help("ui/dialogs/kernpairs.html", NULL);
1067 return( true );
1068 	}
1069 	if ( event->u.chr.chars[0]!='\0' && event->u.chr.chars[1]=='\0' ) {
1070 	    enum sortby sort = GGadgetGetFirstListSelectedItem(GWidgetGetControl(gw,CID_SortBy));
1071 	    KPData *kpd = GDrawGetUserData(gw);
1072 	    if ( sort!=sb_kern ) {
1073 		KPScrollTo(kpd,event->u.chr.chars[0],sort);
1074 return( true );
1075 	    } else
1076 		GDrawBeep(NULL);
1077 	}
1078 return( false );
1079     } else if ( event->type == et_resize && event->u.resize.sized ) {
1080 	KP_Resize((KPData *) GDrawGetUserData(gw) );
1081     }
1082 return( true );
1083 }
1084 
SFShowKernPairs(SplineFont * sf,SplineChar * sc,AnchorClass * ac,int layer)1085 void SFShowKernPairs(SplineFont *sf,SplineChar *sc,AnchorClass *ac,int layer) {
1086     KPData kpd;
1087     GRect pos;
1088     GWindow gw;
1089     GWindowAttrs wattrs;
1090     GGadgetCreateData gcd[9], boxes[6], *hvarray[3][3], *harray[3], *barray[10], *varray[5];
1091     GTextInfo label[9];
1092     FontRequest rq;
1093     int as, ds, ld,i;
1094     static int done=false;
1095     static GFont *font=NULL;
1096 
1097     memset(&kpd,0,sizeof(kpd));
1098     kpd.sf = sf;
1099     kpd.sc = sc;
1100     kpd.ac = ac;
1101     kpd.layer = layer;
1102     kpd.first = true;
1103     kpd.last_index = kpd.selected = -1;
1104     if ( ac==NULL )
1105 	KPBuildKernList(&kpd);
1106     else
1107 	KPBuildAnchorList(&kpd);
1108     if ( kpd.kcnt==0 )
1109 return;
1110 
1111     memset(&wattrs,0,sizeof(wattrs));
1112     wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
1113     wattrs.event_masks = ~(1<<et_charup);
1114     wattrs.restrict_input_to_me = 1;
1115     wattrs.undercursor = 1;
1116     wattrs.cursor = ct_pointer;
1117     wattrs.utf8_window_title = ac==NULL?_("Kern Pairs"):_("Anchored Pairs");
1118     wattrs.is_dlg = true;
1119     pos.x = pos.y = 0;
1120     pos.width = GGadgetScale(200);
1121     pos.height = GDrawPointsToPixels(NULL,500);
1122     kpd.gw = gw = GDrawCreateTopWindow(NULL,&pos,kpd_e_h,&kpd,&wattrs);
1123 
1124     memset(&label,0,sizeof(label));
1125     memset(&gcd,0,sizeof(gcd));
1126     memset(&boxes,0,sizeof(boxes));
1127 
1128     label[0].text = (unichar_t *) _("_Size:");
1129     label[0].text_is_1byte = true;
1130     label[0].text_in_resource = true;
1131     gcd[0].gd.label = &label[0];
1132     gcd[0].gd.pos.x = 5; gcd[0].gd.pos.y = 5+6;
1133     gcd[0].gd.flags = gg_enabled|gg_visible;
1134     gcd[0].creator = GLabelCreate;
1135     hvarray[0][0] = &gcd[0];
1136 
1137     gcd[1].gd.label = &sizes[1];  gcd[1].gd.label->selected = true;
1138     gcd[1].gd.pos.x = 50; gcd[1].gd.pos.y = 5;
1139     gcd[1].gd.flags = gg_enabled|gg_visible;
1140     gcd[1].gd.cid = CID_Size;
1141     gcd[1].gd.u.list = sizes;
1142     gcd[1].gd.handle_controlevent = KP_ChangeSize;
1143     gcd[1].creator = GListButtonCreate;
1144     hvarray[0][1] = &gcd[1]; hvarray[0][2] = NULL;
1145 
1146     label[2].text = (unichar_t *) _("Sort By:");
1147     label[2].text_is_1byte = true;
1148     gcd[2].gd.label = &label[2];
1149     gcd[2].gd.pos.x = gcd[0].gd.pos.x; gcd[2].gd.pos.y = gcd[0].gd.pos.y+25;
1150     gcd[2].gd.flags = gg_enabled|gg_visible;
1151     gcd[2].creator = GLabelCreate;
1152     hvarray[1][0] = &gcd[2];
1153 
1154     if ( !done ) {
1155 	done = true;
1156 	for ( i=0; sortby[i].text!=NULL; ++i )
1157 	    sortby[i].text = (unichar_t *) _((char *) sortby[i].text);
1158     }
1159 
1160     gcd[3].gd.label = &sortby[0]; gcd[3].gd.label->selected = true;
1161     gcd[3].gd.pos.x = 50; gcd[3].gd.pos.y = gcd[1].gd.pos.y+25;
1162     gcd[3].gd.flags = gg_enabled|gg_visible;
1163     gcd[3].gd.cid = CID_SortBy;
1164     gcd[3].gd.u.list = sortby;
1165     gcd[3].gd.handle_controlevent = KP_ChangeSort;
1166     gcd[3].creator = GListButtonCreate;
1167     hvarray[1][1] = &gcd[3]; hvarray[1][2] = NULL; hvarray[2][0] = NULL;
1168 
1169     boxes[2].gd.flags = gg_enabled|gg_visible;
1170     boxes[2].gd.u.boxelements = hvarray[0];
1171     boxes[2].creator = GHVBoxCreate;
1172     varray[0] = &boxes[2];
1173 
1174     gcd[4].gd.pos.width = 40;
1175     gcd[4].gd.pos.height = 250;
1176     gcd[4].gd.flags = gg_visible | gg_enabled;
1177     gcd[4].gd.u.drawable_e_h = kpdv_e_h;
1178     gcd[4].creator = GDrawableCreate;
1179 
1180     gcd[5].gd.flags = gg_enabled|gg_visible|gg_sb_vert;
1181     gcd[5].gd.cid = CID_ScrollBar;
1182     gcd[5].gd.handle_controlevent = KP_Scrolled;
1183     gcd[5].creator = GScrollBarCreate;
1184     harray[0] = &gcd[4]; harray[1] = &gcd[5]; harray[2] = NULL;
1185 
1186     boxes[3].gd.flags = gg_enabled|gg_visible;
1187     boxes[3].gd.u.boxelements = harray;
1188     boxes[3].creator = GHBoxCreate;
1189     varray[1] = &boxes[3];
1190 
1191     gcd[6].gd.pos.x = 20-3; gcd[6].gd.pos.y = 17+37;
1192     gcd[6].gd.pos.width = -1; gcd[6].gd.pos.height = 0;
1193     gcd[6].gd.flags = gg_visible | gg_enabled | gg_but_default;
1194     label[6].text = (unichar_t *) _("_OK");
1195     label[6].text_is_1byte = true;
1196     label[6].text_in_resource = true;
1197     gcd[6].gd.label = &label[6];
1198     gcd[6].gd.cid = CID_OK;
1199     gcd[6].gd.handle_controlevent = KP_OK;
1200     gcd[6].creator = GButtonCreate;
1201 
1202     gcd[7].gd.pos.x = -20; gcd[7].gd.pos.y = gcd[6].gd.pos.y+3;
1203     gcd[7].gd.pos.width = -1; gcd[7].gd.pos.height = 0;
1204     gcd[7].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
1205     label[7].text = (unichar_t *) _("_Cancel");
1206     label[7].text_is_1byte = true;
1207     label[7].text_in_resource = true;
1208     gcd[7].gd.label = &label[7];
1209     gcd[7].gd.cid = CID_Cancel;
1210     gcd[7].gd.handle_controlevent = KP_Cancel;
1211     gcd[7].creator = GButtonCreate;
1212     barray[0] = GCD_Glue; barray[1] = &gcd[6]; barray[2] = GCD_Glue;
1213     barray[3] = GCD_Glue; barray[4] = &gcd[7]; barray[5] = GCD_Glue; barray[6] = NULL;
1214 
1215     boxes[4].gd.flags = gg_enabled|gg_visible;
1216     boxes[4].gd.u.boxelements = barray;
1217     boxes[4].creator = GHBoxCreate;
1218     varray[2] = &boxes[4];
1219     varray[3] = NULL;
1220 
1221     boxes[0].gd.flags = gg_enabled|gg_visible;
1222     boxes[0].gd.u.boxelements = varray;
1223     boxes[0].creator = GVBoxCreate;
1224 
1225 
1226     GGadgetsCreate(gw,boxes);
1227 
1228     GHVBoxSetExpandableRow(boxes[0].ret,1);
1229     GHVBoxSetExpandableCol(boxes[3].ret,0);
1230     GHVBoxSetExpandableCol(boxes[4].ret,gb_expandgluesame);
1231     GHVBoxSetPadding(boxes[0].ret,0,2);
1232     GHVBoxSetPadding(boxes[3].ret,0,0);
1233     kpd.v = GDrawableGetWindow(gcd[4].ret);;
1234 
1235     GGadgetGetSize(gcd[4].ret,&pos);
1236     kpd.sb_width = pos.width;
1237     GGadgetGetSize(gcd[3].ret,&pos);
1238     kpd.header_height = pos.y+pos.height+4;
1239 
1240     kpd.bdf = SplineFontPieceMeal(kpd.sf,kpd.layer,(intpt) (gcd[1].gd.label->userdata),72,true,NULL);
1241 
1242     if ( font==NULL ) {
1243 	memset(&rq,'\0',sizeof(rq));
1244 	rq.utf8_family_name = SANS_UI_FAMILIES;
1245 	rq.point_size = -12;
1246 	rq.weight = 400;
1247 	font = GDrawInstanciateFont(gw,&rq);
1248 	font = GResourceFindFont("Combinations.Font",font);
1249     }
1250     kpd.font = font;
1251     GDrawWindowFontMetrics(gw,kpd.font,&as,&ds,&ld);
1252     kpd.fh = as+ds; kpd.as = as;
1253 
1254     kpd.uh = (4*kpd.bdf->pixelsize/3)+kpd.fh+6;
1255     kpd.vpad = kpd.bdf->pixelsize/5 + 3;
1256 
1257     GHVBoxFitWindow(boxes[0].ret);
1258 
1259     GDrawSetVisible(kpd.v,true);
1260     GDrawSetVisible(kpd.gw,true);
1261     while ( !kpd.done )
1262 	GDrawProcessOneEvent(NULL);
1263     free( kpd.kerns );
1264     GDrawDestroyWindow(gw);
1265 }
1266