af0ec2f2cc81500d25cb172f4caa4bc1c2e01baa
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / tracer / tracerwindow.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2014 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include "automation.h"
22 #include "bcdisplayinfo.h"
23 #include "clip.h"
24 #include "tracer.h"
25 #include "tracerwindow.h"
26 #include "cstrdup.h"
27 #include "cwindow.h"
28 #include "cwindowgui.h"
29 #include "edl.h"
30 #include "edlsession.h"
31 #include "language.h"
32 #include "mainerror.h"
33 #include "mwindow.h"
34 #include "plugin.h"
35 #include "pluginserver.h"
36 #include "theme.h"
37 #include "track.h"
38
39 #define COLOR_W 50
40 #define COLOR_H 30
41
42 TracerNum::TracerNum(TracerWindow *gui, int x, int y, float output)
43  : BC_TumbleTextBox(gui, output, -32767.0f, 32767.0f, x, y, 120)
44 {
45         this->gui = gui;
46         set_increment(1);
47         set_precision(1);
48 }
49
50 TracerNum::~TracerNum()
51 {
52 }
53
54 int TracerPointX::handle_event()
55 {
56         if( !TracerNum::handle_event() ) return 0;
57         TracerPointList *point_list = gui->point_list;
58         int hot_point = point_list->get_selection_number(0, 0);
59         TracerPoints &points = gui->plugin->config.points;
60         int sz = points.size();
61         if( hot_point >= 0 && hot_point < sz ) {
62                 float v = atof(get_text());
63                 points[hot_point]->x = v;
64                 point_list->set_point(hot_point, PT_X, v);
65         }
66         point_list->update_list(hot_point);
67         gui->send_configure_change();
68         return 1;
69 }
70 int TracerPointY::handle_event()
71 {
72         if( !TracerNum::handle_event() ) return 0;
73         TracerPointList *point_list = gui->point_list;
74         int hot_point = point_list->get_selection_number(0, 0);
75         TracerPoints &points = gui->plugin->config.points;
76         int sz = points.size();
77         if( hot_point >= 0 && hot_point < sz ) {
78                 float v = atof(get_text());
79                 points[hot_point]->y = v;
80                 point_list->set_point(hot_point, PT_Y, v);
81         }
82         point_list->update_list(hot_point);
83         gui->send_configure_change();
84         return 1;
85 }
86
87 TracerWindow::TracerWindow(Tracer *plugin)
88  : PluginClientWindow(plugin, 400, 300, 400, 300, 0)
89 {
90         this->plugin = plugin;
91         this->title_x = 0;    this->point_x = 0;
92         this->title_y = 0;    this->point_y = 0;
93         this->new_point = 0;  this->del_point = 0;
94         this->point_up = 0;   this->point_dn = 0;
95         this->drag = 0;       this->draw = 0;
96         this->dragging = 0;
97         this->title_r = 0;    this->title_s = 0;
98         this->radius = 0;     this->scale = 0;
99         this->last_x = 0;     this->last_y = 0;
100         this->point_list = 0; this->pending_config = 0;
101 }
102
103 TracerWindow::~TracerWindow()
104 {
105         delete point_x;
106         delete point_y;
107 }
108
109 void TracerWindow::create_objects()
110 {
111         int x = 10, y = 10;
112         int margin = plugin->get_theme()->widget_border;
113         TracerPoint *pt = plugin->config.points[plugin->config.selected];
114         add_subwindow(title_x = new BC_Title(x, y, _("X:")));
115         int x1 = x + title_x->get_w() + margin;
116         point_x = new TracerPointX(this, x1, y, pt->x);
117         point_x->create_objects();
118         x1 += point_x->get_w() + margin;
119         add_subwindow(new_point = new TracerNewPoint(this, plugin, x1, y));
120         x1 += new_point->get_w() + margin;
121         add_subwindow(point_up = new TracerPointUp(this, x1, y));
122         y += point_x->get_h() + margin;
123         add_subwindow(title_y = new BC_Title(x, y, _("Y:")));
124         x1 = x + title_y->get_w() + margin;
125         point_y = new TracerPointY(this, x1, y, pt->y);
126         point_y->create_objects();
127         x1 += point_y->get_w() + margin;
128         add_subwindow(del_point = new TracerDelPoint(this, plugin, x1, y));
129         x1 += del_point->get_w() + margin;
130         add_subwindow(point_dn = new TracerPointDn(this, x1, y));
131         y += point_y->get_h() + margin + 10;
132
133         add_subwindow(drag = new TracerDrag(this, x, y));
134         if( plugin->config.drag ) {
135                 if( !grab(plugin->server->mwindow->cwindow->gui) )
136                         eprintf("drag enabled, but compositor already grabbed\n");
137         }
138         x1 = x + drag->get_w() + margin + 20;
139         add_subwindow(draw = new TracerDraw(this, x1, y));
140         x1 += draw->get_w() + margin + 20;
141         add_subwindow(fill = new TracerFill(this, x1, y));
142         x1 += drag->get_w() + margin + 20;
143         add_subwindow(reset = new TracerReset(this, plugin, x1, y+3));
144         y += drag->get_h() + margin;
145
146         add_subwindow(title_r = new BC_Title(x1=x, y, _("Radius:")));
147         x1 += title_r->get_w() + margin;
148         add_subwindow(radius = new TracerRadius(this, x1, y, 100));
149         x1 += radius->get_w() + margin + 20;
150         add_subwindow(title_s = new BC_Title(x1, y, _("Scale:")));
151         x1 += title_s->get_w() + margin;
152         add_subwindow(scale = new TracerScale(this, x1, y, 100));
153         y += radius->get_h() + margin + 20;
154
155         add_subwindow(point_list = new TracerPointList(this, plugin, x, y));
156         point_list->update(plugin->config.selected);
157 //      y += point_list->get_h() + 10;
158
159         show_window(1);
160 }
161
162 void TracerWindow::send_configure_change()
163 {
164         pending_config = 0;
165         plugin->send_configure_change();
166 }
167
168 int TracerWindow::grab_event(XEvent *event)
169 {
170         int ret = do_grab_event(event);
171         if( pending_config && !grab_event_count() )
172                 send_configure_change();
173         return ret;
174 }
175
176 int TracerWindow::do_grab_event(XEvent *event)
177 {
178         switch( event->type ) {
179         case ButtonPress: break;
180         case ButtonRelease: break;
181         case MotionNotify: break;
182         default:
183                 return 0;
184         }
185
186         MWindow *mwindow = plugin->server->mwindow;
187         CWindowGUI *cwindow_gui = mwindow->cwindow->gui;
188         CWindowCanvas *canvas = cwindow_gui->canvas;
189         int cx, cy;  cwindow_gui->get_relative_cursor(cx, cy);
190         cx -= canvas->view_x;
191         cy -= canvas->view_y;
192
193         if( !dragging ) {
194                 if( cx < 0 || cx >= canvas->view_w ||
195                     cy < 0 || cy >= canvas->view_h )
196                         return 0;
197         }
198
199         switch( event->type ) {
200         case ButtonPress:
201                 if( dragging ) return 0;
202                 dragging = event->xbutton.state & ShiftMask ? -1 : 1;
203                 break;
204         case ButtonRelease:
205                 if( !dragging ) return 0;
206                 dragging = 0;
207                 return 1;
208         case MotionNotify:
209                 if( !dragging ) return 0;
210                 break;
211         default:
212                 return 0;
213         }
214
215         float cursor_x = cx, cursor_y = cy;
216         canvas->canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
217         int64_t position = plugin->get_source_position();
218         float projector_x, projector_y, projector_z;
219         Track *track = plugin->server->plugin->track;
220         int track_w = track->track_w, track_h = track->track_h;
221         track->automation->get_projector(
222                 &projector_x, &projector_y, &projector_z,
223                 position, PLAY_FORWARD);
224         projector_x += mwindow->edl->session->output_w / 2;
225         projector_y += mwindow->edl->session->output_h / 2;
226         float output_x = (cursor_x - projector_x) / projector_z + track_w / 2;
227         float output_y = (cursor_y - projector_y) / projector_z + track_h / 2;
228         point_x->update((int64_t)(output_x));
229         point_y->update((int64_t)(output_y));
230         TracerPoints &points = plugin->config.points;
231
232         if( dragging > 0 ) {
233                 switch( event->type ) {
234                 case ButtonPress: {
235                         int button_no = event->xbutton.button;
236                         int hot_point = -1;
237                         if( button_no == RIGHT_BUTTON ) {
238                                 hot_point = plugin->new_point();
239                                 TracerPoint *pt = points[hot_point];
240                                 pt->x = output_x;  pt->y = output_y;
241                                 point_list->update(hot_point);
242                                 break;
243                         }
244                         int sz = points.size();
245                         if( hot_point < 0 && sz > 0 ) {
246                                 TracerPoint *pt = points[hot_point=0];
247                                 double dist = DISTANCE(output_x,output_y, pt->x,pt->y);
248                                 for( int i=1; i<sz; ++i ) {
249                                         pt = points[i];
250                                         double d = DISTANCE(output_x,output_y, pt->x,pt->y);
251                                         if( d >= dist ) continue;
252                                         dist = d;  hot_point = i;
253                                 }
254                                 pt = points[hot_point];
255                                 float px = (pt->x - track_w / 2) * projector_z + projector_x;
256                                 float py = (pt->y - track_h / 2) * projector_z + projector_y;
257                                 dist = DISTANCE(px, py, cursor_x,cursor_y);
258                                 if( dist >= HANDLE_W ) hot_point = -1;
259                         }
260                         if( hot_point >= 0 && sz > 0 ) {
261                                 TracerPoint *pt = points[hot_point];
262                                 point_list->set_point(hot_point, PT_X, pt->x = output_x);
263                                 point_list->set_point(hot_point, PT_Y, pt->y = output_y);
264                                 point_list->update_list(hot_point);
265                         }
266                         break; }
267                 case MotionNotify: {
268                         int hot_point = point_list->get_selection_number(0, 0);
269                         if( hot_point >= 0 && hot_point < points.size() ) {
270                                 TracerPoint *pt = points[hot_point];
271                                 if( pt->x == output_x && pt->y == output_y ) break;
272                                 point_list->set_point(hot_point, PT_X, pt->x = output_x);
273                                 point_list->set_point(hot_point, PT_Y, pt->y = output_y);
274                                 point_x->update(pt->x);
275                                 point_y->update(pt->y);
276                                 point_list->update_list(hot_point);
277                         }
278                         break; }
279                 }
280         }
281         else {
282                 switch( event->type ) {
283                 case MotionNotify: {
284                         float dx = output_x - last_x, dy = output_y - last_y;
285                         int sz = points.size();
286                         for( int i=0; i<sz; ++i ) {
287                                 TracerPoint *pt = points[i];
288                                 point_list->set_point(i, PT_X, pt->x += dx);
289                                 point_list->set_point(i, PT_Y, pt->y += dy);
290                         }
291                         int hot_point = point_list->get_selection_number(0, 0);
292                         if( hot_point >= 0 && hot_point < sz ) {
293                                 TracerPoint *pt = points[hot_point];
294                                 point_x->update(pt->x);
295                                 point_y->update(pt->y);
296                                 point_list->update_list(hot_point);
297                         }
298                         break; }
299                 }
300         }
301
302         last_x = output_x;  last_y = output_y;
303         pending_config = 1;
304         return 1;
305 }
306
307 void TracerWindow::done_event(int result)
308 {
309         ungrab(client->server->mwindow->cwindow->gui);
310 }
311
312 TracerPointList::TracerPointList(TracerWindow *gui, Tracer *plugin, int x, int y)
313  : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT)
314 {
315         this->gui = gui;
316         this->plugin = plugin;
317         titles[PT_X] = _("X");    widths[PT_X] = 90;
318         titles[PT_Y] = _("Y");    widths[PT_Y] = 90;
319 }
320 TracerPointList::~TracerPointList()
321 {
322         clear();
323 }
324 void TracerPointList::clear()
325 {
326         for( int i=PT_SZ; --i>=0; )
327                 cols[i].remove_all_objects();
328 }
329
330 int TracerPointList::column_resize_event()
331 {
332         for( int i=PT_SZ; --i>=0; )
333                 widths[i] = get_column_width(i);
334         return 1;
335 }
336
337 int TracerPointList::handle_event()
338 {
339         int hot_point = get_selection_number(0, 0);
340         const char *x_text = "", *y_text = "";
341         TracerPoints &points = plugin->config.points;
342
343         int sz = points.size();
344         if( hot_point >= 0 && sz > 0 ) {
345                 x_text = gui->point_list->cols[PT_X].get(hot_point)->get_text();
346                 y_text = gui->point_list->cols[PT_Y].get(hot_point)->get_text();
347         }
348         gui->point_x->update(x_text);
349         gui->point_y->update(y_text);
350         update(hot_point);
351         gui->send_configure_change();
352         return 1;
353 }
354
355 int TracerPointList::selection_changed()
356 {
357         handle_event();
358         return 1;
359 }
360
361 void TracerPointList::new_point(const char *xp, const char *yp)
362 {
363         cols[PT_X].append(new BC_ListBoxItem(xp));
364         cols[PT_Y].append(new BC_ListBoxItem(yp));
365 }
366
367 void TracerPointList::del_point(int i)
368 {
369         for( int sz1=cols[0].size()-1, c=PT_SZ; --c>=0; )
370                 cols[c].remove_object_number(sz1-i);
371 }
372
373 void TracerPointList::set_point(int i, int c, float v)
374 {
375         char s[BCSTRLEN]; sprintf(s,"%0.4f",v);
376         set_point(i,c,s);
377 }
378 void TracerPointList::set_point(int i, int c, const char *cp)
379 {
380         cols[c].get(i)->set_text(cp);
381 }
382
383 int TracerPointList::set_selected(int k)
384 {
385         TracerPoints &points = plugin->config.points;
386         int sz = points.size();
387         if( !sz ) return -1;
388         bclamp(k, 0, sz-1);
389         update_selection(&cols[0], k);
390         return k;
391 }
392 void TracerPointList::update_list(int k)
393 {
394         int xpos = get_xposition(), ypos = get_yposition();
395         if( k < 0 ) k = get_selection_number(0, 0);
396         update_selection(&cols[0], k);
397         BC_ListBox::update(&cols[0], &titles[0],&widths[0],PT_SZ, xpos,ypos,k);
398         center_selection();
399 }
400 void TracerPointList::update(int k)
401 {
402         clear();
403         TracerPoints &points = plugin->config.points;
404         int sz = points.size();
405         for( int i=0; i<sz; ++i ) {
406                 TracerPoint *pt = points[i];
407                 char xtxt[BCSTRLEN];  sprintf(xtxt,"%0.4f", pt->x);
408                 char ytxt[BCSTRLEN];  sprintf(ytxt,"%0.4f", pt->y);
409                 new_point(xtxt, ytxt);
410         }
411         if( k >= 0 && k < sz ) {
412                 gui->point_x->update(gui->point_list->cols[PT_X].get(k)->get_text());
413                 gui->point_y->update(gui->point_list->cols[PT_Y].get(k)->get_text());
414                 plugin->config.selected = k;
415         }
416
417         update_list(k);
418 }
419
420 void TracerWindow::update_gui()
421 {
422         TracerConfig &config = plugin->config;
423         drag->update(config.drag);
424         draw->update(config.draw);
425         fill->update(config.fill);
426         radius->update(config.radius);
427         scale->update(config.scale);
428         drag->update(config.drag);
429         point_list->update(-1);
430 }
431
432
433 TracerPointUp::TracerPointUp(TracerWindow *gui, int x, int y)
434  : BC_GenericButton(x, y, _("Up"))
435 {
436         this->gui = gui;
437 }
438 TracerPointUp::~TracerPointUp()
439 {
440 }
441
442 int TracerPointUp::handle_event()
443 {
444         TracerPoints &points = gui->plugin->config.points;
445         int sz = points.size();
446         int hot_point = gui->point_list->get_selection_number(0, 0);
447
448         if( sz > 1 && hot_point > 0 ) {
449                 TracerPoint *&pt0 = points[hot_point];
450                 TracerPoint *&pt1 = points[--hot_point];
451                 TracerPoint *t = pt0;  pt0 = pt1;  pt1 = t;
452                 gui->point_list->update(hot_point);
453         }
454         gui->send_configure_change();
455         return 1;
456 }
457
458 TracerPointDn::TracerPointDn(TracerWindow *gui, int x, int y)
459  : BC_GenericButton(x, y, _("Dn"))
460 {
461         this->gui = gui;
462 }
463 TracerPointDn::~TracerPointDn()
464 {
465 }
466
467 int TracerPointDn::handle_event()
468 {
469         TracerPoints &points = gui->plugin->config.points;
470         int sz = points.size();
471         int hot_point = gui->point_list->get_selection_number(0, 0);
472         if( sz > 1 && hot_point < sz-1 ) {
473                 TracerPoint *&pt0 = points[hot_point];
474                 TracerPoint *&pt1 = points[++hot_point];
475                 TracerPoint *t = pt0;  pt0 = pt1;  pt1 = t;
476                 gui->point_list->update(hot_point);
477         }
478         gui->send_configure_change();
479         return 1;
480 }
481
482 TracerDrag::TracerDrag(TracerWindow *gui, int x, int y)
483  : BC_CheckBox(x, y, gui->plugin->config.drag, _("Drag"))
484 {
485         this->gui = gui;
486 }
487 int TracerDrag::handle_event()
488 {
489         CWindowGUI *cwindow_gui = gui->plugin->server->mwindow->cwindow->gui;
490         int value = get_value();
491         if( value ) {
492                 if( !gui->grab(cwindow_gui) ) {
493                         update(value = 0);
494                         flicker(10,50);
495                 }
496         }
497         else
498                 gui->ungrab(cwindow_gui);
499         gui->plugin->config.drag = value;
500         gui->send_configure_change();
501         return 1;
502 }
503
504 TracerDraw::TracerDraw(TracerWindow *gui, int x, int y)
505  : BC_CheckBox(x, y, gui->plugin->config.draw, _("Draw"))
506 {
507         this->gui = gui;
508 }
509 int TracerDraw::handle_event()
510 {
511         gui->plugin->config.draw = get_value();
512         gui->send_configure_change();
513         return 1;
514 }
515
516 TracerFill::TracerFill(TracerWindow *gui, int x, int y)
517  : BC_CheckBox(x, y, gui->plugin->config.fill, _("Fill"))
518 {
519         this->gui = gui;
520 }
521 int TracerFill::handle_event()
522 {
523         gui->plugin->config.fill = get_value();
524         gui->send_configure_change();
525         return 1;
526 }
527
528 TracerRadius::TracerRadius(TracerWindow *gui, int x, int y, int w)
529  : BC_ISlider(x,y,0,w,w, -50,50, gui->plugin->config.radius)
530 {
531         this->gui = gui;
532 }
533 int TracerRadius::handle_event()
534 {
535         gui->plugin->config.radius = get_value();
536         gui->send_configure_change();
537         return 1;
538 }
539
540 TracerScale::TracerScale(TracerWindow *gui, int x, int y, int w)
541  : BC_FSlider(x,y, 0,w,w, 1.f,10.f, gui->plugin->config.scale)
542 {
543         this->gui = gui;
544 }
545 int TracerScale::handle_event()
546 {
547         gui->plugin->config.scale = get_value();
548         gui->send_configure_change();
549         return 1;
550 }
551
552 TracerNewPoint::TracerNewPoint(TracerWindow *gui, Tracer *plugin, int x, int y)
553  : BC_GenericButton(x, y, 80, _("New"))
554 {
555         this->gui = gui;
556         this->plugin = plugin;
557 }
558 TracerNewPoint::~TracerNewPoint()
559 {
560 }
561 int TracerNewPoint::handle_event()
562 {
563         int k = plugin->new_point();
564         gui->point_list->update(k);
565         gui->send_configure_change();
566         return 1;
567 }
568
569 TracerDelPoint::TracerDelPoint(TracerWindow *gui, Tracer *plugin, int x, int y)
570  : BC_GenericButton(x, y, 80, C_("Del"))
571 {
572         this->gui = gui;
573         this->plugin = plugin;
574 }
575 TracerDelPoint::~TracerDelPoint()
576 {
577 }
578 int TracerDelPoint::handle_event()
579 {
580         int hot_point = gui->point_list->get_selection_number(0, 0);
581         TracerPoints &points = plugin->config.points;
582         if( hot_point >= 0 && hot_point < points.size() ) {
583                 plugin->config.del_point(hot_point);
584                 if( !points.size() ) plugin->new_point();
585                 int sz = points.size();
586                 if( hot_point >= sz && hot_point > 0 ) --hot_point;
587                 gui->point_list->update(hot_point);
588                 gui->send_configure_change();
589         }
590         return 1;
591 }
592
593 TracerReset::TracerReset(TracerWindow *gui, Tracer *plugin, int x, int y)
594  : BC_GenericButton(x, y, _("Reset"))
595 {
596         this->gui = gui;
597         this->plugin = plugin;
598 }
599 TracerReset::~TracerReset()
600 {
601 }
602 int TracerReset::handle_event()
603 {
604         TracerConfig &config = plugin->config;
605         config.drag = 0;
606         config.draw = 0;
607         config.fill = 0;
608         config.radius = 0;
609         config.scale = 1;
610         config.selected = 0;
611         TracerPoints &points = plugin->config.points;
612         points.remove_all_objects();
613         plugin->new_point();
614         gui->point_list->update(0);
615         gui->update_gui();
616         gui->send_configure_change();
617         return 1;
618 }
619