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