1 /* editor.cpp is part of UDAV
2  * Copyright (C) 2007-2014 Alexey Balakin <mathgl.abalakin@gmail.ru>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 #include <ctype.h>
18 #include <errno.h>
19 #ifdef __MWERKS__
20 # define FL_DLL
21 #endif
22 #ifdef WIN32
23 #include <direct.h>
24 #else
25 #include <unistd.h>
26 #endif
27 #include "mgllab.h"
28 //-----------------------------------------------------------------------------
29 int changed = 0;
30 std::string filename;
31 Fl_Text_Buffer *textbuf = 0;
32 //-----------------------------------------------------------------------------
33 // Syntax highlighting
34 Fl_Text_Buffer	 *stylebuf = 0;
35 Fl_Text_Display::Style_Table_Entry styletable[10] = {	// Style table
36 		{ FL_BLACK,		FL_COURIER,		14, 0 },		// A - Plain
37 		{ FL_DARK_GREEN,FL_COURIER_ITALIC,	14, 0 },	// B - Line comments
38 		{ FL_BLUE,		FL_COURIER,		14, 0 },		// C - Number
39 		{ FL_RED,		FL_COURIER,		14, 0 },		// D - Strings
40 		{ FL_DARK_BLUE,	FL_COURIER,		14, 0 },		// E - Usual command
41 		{ FL_DARK_CYAN,	FL_COURIER,		14, 0 },		// F - Flow command
42 		{ FL_DARK_MAGENTA,	FL_COURIER,	14, 0 },		// G - New-data command
43 		{ FL_DARK_RED,	FL_COURIER,		14, 0 },		// H - Option
44 		{ FL_GRAY,		FL_COURIER,		14, 0 },		// I - Inactive command
45 		{ FL_MAGENTA,	FL_COURIER,		14, 0 }			// J - Error line ???
46 	};
47 int font_kind;	///< Editor font kind
48 int font_size;	///< Editor font size
49 //-----------------------------------------------------------------------------
set_style(int kind,int size,int fdark)50 void set_style(int kind, int size, int fdark)
51 {
52 	Fl_Color c1[10]={FL_BLACK,FL_DARK_GREEN,FL_BLUE,FL_RED,FL_DARK_BLUE,FL_DARK_CYAN,FL_DARK_MAGENTA,FL_DARK_RED,FL_GRAY,FL_MAGENTA};
53 	Fl_Color c2[10]={FL_WHITE,FL_GREEN,FL_CYAN,FL_YELLOW,FL_BLUE,FL_CYAN,FL_MAGENTA,FL_RED,FL_GRAY,FL_MAGENTA};
54 	if(kind<0 || kind>2)	kind = 1;
55 	if(size<1)	size = 14;
56 	for(int i=0;i<10;i++)	// set font for styles
57 	{	styletable[i].size = size;	styletable[i].font = 4*kind;	}
58 	styletable[1].font = 4*kind+2;
59 	font_kind = kind;	font_size = size;
60 	if(fdark)	for(int i=0;i<10;i++)	styletable[i].color = c2[i];
61 	else		for(int i=0;i<10;i++)	styletable[i].color = c1[i];
62 }
63 //-----------------------------------------------------------------------------
is_sfx(const char * s)64 bool MGL_FUNC_PURE is_sfx(const char *s)	// suffix
65 {
66 	size_t i,n=strlen(s);
67 	for(i=0;i<n && s[i]>='a';i++);
68 	if(i==1 && s[0]=='a')	return true;
69 	if(i==2 && strchr("nmawsk",s[0]) && strchr("axyz",s[1]))	return true;
70 	if(i==3 && (!strncmp("fst",s,3) || !strncmp("lst",s,3) || !strncmp("max",s,3) ||
71 				!strncmp("min",s,3) || !strncmp("sum",s,3)))
72 		return true;
73 	if(i==3 && s[0]=='m' && strchr("xyz",s[1]) && strchr("fl",s[2]))	return true;
74 	return false;
75 //	char *t = new char[i+1];	memcpy(t,s,i*sizeof(char));	t[i]=0;
76 }
77 //-----------------------------------------------------------------------------
is_opt(const char * s)78 bool MGL_FUNC_PURE is_opt(const char *s)	// option
79 {
80 	const char *o[13]={"xrange","yrange","zrange","crange","alpha",
81 					"cut","value","meshnum","size","legend",
82 					"ambient","diffuse","light"};
83 	int l[13] = {6,6,6,6,5, 3,5,7,4,6, 7,7,5};
84 	for(size_t i=0;i<13;i++)	if(!strncmp(o[i],s,l[i]) && s[l[i]]<=' ')	return true;
85 	return false;
86 }
87 //-----------------------------------------------------------------------------
is_num(const char * s)88 bool MGL_FUNC_PURE is_num(const char *s)	// number
89 {
90 	size_t n=strlen(s);
91 //	if(s[0]==':' && (s[1]<=' ' || s[1]==';'))	return true;
92 	if(s[0]<=' ' || s[0]==';' || s[0]==':')	return false;
93 	if(n>=2 && (s[2]<=' ' || s[2]==';' || s[2]==':'))
94 	{
95 		if(!strncmp("pi",s,2))	return true;
96 		if(!strncmp("on",s,2))	return true;
97 	}
98 	if(n>=3 && (s[3]<=' ' || s[3]==';' || s[3]==':'))
99 	{
100 		if(!strncmp("off",s,3))	return true;
101 		if(!strncmp("nan",s,3))	return true;
102 		if(!strncmp("inf",s,3))	return true;
103 		if(!strncmp("all",s,3))	return true;
104 	}
105 	for(size_t i=0;i<n;i++)
106 	{
107 		if(s[i]<=' ' || s[i]==';' || s[i]==':')	break;
108 		if(!strchr("+-.eE0123456789",s[i]))	return false;
109 	}
110 	return true;
111 //	char *t = new char[i+1];	memcpy(t,s,i*sizeof(char));	t[i]=0;
112 }
113 //-----------------------------------------------------------------------------
is_cmd(const char * s)114 char is_cmd(const char *s)	// command
115 {
116 	long i,n=strlen(s)+1;
117 	char res=0, *w=new char[n];	strcpy(w,s);
118 	for(i=0;i<n;i++)	if(!isalnum(s[i]))	w[i]=0;
119 	int rts = Parse->CmdType(w);
120 	if(rts==5)		res = 'G';
121 	else if(rts==7)	res = 'F';
122 	else if(rts)	res = 'E';
123 	delete []w;		return res;
124 }
125 //-----------------------------------------------------------------------------
126 // Parse text and produce style data.
style_parse(const char * text,char * style,int)127 void style_parse(const char *text, char *style, int /*length*/)
128 {
129 	size_t n=strlen(text);
130 	bool nl=true;
131 	// Style letters:
132 	// A - Plain
133 	// B - Line comments
134 	// C - Number
135 	// D - Strings
136 	// E - Usual command
137 	// F - Flow command
138 	// G - New data command
139 	// H - Option
140 
141 	for(size_t i=0;i<n;i++)
142 	{
143 		char ch = text[i], r;	style[i] = 'A';
144 		if(ch=='#')	// comment
145 			for(;i<n && text[i]!='\n';i++)	style[i]='B';
146 		else if(ch=='\'')	// string
147 		{
148 			style[i]='D';	i++;
149 			for(;i<n && text[i]!='\n' && text[i]!='\'';i++)	style[i]='D';
150 			style[i]='D';
151 		}
152 		else if(ch=='\n' || ch==':')	{	nl=true;	continue;	}
153 		else if(nl && (r=is_cmd(text+i)) )	// command name
154 		{	for(;i<n && isalnum(text[i]);i++)	style[i]=r;	i--;	}
155 		else if(!nl && is_opt(text+i))	// option
156 		{	for(;i<n && isalpha(text[i]);i++)	style[i]='H';	i--;	}
157 		else if(!nl && is_num(text+i))	// number
158 		{
159 			for(;i<n && strchr("+-.eE0123456789pionaf",text[i]);i++)	style[i]='C';
160 			i--;
161 		}
162 		else if(ch=='.' && is_sfx(text+i+1))	// option (suffix)
163 		{
164 			style[i]='H';	i++;
165 			for(;i<n && isalpha(text[i]);i++)	style[i]='H';
166 		}
167 		else if(ch=='.' || (ch>='0' && ch<='9'))
168 		{	style[i]='C';	if(text[i+1]=='e' || text[i+1]=='E')	style[i+1]='C';	}
169 		nl = false;
170 	}
171 }
172 //-----------------------------------------------------------------------------
173 // Initialize the style buffer
style_init()174 void style_init()
175 {
176 	long len = textbuf->length();
177 	char *style = new char[len + 1];
178 	char *text = textbuf->text();
179 	memset(style, 'A', len);	style[len] = '\0';
180 	if(!stylebuf)	stylebuf = new Fl_Text_Buffer(len);
181 	style_parse(text, style, len);
182 	stylebuf->text(style);
183 	delete []style;	free(text);
184 }
185 //-----------------------------------------------------------------------------
186 // Update unfinished styles.
style_unfinished_cb(int,void *)187 void style_unfinished_cb(int, void*) {}
188 //-----------------------------------------------------------------------------
189 // Update the style buffer...
style_update(int pos,int nInserted,int nDeleted,int,const char *,void * cbArg)190 void style_update(int pos, int nInserted, int nDeleted, int	/*nRestyled*/, const char */*deletedText*/, void *cbArg)
191 {
192 	long start, end;	// Start and end of text
193 	char last,		// Last style on line
194 		*style,		// Style data
195 		*text;		// Text data
196 
197 	// If this is just a selection change, just unselect the style buffer...
198 	if (nInserted == 0 && nDeleted == 0) {	stylebuf->unselect();	return;  }
199 	// Track changes in the text buffer...
200 	if (nInserted > 0)
201 	{
202 		// Insert characters into the style buffer...
203 		style = new char[nInserted + 1];
204 		memset(style, 'A', nInserted);
205 		style[nInserted] = '\0';
206 
207 		stylebuf->replace(pos, pos + nDeleted, style);
208 		delete[] style;
209 	}
210 	else	// Just delete characters in the style buffer...
211 		stylebuf->remove(pos, pos + nDeleted);
212 	// Select the area that was just updated to avoid unnecessary callbacks...
213 	stylebuf->select(pos, pos + nInserted - nDeleted);
214 	// Re-parse the changed region; we do this by parsing from the
215 	// beginning of the previous line of the changed region to the end of
216 	// the line of the changed region...  Then we check the last
217 	// style character and keep updating if we have a multi-line
218 	// comment character...
219 	start = textbuf->line_start(pos);
220 	end   = textbuf->line_end(pos + nInserted);
221 	text  = textbuf->text_range(start, end);
222 	style = stylebuf->text_range(start, end);
223 	if (start==end)	last = 0;
224 	else	last = style[end-start-1];
225 	style_parse(text, style, end - start);
226 	stylebuf->replace(start, end, style);
227 	((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
228 
229 	if (start==end || last != style[end-start-1])
230 	{
231 		// Either the user deleted some text, or the last character on
232 		// the line changed styles, so reparse the remainder of the buffer...
233 		free(text);	free(style);
234 		end   = textbuf->length();
235 		text  = textbuf->text_range(start, end);
236 		style = stylebuf->text_range(start, end);
237 		style_parse(text, style, end - start);
238 		stylebuf->replace(start, end, style);
239 		((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
240 	}
241 	free(text);	free(style);
242 }
243 //-----------------------------------------------------------------------------
ScriptWindow(int w,int h,const char * t)244 ScriptWindow::ScriptWindow(int w, int h, const char* t) : Fl_Double_Window(w, h, t)
245 {	editor = 0;	}
246 //-----------------------------------------------------------------------------
set_path(char * buf)247 void set_path(char *buf)
248 {
249 #ifdef WIN32
250 	char sep='\\';
251 #else
252 	char sep='/';
253 #endif
254 	for(long i=strlen(buf)-1;i>=0;i--)	if(buf[i]==sep)
255 	{	buf[i]=0;	break;	}
256 	if(!chdir(buf))	printf("chdir to '%s'\n",buf);
257 }
258 //-----------------------------------------------------------------------------
add_filename(const char * fname,ScriptWindow * e)259 void add_filename(const char *fname, ScriptWindow *e)
260 {
261 	static char buf[FL_PATH_MAX];
262 	fl_filename_absolute(buf, FL_PATH_MAX, fname);	fname=buf;
263 	if(!fname || !fname[0] || lastfiles[0]==fname)
264 	{	set_path(buf);	return;	}
265 	pref.set("last_file",fname);
266 	int ii=4;
267 	for(int i=1;i<5;i++)
268 		if(lastfiles[i]==fname)	{	ii=i;	break;	}
269 	for(int i=ii;i>0;i--)	lastfiles[i]=lastfiles[i-1];
270 	lastfiles[0]=fname;
271 	int ir = e->menu->find_index(_("File/Recent files"));
272 	if(ir<0)	ir = 6;
273 	e->menu->replace(ir+1, lastfiles[0].c_str());
274 	e->menu->replace(ir+2, lastfiles[1].c_str());
275 	e->menu->replace(ir+3, lastfiles[2].c_str());
276 	e->menu->replace(ir+4, lastfiles[3].c_str());
277 	e->menu->replace(ir+5, lastfiles[4].c_str());
278 	set_path(buf);	save_pref();
279 }
280 //-----------------------------------------------------------------------------
check_save(void)281 int check_save(void)
282 {
283   if (!changed) return 1;
284   int r = fl_choice(_("The current file has not been saved.\n"
285 					"Would you like to save it now?"),
286 					_("Cancel"), _("Save"), _("Don't Save"));
287   if(r==1)	{	save_cb(0,0);	return !changed;	} // Save the file...
288   return (r==2) ? 1 : 0;
289 }
290 //-----------------------------------------------------------------------------
data_file(const char * fn)291 void data_file(const char *fn)
292 {
293 	static int num=0;
294 	static char name[32], res[256];
295 	snprintf(name,32,"mgl_%d",num);	num++;
296 	mglDataA *v = Parse->AddVar(name);
297 	mglData *d = dynamic_cast<mglData*>(v);
298 	mglDataC *c = dynamic_cast<mglDataC*>(v);
299 	if(d)
300 	{
301 		d->Read(fn);
302 		if(d->nz>1)
303 			snprintf(res,256,"#read %s '%s'\nrotate 40 60\ncrange %s\nbox\nsurf3 %s\n", name, fn, name, name);
304 		else if(d->ny>1)
305 			snprintf(res,256,"#read %s '%s'\nrotate 40 60\ncrange %s\nzrange %s\nbox\nsurf %s\n", name, fn, name, name, name);
306 		else
307 			snprintf(res,256,"#read %s '%s'\nyrange %s\nbox\nplot %s\n", name, fn, name, name);
308 		textbuf->text(res);
309 	}
310 	else if(c)
311 	{
312 		c->Read(fn);
313 		if(c->nz>1)
314 			snprintf(res,256,"#read %s '%s'\nrotate 40 60\ncrange %s\nbox\nsurf3 %s\n", name, fn, name, name);
315 		else if(c->ny>1)
316 			snprintf(res,256,"#read %s '%s'\nrotate 40 60\ncrange %s\nzrange %s\nbox\nsurf %s\n", name, fn, name, name, name);
317 		else
318 			snprintf(res,256,"#read %s '%s'\nyrange %s\nbox\nplot %s\n", name, fn, name, name);
319 		textbuf->text(res);
320 	}
321 }
322 //-----------------------------------------------------------------------------
323 int loading = 0;
load_file(const char * newfile,int ipos,ScriptWindow * e)324 void load_file(const char *newfile, int ipos, ScriptWindow *e)
325 {
326 	long len = strlen(newfile);
327 	if(ipos==-1 && (!strcmp(newfile+len-4,".dat") || !strcmp(newfile+len-4,".csv")))
328 	{
329 		data_file(newfile);
330 		filename = newfile;	filename += ".mgl";
331 		add_filename(filename.c_str(),e);
332 	}
333 	else
334 	{
335 		loading = 1;
336 		int insert = (ipos != -1);
337 		changed = insert;
338 		if(!insert) filename="";
339 		long r;
340 		if(!insert)	r = textbuf->loadfile(newfile);
341 		else r = textbuf->insertfile(newfile, ipos);
342 
343 		char *t = textbuf->text();
344 #ifndef WIN32
345 		size_t i,l=strlen(t);
346 		for(i=0;i<l;i++)	if(t[i]=='\r')	t[i]=' ';
347 		textbuf->text(t);
348 #endif
349 		fill_animate(t, e->draw);	free(t);
350 
351 		if (r)
352 			fl_alert(_("Error reading from file \'%s\':\n%s."), newfile, strerror(errno));
353 		else	if(!insert)
354 		{	filename = newfile;	add_filename(filename.c_str(),e);	}
355 		loading = 0;
356 		textbuf->call_modify_callbacks();
357 	}
358 }
359 //-----------------------------------------------------------------------------
save_file(const char * newfile,ScriptWindow * e)360 void save_file(const char *newfile, ScriptWindow *e)
361 {
362 	if (textbuf->savefile(newfile))
363 		fl_alert(_("Error writing to file \'%s\':\n%s."), newfile, strerror(errno));
364 	else
365 	{
366 		filename = newfile;	add_filename(filename.c_str(),e);
367 		changed = 0;	textbuf->call_modify_callbacks();
368 	}
369 }
370 //-----------------------------------------------------------------------------
undo_cb(Fl_Widget *,void * v)371 void undo_cb(Fl_Widget*, void* v)
372 {
373 	ScriptWindow* e = (ScriptWindow*)v;
374 	Fl_Text_Editor::kf_undo(0, e->editor);
375 }
376 //-----------------------------------------------------------------------------
select_all_cb(Fl_Widget *,void * v)377 void select_all_cb(Fl_Widget *, void *v)
378 {
379 	ScriptWindow* e = (ScriptWindow*)v;
380 	Fl_Text_Editor::kf_select_all(0, e->editor);
381 }
382 //-----------------------------------------------------------------------------
copy_cb(Fl_Widget *,void * v)383 void copy_cb(Fl_Widget*, void* v)
384 {
385 	ScriptWindow* e = (ScriptWindow*)v;
386 	Fl_Text_Editor::kf_copy(0, e->editor);
387 }
388 //-----------------------------------------------------------------------------
cut_cb(Fl_Widget *,void * v)389 void cut_cb(Fl_Widget*, void* v)
390 {
391 	ScriptWindow* e = (ScriptWindow*)v;
392 	Fl_Text_Editor::kf_cut(0, e->editor);
393 }
394 //-----------------------------------------------------------------------------
hide_cb(Fl_Widget *,void * v)395 void hide_cb(Fl_Widget*, void* v)
396 {
397 	ScriptWindow* e = (ScriptWindow*)v;
398 	int p1,p2;
399 	textbuf->selection_position(&p1, &p2);
400 	if(!textbuf->selected())	p2=p1=e->editor->insert_position();
401 	p1 = textbuf->line_start(p1);
402 	while(p1<p2)
403 	{
404 		textbuf->insert(p1,"#h ");
405 		int p = textbuf->line_start(textbuf->line_end(p1)+1);
406 		if(p!=p1)	p1=p;	else	return;
407 	}
408 }
409 //-----------------------------------------------------------------------------
unhide_cb(Fl_Widget *,void * v)410 void unhide_cb(Fl_Widget*, void* v)
411 {
412 	ScriptWindow* e = (ScriptWindow*)v;
413 	int p1,p2;
414 	textbuf->selection_position(&p1, &p2);
415 	if(!textbuf->selected())	p2=p1=e->editor->insert_position();
416 	p1 = textbuf->line_start(p1);
417 	while(p1<p2)
418 	{
419 		if(textbuf->char_at(p1)=='#')
420 		{
421 			if(textbuf->char_at(p1+1)=='h' && textbuf->char_at(p1+2)==' ')
422 				textbuf->remove(p1,p1+3);
423 			else	textbuf->remove(p1,p1+1);
424 		}
425 		int p = textbuf->line_start(textbuf->line_end(p1)+1);
426 		if(p!=p1)	p1=p;	else	return;
427 	}
428 }
429 //-----------------------------------------------------------------------------
delete_cb(Fl_Widget *,void *)430 void delete_cb(Fl_Widget*, void*) {	textbuf->remove_selection();	}
431 //-----------------------------------------------------------------------------
cb_descr(Fl_Widget *,void * v)432 void cb_descr(Fl_Widget*,void *v)
433 {
434 	static size_t len=0;
435 	ScriptWindow *w = (ScriptWindow*)v;
436 	if(!textbuf || !Parse || !w)	return;
437 	int cur = w->editor->insert_position(), br=0;
438 	int beg = textbuf->line_start(cur);
439 	const char *s = textbuf->text();
440 	for(int i=beg;i<cur;i++)
441 	{
442 		if(strchr("({[",s[i]))	br++;
443 		if(strchr(")}]",s[i]))	br--;
444 		if(br==0 && s[i]==':' && i+1<cur)	beg=i+1;
445 	}
446 	for(br=beg;s[br]>' ' && s[br]!=':';br++);
447 	std::string cmd(s+beg,br-beg);
448 	const char *desc = Parse->CmdDesc(cmd.c_str());
449 	const char *form = Parse->CmdFormat(cmd.c_str());
450 	static std::string txt;
451 	txt = desc?std::string(desc)+":  "+form : "";
452 	w->set_status(txt.c_str());
453 
454 	size_t ll = strlen(s);
455 	if(complete_word && br==cur+1 && br-beg>2 && len<ll)	// try complete word
456 	{
457 		long n = Parse->GetCmdNum();
458 		std::vector<std::string> vars;
459 		for(long i=0;i<n;i++)
460 		{
461 			const char *c = Parse->GetCmdName(i);
462 			if(!strncmp(c,cmd.c_str(),cmd.length()))	vars.push_back(c);
463 		}
464 		for(size_t i=0;i<vars.size();i++)
465 			if(vars[i].length()>cmd.length())
466 			{
467 				std::string suggest = vars[i].substr(cmd.length());
468 				textbuf->insert(cur+1, suggest.c_str());
469 				textbuf->select(cur+1, cur+suggest.length()+1);
470 				break;
471 			}
472 	}
473 	len = ll;
474 }
475 //-----------------------------------------------------------------------------
set_status(const char * txt)476 void ScriptWindow::set_status(const char *txt)
477 {	if(txt && status)	{	status->label(txt);	redraw();	}	}
478 //-----------------------------------------------------------------------------
changed_cb(int pos,int nInserted,int nDeleted,int nRestyled,const char * deletedText,void * v)479 void changed_cb(int pos, int nInserted, int nDeleted, int nRestyled, const char *deletedText, void* v)
480 {
481 	if ((nInserted || nDeleted) && !loading) changed = 1;
482 	ScriptWindow *w = (ScriptWindow *)v;
483 	cb_descr(0,v);
484 	set_title(w);
485 	style_update(pos, nInserted, nDeleted, nRestyled, deletedText, w->editor);
486 	if (loading) w->editor->show_insert_position();
487 }
488 //-----------------------------------------------------------------------------
489 // void insert_cb(Fl_Widget*, void *v)
490 // {
491 // 	const char *newfile = mgl_file_chooser(_("Insert file content?"));
492 // 	ScriptWindow *w = (ScriptWindow *)v;
493 // 	if (newfile != NULL) load_file(newfile, w->editor->insert_position(),w);
494 // }
495 //-----------------------------------------------------------------------------
paste_cb(Fl_Widget *,void * v)496 void paste_cb(Fl_Widget*, void* v)
497 {
498 	ScriptWindow* e = (ScriptWindow*)v;
499 	Fl_Text_Editor::kf_paste(0, e->editor);
500 }
501 //-----------------------------------------------------------------------------
502 #include "../widgets/image.h"
503 #include "xpm/box.xpm"
add_editor(ScriptWindow * w,int txtW,int wndH)504 Fl_Widget *add_editor(ScriptWindow *w, int txtW, int wndH)
505 {
506 	Fl_Window *w1=new Fl_Window(0,30,txtW,wndH-55,0);
507 	Fl_Group *g = new Fl_Group(0,0,txtW-10,30);
508 	Fl_Button *o;
509 
510 	o = new Fl_Button(0, 1, 25, 25);	o->image(img_load);	o->callback(open_cb,w);
511 	o->tooltip(_("Open script or data file"));
512 	o = new Fl_Button(25, 1, 25, 25);	o->image(img_save);	o->callback(save_cb,w);
513 	o->tooltip(_("Save script to file"));
514 
515 	o = new Fl_Button(55, 1, 25, 25);	o->image(img_copy);	o->callback(copy_cb,w);
516 	o->tooltip(_("Copy selection to clipboard"));
517 	o = new Fl_Button(80, 1, 25, 25);	o->image(img_paste);o->callback(paste_cb,w);
518 	o->tooltip(_("Paste text from clipboard"));
519 	o = new Fl_Button(105, 1, 25, 25);	o->image(img_find);	o->callback(find_dlg_cb,w);
520 	o->tooltip(_("Find or replace text"));
521 
522 	o = new Fl_Button(135, 1, 25, 25);	o->image(img_insert);	o->callback(newcmd_dlg_cb,w);
523 	o->tooltip(_("Insert MGL command"));
524 	o = new Fl_Button(160, 1, 25, 25);	o->image(img_fname);	o->callback(ins_fname_cb,w);
525 	o->tooltip(_("Insert filename"));
526 	o = new Fl_Button(185, 1, 25, 25);	o->image(new Fl_Pixmap(box_xpm));	o->callback(inplot_dlg_cb,w);
527 	o->tooltip(_("Insert inplot command"));
528 
529 	o = new Fl_Button(210, 1, 25, 25);	o->image(img_calc);	o->callback(calc_dlg_cb,w);
530 	o->tooltip(_("Show calculator window"));
531 	o = new Fl_Button(240, 1, 25, 25);	o->image(img_curve);o->callback(prim_dlg_cb,w);
532 	o->tooltip(_("Show window for primitives"));
533 	g->end();	g->resizable(0);
534 
535 	w->editor = new Fl_Text_Editor(0, 28, txtW, wndH-85);
536 	w->editor->textfont(FL_COURIER);
537 	w->editor->buffer(textbuf);
538 	w->editor->highlight_data(stylebuf, styletable, sizeof(styletable) / sizeof(styletable[0]), 'A', style_unfinished_cb, 0);
539 #if MGL_HAVE_FL_COPY
540 	w->editor->linenumber_width(30);
541 #endif
542 //	w->editor->when(FL_WHEN_RELEASE_ALWAYS);	w->editor->callback(cb_descr,w);
543 
544 	textbuf->add_modify_callback(changed_cb, w);
545 	textbuf->call_modify_callbacks();
546 
547 	w1->end();	w1->resizable(w->editor);
548 	return w1;
549 }
550 //-----------------------------------------------------------------------------
551 void cp_find_next(Fl_Widget*,void*);
552 void cp_repl_next(Fl_Widget*,void*);
553 void cp_repl_all(Fl_Widget*,void*);
554 class FindDlg : public GeneralDlg
555 {
556 	Fl_Input *find, *replace;
557 	Fl_Check_Button *mcase, *sback;
558 public:
FindDlg()559 	FindDlg() : GeneralDlg()
560 	{
561 		Fl_Button* o;
562 		w = new Fl_Double_Window(375, 130, _("Find/Replace"));
563 		find = new Fl_Input(90, 10, 180, 25, _("Find what:"));
564 		o = new Fl_Return_Button(275, 10, 95, 25, _("Find"));	o->callback(cp_find_next);
565 		replace = new Fl_Input(90, 40, 180, 25, _("Replace by:"));
566 		o = new Fl_Button(275, 40, 95, 25, _("Replace"));		o->callback(cp_repl_next);
567 		mcase = new Fl_Check_Button(5, 70, 265, 25, _("Match case"));
568 		sback = new Fl_Check_Button(5, 95, 265, 25, _("Search backward"));
569 		o = new Fl_Button(275, 70, 95, 25, _("Replace all"));	o->callback(cp_repl_all);
570 		o = new Fl_Button(275, 100, 95, 25, _("Close"));	o->callback(cb_dlg_cancel,this);
571 		w->end();
572 	}
to_find()573 	const char *to_find()	{	return find->value();	}
find_next()574 	void find_next()
575 	{
576 		const char *s = find->value();
577 		int c = mcase->value(), b = sback->value();
578 		if(s && *s)
579 		{
580 			int pos = e->editor->insert_position();
581 			int found = b ? textbuf->search_backward(pos,s,&pos,c) : textbuf->search_forward(pos,s,&pos,c);
582 			if(found)
583 			{	// Found a match; select and update the position...
584 				size_t len = strlen(s);
585 				textbuf->select(pos, pos+len);
586 				e->editor->insert_position(pos+len);
587 				e->editor->show_insert_position();
588 			}
589 			else fl_alert(_("No occurrences of \'%s\' found!"), s);
590 		}
591 	}
repl_next()592 	void repl_next()
593 	{
594 		const char *s = find->value();
595 		const char *r = replace->value();
596 		int c = mcase->value(), b = sback->value();
597 		if(s && *s)
598 		{
599 			int pos = e->editor->insert_position();
600 			int found = b ? textbuf->search_backward(pos,s,&pos,c) : textbuf->search_forward(pos,s,&pos,c);
601 			if(found)
602 			{	// Found a match; select and update the position...
603 				size_t len = strlen(r);
604 				textbuf->select(pos, pos+strlen(s));
605 				textbuf->remove_selection();
606 				textbuf->insert(pos, r);
607 				textbuf->select(pos, pos+len);
608 				e->editor->insert_position(pos+len);
609 				e->editor->show_insert_position();
610 			}
611 			else fl_alert(_("No occurrences of \'%s\' found!"), s);
612 		}
613 	}
repl_all()614 	void repl_all()
615 	{
616 		const char *s = find->value();
617 		const char *r = replace->value();
618 		int c = mcase->value(), b = sback->value();
619 		int found = (s && *s)?1:0;
620 		long num=0;
621 		while(found)
622 		{
623 			int pos = e->editor->insert_position();
624 			int found = b ? textbuf->search_backward(pos,s,&pos,c) : textbuf->search_forward(pos,s,&pos,c);
625 			if(!found)	break;
626 			size_t len = strlen(r);
627 			textbuf->select(pos, pos+strlen(s));
628 			textbuf->remove_selection();
629 			textbuf->insert(pos, r);
630 			textbuf->select(pos, pos+len);
631 			e->editor->insert_position(pos+len);
632 			e->editor->show_insert_position();
633 			num++;
634 		}
635 		if(num) fl_message(_("Replaced %ld occurrences."), num);
636 		else fl_alert(_("No occurrences of \'%s\' found!"), s);
637 	}
638 } find_dlg;
639 //-----------------------------------------------------------------------------
cp_find_next(Fl_Widget *,void *)640 void cp_find_next(Fl_Widget*,void*)	{	find_dlg.find_next();	}
cp_repl_next(Fl_Widget *,void *)641 void cp_repl_next(Fl_Widget*,void*)	{	find_dlg.repl_next();	}
cp_repl_all(Fl_Widget *,void *)642 void cp_repl_all(Fl_Widget*,void*)	{	find_dlg.repl_all();	}
643 //-----------------------------------------------------------------------------
find_dlg_cb(Fl_Widget *,void * v)644 void find_dlg_cb(Fl_Widget*,void *v)
645 {	find_dlg.e = (ScriptWindow*)v;	find_dlg.show();	}
646 //-----------------------------------------------------------------------------
find_next_cb(Fl_Widget *,void * v)647 void find_next_cb(Fl_Widget*,void *v)
648 {
649 	find_dlg.e = (ScriptWindow*)v;
650 	const char *s = find_dlg.to_find();
651 	if(s && *s)	find_dlg.find_next();
652 	else	find_dlg.show();
653 }
654 //-----------------------------------------------------------------------------
ins_fname_cb(Fl_Widget *,void * v)655 void ins_fname_cb(Fl_Widget *, void *v)
656 {	// TODO: use previous file name?!?
657 	ScriptWindow* e = (ScriptWindow*)v;
658 	const char *s = mgl_file_chooser(_("Select file name"), "DAT files \t*.{dat,csv}\nHDF files \t*.{hdf,h5}\nImage files \t*.{png,jpg,jpeg}");
659 	if(s)
660 	{
661 		std::string ss=s;	ss = '\''+ss+'\'';
662 		if(e)	e->editor->insert(ss.c_str());
663 		else	cb_args_set(ss.c_str());
664 	}
665 }
666 //-----------------------------------------------------------------------------
ins_path_cb(Fl_Widget *,void * v)667 void ins_path_cb(Fl_Widget *, void *v)
668 {
669 	static std::string prev;
670 	ScriptWindow* e = (ScriptWindow*)v;
671 	const char *s = mgl_dir_chooser(_("Select folder name"), prev.c_str());
672 	if(s)
673 	{
674 		std::string ss=prev=s;	ss = '\''+ss+'\'';
675 		if(e)	e->editor->insert(ss.c_str());
676 		else	cb_args_set(ss.c_str());
677 	}
678 }
679 //-----------------------------------------------------------------------------
ins_fits_cb(Fl_Widget *,void * v)680 void ins_fits_cb(Fl_Widget *, void *v)
681 {
682 	ScriptWindow* e = (ScriptWindow*)v;
683 	HMGL gr = e->graph->get_graph();
684 	std::string ss=mgl_get_fit(gr);
685 	if(ss.empty())	fl_alert(_("There is no fitted formula."));
686 	else	{	ss = '\''+ss+'\'';	e->editor->insert(ss.c_str());	}
687 }
688 //-----------------------------------------------------------------------------
ins_prim_cb(Fl_Widget *,void * v)689 void ins_prim_cb(Fl_Widget *, void *v)
690 {
691 	ScriptWindow* e = (ScriptWindow*)v;
692 	std::string ss = "subplot 1 1 0 '#'\n"+e->graph->FMGL->prim+"subplot 1 1 0\n###### end of primitives\n";
693 	e->editor->insert(ss.c_str());
694 	e->graph->FMGL->prim.clear();
695 }
696 //-----------------------------------------------------------------------------
697