1 /* Copyright (C) 2009-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 "chardata.h"
31 #include "cvundoes.h"
32 #include "delta.h"
33 #include "fontforgeui.h"
34 #include "gimage.h"
35 #include "gkeysym.h"
36 #include "ustring.h"
37 #include "utype.h"
38
39 #include <math.h>
40
41 /* Suggestions for where delta instructions might be wanted */
42
43 #define CID_Sizes 100
44 #define CID_DPI 101
45 #define CID_BW 102
46 #define CID_Within 103
47 #define CID_Msg 104
48 #define CID_Ok 105
49 #define CID_Cancel 106
50 #define CID_Top 107
51
52 static double delta_within = .02;
53 static char *delta_sizes=NULL;
54 static int delta_dpi = 100;
55 static int delta_depth = 1;
56
57 static void StartDeltaDisplay(QGData *qg);
58
Delta_OK(GGadget * g,GEvent * e)59 static int Delta_OK(GGadget *g, GEvent *e) {
60 if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
61 QGData *qg = GDrawGetUserData(GGadgetGetWindow(g));
62 int err=false;
63 int dpi, depth;
64 double within;
65 char *sizes;
66
67 within = GetReal8(qg->gw,CID_Within,_("Proximity"),&err);
68 dpi = GetInt8(qg->gw,CID_DPI,_("DPI"),&err);
69 if ( err )
70 return(true);
71 if ( within<=0 || within>=.5 ) {
72 ff_post_error(_("Bad Number"),_("The \"Proximity\" field must be more than 0 and less than a half."));
73 return( true );
74 }
75 if ( dpi<10 || dpi>5000 ) {
76 ff_post_error(_("Unreasonable DPI"),_("The \"DPI\" field must be more than 10 and less than 5000."));
77 return( true );
78 }
79 depth = GGadgetIsChecked(GWidgetGetControl(qg->gw,CID_BW)) ? 1 : 8;
80 sizes = GGadgetGetTitle8(GWidgetGetControl(qg->gw,CID_Sizes));
81
82 GGadgetSetVisible(GWidgetGetControl(qg->gw,CID_Msg),true);
83 GGadgetSetVisible(GWidgetGetControl(qg->gw,CID_Ok),false);
84 GGadgetSetVisible(GWidgetGetControl(qg->gw,CID_Cancel),false);
85 GDrawSetCursor(qg->gw,ct_watch);
86 GDrawProcessPendingEvents(NULL);
87
88 qg->within = within;
89 qg->dpi = dpi;
90 qg->pixelsizes = sizes;
91 qg->depth = depth;
92 TopFindQuestionablePoints(qg);
93
94 GGadgetSetVisible(GWidgetGetControl(qg->gw,CID_Msg),false);
95 GGadgetSetVisible(GWidgetGetControl(qg->gw,CID_Ok),true);
96 GGadgetSetVisible(GWidgetGetControl(qg->gw,CID_Cancel),true);
97 GDrawSetCursor(qg->gw,ct_pointer);
98 GDrawProcessPendingEvents(NULL);
99
100 if ( qg->error!=qg_ok ) {
101 switch ( qg->error ) {
102 case qg_notnumber:
103 ff_post_error(_("Bad Number"),_("An entry in the \"Sizes\" field is not a number."));
104 break;
105 case qg_badnumber:
106 ff_post_error(_("Bad Number"),_("An entry in the \"Sizes\" field is unreasonable."));
107 break;
108 case qg_badrange:
109 ff_post_error(_("Bad Number"),_("An range in the \"Sizes\" field is incorrectly ordered."));
110 break;
111 case qg_nofont:
112 ff_post_error(_("FreeType unavailable"),_("FreeType unavailable."));
113 break;
114 default:
115 IError(_("Unexpected error"));
116 break;
117 }
118 free(sizes);
119 qg->cur = 0;
120 return( true );
121 }
122
123 free(delta_sizes);
124 delta_within = within;
125 delta_dpi = dpi;
126 delta_depth = depth;
127 delta_sizes = sizes;
128
129 if ( qg->cur==0 ) {
130 ff_post_error(_("Nothing found"),_("Nothng found."));
131 qg->done = true;
132 return( true );
133 }
134
135 if ( qg->cur >= qg->max )
136 qg->qg = realloc(qg->qg,(qg->max += 1) * sizeof(QuestionableGrid));
137 memset(qg->qg+qg->cur,0,sizeof(QuestionableGrid));
138 GDrawSetVisible(qg->gw,false);
139 StartDeltaDisplay(qg);
140 qg->done = true;
141 }
142 return( true );
143 }
144
Delta_Cancel(GGadget * g,GEvent * e)145 static int Delta_Cancel(GGadget *g, GEvent *e) {
146 if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
147 QGData *qg = GDrawGetUserData(GGadgetGetWindow(g));
148 qg->done = true;
149 }
150 return( true );
151 }
152
delta_e_h(GWindow gw,GEvent * event)153 static int delta_e_h(GWindow gw, GEvent *event) {
154 if ( event->type==et_close ) {
155 QGData *qg = GDrawGetUserData(gw);
156 qg->done = true;
157 } else if ( event->type == et_char ) {
158 return( false );
159 } else if ( event->type == et_map ) {
160 /* Above palettes */
161 GDrawRaise(gw);
162 } else if ( event->type == et_destroy ) {
163 QGData *qg = GDrawGetUserData(gw);
164 free(qg->qg);
165 free(qg);
166 }
167 return( true );
168 }
169
DeltaSuggestionDlg(FontView * fv,CharView * cv)170 void DeltaSuggestionDlg(FontView *fv,CharView *cv) {
171 GRect pos;
172 GWindow gw;
173 GWindowAttrs wattrs;
174 GGadgetCreateData gcd[13], boxes[4];
175 GTextInfo label[13];
176 GGadgetCreateData *varray[7][5], *barray[9];
177 char dpi_buffer[40], within_buffer[40];
178 QGData *data;
179 int failed = false;
180 int k, r;
181 FontView *savefv;
182
183 if ( !hasFreeType() ) {
184 ff_post_error(_("No FreeType"),_("You must install the freetype library before using this command."));
185 return;
186 }
187 if ( !hasFreeTypeByteCode() ) {
188 ff_post_error(_("No FreeType"),_("Your version of the freetype library does not contain the bytecode interpreter."));
189 return;
190 }
191
192 if ( delta_sizes==NULL )
193 delta_sizes = copy("7-40,72,80,88,96");
194
195 data = calloc(1,sizeof(QGData));
196 data->fv = (FontViewBase *) fv;
197 data->cv = cv;
198 if ( cv!=NULL ) {
199 data->sc = cv->b.sc;
200 savefv = (FontView *) cv->b.fv;
201 data->layer = CVLayer((CharViewBase *) cv);
202 if ( !data->sc->parent->layers[data->layer].order2 )
203 failed = true;
204 if ( !failed && data->sc->ttf_instrs_len==0 )
205 ff_post_notice(_("No Instructions"),_("This glyph has no instructions. Adding instructions (a DELTA) may change its rasterization significantly."));
206 cv->qg = data;
207 cv->note_x = cv->note_y = 32766;
208 } else {
209 savefv = fv;
210 data->layer = fv->b.active_layer;
211 if ( !fv->b.sf->layers[data->layer].order2 )
212 failed = true;
213 }
214 if ( failed ) {
215 ff_post_error(_("Not quadratic"),_("This must be a truetype layer."));
216 free(data);
217 if ( cv!=NULL )
218 cv->qg = NULL;
219 return;
220 }
221 savefv->qg = data;
222
223 memset(&wattrs,0,sizeof(wattrs));
224 wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
225 wattrs.event_masks = ~(1<<et_charup);
226 wattrs.restrict_input_to_me = 1;
227 wattrs.undercursor = 1;
228 wattrs.cursor = ct_pointer;
229 wattrs.utf8_window_title = _("DELTA suggestions");
230 wattrs.is_dlg = true;
231 pos.x = pos.y = 0;
232 pos.width = GGadgetScale(GDrawPointsToPixels(NULL,190));
233 pos.height = GDrawPointsToPixels(NULL,106);
234 data->gw = gw = GDrawCreateTopWindow(NULL,&pos,delta_e_h,data,&wattrs);
235
236 memset(&label,0,sizeof(label));
237 memset(&gcd,0,sizeof(gcd));
238 memset(&boxes,0,sizeof(boxes));
239
240 k=r=0;
241
242 label[k].text = (unichar_t *) _(
243 "When a curve passes very close to the center of a\n"
244 "pixel you might want to check that the curve is on\n"
245 "the intended side of that pixel.\n"
246 "If it's on the wrong side, consider using a DELTA\n"
247 "instruction to adjust the closest point at the\n"
248 "current pixelsize."
249 );
250 label[k].text_is_1byte = true;
251 label[k].text_in_resource = true;
252 gcd[k].gd.label = &label[k];
253 gcd[k].gd.flags = gg_enabled|gg_visible;
254 gcd[k++].creator = GLabelCreate;
255 varray[r][0] = &gcd[k-1]; varray[r][1] = GCD_ColSpan; varray[r][2] = GCD_ColSpan; varray[r][3] = GCD_ColSpan; varray[r++][4] = NULL;
256
257 label[k].text = (unichar_t *) _("Rasterize at sizes:");
258 label[k].text_is_1byte = true;
259 label[k].text_in_resource = true;
260 gcd[k].gd.label = &label[k];
261 gcd[k].gd.flags = gg_enabled|gg_visible;
262 gcd[k++].creator = GLabelCreate;
263
264 label[k].text = (unichar_t *) delta_sizes;
265 label[k].text_is_1byte = true;
266 label[k].text_in_resource = true;
267 gcd[k].gd.label = &label[k];
268 gcd[k].gd.flags = gg_enabled|gg_visible;
269 gcd[k].gd.cid = CID_Sizes;
270 gcd[k++].creator = GTextFieldCreate;
271 varray[r][0] = &gcd[k-2]; varray[r][1] = &gcd[k-1]; varray[r][2] = GCD_ColSpan; varray[r][3] = GCD_ColSpan; varray[r++][4] = NULL;
272
273 label[k].text = (unichar_t *) _("DPI:");
274 label[k].text_is_1byte = true;
275 label[k].text_in_resource = true;
276 gcd[k].gd.label = &label[k];
277 gcd[k].gd.flags = gg_enabled|gg_visible;
278 gcd[k++].creator = GLabelCreate;
279
280 sprintf( dpi_buffer, "%d", delta_dpi );
281 label[k].text = (unichar_t *) dpi_buffer;
282 label[k].text_is_1byte = true;
283 label[k].text_in_resource = true;
284 gcd[k].gd.label = &label[k];
285 gcd[k].gd.pos.width = 40;
286 gcd[k].gd.cid = CID_DPI;
287 gcd[k].gd.flags = gg_enabled|gg_visible;
288 gcd[k++].creator = GTextFieldCreate;
289
290 label[k].text = (unichar_t *) _("_Mono");
291 label[k].text_is_1byte = true;
292 label[k].text_in_resource = true;
293 gcd[k].gd.label = &label[k];
294 gcd[k].gd.flags = delta_depth==1 ? (gg_enabled|gg_visible|gg_cb_on) : (gg_enabled|gg_visible);
295 gcd[k].gd.cid = CID_BW;
296 gcd[k++].creator = GRadioCreate;
297 varray[r][0] = &gcd[k-1]; varray[r][1] = GCD_HPad10;
298
299 label[k].text = (unichar_t *) _("_Anti-Aliased");
300 label[k].text_is_1byte = true;
301 label[k].text_in_resource = true;
302 gcd[k].gd.label = &label[k];
303 gcd[k].gd.flags = delta_depth!=1 ? (gg_enabled|gg_visible|gg_cb_on) : (gg_enabled|gg_visible);
304 gcd[k++].creator = GRadioCreate;
305 varray[r][0] = &gcd[k-4]; varray[r][1] = &gcd[k-3]; varray[r][2] = &gcd[k-2]; varray[r][3] = &gcd[k-1]; varray[r++][4] = NULL;
306
307 label[k].text = (unichar_t *) _("Proximity:");
308 label[k].text_is_1byte = true;
309 label[k].text_in_resource = true;
310 gcd[k].gd.label = &label[k];
311 gcd[k].gd.flags = gg_enabled|gg_visible;
312 gcd[k++].creator = GLabelCreate;
313
314 sprintf( within_buffer, "%g", delta_within );
315 label[k].text = (unichar_t *) within_buffer;
316 label[k].text_is_1byte = true;
317 label[k].text_in_resource = true;
318 gcd[k].gd.label = &label[k];
319 gcd[k].gd.flags = gg_enabled|gg_visible;
320 gcd[k].gd.pos.width = 40;
321 gcd[k].gd.cid = CID_Within;
322 gcd[k++].creator = GTextFieldCreate;
323
324 label[k].text = (unichar_t *) _("pixels");
325 label[k].text_is_1byte = true;
326 label[k].text_in_resource = true;
327 gcd[k].gd.label = &label[k];
328 gcd[k].gd.flags = gg_enabled|gg_visible;
329 gcd[k++].creator = GLabelCreate;
330 varray[r][0] = &gcd[k-3]; varray[r][1] = &gcd[k-2]; varray[r][2] = &gcd[k-1]; varray[r][3] = GCD_ColSpan; varray[r++][4] = NULL;
331
332 label[k].text = (unichar_t *) _( "This may take a while. Please be patient..." );
333 label[k].text_is_1byte = true;
334 label[k].text_in_resource = true;
335 gcd[k].gd.label = &label[k];
336 gcd[k].gd.flags = gg_enabled;
337 gcd[k].gd.cid = CID_Msg;
338 gcd[k++].creator = GLabelCreate;
339 varray[r][0] = &gcd[k-1]; varray[r][1] = GCD_ColSpan; varray[r][2] = GCD_ColSpan; varray[r][3] = GCD_ColSpan; varray[r++][4] = NULL;
340
341 gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
342 label[k].text = (unichar_t *) _("_OK");
343 label[k].text_is_1byte = true;
344 label[k].text_in_resource = true;
345 gcd[k].gd.label = &label[k];
346 gcd[k].gd.handle_controlevent = Delta_OK;
347 gcd[k].gd.cid = CID_Ok;
348 gcd[k++].creator = GButtonCreate;
349 barray[0] = GCD_Glue; barray[1] = &gcd[k-1]; barray[2] = GCD_Glue;
350
351 gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
352 label[k].text = (unichar_t *) _("_Cancel");
353 label[k].text_is_1byte = true;
354 label[k].text_in_resource = true;
355 gcd[k].gd.label = &label[k];
356 gcd[k].gd.handle_controlevent = Delta_Cancel;
357 gcd[k].gd.cid = CID_Cancel;
358 gcd[k++].creator = GButtonCreate;
359 barray[3] = GCD_Glue; barray[4] = &gcd[k-1]; barray[5] = GCD_Glue; barray[6] = NULL;
360
361 boxes[2].gd.flags = gg_enabled|gg_visible;
362 boxes[2].gd.u.boxelements = barray;
363 boxes[2].creator = GHBoxCreate;
364 varray[r][0] = &boxes[2]; varray[r][1] = GCD_ColSpan; varray[r][2] = GCD_ColSpan; varray[r][3] = GCD_ColSpan; varray[r++][4] = NULL;
365 varray[r][0] = NULL;
366
367 boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
368 boxes[0].gd.flags = gg_enabled|gg_visible;
369 boxes[0].gd.u.boxelements = varray[0];
370 boxes[0].gd.cid = CID_Top;
371 boxes[0].creator = GHVGroupCreate;
372
373
374 GGadgetsCreate(gw,boxes);
375 GHVBoxFitWindow(boxes[0].ret);
376
377 GDrawSetVisible(gw,true);
378 while ( !data->done )
379 GDrawProcessOneEvent(NULL);
380 GDrawDestroyWindow(gw);
381 if ( data->cv!=NULL )
382 data->cv->qg = NULL;
383 if ( savefv->qg == data )
384 savefv->qg = NULL;
385 }
386
QGRmCharView(QGData * qg,CharView * cv)387 void QGRmCharView(QGData *qg,CharView *cv) {
388 if ( qg->cv==cv )
389 qg->cv = NULL;
390 }
391
QGRmFontView(QGData * qg,FontView * fv)392 void QGRmFontView(QGData *qg,FontView *fv) {
393 qg->done = true;
394 fv->qg = NULL;
395 }
396 /* ************************************************************************** */
397 /* ************************************************************************** */
398
399 #define CID_Sort 200
400 #define CID_GlyphSort 201
401
402 static GTextInfo sorts[] = {
403 { (unichar_t *) N_("Glyph, Size, Point"), NULL, 0, 0, (void *) is_glyph_size_pt, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
404 { (unichar_t *) N_("Glyph, Point, Size"), NULL, 0, 0, (void *) is_glyph_pt_size, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
405 { (unichar_t *) N_("Size, Glyph, Point"), NULL, 0, 0, (void *) is_size_glyph_pt, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
406 GTEXTINFO_EMPTY
407 };
408 static GTextInfo glyphsorts[] = {
409 { (unichar_t *) N_("Unicode"), NULL, 0, 0, (void *) gs_unicode, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
410 { (unichar_t *) N_("Sort|Alphabetic"), NULL, 0, 0, (void *) gs_alpha, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
411 { (unichar_t *) N_("Glyph Order"), NULL, 0, 0, (void *) gs_gid, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
412 GTEXTINFO_EMPTY
413 };
414
QG_VScroll(GGadget * g,GEvent * e)415 static int QG_VScroll(GGadget *g, GEvent *e) {
416 QGData *qg = GDrawGetUserData(GGadgetGetWindow(g));
417 int newpos = qg->loff_top;
418
419 switch( e->u.control.u.sb.type ) {
420 case et_sb_top:
421 newpos = 0;
422 break;
423 case et_sb_uppage:
424 newpos -= 9*qg->vlcnt/10;
425 break;
426 case et_sb_up:
427 newpos -= qg->vlcnt/15;
428 break;
429 case et_sb_down:
430 newpos += qg->vlcnt/15;
431 break;
432 case et_sb_downpage:
433 newpos += 9*qg->vlcnt/10;
434 break;
435 case et_sb_bottom:
436 newpos = 0;
437 break;
438 case et_sb_thumb:
439 case et_sb_thumbrelease:
440 newpos = e->u.control.u.sb.pos;
441 break;
442 case et_sb_halfup:
443 newpos -= qg->vlcnt/30;
444 break;
445 case et_sb_halfdown:
446 newpos += qg->vlcnt/30;
447 break;
448 }
449 if ( newpos + qg->vlcnt > qg->lcnt )
450 newpos = qg->lcnt-qg->vlcnt;
451 if ( newpos<0 )
452 newpos = 0;
453 if ( qg->loff_top!=newpos ) {
454 qg->loff_top = newpos;
455 GScrollBarSetPos(qg->vsb,newpos);
456 GDrawRequestExpose(qg->v,NULL,false);
457 }
458 return( true );
459 }
460
QG_SetSb(QGData * qg)461 static void QG_SetSb(QGData *qg) {
462 if ( qg->loff_top + qg->vlcnt > qg->lcnt )
463 qg->loff_top = qg->lcnt-qg->vlcnt;
464 if ( qg->loff_top<0 )
465 qg->loff_top = 0;
466 GScrollBarSetBounds(qg->vsb,0,qg->lcnt,qg->vlcnt);
467 GScrollBarSetPos(qg->vsb,qg->loff_top);
468 }
469
QG_Count(struct qgnode * parent)470 static int QG_Count(struct qgnode *parent) {
471 int l, cnt=0;
472
473 if ( !parent->open )
474 return( 0 );
475 if ( parent->kids==NULL )
476 return( parent->qg_cnt );
477
478 for ( l=0; l<parent->kid_cnt; ++l ) {
479 parent->kids[l].tot_under = QG_Count(&parent->kids[l]);
480 cnt += 1 + parent->kids[l].tot_under;
481 }
482 return( cnt );
483 }
484
QG_Remetric(QGData * qg)485 static void QG_Remetric(QGData *qg) {
486 GRect size;
487
488 GDrawGetSize(qg->v,&size);
489 qg->vlcnt = size.height/qg->fh;
490 qg->lcnt = QG_Count(&qg->list);
491 QG_SetSb(qg);
492 }
493
494 struct navigate {
495 struct qgnode *parent;
496 int offset; /* offset of -1 means the qgnode */
497 };
498
QG_FindLine(struct qgnode * parent,int l,struct navigate * where)499 static void QG_FindLine(struct qgnode *parent, int l,struct navigate *where) {
500 int k;
501
502 if ( parent->kids == NULL ) {
503 if ( l<parent->qg_cnt ) {
504 where->parent = parent;
505 where->offset = l;
506 } else {
507 where->parent = NULL;
508 where->offset = -1;
509 }
510 return;
511 }
512 for ( k=0; k<parent->kid_cnt; ++k ) {
513 if ( l==0 ) {
514 where->parent = &parent->kids[k];
515 where->offset = -1;
516 return;
517 }
518 --l;
519 if ( parent->kids[k].open ) {
520 if ( l<parent->kids[k].tot_under ) {
521 QG_FindLine(&parent->kids[k],l,where);
522 return;
523 } else
524 l -= parent->kids[k].tot_under;
525 }
526 }
527 where->parent = NULL;
528 where->offset = -1;
529 }
530
QG_NextLine(struct navigate * where)531 static void QG_NextLine(struct navigate *where) {
532 int k;
533
534 if ( where->parent==NULL )
535 return;
536 if ( where->offset!=-1 && where->offset+1<where->parent->qg_cnt ) {
537 ++where->offset;
538 return;
539 }
540 if ( where->parent->open && where->offset==-1 ) {
541 if ( where->parent->kids==NULL ) {
542 where->offset = 0;
543 return;
544 } else {
545 where->parent = &where->parent->kids[0];
546 where->offset = -1;
547 return;
548 }
549 }
550 for (;;) {
551 if ( where->parent->parent==NULL ) {
552 where->parent = NULL;
553 where->offset = -1;
554 return;
555 }
556 k = where->parent - where->parent->parent->kids;
557 if ( k+1< where->parent->parent->kid_cnt ) {
558 ++where->parent;
559 where->offset = -1;
560 return;
561 }
562 where->parent = where->parent->parent;
563 }
564 }
565
qgnodeFree(struct qgnode * parent)566 static void qgnodeFree(struct qgnode *parent) {
567 int i;
568
569 for ( i=0; i<parent->kid_cnt; ++i )
570 qgnodeFree(&parent->kids[i]);
571 free(parent->kids);
572 free(parent->name);
573 }
574
575 static const QGData *kludge;
576
qg_sorter(const void * pt1,const void * pt2)577 static int qg_sorter(const void *pt1, const void *pt2) {
578 const QuestionableGrid *q1 = pt1, *q2 = pt2;
579 const QGData *qg = kludge;
580 int pt_cmp = q1->nearestpt - q2->nearestpt;
581 int size_cmp = q1->size - q2->size;
582 int g_cmp;
583 int t1, t2, t3;
584
585 switch ( qg->glyph_sort ) {
586 case gs_unicode:
587 g_cmp = q1->sc->unicodeenc - q2->sc->unicodeenc;
588 break;
589 case gs_gid:
590 g_cmp = q1->sc->orig_pos - q2->sc->orig_pos;
591 break;
592 case gs_alpha:
593 g_cmp = strcmp(q1->sc->name,q2->sc->name);
594 break;
595 }
596 switch ( qg->info_sort ) {
597 case is_glyph_size_pt:
598 t1 = g_cmp; t2 = size_cmp; t3 = pt_cmp;
599 break;
600 case is_glyph_pt_size:
601 t1 = g_cmp; t2 = pt_cmp; t3 = size_cmp;
602 break;
603 case is_size_glyph_pt:
604 t1 = size_cmp; t2 = g_cmp; t3 = pt_cmp;
605 break;
606 }
607 if ( t1!=0 )
608 return( t1 );
609 if ( t2!=0 )
610 return( t2 );
611
612 return( t3 );
613 }
614
QGSecondLevel(QGData * qg,struct qgnode * parent)615 static void QGSecondLevel(QGData *qg, struct qgnode *parent) {
616 int cnt, l, lstart;
617 int size;
618 SplineChar *sc;
619 int pt;
620 char buffer[200];
621
622 switch ( qg->info_sort ) {
623 case is_glyph_size_pt: /* size */
624 size = -1;
625 cnt = 0;
626 for ( l=0; l<parent->qg_cnt; ++l ) {
627 if ( size!=parent->first[l].size ) {
628 ++cnt;
629 size = parent->first[l].size;
630 }
631 }
632 parent->kid_cnt = cnt;
633 parent->kids = calloc(cnt,sizeof(struct qgnode));
634 cnt = 0;
635 lstart = 0; size=-1;
636 for ( l=0; l<parent->qg_cnt; ++l ) {
637 if ( size!=parent->first[l].size && size!=-1 ) {
638 sprintf( buffer, _("Size: %d (%d)"), size, l-lstart );
639 parent->kids[cnt].name = copy(buffer);
640 parent->kids[cnt].parent = parent;
641 parent->kids[cnt].first = &parent->first[lstart];
642 parent->kids[cnt].qg_cnt = l-lstart;
643 ++cnt;
644 lstart = l;
645 size = parent->first[l].size;
646 } else if ( size!=parent->first[l].size )
647 size = parent->first[l].size;
648 }
649 if ( size!=-1 ) {
650 sprintf( buffer, _("Size: %d (%d)"), size, l-lstart );
651 parent->kids[cnt].name = copy(buffer);
652 parent->kids[cnt].parent = parent;
653 parent->kids[cnt].first = &parent->first[lstart];
654 parent->kids[cnt].qg_cnt = l-lstart;
655 }
656 break;
657 case is_glyph_pt_size: /* pt */
658 pt = -1;
659 cnt = 0;
660 for ( l=0; l<parent->qg_cnt; ++l ) {
661 if ( pt!=parent->first[l].nearestpt ) {
662 ++cnt;
663 pt = parent->first[l].nearestpt;
664 }
665 }
666 parent->kid_cnt = cnt;
667 parent->kids = calloc(cnt,sizeof(struct qgnode));
668 cnt = 0;
669 lstart = 0; pt=-1;
670 for ( l=0; l<parent->qg_cnt; ++l ) {
671 if ( pt!=parent->first[l].nearestpt && pt!=-1 ) {
672 sprintf( buffer, _("Point: %d (%d)"), pt, l-lstart );
673 parent->kids[cnt].name = copy(buffer);
674 parent->kids[cnt].parent = parent;
675 parent->kids[cnt].first = &parent->first[lstart];
676 parent->kids[cnt].qg_cnt = l-lstart;
677 ++cnt;
678 lstart = l;
679 pt = parent->first[l].nearestpt;
680 } else if ( pt!=parent->first[l].nearestpt )
681 pt = parent->first[l].nearestpt;
682 }
683 if ( pt!=-1 ) {
684 sprintf( buffer, _("Point: %d (%d)"), pt, l-lstart );
685 parent->kids[cnt].name = copy(buffer);
686 parent->kids[cnt].parent = parent;
687 parent->kids[cnt].first = &parent->first[lstart];
688 parent->kids[cnt].qg_cnt = l-lstart;
689 }
690 break;
691 case is_size_glyph_pt: /* glyph */
692 sc = NULL;
693 cnt = 0;
694 for ( l=0; l<parent->qg_cnt; ++l ) {
695 if ( sc!=parent->first[l].sc ) {
696 ++cnt;
697 sc = parent->first[l].sc;
698 }
699 }
700 parent->kid_cnt = cnt;
701 parent->kids = calloc(cnt,sizeof(struct qgnode));
702 cnt = 0;
703 lstart = 0;
704 sc = NULL;
705 for ( l=0; l<parent->qg_cnt; ++l ) {
706 if ( sc!=parent->first[l].sc && sc!=NULL ) {
707 sprintf( buffer, "\"%.40s\" (%d)", sc->name, l-lstart );
708 parent->kids[cnt].name = copy(buffer);
709 parent->kids[cnt].parent = parent;
710 parent->kids[cnt].first = &parent->first[lstart];
711 parent->kids[cnt].qg_cnt = l-lstart;
712 ++cnt;
713 lstart = l;
714 sc = parent->first[l].sc;
715 } else if ( sc!=parent->first[l].sc )
716 sc = parent->first[l].sc;
717 }
718 if ( sc!=NULL ) {
719 sprintf( buffer, "\"%.40s\" (%d)", sc->name, l-lstart );
720 parent->kids[cnt].name = copy(buffer);
721 parent->kids[cnt].parent = parent;
722 parent->kids[cnt].first = &parent->first[lstart];
723 parent->kids[cnt].qg_cnt = l-lstart;
724 }
725 break;
726 }
727 }
728
QGDoSort(QGData * qg)729 static void QGDoSort(QGData *qg) {
730 int pos, l, k, cnt, lstart;
731 char buffer[200];
732
733 pos = GGadgetGetFirstListSelectedItem(GWidgetGetControl(qg->gw,CID_Sort));
734 qg->info_sort = (intpt) sorts[pos].userdata;
735
736 pos = GGadgetGetFirstListSelectedItem(GWidgetGetControl(qg->gw,CID_GlyphSort));
737 qg->glyph_sort = (intpt) glyphsorts[pos].userdata;
738
739 kludge = qg;
740 qsort(qg->qg,qg->cur,sizeof(QuestionableGrid),qg_sorter);
741
742 qgnodeFree(&qg->list);
743 memset(&qg->list,0,sizeof(struct qgnode));
744 qg->list.open = true;
745 qg->list.first = qg->qg;
746 if ( qg->info_sort == is_glyph_size_pt || qg->info_sort == is_glyph_pt_size ) {
747 SplineChar *sc = NULL;
748 cnt = 0;
749 for ( l=0; l<qg->cur; ++l ) {
750 if ( sc!=qg->qg[l].sc ) {
751 ++cnt;
752 sc = qg->qg[l].sc;
753 }
754 }
755 qg->list.kid_cnt = cnt;
756 qg->list.kids = calloc(cnt,sizeof(struct qgnode));
757 cnt = 0;
758 lstart = 0; sc=NULL;
759 for ( l=0; l<qg->cur; ++l ) {
760 if ( sc!=qg->qg[l].sc && sc!=NULL ) {
761 sprintf( buffer, "\"%.40s\" (%d)", sc->name, l-lstart );
762 qg->list.kids[cnt].name = copy(buffer);
763 qg->list.kids[cnt].parent = &qg->list;
764 qg->list.kids[cnt].first = &qg->qg[lstart];
765 qg->list.kids[cnt].qg_cnt = l-lstart;
766 ++cnt;
767 lstart = l;
768 sc = qg->qg[l].sc;
769 } else if ( sc!=qg->qg[l].sc )
770 sc = qg->qg[l].sc;
771 }
772 if ( sc!=NULL ) {
773 sprintf( buffer, "\"%.40s\" (%d)", sc->name, l-lstart );
774 qg->list.kids[cnt].name = copy(buffer);
775 qg->list.kids[cnt].parent = &qg->list;
776 qg->list.kids[cnt].first = &qg->qg[lstart];
777 qg->list.kids[cnt].qg_cnt = l-lstart;
778 }
779 } else {
780 int size = -1;
781 cnt = 0;
782 for ( l=0; l<qg->cur; ++l ) {
783 if ( size!=qg->qg[l].size ) {
784 ++cnt;
785 size = qg->qg[l].size;
786 }
787 }
788 qg->list.kid_cnt = cnt;
789 qg->list.kids = calloc(cnt,sizeof(struct qgnode));
790 cnt = 0;
791 lstart = 0; size=-1;
792 for ( l=0; l<qg->cur; ++l ) {
793 if ( size!=qg->qg[l].size && size!=-1 ) {
794 sprintf( buffer, _("Size: %d (%d)"), size, l-lstart );
795 qg->list.kids[cnt].name = copy(buffer);
796 qg->list.kids[cnt].parent = &qg->list;
797 qg->list.kids[cnt].first = &qg->qg[lstart];
798 qg->list.kids[cnt].qg_cnt = l-lstart;
799 ++cnt;
800 lstart = l;
801 size = qg->qg[l].size;
802 } else if ( size!=qg->qg[l].size )
803 size = qg->qg[l].size;
804 }
805 if ( size!=-1 ) {
806 sprintf( buffer, _("Size: %d (%d)"), size, l-lstart );
807 qg->list.kids[cnt].name = copy(buffer);
808 qg->list.kids[cnt].parent = &qg->list;
809 qg->list.kids[cnt].first = &qg->qg[lstart];
810 qg->list.kids[cnt].qg_cnt = l-lstart;
811 }
812 }
813 if ( qg->list.kid_cnt==1 )
814 qg->list.kids[0].open = true;
815 for ( k=0; k<qg->list.kid_cnt; ++k )
816 QGSecondLevel(qg,&qg->list.kids[k]);
817 QG_Remetric(qg);
818 }
819
QGSorter(GGadget * g,GEvent * e)820 static int QGSorter(GGadget *g, GEvent *e) {
821 if ( e->u.control.subtype == et_listselected ) {
822 QGData *qg = GDrawGetUserData(GGadgetGetWindow(g));
823 QGDoSort(qg);
824 GDrawRequestExpose(qg->v,NULL,false);
825 }
826 return( true );
827 }
828
QGDrawWindow(GWindow pixmap,QGData * qg,GEvent * e)829 static void QGDrawWindow(GWindow pixmap, QGData *qg, GEvent *e) {
830 int l, y, depth;
831 char buffer[200];
832 GRect old, r;
833 struct navigate where;
834 struct qgnode *parent;
835
836 GDrawPushClip(pixmap,&e->u.expose.rect,&old);
837 r.width = r.height = qg->as;
838 y = qg->as;
839 memset(&where,0,sizeof(where));
840 QG_FindLine(&qg->list,qg->loff_top,&where);
841
842 for ( l=0; l<qg->vlcnt && where.parent!=NULL; ++l ) {
843 for ( parent=where.parent, depth= -2; parent!=NULL; parent=parent->parent, ++depth );
844 if ( where.offset==-1 ) {
845 r.x = 2+depth*qg->fh; r.y = y-qg->as+1;
846 GDrawDrawRect(pixmap,&r,0x000000);
847 GDrawDrawLine(pixmap,r.x+2,r.y+qg->as/2,r.x+qg->as-2,r.y+qg->as/2,
848 0x000000);
849 if ( !where.parent->open )
850 GDrawDrawLine(pixmap,r.x+qg->as/2,r.y+2,r.x+qg->as/2,r.y+qg->as-2,
851 0x000000);
852 GDrawDrawText8(pixmap,r.x+qg->fh,y,where.parent->name,-1, 0x000000);
853 } else {
854 QuestionableGrid *q = &where.parent->first[where.offset];
855 sprintf( buffer, _("\"%.40s\" size=%d point=%d (%d,%d) distance=%g"),
856 q->sc->name, q->size, q->nearestpt, q->x, q->y, q->distance );
857 GDrawDrawText8(pixmap,2+(depth+1)*qg->fh,y,buffer,-1, 0x000000);
858 }
859 y += qg->fh;
860 QG_NextLine(&where);
861 }
862 GDrawPopClip(pixmap,&old);
863 }
864
QGMouse(QGData * qg,GEvent * e)865 static void QGMouse( QGData *qg, GEvent *e) {
866 int l = qg->loff_top + e->u.mouse.y/qg->fh;
867 struct navigate where;
868
869 if ( (e->type == et_mousedown) && (e->u.mouse.button==1)) {
870 memset(&where,0,sizeof(where));
871 QG_FindLine(&qg->list,l,&where);
872 if ( where.parent==NULL )
873 return;
874 if ( where.offset==-1 ) {
875 where.parent->open = !where.parent->open;
876 QG_Remetric(qg);
877 GDrawRequestExpose(qg->v,NULL,false);
878 return;
879 } else {
880 QuestionableGrid *q = &where.parent->first[where.offset];
881 CharView *cv;
882 if ( qg->inprocess )
883 return;
884 cv = qg->cv;
885 if ( cv==NULL && qg->fv!=NULL ) {
886 qg->inprocess = true;
887 cv = qg->cv = CharViewCreate(q->sc,(FontView *) (qg->fv),qg->fv->map->backmap[q->sc->orig_pos]);
888 if ( qg->layer == ly_fore ) {
889 cv->b.drawmode = dm_fore;
890 } else {
891 cv->b.layerheads[dm_back] = &qg->sc->layers[qg->layer];
892 cv->b.drawmode = dm_back;
893 }
894 cv->qg = qg;
895 qg->inprocess = false;
896 } else if ( qg->cv==NULL )
897 return;
898 else if ( qg->cv->b.sc != q->sc ) {
899 CVChangeSC(qg->cv,q->sc);
900 }
901 cv->ft_pointsizex = cv->ft_pointsizey = q->size;
902 cv->ft_ppemy = cv->ft_ppemx = rint(q->size*qg->dpi/72.0);
903 cv->ft_dpi = qg->dpi;
904 cv->ft_depth = qg->depth;
905 cv->note_x = q->x; cv->note_y = q->y;
906 cv->show_ft_results = true; cv->showgrids = true;
907 CVGridFitChar(cv);
908 }
909 }
910 }
911
qgv_e_h(GWindow gw,GEvent * event)912 static int qgv_e_h(GWindow gw, GEvent *event) {
913 QGData *qg = (QGData *) GDrawGetUserData(gw);
914
915 switch ( event->type ) {
916 case et_expose:
917 QGDrawWindow(gw,qg,event);
918 break;
919 case et_mouseup:
920 case et_mousedown:
921 case et_mousemove:
922 QGMouse(qg,event);
923 break;
924 case et_char:
925 return( false );
926 break;
927 case et_resize: {
928 int vlcnt = event->u.resize.size.height/qg->fh;
929 qg->vlcnt = vlcnt;
930 QG_SetSb(qg);
931 GDrawRequestExpose(qg->v,NULL,false);
932 } break;
933 }
934 return( true );
935 }
936
QG_OK(GGadget * g,GEvent * e)937 static int QG_OK(GGadget *g, GEvent *e) {
938
939 if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
940 QGData *qg = GDrawGetUserData(GGadgetGetWindow(g));
941 qg->done = true;
942 }
943 return( true );
944 }
945
qg_e_h(GWindow gw,GEvent * event)946 static int qg_e_h(GWindow gw, GEvent *event) {
947
948 if ( event->type==et_close ) {
949 QGData *qg = (QGData *) GDrawGetUserData(gw);
950 qg->done = true;
951 } else if ( event->type == et_char ) {
952 return( false );
953 }
954 return( true );
955 }
956
StartDeltaDisplay(QGData * qg)957 static void StartDeltaDisplay(QGData *qg) {
958 GWindowAttrs wattrs;
959 GRect pos;
960 GWindow gw, oldgw = qg->gw;
961 GGadgetCreateData gcd[8], boxes[5], *harray[4], *harray2[5], *butarray[8],
962 *varray[4];
963 GTextInfo label[8];
964 int i, k;
965 FontRequest rq;
966 int as, ds, ld;
967 static GFont *valfont=NULL;
968 static int sorts_translated = 0;
969
970 if (!sorts_translated)
971 {
972 for (i=0; i<sizeof(sorts)/sizeof(sorts[0]); i++)
973 sorts[i].text = (unichar_t *) _((char *) sorts[i].text);
974 for (i=0; i<sizeof(glyphsorts)/sizeof(glyphsorts[0]); i++)
975 glyphsorts[i].text = (unichar_t *) _((char *) glyphsorts[i].text);
976 sorts_translated=1;
977 }
978
979 memset(&wattrs,0,sizeof(wattrs));
980 wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg;
981 wattrs.event_masks = -1;
982 wattrs.cursor = ct_mypointer;
983 wattrs.utf8_window_title = _("Potential spots for Delta instructions");
984 wattrs.is_dlg = true;
985 wattrs.undercursor = 1;
986 pos.x = pos.y = 0;
987 pos.width = GDrawPointsToPixels(NULL,200);
988 pos.height = GDrawPointsToPixels(NULL,300);
989 qg->gw = gw = GDrawCreateTopWindow(NULL,&pos,qg_e_h,qg,&wattrs);
990 qg->done = false;
991
992 if ( valfont==NULL ) {
993 memset(&rq,0,sizeof(rq));
994 rq.utf8_family_name = "Helvetica";
995 rq.point_size = 11;
996 rq.weight = 400;
997 valfont = GDrawInstanciateFont(gw,&rq);
998 valfont = GResourceFindFont("Validate.Font",valfont);
999 }
1000 qg->font = valfont;
1001 GDrawWindowFontMetrics(gw,qg->font,&as,&ds,&ld);
1002 qg->fh = as+ds;
1003 qg->as = as;
1004
1005 memset(&label,0,sizeof(label));
1006 memset(&gcd,0,sizeof(gcd));
1007 memset(&boxes,0,sizeof(boxes));
1008
1009 k = 0;
1010 label[k].text = (unichar_t *) _("Sort:");
1011 label[k].text_is_1byte = true;
1012 gcd[k].gd.label = &label[k];
1013 gcd[k].gd.flags = gg_enabled|gg_visible;
1014 gcd[k++].creator = GLabelCreate;
1015
1016 sorts[0].selected = true; sorts[1].selected = sorts[2].selected = false;
1017 sorts[2].disabled = qg->fv==NULL;
1018 gcd[k].gd.u.list = sorts;
1019 gcd[k].gd.cid = CID_Sort;
1020 gcd[k].gd.flags = gg_enabled|gg_visible;
1021 gcd[k].gd.handle_controlevent = QGSorter;
1022 gcd[k++].creator = GListButtonCreate;
1023
1024 label[k].text = (unichar_t *) _("Glyph:");
1025 label[k].text_is_1byte = true;
1026 gcd[k].gd.label = &label[k];
1027 gcd[k].gd.flags = gg_enabled|gg_visible;
1028 gcd[k++].creator = GLabelCreate;
1029
1030 gcd[k].gd.u.list = glyphsorts;
1031 gcd[k].gd.flags = gg_enabled|gg_visible;
1032 gcd[k].gd.cid = CID_GlyphSort;
1033 gcd[k].gd.handle_controlevent = QGSorter;
1034 gcd[k++].creator = GListButtonCreate;
1035 if ( qg->fv==NULL )
1036 gcd[k-1].gd.flags = gcd[k-2].gd.flags = gg_enabled;
1037 harray2[0] = &gcd[k-4]; harray2[1] = &gcd[k-3]; harray2[2] = &gcd[k-2]; harray2[3] = &gcd[k-1]; harray2[4] = NULL;
1038
1039
1040 gcd[k].gd.flags = gg_visible | gg_enabled;
1041 gcd[k].gd.u.drawable_e_h = qgv_e_h;
1042 gcd[k++].creator = GDrawableCreate;
1043
1044 gcd[k].gd.flags = gg_visible | gg_enabled | gg_sb_vert;
1045 gcd[k].gd.handle_controlevent = QG_VScroll;
1046 gcd[k++].creator = GScrollBarCreate;
1047 harray[0] = &gcd[k-2]; harray[1] = &gcd[k-1]; harray[2] = NULL; harray[3] = NULL;
1048
1049 gcd[k].gd.flags = gg_visible | gg_enabled | gg_but_default;
1050 label[k].text = (unichar_t *) _("_OK");
1051 label[k].text_is_1byte = true;
1052 label[k].text_in_resource = true;
1053 gcd[k].gd.label = &label[k];
1054 gcd[k].gd.handle_controlevent = QG_OK;
1055 gcd[k++].creator = GButtonCreate;
1056 butarray[0] = GCD_Glue; butarray[1] = &gcd[k-1]; butarray[2] = GCD_Glue; butarray[3] = NULL;
1057
1058 boxes[2].gd.flags = gg_enabled|gg_visible;
1059 boxes[2].gd.u.boxelements = harray;
1060 boxes[2].creator = GHVGroupCreate;
1061
1062 boxes[3].gd.flags = gg_enabled|gg_visible;
1063 boxes[3].gd.u.boxelements = butarray;
1064 boxes[3].creator = GHBoxCreate;
1065
1066 boxes[4].gd.flags = gg_enabled|gg_visible;
1067 boxes[4].gd.u.boxelements = harray2;
1068 boxes[4].creator = GHBoxCreate;
1069 varray[0] = &boxes[4]; varray[1] = &boxes[2]; varray[2] = &boxes[3]; varray[3] = NULL;
1070
1071 boxes[0].gd.flags = gg_enabled|gg_visible;
1072 boxes[0].gd.u.boxelements = varray;
1073 boxes[0].creator = GVBoxCreate;
1074
1075 GGadgetsCreate(gw,boxes);
1076 qg->vsb = gcd[5].ret;
1077 qg->v = GDrawableGetWindow(gcd[4].ret);
1078 GHVBoxSetExpandableRow(boxes[0].ret,1);
1079 GHVBoxSetExpandableCol(boxes[2].ret,0);
1080 GHVBoxSetPadding(boxes[2].ret,0,0);
1081 GHVBoxSetExpandableCol(boxes[3].ret,gb_expandglue);
1082 GHVBoxFitWindow(boxes[0].ret);
1083
1084 QGDoSort(qg);
1085
1086 GDrawSetVisible(gw,true);
1087 while ( !qg->done )
1088 GDrawProcessOneEvent(NULL);
1089 GDrawDestroyWindow(gw);
1090
1091 qgnodeFree(&qg->list);
1092 qg->gw = oldgw;
1093 }
1094