3 * Copyright (C) 2014 Adam Williams <broadcast at earthling dot net>
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.
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.
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
21 #include "automation.h"
22 #include "bcdisplayinfo.h"
25 #include "tracerwindow.h"
28 #include "cwindowgui.h"
30 #include "edlsession.h"
32 #include "mainerror.h"
35 #include "pluginserver.h"
42 TracerNum::TracerNum(TracerWindow *gui, int x, int y, float output)
43 : BC_TumbleTextBox(gui, output, -32767.0f, 32767.0f, x, y, 120)
50 TracerNum::~TracerNum()
54 int TracerPointX::handle_event()
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);
66 point_list->update_list(hot_point);
67 gui->send_configure_change();
70 int TracerPointY::handle_event()
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);
82 point_list->update_list(hot_point);
83 gui->send_configure_change();
87 TracerWindow::TracerWindow(Tracer *plugin)
88 : PluginClientWindow(plugin, 400, 420, 400, 420, 0)
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->button_no = 0; this->invert = 0;
97 this->title_r = 0; this->title_s = 0;
98 this->feather = 0; this->radius = 0;
99 this->last_x = 0; this->last_y = 0;
100 this->point_list = 0; this->pending_config = 0;
103 TracerWindow::~TracerWindow()
109 void TracerWindow::create_objects()
112 int margin = plugin->get_theme()->widget_border;
113 int hot_point = plugin->config.selected;
114 add_subwindow(title_x = new BC_Title(x, y, _("X:")));
115 int x1 = x + title_x->get_w() + margin;
116 TracerPoint *pt = hot_point >= 0 ? plugin->config.points[hot_point] : 0;
117 point_x = new TracerPointX(this, x1, y, !pt ? 0 : pt->x);
118 point_x->create_objects();
119 x1 += point_x->get_w() + margin;
120 add_subwindow(new_point = new TracerNewPoint(this, plugin, x1, y));
121 x1 += new_point->get_w() + margin;
122 add_subwindow(point_up = new TracerPointUp(this, x1, y));
123 y += point_x->get_h() + margin;
124 add_subwindow(title_y = new BC_Title(x, y, _("Y:")));
125 x1 = x + title_y->get_w() + margin;
126 point_y = new TracerPointY(this, x1, y, !pt ? 0 : pt->y);
127 point_y->create_objects();
128 x1 += point_y->get_w() + margin;
129 add_subwindow(del_point = new TracerDelPoint(this, plugin, x1, y));
130 x1 += del_point->get_w() + margin;
131 add_subwindow(point_dn = new TracerPointDn(this, x1, y));
132 y += point_y->get_h() + margin + 10;
134 add_subwindow(drag = new TracerDrag(this, x, y));
135 if( plugin->config.drag ) {
136 if( !grab(plugin->server->mwindow->cwindow->gui) )
137 eprintf("drag enabled, but compositor already grabbed\n");
139 x1 = x + drag->get_w() + margin + 20;
140 add_subwindow(draw = new TracerDraw(this, x1, y));
141 x1 += draw->get_w() + margin + 20;
142 add_subwindow(fill = new TracerFill(this, x1, y));
143 x1 += drag->get_w() + margin + 20;
145 add_subwindow(reset = new TracerReset(this, plugin, x1, y1));
146 y1 += reset->get_h() + margin;
147 add_subwindow(invert = new TracerInvert(this, plugin, x1, y1));
148 y += drag->get_h() + margin + 15;
151 add_subwindow(title_r = new BC_Title(x, y, _("Feather:")));
152 add_subwindow(feather = new TracerFeather(this, x1, y, 150));
153 y += feather->get_h() + margin;
154 add_subwindow(title_s = new BC_Title(x, y, _("Radius:")));
155 add_subwindow(radius = new TracerRadius(this, x1, y, 150));
156 y += radius->get_h() + margin + 5;
158 add_subwindow(point_list = new TracerPointList(this, plugin, x, y));
159 point_list->update(plugin->config.selected);
160 y += point_list->get_h() + 10;
162 add_subwindow(new BC_Title(x, y, _(
163 "Btn1: select/drag point\n"
164 "Btn2: drag all points\n"
165 "Btn3: add point on nearest line\n"
166 "Btn3: shift: append point to end\n"
167 "Wheel: rotate, centered on cursor\n"
168 "Wheel: shift: scale, centered on cursor\n")));
172 void TracerWindow::send_configure_change()
175 plugin->send_configure_change();
178 int TracerWindow::grab_event(XEvent *event)
180 int ret = do_grab_event(event);
181 if( pending_config && !grab_event_count() )
182 send_configure_change();
186 int TracerWindow::do_grab_event(XEvent *event)
188 switch( event->type ) {
189 case ButtonPress: break;
190 case ButtonRelease: break;
191 case MotionNotify: break;
196 MWindow *mwindow = plugin->server->mwindow;
197 CWindowGUI *cwindow_gui = mwindow->cwindow->gui;
198 CWindowCanvas *canvas = cwindow_gui->canvas;
199 int cx, cy; cwindow_gui->get_relative_cursor(cx, cy);
200 cx -= canvas->view_x;
201 cy -= canvas->view_y;
204 if( cx < 0 || cx >= canvas->view_w ||
205 cy < 0 || cy >= canvas->view_h )
209 switch( event->type ) {
211 if( button_no ) return 0;
212 button_no = event->xbutton.button;
215 if( !button_no ) return 0;
219 if( !button_no ) return 0;
225 float cursor_x = cx, cursor_y = cy;
226 canvas->canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
227 int64_t position = plugin->get_source_position();
228 float projector_x, projector_y, projector_z;
229 Track *track = plugin->server->plugin->track;
230 int track_w = track->track_w, track_h = track->track_h;
231 track->automation->get_projector(
232 &projector_x, &projector_y, &projector_z,
233 position, PLAY_FORWARD);
234 projector_x += mwindow->edl->session->output_w / 2;
235 projector_y += mwindow->edl->session->output_h / 2;
236 float output_x = (cursor_x - projector_x) / projector_z + track_w / 2;
237 float output_y = (cursor_y - projector_y) / projector_z + track_h / 2;
238 point_x->update((int64_t)(output_x));
239 point_y->update((int64_t)(output_y));
240 TracerPoints &points = plugin->config.points;
242 switch( event->type ) {
245 float th = M_PI/360.f; // .5 deg per wheel_btn
246 int shift_down = event->xbutton.state & ShiftMask;
247 switch( button_no ) {
250 th = -th; // fall thru
252 // shift_down scale, !shift_down rotate
253 float st = sin(th), ct = cos(th);
254 int sz = points.size();
255 for( int i=0; i<sz; ++i ) {
256 TracerPoint *pt = points[i];
257 float px = pt->x - output_x, py = pt->y - output_y;
258 float nx = shift_down ? px*s : px*ct + py*st;
259 float ny = shift_down ? py*s : py*ct - px*st;
260 point_list->set_point(i, PT_X, pt->x = nx + output_x);
261 point_list->set_point(i, PT_Y, pt->y = ny + output_y);
263 point_list->update(-1);
267 // shift_down adds to end
268 int sz = !shift_down ? points.size() : 0;
269 int k = !shift_down ? -1 : points.size()-1;
271 for( int i=0; i<sz; ++i ) {
272 // pt on line pt[i+0]..pt[i+1] nearest cx,cy
273 TracerPoint *pt0 = points[i+0];
274 TracerPoint *pt1 = i+1<sz ? points[i+1] : points[0];
275 float x0 = pt0->x, y0 = pt0->y;
276 float x1 = pt1->x, y1 = pt1->y;
277 float dx = x1-x0, dy = y1-y0;
278 float rr = dx*dx + dy*dy;
280 float u = ((x1-output_x)*dx + (y1-output_y)*dy) / rr;
281 if( u < 0 || u > 1 ) continue; // past endpts
282 float x = x0*u + x1*(1-u);
283 float y = y0*u + y1*(1-u);
284 dx = output_x-x; dy = output_y-y;
285 float dd = dx*dx + dy*dy; // d**2 closest approach
286 if( mx > dd ) { mx = dd; k = i; }
288 TracerPoint *pt = points[sz=plugin->new_point()];
290 for( int i=sz; i>hot_point; --i ) points[i] = points[i-1];
291 points[hot_point] = pt;
292 pt->x = output_x; pt->y = output_y;
293 point_list->update(hot_point);
296 int hot_point = -1, sz = points.size();
298 TracerPoint *pt = points[hot_point=0];
299 double dist = DISTANCE(output_x,output_y, pt->x,pt->y);
300 for( int i=1; i<sz; ++i ) {
302 double d = DISTANCE(output_x,output_y, pt->x,pt->y);
303 if( d >= dist ) continue;
304 dist = d; hot_point = i;
306 pt = points[hot_point];
307 float px = (pt->x - track_w / 2) * projector_z + projector_x;
308 float py = (pt->y - track_h / 2) * projector_z + projector_y;
309 dist = DISTANCE(px, py, cursor_x,cursor_y);
310 if( dist >= HANDLE_W ) hot_point = -1;
312 if( hot_point >= 0 && sz > 0 ) {
313 TracerPoint *pt = points[hot_point];
314 point_list->set_point(hot_point, PT_X, pt->x = output_x);
315 point_list->set_point(hot_point, PT_Y, pt->y = output_y);
316 point_list->update_list(hot_point);
322 switch( button_no ) {
324 int hot_point = point_list->get_selection_number(0, 0);
325 if( hot_point >= 0 && hot_point < points.size() ) {
326 TracerPoint *pt = points[hot_point];
327 if( pt->x == output_x && pt->y == output_y ) break;
328 point_list->set_point(hot_point, PT_X, pt->x = output_x);
329 point_list->set_point(hot_point, PT_Y, pt->y = output_y);
330 point_x->update(pt->x);
331 point_y->update(pt->y);
332 point_list->update_list(hot_point);
335 case MIDDLE_BUTTON: {
336 float dx = output_x - last_x, dy = output_y - last_y;
337 int sz = points.size();
338 for( int i=0; i<sz; ++i ) {
339 TracerPoint *pt = points[i];
340 point_list->set_point(i, PT_X, pt->x += dx);
341 point_list->set_point(i, PT_Y, pt->y += dy);
343 int hot_point = point_list->get_selection_number(0, 0);
344 if( hot_point >= 0 && hot_point < sz ) {
345 TracerPoint *pt = points[hot_point];
346 point_x->update(pt->x);
347 point_y->update(pt->y);
348 point_list->update_list(hot_point);
355 last_x = output_x; last_y = output_y;
360 void TracerWindow::done_event(int result)
362 ungrab(client->server->mwindow->cwindow->gui);
365 TracerPointList::TracerPointList(TracerWindow *gui, Tracer *plugin, int x, int y)
366 : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT)
369 this->plugin = plugin;
370 titles[PT_X] = _("X"); widths[PT_X] = 90;
371 titles[PT_Y] = _("Y"); widths[PT_Y] = 90;
373 TracerPointList::~TracerPointList()
377 void TracerPointList::clear()
379 for( int i=PT_SZ; --i>=0; )
380 cols[i].remove_all_objects();
383 int TracerPointList::column_resize_event()
385 for( int i=PT_SZ; --i>=0; )
386 widths[i] = get_column_width(i);
390 int TracerPointList::handle_event()
392 int hot_point = get_selection_number(0, 0);
393 const char *x_text = "", *y_text = "";
394 TracerPoints &points = plugin->config.points;
396 int sz = points.size();
397 if( hot_point >= 0 && sz > 0 ) {
398 x_text = gui->point_list->cols[PT_X].get(hot_point)->get_text();
399 y_text = gui->point_list->cols[PT_Y].get(hot_point)->get_text();
401 gui->point_x->update(x_text);
402 gui->point_y->update(y_text);
404 gui->send_configure_change();
408 int TracerPointList::selection_changed()
414 void TracerPointList::new_point(const char *xp, const char *yp)
416 cols[PT_X].append(new BC_ListBoxItem(xp));
417 cols[PT_Y].append(new BC_ListBoxItem(yp));
420 void TracerPointList::del_point(int i)
422 for( int sz1=cols[0].size()-1, c=PT_SZ; --c>=0; )
423 cols[c].remove_object_number(sz1-i);
426 void TracerPointList::set_point(int i, int c, float v)
428 char s[BCSTRLEN]; sprintf(s,"%0.4f",v);
431 void TracerPointList::set_point(int i, int c, const char *cp)
433 cols[c].get(i)->set_text(cp);
436 int TracerPointList::set_selected(int k)
438 TracerPoints &points = plugin->config.points;
439 int sz = points.size();
442 update_selection(&cols[0], k);
445 void TracerPointList::update_list(int k)
447 int sz = plugin->config.points.size();
448 if( k < 0 || k >= sz ) k = -1;
449 plugin->config.selected = k;
450 update_selection(&cols[0], k);
451 int xpos = get_xposition(), ypos = get_yposition();
452 BC_ListBox::update(&cols[0], &titles[0],&widths[0],PT_SZ, xpos,ypos,k);
455 void TracerPointList::update(int k)
458 TracerPoints &points = plugin->config.points;
459 int sz = points.size();
460 for( int i=0; i<sz; ++i ) {
461 TracerPoint *pt = points[i];
462 char xtxt[BCSTRLEN]; sprintf(xtxt,"%0.4f", pt->x);
463 char ytxt[BCSTRLEN]; sprintf(ytxt,"%0.4f", pt->y);
464 new_point(xtxt, ytxt);
466 if( k >= 0 && k < sz ) {
467 gui->point_x->update(gui->point_list->cols[PT_X].get(k)->get_text());
468 gui->point_y->update(gui->point_list->cols[PT_Y].get(k)->get_text());
473 void TracerWindow::update_gui()
475 TracerConfig &config = plugin->config;
476 drag->update(config.drag);
477 draw->update(config.draw);
478 fill->update(config.fill);
479 feather->update(config.feather);
480 radius->update(config.radius);
481 invert->update(config.invert);
482 point_list->update(-1);
486 TracerPointUp::TracerPointUp(TracerWindow *gui, int x, int y)
487 : BC_GenericButton(x, y, _("Up"))
491 TracerPointUp::~TracerPointUp()
495 int TracerPointUp::handle_event()
497 TracerPoints &points = gui->plugin->config.points;
498 int sz = points.size();
499 int hot_point = gui->point_list->get_selection_number(0, 0);
501 if( sz > 1 && hot_point > 0 ) {
502 TracerPoint *&pt0 = points[hot_point];
503 TracerPoint *&pt1 = points[--hot_point];
504 TracerPoint *t = pt0; pt0 = pt1; pt1 = t;
505 gui->point_list->update(hot_point);
507 gui->send_configure_change();
511 TracerPointDn::TracerPointDn(TracerWindow *gui, int x, int y)
512 : BC_GenericButton(x, y, _("Dn"))
516 TracerPointDn::~TracerPointDn()
520 int TracerPointDn::handle_event()
522 TracerPoints &points = gui->plugin->config.points;
523 int sz = points.size();
524 int hot_point = gui->point_list->get_selection_number(0, 0);
525 if( sz > 1 && hot_point < sz-1 ) {
526 TracerPoint *&pt0 = points[hot_point];
527 TracerPoint *&pt1 = points[++hot_point];
528 TracerPoint *t = pt0; pt0 = pt1; pt1 = t;
529 gui->point_list->update(hot_point);
531 gui->send_configure_change();
535 TracerDrag::TracerDrag(TracerWindow *gui, int x, int y)
536 : BC_CheckBox(x, y, gui->plugin->config.drag, _("Drag"))
540 int TracerDrag::handle_event()
542 CWindowGUI *cwindow_gui = gui->plugin->server->mwindow->cwindow->gui;
543 int value = get_value();
545 if( !gui->grab(cwindow_gui) ) {
551 gui->ungrab(cwindow_gui);
552 gui->plugin->config.drag = value;
553 gui->send_configure_change();
557 TracerDraw::TracerDraw(TracerWindow *gui, int x, int y)
558 : BC_CheckBox(x, y, gui->plugin->config.draw, _("Draw"))
562 int TracerDraw::handle_event()
564 gui->plugin->config.draw = get_value();
565 gui->send_configure_change();
569 TracerFill::TracerFill(TracerWindow *gui, int x, int y)
570 : BC_CheckBox(x, y, gui->plugin->config.fill, _("Fill"))
574 int TracerFill::handle_event()
576 gui->plugin->config.fill = get_value();
577 gui->send_configure_change();
581 TracerFeather::TracerFeather(TracerWindow *gui, int x, int y, int w)
582 : BC_ISlider(x,y,0,w,w, -50,50, gui->plugin->config.feather)
586 int TracerFeather::handle_event()
588 gui->plugin->config.feather = get_value();
589 gui->send_configure_change();
593 TracerRadius::TracerRadius(TracerWindow *gui, int x, int y, int w)
594 : BC_FSlider(x,y, 0,w,w, -5.f,5.f, gui->plugin->config.radius)
598 int TracerRadius::handle_event()
600 gui->plugin->config.radius = get_value();
601 gui->send_configure_change();
605 TracerNewPoint::TracerNewPoint(TracerWindow *gui, Tracer *plugin, int x, int y)
606 : BC_GenericButton(x, y, 80, _("New"))
609 this->plugin = plugin;
611 TracerNewPoint::~TracerNewPoint()
614 int TracerNewPoint::handle_event()
616 int k = plugin->new_point();
617 gui->point_list->update(k);
618 gui->send_configure_change();
622 TracerDelPoint::TracerDelPoint(TracerWindow *gui, Tracer *plugin, int x, int y)
623 : BC_GenericButton(x, y, 80, C_("Del"))
626 this->plugin = plugin;
628 TracerDelPoint::~TracerDelPoint()
631 int TracerDelPoint::handle_event()
633 int hot_point = gui->point_list->get_selection_number(0, 0);
634 TracerPoints &points = plugin->config.points;
635 if( hot_point >= 0 && hot_point < points.size() ) {
636 plugin->config.del_point(hot_point);
637 gui->point_list->update(--hot_point);
638 gui->send_configure_change();
643 TracerReset::TracerReset(TracerWindow *gui, Tracer *plugin, int x, int y)
644 : BC_GenericButton(x, y, _("Reset"))
647 this->plugin = plugin;
649 TracerReset::~TracerReset()
652 int TracerReset::handle_event()
654 TracerConfig &config = plugin->config;
656 MWindow *mwindow = plugin->server->mwindow;
657 CWindowGUI *cwindow_gui = mwindow->cwindow->gui;
658 if( gui->grab(cwindow_gui) )
661 gui->drag->flicker(10,50);
668 config.selected = -1;
669 TracerPoints &points = plugin->config.points;
670 points.remove_all_objects();
671 gui->point_list->update(-1);
673 gui->send_configure_change();
677 TracerInvert::TracerInvert(TracerWindow *gui, Tracer *plugin, int x, int y)
678 : BC_CheckBox(x, y, gui->plugin->config.invert, _("Invert"))
681 this->plugin = plugin;
683 TracerInvert::~TracerInvert()
686 int TracerInvert::handle_event()
688 plugin->config.invert = get_value();
689 gui->send_configure_change();