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 "bvedit.h"
32 #include "fontforgeui.h"
33 #include "gkeysym.h"
34 #include "ustring.h"
35 
36 #include <math.h>
37 
38 #define TCnt	3
39 
40 typedef struct transdata {
41     void *userdata;
42     void (*transfunc)(void *,real trans[6],int otype,BVTFunc *,enum fvtrans_flags);
43     int  (*getorigin)(void *,BasePoint *bp,int otype);
44     GWindow gw;
45     int applied;
46     int done;
47 } TransData;
48 
49 #define CID_Origin		3001
50 #define CID_AllLayers		3002
51 #define CID_Round2Int		3003
52 #define CID_DoKerns		3004
53 #define CID_DoSimplePos		3005
54 #define CID_DoGrid		3006
55 #define CID_DoWidth		3007
56 #define CID_Apply		3008
57 
58 #define CID_Type	1001
59 #define CID_XMove	1002
60 #define CID_YMove	1003
61 #define CID_Angle	1004
62 #define CID_Scale	1005
63 #define CID_XScale	1006
64 #define CID_YScale	1007
65 #define CID_Horizontal	1008
66 #define CID_Vertical	1009
67 #define CID_SkewAng	1010
68 #define CID_XLab	1011
69 #define CID_YLab	1012
70 #define CID_CounterClockwise	1013
71 #define CID_Clockwise	1014
72 #define CID_XPercent	1015
73 #define CID_YPercent	1016
74 #define CID_XDegree	1017
75 #define CID_YDegree	1018
76 #define CID_XAxis	1019
77 #define CID_YAxis	1020
78 
79 #define CID_First	CID_Type
80 #define CID_Last	CID_YAxis
81 
82 #define CID_ClockBox	1021
83 #define CID_HVBox	1022
84 #define CID_HBox	1023
85 
86 #define TBlock_Width	270
87 #define TBlock_Height	40
88 #define TBlock_Top	32
89 #define TBlock_XStart	130
90 #define TBlock_CIDOffset	100
91 
92 static GTextInfo origin[] = {
93     { (unichar_t *) N_("Glyph Origin"), NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
94     { (unichar_t *) N_("Center of Selection"), NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
95 /* GT: The (x,y) position on the window where the user last pressed a mouse button */
96     { (unichar_t *) N_("Last Press"), NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
97     GTEXTINFO_EMPTY
98 };
99 
100 static int selcid[] = { 0, CID_XMove, CID_Angle, CID_Scale, CID_XScale, 0, CID_SkewAng, CID_XAxis };
101 static GTextInfo transformtypes[] = {
102     { (unichar_t *) N_("Do Nothing"), NULL, 0, 0, (void *) 0x1000, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
103     { (unichar_t *) N_("Move..."), NULL, 0, 0, (void *) 0x1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
104     { (unichar_t *) N_("Rotate..."), NULL, 0, 0, (void *) 0x2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
105     { (unichar_t *) N_("Scale Uniformly..."), NULL, 0, 0, (void *) 0x4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
106     { (unichar_t *) N_("Scale..."), NULL, 0, 0, (void *) 0x8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
107     { (unichar_t *) N_("Flip..."), NULL, 0, 0, (void *) 0x10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
108     { (unichar_t *) N_("Skew..."), NULL, 0, 0, (void *) 0x20, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
109     { (unichar_t *) N_("Rotate 3D Around..."), NULL, 0, 0, (void *) 0x40, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
110     { (unichar_t *) N_("Move by Ruler..."), NULL, 0, 0, (void *) 0x401, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
111     { (unichar_t *) N_("Rotate by Ruler..."), NULL, 0, 0, (void *) 0x402, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
112     { (unichar_t *) N_("Skew by Ruler..."), NULL, 0, 0, (void *) 0x420, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
113     GTEXTINFO_EMPTY
114 };
115 
Trans_OK(GGadget * g,GEvent * e)116 static int Trans_OK(GGadget *g, GEvent *e) {
117     real transform[6], trans[6], t[6];
118     bigreal angle, angle2;
119     int i, index, err;
120     int alllayers = false, round_2_int = false, dokerns = false, dokp=false;
121     int dogrid = false, dowidth = false;
122     BasePoint base;
123     int origin, bvpos=0;
124     BVTFunc bvts[TCnt+1];
125     static int warned = false;
126     int isapply = GGadgetGetCid(g) == CID_Apply;
127 
128     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
129 	TransData *td = GDrawGetUserData(GGadgetGetWindow(g));
130 
131 	transform[0] = transform[3] = 1.0;
132 	transform[1] = transform[2] = transform[4] = transform[5] = 0;
133 	base.x = base.y = 0;
134 
135 	origin = GGadgetGetFirstListSelectedItem( GWidgetGetControl(td->gw,CID_Origin));
136 	if ( GWidgetGetControl(td->gw,CID_AllLayers)!=NULL )
137 	    alllayers = GGadgetIsChecked(GWidgetGetControl(td->gw,CID_AllLayers));
138 	if ( GWidgetGetControl(td->gw,CID_DoGrid)!=NULL )
139 	    dogrid = GGadgetIsChecked(GWidgetGetControl(td->gw,CID_DoGrid));
140 	if ( GWidgetGetControl(td->gw,CID_DoWidth)!=NULL )
141 	    dowidth = GGadgetIsChecked(GWidgetGetControl(td->gw,CID_DoWidth));
142 	else
143 	    dowidth = true;
144 	if ( GWidgetGetControl(td->gw,CID_DoSimplePos)!=NULL )
145 	    dokp = GGadgetIsChecked(GWidgetGetControl(td->gw,CID_DoSimplePos));
146 	if ( GWidgetGetControl(td->gw,CID_DoKerns)!=NULL )
147 	    dokerns = GGadgetIsChecked(GWidgetGetControl(td->gw,CID_DoKerns));
148 	round_2_int = GGadgetIsChecked(GWidgetGetControl(td->gw,CID_Round2Int));
149 	if ( isapply )
150 	    alllayers = dogrid = dokp = dokerns = false;
151 	if ( td->getorigin!=NULL ) {
152 	    (td->getorigin)(td->userdata,&base,origin );
153 	    transform[4] = -base.x;
154 	    transform[5] = -base.y;
155 	}
156 	for ( i=0; i<TCnt; ++i ) {
157 	    index =  GGadgetGetFirstListSelectedItem(
158 		    GWidgetGetControl(td->gw,CID_Type+i*TBlock_CIDOffset));
159 	    trans[0] = trans[3] = 1.0;
160 	    trans[1] = trans[2] = trans[4] = trans[5] = 0;
161 	    err = 0;
162 	    switch ( index ) {
163 	      case 0:		/* Do Nothing */
164 	      break;
165 	      case 1:		/* Move */
166 		trans[4] = GetReal8(td->gw,CID_XMove+i*TBlock_CIDOffset,_("X Movement"),&err);
167 		trans[5] = GetReal8(td->gw,CID_YMove+i*TBlock_CIDOffset,_("Y Movement"),&err);
168 		bvts[bvpos].x = trans[4]; bvts[bvpos].y = trans[5]; bvts[bvpos++].func = bvt_transmove;
169 	      break;
170 	      case 2:		/* Rotate */
171 		angle = GetReal8(td->gw,CID_Angle+i*TBlock_CIDOffset,_("Rotation Angle"),&err);
172 		if ( GGadgetIsChecked( GWidgetGetControl(td->gw,CID_Clockwise+i*TBlock_CIDOffset)) )
173 		    angle = -angle;
174 		if ( fmod(angle,90)!=0 )
175 		    bvts[0].func = bvt_none;		/* Bad trans=> No trans */
176 		else {
177 		    angle = fmod(angle,360);
178 		    if ( angle<0 ) angle+=360;
179 		    if ( angle==90 ) bvts[bvpos++].func = bvt_rotate90ccw;
180 		    else if ( angle==180 ) bvts[bvpos++].func = bvt_rotate180;
181 		    else if ( angle==270 ) bvts[bvpos++].func = bvt_rotate90cw;
182 		}
183 		angle *= FF_PI/180;
184 		trans[0] = trans[3] = cos(angle);
185 		trans[2] = -(trans[1] = sin(angle));
186 	      break;
187 	      case 3:		/* Scale Uniformly */
188 		trans[0] = trans[3] = GetReal8(td->gw,CID_Scale+i*TBlock_CIDOffset,_("Scale Factor"),&err)/100.0;
189 		bvts[0].func = bvt_none;		/* Bad trans=> No trans */
190 	      break;
191 	      case 4:		/* Scale */
192 		trans[0] = GetReal8(td->gw,CID_XScale+i*TBlock_CIDOffset,_("X Scale Factor"),&err)/100.0;
193 		trans[3] = GetReal8(td->gw,CID_YScale+i*TBlock_CIDOffset,_("Y Scale Factor"),&err)/100.0;
194 		bvts[0].func = bvt_none;		/* Bad trans=> No trans */
195 	      break;
196 	      case 5:		/* Flip */
197 		if ( GGadgetIsChecked( GWidgetGetControl(td->gw,CID_Horizontal+i*TBlock_CIDOffset)) ) {
198 		    trans[0] = -1;
199 		    bvts[bvpos++].func = bvt_fliph;
200 		} else {
201 		    trans[3] = -1;
202 		    bvts[bvpos++].func = bvt_flipv;
203 		}
204 	      break;
205 	      case 6:		/* Skew */
206 		angle = GetReal8(td->gw,CID_SkewAng+i*TBlock_CIDOffset,_("Skew Angle"),&err);
207 		if ( GGadgetIsChecked( GWidgetGetControl(td->gw,CID_CounterClockwise+i*TBlock_CIDOffset)) )
208 		    angle = -angle;
209 		angle *= FF_PI/180;
210 		trans[2] = tan(angle);
211 		skewselect(&bvts[bvpos],trans[2]); ++bvpos;
212 	      break;
213 	      case 7:		/* 3D rotate */
214 		angle =  GetReal8(td->gw,CID_XAxis+i*TBlock_CIDOffset,_("Rotation about X Axis"),&err) * FF_PI/180;
215 		angle2 = GetReal8(td->gw,CID_YAxis+i*TBlock_CIDOffset,_("Rotation about Y Axis"),&err) * FF_PI/180;
216 		trans[0] = cos(angle2);
217 		trans[3] = cos(angle );
218 		bvts[0].func = bvt_none;		/* Bad trans=> No trans */
219 	      break;
220 	      default:
221 		IError("Unexpected selection in Transform");
222 		err = 1;
223 	      break;
224 	    }
225 	    if ( err )
226 return(true);
227 	    t[0] = transform[0]*trans[0] +
228 			transform[1]*trans[2];
229 	    t[1] = transform[0]*trans[1] +
230 			transform[1]*trans[3];
231 	    t[2] = transform[2]*trans[0] +
232 			transform[3]*trans[2];
233 	    t[3] = transform[2]*trans[1] +
234 			transform[3]*trans[3];
235 	    t[4] = transform[4]*trans[0] +
236 			transform[5]*trans[2] +
237 			trans[4];
238 	    t[5] = transform[4]*trans[1] +
239 			transform[5]*trans[3] +
240 			trans[5];
241 	    memcpy(transform,t,sizeof(t));
242 	}
243 	bvts[bvpos++].func = bvt_none;		/* Done */
244 	for ( i=0; i<6; ++i )
245 	    if ( RealNear(transform[i],0)) transform[i] = 0;
246 	transform[4] += base.x;
247 	transform[5] += base.y;
248 
249 	if (( transform[1]!=0 || transform[2]!=0 ) && !warned ) {
250 	    ff_post_notice(_("Warning"),_("After rotating or skewing a glyph you should probably apply Element->Add Extrema"));
251 	    warned = true;
252 	}
253 	(td->transfunc)(td->userdata,transform,origin,bvts,
254 		(alllayers?fvt_alllayers:0)|
255 		(dogrid?fvt_dogrid:0)|
256 		 (dowidth?0:fvt_dontmovewidth)|
257 		 (round_2_int?fvt_round_to_int:0)|
258 		 (dokp?fvt_scalepstpos:0)|
259 		 (dokerns?fvt_scalekernclasses:0)|
260 		 (isapply?fvt_justapply:0));
261 	td->done = !isapply;
262 	td->applied = isapply;
263     }
264 return( true );
265 }
266 
Trans_Cancel(GGadget * g,GEvent * e)267 static int Trans_Cancel(GGadget *g, GEvent *e) {
268     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
269 	TransData *td = GDrawGetUserData(GGadgetGetWindow(g));
270 	if ( td->applied )
271 	    (td->transfunc)(td->userdata,NULL,0,NULL,fvt_revert);
272 	td->done = true;
273     }
274 return( true );
275 }
276 
Trans_TypeChange(GGadget * g,GEvent * e)277 static int Trans_TypeChange(GGadget *g, GEvent *e) {
278     if ( e->type==et_controlevent && e->u.control.subtype == et_listselected ) {
279 	GWindow bw = GGadgetGetWindow(g);
280 	int offset = GGadgetGetCid(g)-CID_Type;
281 	int index = GGadgetGetFirstListSelectedItem(g);
282 	if ( index < 0 ) return( false );
283 	int mask = (intpt) transformtypes[index].userdata;
284 	int i;
285 
286 	if ( mask & 0x400 ) {
287 	    real xoff = last_ruler_offset[0].x, yoff = last_ruler_offset[0].y;
288 	    char buf[24]; unichar_t ubuf[24];
289 	    if ( mask & 0x20 )
290 		index -= 4;	/* skew */
291 	    else
292 		index -= 7;	/* move or rotate */
293 	    GGadgetSelectOneListItem( g,index );
294 	    mask &= ~0x400;
295 	    if ( mask&1 ) {		/* Move */
296 		sprintf( buf, "%.1f", (double) xoff );
297 		uc_strcpy(ubuf,buf);
298 		GGadgetSetTitle(GWidgetGetControl(bw,CID_XMove+offset), ubuf );
299 		sprintf( buf, "%.1f", (double) yoff );
300 		uc_strcpy(ubuf,buf);
301 		GGadgetSetTitle(GWidgetGetControl(bw,CID_YMove+offset), ubuf );
302 	    } else {
303 		sprintf( buf, "%.0f", atan2(yoff,xoff)*180/FF_PI );
304 		uc_strcpy(ubuf,buf);
305 		GGadgetSetTitle(GWidgetGetControl(bw,((mask&0x2)?CID_Angle:CID_SkewAng)+offset), ubuf );
306 		GGadgetSetChecked(GWidgetGetControl(bw,CID_Clockwise+offset), false );
307 		GGadgetSetChecked(GWidgetGetControl(bw,CID_CounterClockwise+offset), true );
308 	    }
309 	}
310 
311 	for ( i=CID_First; i<=CID_Last; ++i ) {
312 	    GGadget *sg;
313 	    sg = GWidgetGetControl(bw,i+offset);
314 	    GGadgetSetVisible(sg, ( ((intpt) GGadgetGetUserData(sg))&mask )?1:0);
315 	}
316 	if ( selcid[index]!=0 ) {
317 	    GGadget *tf = GWidgetGetControl(bw,selcid[index]+offset);
318 	    GWidgetIndicateFocusGadget(tf);
319 	    GTextFieldSelect(tf,0,-1);
320 	}
321 	GWidgetToDesiredSize(bw);
322 	GDrawRequestExpose(bw,NULL,false);
323     }
324 return( true );
325 }
326 
trans_e_h(GWindow gw,GEvent * event)327 static int trans_e_h(GWindow gw, GEvent *event) {
328     if ( event->type==et_close ) {
329 	TransData *td = GDrawGetUserData(gw);
330 	td->done = true;
331     } else if ( event->type == et_map && event->u.map.is_visible ) {
332 	/* Above palettes */
333 	GDrawRaise(gw);
334     } else if ( event->type==et_char ) {
335 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
336 	    help("ui/dialogs/transform.html", NULL);
337 return( true );
338 	}
339 return( false );
340     }
341 return( true );
342 }
343 
MakeTransBlock(TransData * td,int bnum,GGadgetCreateData * gcd,GTextInfo * label,GGadgetCreateData ** array)344 static GGadgetCreateData *MakeTransBlock(TransData *td,int bnum,
345 	GGadgetCreateData *gcd, GTextInfo *label, GGadgetCreateData **array) {
346     int offset = bnum*TBlock_CIDOffset;
347 
348     gcd[0].gd.pos.x = 5; gcd[0].gd.pos.y = 9;
349     gcd[0].gd.flags = gg_visible | gg_enabled;
350     gcd[0].gd.label = &transformtypes[0];
351     gcd[0].gd.u.list = transformtypes;
352     gcd[0].gd.cid = CID_Type+offset;
353     gcd[0].creator = GListButtonCreate;
354     gcd[0].data = (void *) -1;
355     gcd[0].gd.handle_controlevent = Trans_TypeChange;
356     transformtypes[0].selected = true;
357 
358     label[1].text = (unichar_t *) _("_X");
359     label[1].text_is_1byte = true;
360     label[1].text_in_resource = true;
361     gcd[1].gd.label = &label[1];
362     gcd[1].gd.pos.x = TBlock_XStart; gcd[1].gd.pos.y = 15;
363     gcd[1].gd.flags = bnum==0? (gg_enabled|gg_visible) : gg_enabled;
364     gcd[1].data = (void *) 0x49;
365     gcd[1].gd.cid = CID_XLab+offset;
366     gcd[1].creator = GLabelCreate;
367 
368     label[2].text = (unichar_t *) "0";
369     label[2].text_is_1byte = true;
370     gcd[2].gd.label = &label[2];
371     gcd[2].gd.pos.x = TBlock_XStart+10; gcd[2].gd.pos.y = 9;  gcd[2].gd.pos.width = 40;
372     gcd[2].gd.flags = bnum==0? (gg_enabled|gg_visible) : gg_enabled;
373     gcd[2].data = (void *) 0x1;
374     gcd[2].gd.cid = CID_XMove+offset;
375     gcd[2].creator = GTextFieldCreate;
376 
377     label[3].text = (unichar_t *) _("_Y");
378     label[3].text_is_1byte = true;
379     label[3].text_in_resource = true;
380     gcd[3].gd.label = &label[3];
381     gcd[3].gd.pos.x = TBlock_XStart+70; gcd[3].gd.pos.y = 15;
382     gcd[3].gd.flags = bnum==0? (gg_enabled|gg_visible) : gg_enabled;
383     gcd[3].data = (void *) 0x49;
384     gcd[3].gd.cid = CID_YLab+offset;
385     gcd[3].creator = GLabelCreate;
386 
387     label[4].text = (unichar_t *) "0";
388     label[4].text_is_1byte = true;
389     gcd[4].gd.label = &label[4];
390     gcd[4].gd.pos.x = TBlock_XStart+80; gcd[4].gd.pos.y = 9;  gcd[4].gd.pos.width = 40;
391     gcd[4].gd.flags = bnum==0? (gg_enabled|gg_visible) : gg_enabled;
392     gcd[4].data = (void *) 0x1;
393     gcd[4].gd.cid = CID_YMove+offset;
394     gcd[4].creator = GTextFieldCreate;
395 
396     label[5].text = (unichar_t *) "100";
397     label[5].text_is_1byte = true;
398     gcd[5].gd.label = &label[5];
399     gcd[5].gd.pos.x = TBlock_XStart+10; gcd[5].gd.pos.y = 9;  gcd[5].gd.pos.width = 40;
400     gcd[5].gd.flags = gg_enabled;
401     gcd[5].data = (void *) 0x8;
402     gcd[5].gd.cid = CID_XScale+offset;
403     gcd[5].creator = GTextFieldCreate;
404 
405     label[6].text = (unichar_t *) "100";
406     label[6].text_is_1byte = true;
407     gcd[6].gd.label = &label[6];
408     gcd[6].gd.pos.x = TBlock_XStart+80; gcd[6].gd.pos.y = 9;  gcd[6].gd.pos.width = 40;
409     gcd[6].gd.flags = gg_enabled;
410     gcd[6].data = (void *) 0x8;
411     gcd[6].gd.cid = CID_YScale+offset;
412     gcd[6].creator = GTextFieldCreate;
413 
414     label[7].text = (unichar_t *) "100";
415     label[7].text_is_1byte = true;
416     gcd[7].gd.label = &label[7];
417     gcd[7].gd.pos.x = TBlock_XStart+10; gcd[7].gd.pos.y = 9;  gcd[7].gd.pos.width = 40;
418     gcd[7].gd.flags = gg_enabled;
419     gcd[7].data = (void *) 0x4;
420     gcd[7].gd.cid = CID_Scale+offset;
421     gcd[7].creator = GTextFieldCreate;
422 
423     label[8].text = (unichar_t *) U_("° Clockwise");
424     label[8].text_is_1byte = true;
425     gcd[8].gd.label = &label[8];
426     gcd[8].gd.pos.x = TBlock_XStart+53; gcd[8].gd.pos.y = 2; gcd[8].gd.pos.height = 12;
427     gcd[8].gd.flags = gg_enabled;
428     gcd[8].data = (void *) 0x22;
429     gcd[8].gd.cid = CID_Clockwise+offset;
430     gcd[8].creator = GRadioCreate;
431 
432 /* GT: Sometimes spelled Widdershins. An old word which means counter clockwise. */
433 /* GT: I used it because "counter clockwise" took too much space. */
434     label[9].text = (unichar_t *) U_("° Withershins");	/* deiseal */
435     label[9].text_is_1byte = true;
436     gcd[9].gd.label = &label[9];
437     gcd[9].gd.pos.x = TBlock_XStart+53; gcd[9].gd.pos.y = 17; gcd[9].gd.pos.height = 12;
438     gcd[9].gd.flags = gg_enabled | gg_cb_on;
439     gcd[9].data = (void *) 0x22;
440     gcd[9].gd.cid = CID_CounterClockwise+offset;
441     gcd[9].creator = GRadioCreate;
442 
443     label[10].text = (unichar_t *) "180";
444     label[10].text_is_1byte = true;
445     gcd[10].gd.label = &label[10];
446     gcd[10].gd.pos.x = TBlock_XStart+10; gcd[10].gd.pos.y = 9;  gcd[10].gd.pos.width = 40;
447     gcd[10].gd.flags = gg_enabled;
448     gcd[10].data = (void *) 0x2;
449     gcd[10].gd.cid = CID_Angle+offset;
450     gcd[10].creator = GTextFieldCreate;
451 
452     label[11].text = (unichar_t *) "10";	/* -10 if we default clockwise */
453     label[11].text_is_1byte = true;
454     gcd[11].gd.label = &label[11];
455     gcd[11].gd.pos.x = TBlock_XStart+10; gcd[11].gd.pos.y = 9;  gcd[11].gd.pos.width = 40;
456     gcd[11].gd.flags = gg_enabled;
457     gcd[11].data = (void *) 0x20;
458     gcd[11].gd.cid = CID_SkewAng+offset;
459     gcd[11].creator = GTextFieldCreate;
460 
461     label[12].text = (unichar_t *) _("Horizontal");
462     label[12].text_is_1byte = true;
463     gcd[12].gd.label = &label[12];
464     gcd[12].gd.pos.x = TBlock_XStart; gcd[12].gd.pos.y = 2; gcd[12].gd.pos.height = 12;
465     gcd[12].gd.flags = gg_enabled | gg_cb_on;
466     gcd[12].data = (void *) 0x10;
467     gcd[12].gd.cid = CID_Horizontal+offset;
468     gcd[12].creator = GRadioCreate;
469 
470     label[13].text = (unichar_t *) _("Vertical");
471     label[13].text_is_1byte = true;
472     gcd[13].gd.label = &label[13];
473     gcd[13].gd.pos.x = TBlock_XStart; gcd[13].gd.pos.y = 17; gcd[13].gd.pos.height = 12;
474     gcd[13].gd.flags = gg_enabled;
475     gcd[13].data = (void *) 0x10;
476     gcd[13].gd.cid = CID_Vertical+offset;
477     gcd[13].creator = GRadioCreate;
478 
479     label[14].text = (unichar_t *) _("%");
480     label[14].text_is_1byte = true;
481     gcd[14].gd.label = &label[14];
482     gcd[14].gd.pos.x = TBlock_XStart+51; gcd[14].gd.pos.y = 15;
483     gcd[14].gd.flags = gg_enabled;
484     gcd[14].data = (void *) 0xc;
485     gcd[14].gd.cid = CID_XPercent+offset;
486     gcd[14].creator = GLabelCreate;
487 
488     label[15].text = (unichar_t *) _("%");
489     label[15].text_is_1byte = true;
490     gcd[15].gd.label = &label[15];
491     gcd[15].gd.pos.x = TBlock_XStart+121; gcd[15].gd.pos.y = 15;
492     gcd[15].gd.flags = gg_enabled;
493     gcd[15].data = (void *) 0x8;
494     gcd[15].gd.cid = CID_YPercent+offset;
495     gcd[15].creator = GLabelCreate;
496 
497     label[16].text = (unichar_t *) U_("°");
498     label[16].text_is_1byte = true;
499     gcd[16].gd.label = &label[16];
500     gcd[16].gd.pos.x = TBlock_XStart+51; gcd[16].gd.pos.y = 15;
501     gcd[16].gd.flags = gg_enabled;
502     gcd[16].data = (void *) 0x40;
503     gcd[16].gd.cid = CID_XDegree+offset;
504     gcd[16].creator = GLabelCreate;
505 
506     label[17].text = (unichar_t *) U_("°");
507     label[17].text_is_1byte = true;
508     gcd[17].gd.label = &label[17];
509     gcd[17].gd.pos.x = TBlock_XStart+121; gcd[17].gd.pos.y = 15;
510     gcd[17].gd.flags = gg_enabled;
511     gcd[17].data = (void *) 0x40;
512     gcd[17].gd.cid = CID_YDegree+offset;
513     gcd[17].creator = GLabelCreate;
514 
515     label[18].text = (unichar_t *) "45";
516     label[18].text_is_1byte = true;
517     gcd[18].gd.label = &label[18];
518     gcd[18].gd.pos.x = TBlock_XStart+10; gcd[18].gd.pos.y = 9;  gcd[18].gd.pos.width = 40;
519     gcd[18].gd.flags = gg_enabled;
520     gcd[18].data = (void *) 0x40;
521     gcd[18].gd.cid = CID_XAxis+offset;
522     gcd[18].creator = GTextFieldCreate;
523 
524     label[19].text = (unichar_t *) "0";
525     label[19].text_is_1byte = true;
526     gcd[19].gd.label = &label[19];
527     gcd[19].gd.pos.x = TBlock_XStart+80; gcd[19].gd.pos.y = 9;  gcd[19].gd.pos.width = 40;
528     gcd[19].gd.flags = gg_enabled;
529     gcd[19].data = (void *) 0x40;
530     gcd[19].gd.cid = CID_YAxis+offset;
531     gcd[19].creator = GTextFieldCreate;
532 
533 
534     array[0] = &gcd[12]; array[1] = &gcd[13]; array[2] = NULL;
535     array[3] = &gcd[8]; array[4] = &gcd[9]; array[5] = NULL;
536 
537     gcd[20].gd.flags = gg_enabled|gg_visible;
538     gcd[20].gd.u.boxelements = array;
539     gcd[20].gd.cid = CID_HVBox+offset;
540     gcd[20].creator = GVBoxCreate;
541 
542     gcd[21].gd.flags = gg_enabled|gg_visible;
543     gcd[21].gd.u.boxelements = array+3;
544     gcd[21].gd.cid = CID_ClockBox+offset;
545     gcd[21].creator = GVBoxCreate;
546 
547     array[6] = &gcd[0];
548     array[7] = &gcd[20];
549     array[8] = &gcd[1];
550     array[9] = &gcd[2];
551     array[10] = &gcd[5];
552     array[11] = &gcd[7];
553     array[12] = &gcd[10];
554     array[13] = &gcd[11];
555     array[14] = &gcd[18];
556     array[15] = &gcd[14];
557     array[16] = &gcd[16];
558     array[17] = &gcd[21];
559     array[18] = &gcd[3];
560     array[19] = &gcd[4];
561     array[20] = &gcd[6];
562     array[21] = &gcd[19];
563     array[22] = &gcd[15];
564     array[23] = &gcd[17];
565     array[24] = GCD_Glue;
566     array[25] = NULL;
567     array[26] = NULL;
568 
569     gcd[22].gd.flags = gg_enabled|gg_visible;
570     gcd[22].gd.u.boxelements = array+6;
571     gcd[22].gd.cid = CID_HBox+offset;
572     gcd[22].creator = GHVGroupCreate;
573 return( gcd+22 );
574 }
575 
TransformDlgCreate(void * data,void (* transfunc)(void *,real *,int,BVTFunc *,enum fvtrans_flags),int (* getorigin)(void *,BasePoint *,int),enum transdlg_flags flags,enum cvtools cvt)576 void TransformDlgCreate(void *data,void (*transfunc)(void *,real *,int,BVTFunc *,enum fvtrans_flags),
577 	int (*getorigin)(void *,BasePoint *,int), enum transdlg_flags flags,
578 	enum cvtools cvt) {
579     GRect pos;
580     GWindow gw;
581     GWindowAttrs wattrs;
582     GGadgetCreateData gcd[12+TCnt*25], boxes[4], *subarray[TCnt*27], *array[2*(TCnt+8)+4], *buttons[13], *origarray[4];
583     GTextInfo label[9+TCnt*24];
584     static TransData td;
585     int i, y, gci, subai, ai;
586     int32 len;
587     GGadget *orig;
588     BasePoint junk;
589     GTextInfo **ti;
590     static int done = false;
591 
592     if ( !done ) {
593 	int i;
594 	for ( i=0; transformtypes[i].text!=NULL; ++i )
595 	    transformtypes[i].text = (unichar_t *) _((char *) transformtypes[i].text);
596 	for ( i=0; origin[i].text!=NULL; ++i )
597 	    origin[i].text = (unichar_t *) _((char *) origin[i].text);
598 	done = true;
599     }
600 
601     td.userdata = data;
602     td.transfunc = transfunc;
603     td.getorigin = getorigin;
604     td.done = false;
605 
606     if ( td.gw==NULL ) {
607 	memset(&wattrs,0,sizeof(wattrs));
608 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_isdlg|wam_restrict;
609 	wattrs.event_masks = ~(1<<et_charup);
610 	wattrs.restrict_input_to_me = 1;
611 	wattrs.undercursor = 1;
612 	wattrs.cursor = ct_pointer;
613 	wattrs.utf8_window_title = _("Transform");
614 	wattrs.is_dlg = true;
615 	pos.x = pos.y = 0;
616 	pos.width = GGadgetScale(GDrawPointsToPixels(NULL,TBlock_Width));
617 	pos.height = GDrawPointsToPixels(NULL,TBlock_Top+TCnt*TBlock_Height+110);
618 	td.gw = gw = GDrawCreateTopWindow(NULL,&pos,trans_e_h,&td,&wattrs);
619 
620 	memset(&label,0,sizeof(label));
621 	memset(&gcd,0,sizeof(gcd));
622 	memset(&boxes,0,sizeof(boxes));
623 
624 	label[0].text = (unichar_t *) _("Origin:");
625 	label[0].text_is_1byte = true;
626 	label[0].text_in_resource = true;
627 	gcd[0].gd.label = &label[0];
628 	gcd[0].gd.pos.x = 5; gcd[0].gd.pos.y = 4;
629 	gcd[0].gd.flags = (getorigin==NULL) ? gg_visible : (gg_visible | gg_enabled);
630 	gcd[0].creator = GLabelCreate;
631 
632 	gcd[1].gd.pos.x = 5; gcd[1].gd.pos.y = 4;
633 	gcd[1].gd.flags = (getorigin==NULL) ? gg_visible : (gg_visible | gg_enabled);
634 	gcd[1].gd.label = &origin[1];
635 	gcd[1].gd.u.list = origin;
636 	gcd[1].gd.cid = CID_Origin;
637 	gcd[1].creator = GListButtonCreate;
638 	origin[1].selected = true;
639 
640 	origarray[0] = &gcd[0]; origarray[1] = &gcd[1]; origarray[2] = GCD_Glue; origarray[3] = NULL;
641 
642 	boxes[3].gd.flags = gg_enabled|gg_visible;
643 	boxes[3].gd.u.boxelements = origarray;
644 	boxes[3].creator = GHBoxCreate;
645 
646 	array[0] = &boxes[3]; array[1] = NULL;
647 
648 	gci = 2; subai = 0; ai = 2;
649 	for ( i=0; i<TCnt; ++i ) {
650 	    array[ai++] = MakeTransBlock(&td,i,gcd+gci,label+gci,subarray+subai);
651 	    array[ai++] = NULL;
652 	    gci += 23; subai += 27;
653 	}
654 
655 	y = TBlock_Top+TCnt*TBlock_Height+4;
656 
657 	    gcd[gci].gd.pos.x = 10; gcd[gci].gd.pos.y = y;
658 	    gcd[gci].gd.flags = (flags&tdf_enableback) ? (gg_visible | gg_enabled) : gg_visible;
659 	    label[gci].text = (unichar_t *) _("Transform _All Layers");
660 	    label[gci].text_is_1byte = true;
661 	    label[gci].text_in_resource = true;
662 	    gcd[gci].gd.label = &label[gci];
663 	    gcd[gci].gd.cid = CID_AllLayers;
664 	    gcd[gci++].creator = GCheckBoxCreate;
665 	    array[ai++] = &gcd[gci-1]; array[ai++] = NULL;
666 	    y += 16;
667 
668 	    gcd[gci].gd.pos.x = 10; gcd[gci].gd.pos.y = y;
669 	    gcd[gci].gd.flags = (flags&tdf_enableback) ? (gg_visible | gg_enabled) : gg_visible;
670 	    label[gci].text = (unichar_t *) _("Transform _Guide Layer Too");
671 	    label[gci].text_is_1byte = true;
672 	    label[gci].text_in_resource = true;
673 	    gcd[gci].gd.label = &label[gci];
674 	    gcd[gci].gd.cid = CID_DoGrid;
675 	    gcd[gci++].creator = GCheckBoxCreate;
676 	    array[ai++] = &gcd[gci-1]; array[ai++] = NULL;
677 	    y += 16;
678 
679 	    gcd[gci].gd.pos.x = 10; gcd[gci].gd.pos.y = y;
680 	    gcd[gci].gd.flags = (flags&tdf_enableback) ? (gg_visible | gg_enabled | gg_cb_on) : gg_visible;
681 	    label[gci].text = (unichar_t *) _("Transform _Width Too");
682 	    label[gci].text_is_1byte = true;
683 	    label[gci].text_in_resource = true;
684 	    gcd[gci].gd.label = &label[gci];
685 	    gcd[gci].gd.cid = CID_DoWidth;
686 	    gcd[gci++].creator = GCheckBoxCreate;
687 	    array[ai++] = &gcd[gci-1]; array[ai++] = NULL;
688 	    y += 16;
689 
690 	    gcd[gci].gd.pos.x = 10; gcd[gci].gd.pos.y = y;
691 	    gcd[gci].gd.flags = gg_visible | (flags&tdf_enablekerns ? gg_enabled : 0) |
692 		    (flags&tdf_defaultkerns ? gg_cb_on : 0);
693 	    label[gci].text = (unichar_t *) _("Transform kerning _classes too");
694 	    label[gci].text_is_1byte = true;
695 	    label[gci].text_in_resource = true;
696 	    gcd[gci].gd.label = &label[gci];
697 	    gcd[gci].gd.cid = CID_DoKerns;
698 	    gcd[gci++].creator = GCheckBoxCreate;
699 	    array[ai++] = &gcd[gci-1]; array[ai++] = NULL;
700 	    y += 16;
701 
702 	    gcd[gci].gd.pos.x = 10; gcd[gci].gd.pos.y = y;
703 	    gcd[gci].gd.flags = gg_visible |
704 		    (flags&tdf_enableback ? gg_enabled : 0) |
705 		    (flags&tdf_enablekerns ? gg_cb_on : 0);
706 	    label[gci].text = (unichar_t *) _("Transform simple positioning features & _kern pairs");
707 	    label[gci].text_is_1byte = true;
708 	    label[gci].text_in_resource = true;
709 	    gcd[gci].gd.label = &label[gci];
710 	    gcd[gci].gd.cid = CID_DoSimplePos;
711 	    gcd[gci++].creator = GCheckBoxCreate;
712 	    array[ai++] = &gcd[gci-1]; array[ai++] = NULL;
713 	    y += 16;
714 
715 	    gcd[gci].gd.pos.x = 10; gcd[gci].gd.pos.y = y;
716 	    gcd[gci].gd.flags = gg_visible | gg_enabled;
717 	    label[gci].text = (unichar_t *) _("Round To _Int");
718 	    label[gci].text_is_1byte = true;
719 	    label[gci].text_in_resource = true;
720 	    gcd[gci].gd.label = &label[gci];
721 	    gcd[gci].gd.cid = CID_Round2Int;
722 	    gcd[gci++].creator = GCheckBoxCreate;
723 	    array[ai++] = &gcd[gci-1]; array[ai++] = NULL;
724 	    y += 24;
725 
726 	array[ai++] = GCD_Glue; array[ai++] = NULL;
727 
728 	gcd[gci].gd.pos.x = 30-3; gcd[gci].gd.pos.y = y;
729 	gcd[gci].gd.flags = gg_visible | gg_enabled | gg_but_default;
730 	label[gci].text = (unichar_t *) _("_OK");
731 	label[gci].text_is_1byte = true;
732 	label[gci].text_in_resource = true;
733 	gcd[gci].gd.mnemonic = 'O';
734 	gcd[gci].gd.label = &label[gci];
735 	gcd[gci].gd.handle_controlevent = Trans_OK;
736 	gcd[gci++].creator = GButtonCreate;
737 	buttons[0] = GCD_Glue; buttons[1] = &gcd[gci-1]; buttons[2] = GCD_Glue; buttons[3] = GCD_Glue;
738 
739 	gcd[gci].gd.flags = gg_visible | gg_enabled;
740 	label[gci].text = (unichar_t *) _("_Apply");
741 	label[gci].text_is_1byte = true;
742 	label[gci].text_in_resource = true;
743 	gcd[gci].gd.label = &label[gci];
744 	gcd[gci].gd.handle_controlevent = Trans_OK;
745 	gcd[gci].gd.cid = CID_Apply;
746 	gcd[gci++].creator = GButtonCreate;
747 	buttons[4] = GCD_Glue; buttons[5] = &gcd[gci-1]; buttons[6] = GCD_Glue; buttons[7] = GCD_Glue;
748 
749 	gcd[gci].gd.pos.x = -30; gcd[gci].gd.pos.y = gcd[gci-1].gd.pos.y+3;
750 	gcd[gci].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
751 	label[gci].text = (unichar_t *) _("_Cancel");
752 	label[gci].text_is_1byte = true;
753 	label[gci].text_in_resource = true;
754 	gcd[gci].gd.label = &label[gci];
755 	gcd[gci].gd.mnemonic = 'C';
756 	gcd[gci].gd.handle_controlevent = Trans_Cancel;
757 	gcd[gci++].creator = GButtonCreate;
758 	buttons[8] = GCD_Glue; buttons[9] = &gcd[gci-1]; buttons[10] = GCD_Glue;
759 	buttons[11] = NULL;
760 
761 	boxes[2].gd.flags = gg_enabled|gg_visible;
762 	boxes[2].gd.u.boxelements = buttons;
763 	boxes[2].creator = GHBoxCreate;
764 
765 	array[ai++] = &boxes[2]; array[ai++] = NULL; array[ai++] = NULL;
766 
767 	boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
768 	boxes[0].gd.flags = gg_enabled|gg_visible;
769 	boxes[0].gd.u.boxelements = array;
770 	boxes[0].creator = GHVGroupCreate;
771 
772 	GGadgetsCreate(gw,boxes);
773 	GHVBoxSetExpandableRow(boxes[0].ret,gb_expandglue);
774 	GHVBoxSetExpandableCol(boxes[2].ret,gb_expandgluesame);
775 	GHVBoxSetExpandableCol(boxes[3].ret,gb_expandglue);
776 	for ( i=0; i<TCnt; ++i ) {
777 	    GHVBoxSetPadding( GWidgetGetControl(gw,CID_ClockBox+i*TBlock_CIDOffset),0,0);
778 	    GHVBoxSetPadding( GWidgetGetControl(gw,CID_HVBox+i*TBlock_CIDOffset),0,0);
779 	    GHVBoxSetExpandableCol( GWidgetGetControl(gw,CID_HBox+i*TBlock_CIDOffset),gb_expandglue);
780 	}
781 	GGadgetSelectOneListItem( GWidgetGetControl(gw,CID_Type), 1);
782 	GWidgetToDesiredSize(gw);
783     } else
784 	GDrawSetTransientFor(td.gw,(GWindow) -1);
785     gw = td.gw;
786 
787     GGadgetSetEnabled( GWidgetGetControl(gw,CID_AllLayers), flags&tdf_enableback);
788     GGadgetSetEnabled( GWidgetGetControl(gw,CID_DoGrid), flags&tdf_enableback);
789     GGadgetSetEnabled( GWidgetGetControl(gw,CID_DoSimplePos), flags&tdf_enableback);
790     GGadgetSetEnabled( GWidgetGetControl(gw,CID_DoKerns), flags&tdf_enablekerns);
791     GGadgetSetVisible( GWidgetGetControl(gw,CID_Apply), flags&tdf_addapply);
792     if ( !(flags&tdf_enableback) ) {
793 	GGadgetSetChecked( GWidgetGetControl(gw,CID_AllLayers), false );
794 	GGadgetSetChecked( GWidgetGetControl(gw,CID_DoGrid), false );
795     }
796     GGadgetSetChecked( GWidgetGetControl(gw,CID_DoKerns),
797 	    !(flags&tdf_enablekerns)?false:(flags&tdf_defaultkerns)?true:false );
798     /* Yes, this is set differently from the previous, that's intended */
799     GGadgetSetChecked( GWidgetGetControl(gw,CID_DoSimplePos),
800 	    !(flags&tdf_enableback)?false:(flags&tdf_enablekerns)?true:false );
801     orig = GWidgetGetControl(gw,CID_Origin);
802     GGadgetSetEnabled( orig, getorigin!=NULL );
803     ti = GGadgetGetList(orig,&len);
804     for ( i=0; i<len; ++i ) {
805 	ti[i]->disabled = !getorigin(data,&junk,i);
806 	if ( ti[i]->disabled && ti[i]->selected ) {
807 	    ti[i]->selected = false;
808 	    ti[0]->selected = true;
809 	    GGadgetSetTitle(orig,ti[0]->text);
810 	}
811     }
812 
813     if ( cvt!=cvt_none ) {
814 	int index = cvt == cvt_scale  ? 4 :
815 		    cvt == cvt_flip   ? 5 :
816 		    cvt == cvt_rotate ? 2 :
817 		    cvt == cvt_skew   ? 6 :
818 			   /* 3d rot*/  7 ;
819 	GGadget *firstoption = GWidgetGetControl(td.gw,CID_Type);
820 	GEvent dummy;
821 	GGadgetSelectOneListItem( firstoption, index );
822 	memset(&dummy,0,sizeof(dummy));
823 	dummy.type = et_controlevent; dummy.u.control.subtype = et_listselected;
824 	Trans_TypeChange( firstoption, &dummy );
825     }
826 
827     for ( i=0; i<TCnt; ++i ) {
828 	int index = GGadgetGetFirstListSelectedItem(GWidgetGetControl(td.gw,CID_Type+i*TBlock_CIDOffset));
829 	if ( selcid[index]>0 ) {
830 	    GGadget *tf = GWidgetGetControl(td.gw,selcid[index]+i*TBlock_CIDOffset);
831 	    GWidgetIndicateFocusGadget(tf);
832 	    GTextFieldSelect(tf,0,-1);
833     break;
834 	}
835     }
836 
837     GWidgetHidePalettes();
838     GDrawSetVisible(gw,true);
839     while ( !td.done )
840 	GDrawProcessOneEvent(NULL);
841     GDrawSetVisible(gw,false);
842 }
843