1 /* Copyright (C) 2002-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 #if !defined(_NO_FFSCRIPT) || !defined(_NO_PYTHON)
31 
32 #include "cvundoes.h"
33 #include "fontforgeui.h"
34 #include "gfile.h"
35 #include "gkeysym.h"
36 #include "gresource.h"
37 #include "scriptfuncs.h"
38 #include "scripting.h"
39 #include "ustring.h"
40 #include "utype.h"
41 
42 struct sd_data {
43     int done;
44     FontView *fv;
45     SplineChar *sc;
46     int layer;
47     GWindow gw;
48     int oldh;
49 };
50 
51 #define SD_Width	250
52 #define SD_Height	270
53 #define CID_Script	1001
54 #define CID_Box		1002
55 #define CID_OK		1003
56 #define CID_Call	1004
57 #define CID_Cancel	1005
58 #define CID_Python	1006
59 #define CID_FF		1007
60 
SD_Call(GGadget * g,GEvent * e)61 static int SD_Call(GGadget *g, GEvent *e) {
62     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
63 	char *fn;
64 	unichar_t *insert;
65 
66 	fn = gwwv_open_filename(_("Call Script"), NULL, "*",NULL);
67 	if ( fn==NULL )
68 return(true);
69 	insert = malloc((strlen(fn)+10)*sizeof(unichar_t));
70 	*insert = '"';
71 	utf82u_strcpy(insert+1,fn);
72 	uc_strcat(insert,"\"()");
73 	GTextFieldReplace(GWidgetGetControl(GGadgetGetWindow(g),CID_Script),insert);
74 	free(insert);
75 	free(fn);
76     }
77 return( true );
78 }
79 
80 #if !defined(_NO_FFSCRIPT)
ExecNative(GGadget * g,GEvent * e)81 static void ExecNative(GGadget *g, GEvent *e) {
82     struct sd_data *sd = GDrawGetUserData(GGadgetGetWindow(g));
83     Context c;
84     Val args[1];
85     jmp_buf env;
86 
87     memset( &c,0,sizeof(c));
88     memset( args,0,sizeof(args));
89     running_script = true;
90     c.a.argc = 1;
91     c.a.vals = args;
92     c.filename = args[0].u.sval = "ScriptDlg";
93     args[0].type = v_str;
94     c.return_val.type = v_void;
95     c.err_env = &env;
96     c.curfv = (FontViewBase *) sd->fv;
97     if ( setjmp(env)!=0 ) {
98 	running_script = false;
99 return;			/* Error return */
100     }
101 
102     c.script = GFileTmpfile();
103     if ( c.script==NULL )
104 	ScriptError(&c, "Can't create temporary file");
105     else {
106 	const unichar_t *ret = _GGadgetGetTitle(GWidgetGetControl(sd->gw,CID_Script));
107 	while ( *ret ) {
108 	    /* There's a bug here. Filenames need to be converted to the local charset !!!! */
109 	    putc(*ret,c.script);
110 	    ++ret;
111 	}
112 	rewind(c.script);
113 	ff_VerboseCheck();
114 	c.lineno = 1;
115 	while ( !c.returned && !c.broken && ff_NextToken(&c)!=tt_eof ) {
116 	    ff_backuptok(&c);
117 	    ff_statement(&c);
118 	}
119 	fclose(c.script);
120 	sd->done = true;
121     }
122     running_script = false;
123 }
124 #endif
125 
126 #if !defined(_NO_PYTHON)
ExecPython(GGadget * g,GEvent * e)127 static void ExecPython(GGadget *g, GEvent *e) {
128     struct sd_data *sd = GDrawGetUserData(GGadgetGetWindow(g));
129     char *str;
130 
131     running_script = true;
132 
133     str = GGadgetGetTitle8(GWidgetGetControl(sd->gw,CID_Script));
134     PyFF_ScriptString((FontViewBase *) sd->fv,sd->sc,sd->layer,str);
135     free(str);
136     running_script = false;
137 }
138 #endif
139 
140 #if !defined(_NO_FFSCRIPT) && !defined(_NO_PYTHON)
_SD_LangChanged(struct sd_data * sd)141 static void _SD_LangChanged(struct sd_data *sd) {
142     GGadgetSetEnabled(GWidgetGetControl(sd->gw,CID_Call),
143 	    !GGadgetIsChecked(GWidgetGetControl(sd->gw,CID_Python)));
144 }
145 
SD_LangChanged(GGadget * g,GEvent * e)146 static int SD_LangChanged(GGadget *g, GEvent *e) {
147     if ( e->type==et_controlevent && e->u.control.subtype == et_radiochanged ) {
148 	struct sd_data *sd = GDrawGetUserData(GGadgetGetWindow(g));
149 	_SD_LangChanged(sd);
150     }
151 return( true );
152 }
153 #endif
154 
SD_OK(GGadget * g,GEvent * e)155 static int SD_OK(GGadget *g, GEvent *e) {
156     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
157 	struct sd_data *sd = GDrawGetUserData(GGadgetGetWindow(g));
158 #if !defined(_NO_FFSCRIPT) && !defined(_NO_PYTHON)
159 	if ( GGadgetIsChecked(GWidgetGetControl(GGadgetGetWindow(g),CID_Python)) )
160 	    ExecPython(g,e);
161 	else
162 	    ExecNative(g,e);
163 #elif !defined(_NO_PYTHON)
164 	ExecPython(g,e);
165 #elif !defined(_NO_FFSCRIPT)
166 	ExecNative(g,e);
167 #endif
168 	sd->done = true;
169     }
170 return( true );
171 }
172 
SD_DoCancel(struct sd_data * sd)173 static void SD_DoCancel(struct sd_data *sd) {
174     sd->done = true;
175 }
176 
SD_Cancel(GGadget * g,GEvent * e)177 static int SD_Cancel(GGadget *g, GEvent *e) {
178     if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
179 	SD_DoCancel( GDrawGetUserData(GGadgetGetWindow(g)));
180     }
181 return( true );
182 }
183 
sd_e_h(GWindow gw,GEvent * event)184 static int sd_e_h(GWindow gw, GEvent *event) {
185     struct sd_data *sd = GDrawGetUserData(gw);
186 
187     if ( sd==NULL )
188 return( true );
189 
190     if ( event->type==et_close ) {
191 	SD_DoCancel( sd );
192     } else if ( event->type==et_controlevent && event->u.control.subtype==et_textchanged ) {
193     sd->fv->script_unsaved = !GTextFieldIsEmpty(GWidgetGetControl(sd->gw,CID_Script));
194     } else if ( event->type==et_controlevent && event->u.control.subtype==et_save ) {
195     sd->fv->script_unsaved = false;
196     } else if ( event->type==et_char ) {
197 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
198 	    help("scripting/scripting.html", NULL);
199 return( true );
200 	}
201 return( false );
202     } else if ( event->type == et_map )	/* Above palettes */
203 	GDrawRaise(gw);
204     else if ( event->type == et_resize )
205 	GDrawRequestExpose(gw,NULL,false);
206 return( true );
207 }
208 
ScriptDlg(FontView * fv,CharView * cv)209 void ScriptDlg(FontView *fv,CharView *cv) {
210     GRect pos;
211     static GWindow gw;
212     GWindowAttrs wattrs;
213     GGadgetCreateData gcd[12], boxes[5], *barray[4][8], *hvarray[4][2];
214 #if !defined(_NO_FFSCRIPT) && !defined(_NO_PYTHON)
215     GGadgetCreateData *rarray[4];
216 #endif
217     GTextInfo label[12];
218     struct sd_data sd;
219     FontView *list;
220     int i,l;
221 
222     memset(&sd,0,sizeof(sd));
223     sd.fv = fv;
224     sd.sc = cv==NULL ? NULL : cv->b.sc;
225     sd.layer = cv==NULL ? ly_fore : CVLayer((CharViewBase *) cv);
226     sd.oldh = pos.height = GDrawPointsToPixels(NULL,SD_Height);
227 
228     if ( gw==NULL ) {
229 	memset(&wattrs,0,sizeof(wattrs));
230 	wattrs.mask = wam_events|wam_cursor|wam_utf8_wtitle|wam_undercursor|wam_restrict|wam_isdlg;
231 	wattrs.event_masks = ~(1<<et_charup);
232 	wattrs.restrict_input_to_me = 1;
233 	wattrs.undercursor = 1;
234 	wattrs.cursor = ct_pointer;
235 	wattrs.utf8_window_title = _("Execute Script");
236 	wattrs.is_dlg = true;
237 	pos.x = pos.y = 0;
238 	pos.width = GDrawPointsToPixels(NULL,GGadgetScale(SD_Width));
239 	gw = GDrawCreateTopWindow(NULL,&pos,sd_e_h,&sd,&wattrs);
240 
241 	memset(&boxes,0,sizeof(boxes));
242 	memset(&gcd,0,sizeof(gcd));
243 	memset(&label,0,sizeof(label));
244 
245 	i = l = 0;
246 	gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = 10;
247 	gcd[i].gd.pos.width = SD_Width-20; gcd[i].gd.pos.height = SD_Height-54;
248 	gcd[i].gd.flags = gg_visible | gg_enabled | gg_textarea_wrap;
249 	gcd[i].gd.cid = CID_Script;
250 	gcd[i++].creator = GTextAreaCreate;
251 	hvarray[l][0] = &gcd[i-1]; hvarray[l++][1] = NULL;
252 
253 #if !defined(_NO_FFSCRIPT) && !defined(_NO_PYTHON)
254 	gcd[i-1].gd.pos.height -= 24;
255 
256 	gcd[i].gd.pos.x = 10; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y+gcd[i-1].gd.pos.height+1;
257 	gcd[i].gd.flags = gg_visible | gg_enabled | gg_cb_on;
258 	gcd[i].gd.cid = CID_Python;
259 	label[i].text = (unichar_t *) _("_Python");
260 	label[i].text_is_1byte = true;
261 	label[i].text_in_resource = true;
262 	gcd[i].gd.label = &label[i];
263 	gcd[i].gd.handle_controlevent = SD_LangChanged;
264 	gcd[i++].creator = GRadioCreate;
265 	rarray[0] = &gcd[i-1];
266 
267 	gcd[i].gd.pos.x = 70; gcd[i].gd.pos.y = gcd[i-1].gd.pos.y;
268 	gcd[i].gd.flags = gg_visible | gg_enabled;	/* disabled if cv!=NULL later */
269 	gcd[i].gd.cid = CID_FF;
270 	label[i].text = (unichar_t *) _("_FF");
271 	label[i].text_is_1byte = true;
272 	label[i].text_in_resource = true;
273 	gcd[i].gd.label = &label[i];
274 	gcd[i].gd.handle_controlevent = SD_LangChanged;
275 	gcd[i++].creator = GRadioCreate;
276 	rarray[1] = &gcd[i-1]; rarray[2] = GCD_Glue; rarray[3] = NULL;
277 
278 	boxes[2].gd.flags = gg_enabled | gg_visible;
279 	boxes[2].gd.u.boxelements = rarray;
280 	boxes[2].creator = GHBoxCreate;
281 	hvarray[l][0] = &boxes[2]; hvarray[l++][1] = NULL;
282 #endif
283 
284 	barray[0][0] = barray[1][0] = barray[0][6] = barray[1][6] = GCD_Glue;
285 	barray[0][2] = barray[1][2] = barray[0][4] = barray[1][4] = GCD_Glue;
286 	barray[0][1] = barray[0][5] = GCD_RowSpan;
287 	barray[0][7] = barray[1][7] = barray[2][0] = NULL;
288 	gcd[i].gd.pos.x = 25-3; gcd[i].gd.pos.y = SD_Height-32-3;
289 	gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_default;
290 	label[i].text = (unichar_t *) _("_OK");
291 	label[i].text_is_1byte = true;
292 	label[i].text_in_resource = true;
293 	gcd[i].gd.mnemonic = 'O';
294 	gcd[i].gd.label = &label[i];
295 	gcd[i].gd.handle_controlevent = SD_OK;
296 	gcd[i].gd.cid = CID_OK;
297 	gcd[i++].creator = GButtonCreate;
298 	barray[1][1] = &gcd[i-1];
299 
300 	gcd[i].gd.pos.x = -25; gcd[i].gd.pos.y = SD_Height-32;
301 	gcd[i].gd.flags = gg_visible | gg_enabled | gg_but_cancel;
302 	label[i].text = (unichar_t *) _("_Cancel");
303 	label[i].text_is_1byte = true;
304 	label[i].text_in_resource = true;
305 	gcd[i].gd.label = &label[i];
306 	gcd[i].gd.mnemonic = 'C';
307 	gcd[i].gd.handle_controlevent = SD_Cancel;
308 	gcd[i].gd.cid = CID_Cancel;
309 	gcd[i++].creator = GButtonCreate;
310 	barray[1][5] = &gcd[i-1];
311 
312 	gcd[i].gd.pos.x = (SD_Width-GIntGetResource(_NUM_Buttonsize)*100/GIntGetResource(_NUM_ScaleFactor))/2; gcd[i].gd.pos.y = SD_Height-40;
313 	gcd[i].gd.flags = gg_visible | gg_enabled;
314 	label[i].text = (unichar_t *) _("C_all...");
315 	label[i].text_is_1byte = true;
316 	label[i].text_in_resource = true;
317 	gcd[i].gd.label = &label[i];
318 	gcd[i].gd.mnemonic = 'a';
319 	gcd[i].gd.handle_controlevent = SD_Call;
320 	gcd[i].gd.cid = CID_Call;
321 	gcd[i++].creator = GButtonCreate;
322 	barray[0][3] = &gcd[i-1];
323 
324 #if !defined(_NO_FFSCRIPT)
325 	gcd[i].gd.pos.width = gcd[i].gd.pos.height = 5;
326 	gcd[i].gd.flags = gg_visible | gg_enabled;
327 	gcd[i++].creator = GSpacerCreate;
328 	barray[1][3] = &gcd[i-1];
329 #else
330 	barray[1][3] = GCD_RowSpan;
331 #endif
332 
333 	barray[3][0] = NULL;
334 
335 	boxes[3].gd.flags = gg_enabled | gg_visible;
336 	boxes[3].gd.u.boxelements = barray[0];
337 	boxes[3].creator = GHVBoxCreate;
338 	hvarray[l][0] = &boxes[3]; hvarray[l++][1] = NULL;
339 	hvarray[l][0] = NULL;
340 
341 	boxes[0].gd.pos.x = boxes[0].gd.pos.y = 2;
342 	boxes[0].gd.flags = gg_enabled | gg_visible;
343 	boxes[0].gd.u.boxelements = hvarray[0];
344 	boxes[0].creator = GHVGroupCreate;
345 
346 	GGadgetsCreate(gw,boxes);
347 	if ( boxes[2].ret!=NULL )
348 	    GHVBoxSetExpandableCol(boxes[2].ret,gb_expandglue);
349 	GHVBoxSetExpandableCol(boxes[3].ret,gb_expandgluesame);
350 	GHVBoxSetExpandableRow(boxes[0].ret,0);
351 	GHVBoxFitWindow(boxes[0].ret);
352     }
353 #if !defined(_NO_FFSCRIPT) && !defined(_NO_PYTHON)
354     GGadgetSetEnabled(GWidgetGetControl(gw,CID_FF),cv==NULL);
355 #endif
356     sd.gw = gw;
357     GDrawSetUserData(gw,&sd);
358     GWidgetIndicateFocusGadget(GWidgetGetControl(gw,CID_Script));
359 #if !defined(_NO_FFSCRIPT) && !defined(_NO_PYTHON)
360     _SD_LangChanged(&sd);
361 #endif
362     GDrawSetVisible(gw,true);
363     while ( !sd.done )
364 	GDrawProcessOneEvent(NULL);
365     GDrawSetVisible(gw,false);
366 
367     /* Selection may be out of date, force a refresh */
368     for ( list = fv_list; list!=NULL; list=(FontView *) list->b.next )
369 	GDrawRequestExpose(list->v,NULL,false);
370     GDrawSync(NULL);
371     GDrawProcessPendingEvents(NULL);
372     GDrawSetUserData(gw,NULL);
373 }
374 #endif	/* No scripting */
375