1 /* mathgl.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 "mgl2/mgl.h"
18 #include "mgllab.h"
19 #include "../widgets/image.h"
20 //-----------------------------------------------------------------------------
21 mglParse *Parse=0;
22 //-----------------------------------------------------------------------------
udav_delay(void * v)23 mreal MGL_FUNC_PURE udav_delay(void *v)
24 { return ((Fl_MGL*)v)->delay; }
udav_reload(void * v)25 void udav_reload(void *v)
26 { Parse->RestoreOnce(); ((Fl_MGL*)v)->update(); }
27 //-----------------------------------------------------------------------------
udav_next(void * v)28 void udav_next(void *v) { ((Fl_MGL*)v)->next_frame(); }
next_frame()29 void Fl_MGL::next_frame()
30 {
31 size_t n=anim.size();
32 if(n==0 && animate_cb(this)) { gr->toggle_sshow(); return; }
33 n=anim.size();
34 cur = (cur+1)%n;
35 Parse->AddParam(0,anim[cur].c_str());
36 update();
37 }
38 //-----------------------------------------------------------------------------
udav_prev(void * v)39 void udav_prev(void *v) { ((Fl_MGL*)v)->prev_frame(); }
prev_frame()40 void Fl_MGL::prev_frame()
41 {
42 size_t n=anim.size();
43 if(n==0 && animate_cb(this)) { gr->toggle_sshow(); return; }
44 n=anim.size();
45 cur = (cur+n-1)%n;
46 Parse->AddParam(0,anim[cur].c_str());
47 update();
48 }
49 //-----------------------------------------------------------------------------
Fl_MGL(Fl_MGLView * GR)50 Fl_MGL::Fl_MGL(Fl_MGLView *GR)
51 {
52 if(!Parse) Parse = new mglParse;
53 Parse->AllowSetSize(true);
54 gr = GR; gr->par = this; e = 0;
55 gr->next = udav_next; gr->delay = udav_delay;
56 gr->prev = udav_prev; gr->reload = udav_reload;
57 gr->FMGL->set_draw(this);
58 gr->FMGL->set_show_warn(false);
59 a1=a2=0; da=1; cur=0; delay=0.5;
60 }
61 //-----------------------------------------------------------------------------
~Fl_MGL()62 Fl_MGL::~Fl_MGL() {}
63 //-----------------------------------------------------------------------------
Param(char id,const char * val)64 void Fl_MGL::Param(char id, const char *val)
65 { Parse->AddParam(id<='9' ? id-'0' : id-'a'+10, val); }
66 //-----------------------------------------------------------------------------
Reload()67 void Fl_MGL::Reload()
68 {
69 Parse->RestoreOnce();
70 e->graph->update();
71 }
72 //-----------------------------------------------------------------------------
Click()73 void Fl_MGL::Click()
74 {
75 int id = e->graph->FMGL->get_last_id();
76 if(id>0)
77 {
78 Fl_Text_Editor::kf_ctrl_move(FL_Home, e->editor);
79 for(int i=0;i<id;i++) Fl_Text_Editor::kf_down(0, e->editor);
80 Fl_Text_Editor::kf_up(0, e->editor);
81 Fl::focus(e->editor);
82 }
83 e->graph->update();
84 }
85 //-----------------------------------------------------------------------------
Draw(mglGraph * gr)86 int Fl_MGL::Draw(mglGraph *gr)
87 {
88 if(exec_save) save_cb(0,e);
89 Parse->Execute(gr,script.c_str());
90 if(textbuf)
91 {
92 char *text = textbuf->text();
93 if(highlight) gr->Highlight(e->graph->FMGL->get_last_id());
94 Parse->Execute(gr,text);
95 free(text);
96 }
97 // TODO go to line with warning?!!
98 message_set(gr->Message(), e);
99 if(e && e->rtab) e->rtab->value(e->gplot);
100 return 0;
101 }
102 //-----------------------------------------------------------------------------
update()103 void Fl_MGL::update()
104 {
105 // NOTE: hint for old style View(). May be I should remove it!
106 char *text = textbuf->text();
107 if(script.empty() || !text || !strstr(text,"rotate"))
108 mgl_rotate(gr->get_graph(),0,0,0);
109
110 gr->update();
111 for(long i=0;i<Parse->GetNumVar();i++)
112 {
113 mglDataA *v = Parse->GetVar(i);
114 if(v && v->o) ((TableWindow *)v->o)->update(v);
115 }
116 }
117 //-----------------------------------------------------------------------------
118 // void add_suffix(char *fname, const char *ext)
119 // {
120 // long n=strlen(fname);
121 // if(n>4 && fname[n-4]=='.')
122 // { fname[n-3]=ext[0]; fname[n-2]=ext[1]; fname[n-1]=ext[2]; }
123 // else { strcat(fname,"."); strcat(fname,ext); }
124 // }
125 //-----------------------------------------------------------------------------
126 class ArgsDlg : public GeneralDlg
127 {
128 Fl_Input *arg[10];
129 public:
cb_ok()130 void cb_ok()
131 {
132 for(int i=0;i<10;i++) Parse->AddParam(i,arg[i]->value());
133 hide();
134 }
set(int id,const char * val)135 void set(int id, const char *val)
136 {
137 if(id>=0 && id<10)
138 { arg[id]->value(val); Parse->AddParam(id,val); }
139 }
init()140 void init()
141 { // NOTE I'm not sure that I need to get current arguments. So keep function empty.
142 }
ArgsDlg()143 ArgsDlg() : GeneralDlg()
144 {
145 w = new Fl_Double_Window(290, 320, _("Set script arguments"));
146 arg[1] = new Fl_Input(5, 20, 135, 30, _("String for $1"));
147 arg[1]->align(FL_ALIGN_TOP_LEFT);
148 arg[2] = new Fl_Input(150, 20, 135, 30, _("String for $2"));
149 arg[2]->align(FL_ALIGN_TOP_LEFT);
150 arg[3] = new Fl_Input(5, 75, 135, 30, _("String for $3"));
151 arg[3]->align(FL_ALIGN_TOP_LEFT);
152 arg[4] = new Fl_Input(150, 75, 135, 30, _("String for $4"));
153 arg[4]->align(FL_ALIGN_TOP_LEFT);
154 arg[5] = new Fl_Input(5, 130, 135, 30, _("String for $5"));
155 arg[5]->align(FL_ALIGN_TOP_LEFT);
156 arg[6] = new Fl_Input(150, 130, 135, 30, _("String for $6"));
157 arg[6]->align(FL_ALIGN_TOP_LEFT);
158 arg[7] = new Fl_Input(5, 185, 135, 30, _("String for $7"));
159 arg[7]->align(FL_ALIGN_TOP_LEFT);
160 arg[8] = new Fl_Input(150, 185, 135, 30, _("String for $8"));
161 arg[8]->align(FL_ALIGN_TOP_LEFT);
162 arg[9] = new Fl_Input(5, 240, 135, 30, _("String for $9"));
163 arg[9]->align(FL_ALIGN_TOP_LEFT);
164 arg[0] = new Fl_Input(150, 240, 135, 30, _("String for $0"));
165 arg[0]->align(FL_ALIGN_TOP_LEFT);
166 Fl_Button* o = new Fl_Button(60, 290, 75, 25, _("Cancel"));
167 o->callback(cb_dlg_cancel, this);
168 o = new Fl_Return_Button(155, 290, 75, 25, _("Set"));
169 o->callback(cb_dlg_ok, this);
170 w->set_modal(); w->end();
171 }
172 } args_dlg;
173 //-----------------------------------------------------------------------------
args_dlg_cb(Fl_Widget *,void *)174 void args_dlg_cb(Fl_Widget *, void *) { args_dlg.show(); }
argument_set(int id,const char * val)175 void argument_set(int id, const char *val) { args_dlg.set(id,val); }
176 //-----------------------------------------------------------------------------
177 void cb_anim_put(Fl_Widget *, void *);
cb_dlg_only(Fl_Widget *,void * v)178 void cb_dlg_only(Fl_Widget*,void *v) { ((Fl_Round_Button*)v)->setonly(); }
179 class AnimateDlg : public GeneralDlg
180 {
181 public:
182 Fl_MGL* dr;
AnimateDlg()183 AnimateDlg() : GeneralDlg()
184 {
185 w = new Fl_Double_Window(335, 350, _("Animation"));
186 new Fl_Box(10, 5, 315, 25, _("Redraw picture for $0 equal to"));
187 rt = new Fl_Round_Button(10, 30, 200, 25, _("strings"));
188 rt->callback(cb_dlg_only, rt);
189 rv = new Fl_Round_Button(220, 30, 105, 25, _("values"));
190 rv->callback(cb_dlg_only, rv);
191 txt = new Fl_Multiline_Input(10, 60, 200, 250);
192 x0 = new Fl_Float_Input(220, 80, 105, 25, _("from")); x0->align(FL_ALIGN_TOP_LEFT);
193 x1 = new Fl_Float_Input(220, 130, 105, 25, _("to")); x1->align(FL_ALIGN_TOP_LEFT);
194 dx = new Fl_Float_Input(220, 180, 105, 25, _("with step")); dx->align(FL_ALIGN_TOP_LEFT);
195
196 Fl_Button *o;
197 o = new Fl_Button(230, 215, 80, 25, _("Cancel")); o->callback(cb_dlg_cancel, this);
198 o = new Fl_Return_Button(230, 250, 80, 25, _("OK"));o->callback(cb_dlg_ok, this);
199 save = new Fl_Check_Button(220, 285, 105, 25, _("save slides"));
200 save->tooltip(_("Keep slides in memory (faster animation but require more memory)"));
201 save->down_box(FL_DOWN_BOX); save->deactivate();
202
203 o = new Fl_Button(10, 315, 100, 25, _("Put to script")); o->callback(cb_anim_put,w);
204 dt = new Fl_Float_Input(220, 315, 105, 25, _("Delay (in sec)"));// dx->align(FL_ALIGN_TOP_LEFT);
205 w->end();
206 }
init()207 void init()
208 {
209 if(e) dr = e->draw;
210 if(dr && dr->da*(dr->a2-dr->a1)>0)
211 {
212 char buf[128]; rv->setonly();
213 snprintf(buf,127,"%g",dr->a1); x0->value(buf);
214 snprintf(buf,127,"%g",dr->a2); x1->value(buf);
215 snprintf(buf,127,"%g",dr->da); dx->value(buf);
216 }
217 else if(dr)
218 {
219 rt->setonly();
220 std::string str;
221 for(size_t i=0;i<dr->anim.size();i++)
222 str += dr->anim[i]+'\n';
223 txt->value(str.c_str());
224 }
225 }
prepare()226 void prepare()
227 {
228 result.clear();
229 if(dr) dr->anim.clear();
230 if(rv->value())
231 {
232 const char *s1=x0->value(), *s2=x1->value(), *s3=dx->value();
233 double a1=s1?atof(s1):NAN, a2=s2?atof(s2):NAN, a3=s3?atof(s3):1;
234 if(a3*(a2-a1)>0)
235 {
236 result = result + "##c "+(s1?s1:"nan")+' '+(s2?s2:"nan")+' '+(s3?s3:"1")+'\n';
237 if(dr)
238 {
239 dr->a1=a1; dr->a2=a2; dr->da=a3;
240 for(double a=a1;a3*(a2-a)>=0;a+=a3)
241 {
242 char buf[128]; snprintf(buf,128,"%g",a);
243 dr->anim.push_back(buf);
244 }
245 }
246 }
247 else fl_alert(_("Incompatible loop parameters!"));
248 }
249 else if(rt->value())
250 {
251 const char *s = txt->value();
252 while(s && *s)
253 {
254 const char *j = strchr(s,'\n');
255 size_t len = j?(j-s):strlen(s);
256 std::string val(s,len);
257 if(dr) dr->anim.push_back(val);
258 result = result+"##a "+val+'\n';
259 s=j?j+1:NULL;
260 }
261 }
262 else fl_message(_("No selection. So nothing to do"));
263
264 }
into_script()265 void into_script()
266 {
267 prepare();
268 if(e)
269 {
270 int p = textbuf->line_start(e->editor->insert_position());
271 textbuf->insert(p, (result+'\n').c_str());
272 }
273 }
cb_ok()274 void cb_ok()
275 {
276 if(!dr) return;
277 prepare();
278 const char *s = dt->value();
279 if(s && *s) dr->delay = atof(s);
280 hide();
281 }
282 protected:
283 bool swap;
284 Fl_Round_Button *rt, *rv;
285 Fl_Multiline_Input *txt;
286 Fl_Float_Input *x0, *x1, *dx, *dt;
287 Fl_Check_Button *save;
288 void create_dlg();
289 } animate_dlg;
290 //-----------------------------------------------------------------------------
animate_dlg_cb(Fl_Widget *,void * v)291 void animate_dlg_cb(Fl_Widget *, void *v)
292 { animate_dlg.e = (ScriptWindow*)v; animate_dlg.show(); }
cb_anim_put(Fl_Widget *,void *)293 void cb_anim_put(Fl_Widget *, void *) { animate_dlg.into_script(); }
294 //-----------------------------------------------------------------------------
animate_cb(Fl_MGL * d)295 bool animate_cb(Fl_MGL *d)
296 { animate_dlg.dr = d; animate_dlg.show(); return animate_dlg.wait(); }
297 //-----------------------------------------------------------------------------
fill_animate(const char * text,Fl_MGL * dr)298 void fill_animate(const char *text, Fl_MGL *dr)
299 {
300 char tmp[4]="#$0";
301 for(int i=0;i<10;i++) // first read script arguments (if one)
302 {
303 tmp[2] = '0'+i;
304 const char *str=strstr(text,tmp);
305 if(str)
306 {
307 str+=3;
308 while(*str>0 && *str<=' ' && *str!='\n') str++;
309 if(*str>' ')
310 {
311 size_t j=0; while(str[j]>' ') j++;
312 std::string val(str,j);
313 argument_set(i,val.c_str());
314 }
315 }
316 }
317 dr->anim.clear();
318 std::string ids;
319 std::vector<std::string> par;
320 mgl_parse_comments(text, dr->a1, dr->a2, dr->da, dr->anim, ids, par);
321 if(!ids.empty()) dr->gr->dialog(ids,par);
322 }
323 //-----------------------------------------------------------------------------
324 Fl_Text_Display::Style_Table_Entry stylemess[2] = { // Style table
325 { FL_BLACK, FL_COURIER, 12, 0 }, // A - Plain
326 { FL_RED, FL_COURIER, 12, 0 } }; // B - Strings
mess_parse(const char * text,char * style,int)327 void mess_parse(const char *text, char *style, int /*length*/)
328 {
329 size_t n=strlen(text);
330 // Style letters: A - Plain; B - Error
331 for(size_t i=0;i<n;i++) style[i] = 'A';
332 const char *l1=text, *l2=strchr(l1, '\n');
333 while(l1)
334 {
335 size_t len = l2?l2-l1:strlen(l1), st=l1-text;
336 const char *p = strstr(l1,"in line");
337 if(p && size_t(p-l1)<len)
338 for(size_t i=0;i<len;i++) style[i+st]='B';
339 l1=l2?l2+1:NULL; l2=l1?strchr(l1, '\n'):NULL;
340 }
341 }
342 //-----------------------------------------------------------------------------
343 static Fl_Text_Buffer *sbuf=0;
mess_update(int pos,int nInserted,int nDeleted,int,const char *,void * cbArg)344 void mess_update(int pos, int nInserted, int nDeleted, int, const char *, void *cbArg)
345 {
346 Fl_Text_Buffer *mbuf=((Fl_Text_Editor *)cbArg)->buffer();
347 long start, end; // Start and end of text
348 char last, *style, *text; // Text data
349 if (nInserted == 0 && nDeleted == 0) { sbuf->unselect(); return; }
350 if (nInserted > 0)
351 {
352 style = new char[nInserted + 1];
353 memset(style, 'A', nInserted);
354 style[nInserted] = '\0';
355 sbuf->replace(pos, pos + nDeleted, style);
356 delete[] style;
357 }
358 else sbuf->remove(pos, pos + nDeleted);
359 sbuf->select(pos, pos + nInserted - nDeleted);
360 start = mbuf->line_start(pos);
361 end = mbuf->line_end(pos + nInserted);
362 text = mbuf->text_range(start, end);
363 style = sbuf->text_range(start, end);
364 if (start==end) last = 0;
365 else last = style[end-start-1];
366 mess_parse(text, style, end - start);
367 sbuf->replace(start, end, style);
368 ((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
369
370 if (start==end || last != style[end-start-1])
371 {
372 // Either the user deleted some text, or the last character on
373 // the line changed styles, so reparse the remainder of the buffer...
374 free(text); free(style);
375 end = mbuf->length();
376 text = mbuf->text_range(start, end);
377 style = sbuf->text_range(start, end);
378 mess_parse(text, style, end - start);
379 sbuf->replace(start, end, style);
380 ((Fl_Text_Editor *)cbArg)->redisplay_range(start, end);
381 }
382 free(text); free(style);
383 }
384 //-----------------------------------------------------------------------------
385 void style_unfinished_cb(int, void*);
386 void cb_mess_copy(Fl_Widget*,void *v);
387 void cb_mess_jump(Fl_Widget*,void *v);
388 class MessDlg : public GeneralDlg
389 {
390 Fl_Text_Display *mess;
391 Fl_Text_Buffer *mbuf;
392 int pos, last;
393 public:
MessDlg()394 MessDlg() : GeneralDlg()
395 {
396 Fl_Button *o;
397 w = new Fl_Double_Window(500, 195, _("MGL messages"));
398 mess = new Fl_Text_Display(30, 5, 460, 190);
399 o = new Fl_Return_Button(5,5,25,25); o->callback(cb_mess_jump,e);
400 o = new Fl_Button(5,35,25,25); o->callback(cb_mess_copy,e); o->image(img_copy);
401 w->end(); w->resizable(mess);
402 mbuf = new Fl_Text_Buffer; sbuf = new Fl_Text_Buffer;
403 mess->buffer(mbuf); pos=last=0;
404 mess->highlight_data(sbuf, stylemess, sizeof(stylemess) / sizeof(stylemess[0]), 'A', style_unfinished_cb, 0);
405 mbuf->add_modify_callback(mess_update, mess);
406 mbuf->call_modify_callbacks();
407 }
copy()408 void copy()
409 {
410 char *s = mbuf->selection_text();
411 if(s && *s==0) { free(s); s=0; }
412 if(!s) s = mbuf->text();
413 Fl::copy(s,strlen(s),1); free(s);
414 }
jump()415 void jump()
416 {
417 if(!e) return;
418 char *s = mbuf->text();
419 if(*s==0) { free(s); return; }
420 int ipos = mess->line_start(mess->insert_position());
421 if(ipos!=last) last=pos=ipos;
422
423 int id=-1;
424 const char *p = strstr(s+pos,"in line");
425 if(p) { pos = p-s+7; id = atoi(p+8); }
426 else // try from beginning
427 {
428 p = strstr(s,"in line");
429 if(p) { pos = p-s+7; id = atoi(p+8); }
430 }
431 free(s);
432
433 if(id>=0)
434 {
435 Fl_Text_Editor::kf_ctrl_move(FL_Home, e->editor);
436 for(int i=0;i<id;i++) Fl_Text_Editor::kf_down(0, e->editor);
437 Fl_Text_Editor::kf_up(0, e->editor);
438 Fl::focus(e->editor);
439 }
440 }
set(const char * s)441 void set(const char *s)
442 { mbuf->text(s); show(); }
443 } mess_wnd;
444 //-----------------------------------------------------------------------------
message_cb(Fl_Widget *,void * v)445 void message_cb(Fl_Widget*,void *v)
446 { animate_dlg.e = (ScriptWindow*)v; mess_wnd.show(); }
447 //-----------------------------------------------------------------------------
message_set(const char * s,ScriptWindow * e)448 void message_set(const char *s, ScriptWindow *e)
449 { mess_wnd.e = e; if(s && *s) mess_wnd.set(s); else mess_wnd.hide(); }
450 //-----------------------------------------------------------------------------
cb_mess_copy(Fl_Widget *,void * v)451 void cb_mess_copy(Fl_Widget*,void *v) { mess_wnd.copy(); }
452 //-----------------------------------------------------------------------------
cb_mess_jump(Fl_Widget *,void * v)453 void cb_mess_jump(Fl_Widget*,void *v) { mess_wnd.jump(); }
454 //-----------------------------------------------------------------------------
455