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