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 "crikeywindow.h"
28 #include "cwindowgui.h"
30 #include "edlsession.h"
32 #include "mainerror.h"
35 #include "pluginserver.h"
42 CriKeyNum::CriKeyNum(CriKeyWindow *gui, int x, int y, float output)
43 : BC_TumbleTextBox(gui, output, -32767.0f, 32767.0f, x, y, 120)
50 CriKeyNum::~CriKeyNum()
54 int CriKeyPointX::handle_event()
56 if( !CriKeyNum::handle_event() ) return 0;
57 CriKeyPointList *point_list = gui->point_list;
58 int hot_point = point_list->get_selection_number(0, 0);
59 CriKeyPoints &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 CriKeyPointY::handle_event()
72 if( !CriKeyNum::handle_event() ) return 0;
73 CriKeyPointList *point_list = gui->point_list;
74 int hot_point = point_list->get_selection_number(0, 0);
75 CriKeyPoints &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 int CriKeyDrawModeItem::handle_event()
89 ((CriKeyDrawMode *)get_popup_menu())->update(id);
92 CriKeyDrawMode::CriKeyDrawMode(CriKeyWindow *gui, int x, int y)
93 : BC_PopupMenu(x, y, 100, "", 1)
96 draw_modes[DRAW_ALPHA] = _("Alpha");
97 draw_modes[DRAW_EDGE] = _("Edge");
98 draw_modes[DRAW_MASK] = _("Mask");
101 void CriKeyDrawMode::create_objects()
103 for( int i=0; i<DRAW_MODES; ++i )
104 add_item(new CriKeyDrawModeItem(draw_modes[i], i));
105 update(gui->plugin->config.draw_mode, 0);
107 void CriKeyDrawMode::update(int mode, int send)
109 if( this->mode == mode ) return;
111 set_text(draw_modes[mode]);
112 gui->plugin->config.draw_mode = mode;
113 if( send ) gui->send_configure_change();
117 CriKeyWindow::CriKeyWindow(CriKey *plugin)
118 : PluginClientWindow(plugin, 380, 400, 380, 400, 0)
120 this->plugin = plugin;
121 this->title_x = 0; this->point_x = 0;
122 this->title_y = 0; this->point_y = 0;
123 this->new_point = 0; this->del_point = 0;
124 this->point_up = 0; this->point_dn = 0;
125 this->drag = 0; this->dragging = 0;
126 this->last_x = 0; this->last_y = 0;
127 this->point_list = 0; this->pending_config = 0;
130 CriKeyWindow::~CriKeyWindow()
134 void CriKeyWindow::create_objects()
137 int margin = plugin->get_theme()->widget_border;
139 add_subwindow(title = new BC_Title(x, y+5, _("Draw mode:")));
140 int x1 = x + title->get_w() + 10 + margin;
141 add_subwindow(draw_mode = new CriKeyDrawMode(this, x1, y));
142 draw_mode->create_objects();
143 y += draw_mode->get_h() + 10 + margin;
145 CriKeyPoint *pt = plugin->config.points[plugin->config.selected];
146 add_subwindow(title_x = new BC_Title(x, y, _("X:")));
147 x1 = x + title_x->get_w() + margin;
148 point_x = new CriKeyPointX(this, x1, y, pt->x);
149 point_x->create_objects();
150 x1 += point_x->get_w() + margin;
151 add_subwindow(new_point = new CriKeyNewPoint(this, plugin, x1, y));
152 x1 += new_point->get_w() + margin;
153 add_subwindow(point_up = new CriKeyPointUp(this, x1, y));
154 y += point_x->get_h() + margin;
155 add_subwindow(title_y = new BC_Title(x, y, _("Y:")));
156 x1 = x + title_y->get_w() + margin;
157 point_y = new CriKeyPointY(this, x1, y, pt->y);
158 point_y->create_objects();
159 x1 += point_y->get_w() + margin;
160 add_subwindow(del_point = new CriKeyDelPoint(this, plugin, x1, y));
161 x1 += del_point->get_w() + margin;
162 add_subwindow(point_dn = new CriKeyPointDn(this, x1, y));
163 y += point_y->get_h() + margin + 10;
164 add_subwindow(title = new BC_Title(x, y, _("Threshold:")));
165 y += title->get_h() + margin;
166 add_subwindow(threshold = new CriKeyThreshold(this, x, y, get_w() - x * 2));
167 y += threshold->get_h() + margin;
169 add_subwindow(drag = new CriKeyDrag(this, x, y));
170 if( plugin->config.drag ) {
171 if( !grab(plugin->server->mwindow->cwindow->gui) )
172 eprintf("drag enabled, but compositor already grabbed\n");
174 x1 = x + drag->get_w() + margin + 32;
175 add_subwindow(reset = new CriKeyReset(this, plugin, x1, y+3));
176 y += drag->get_h() + margin;
178 add_subwindow(point_list = new CriKeyPointList(this, plugin, x, y));
179 point_list->update(plugin->config.selected);
181 y += point_list->get_h() + 10;
182 add_subwindow(notes = new BC_Title(x, y,
183 _("Right click in composer: create new point\n"
184 "Shift-left click in Enable field:\n"
185 " if any off, turns all on\n"
186 " if all on, turns rest off.")));
190 void CriKeyWindow::send_configure_change()
193 plugin->send_configure_change();
195 int CriKeyWindow::check_configure_change(int ret)
197 if( pending_config && !grab_event_count() )
198 send_configure_change();
202 int CriKeyWindow::grab_event(XEvent *event)
204 switch( event->type ) {
205 case ButtonPress: break;
206 case ButtonRelease: break;
207 case MotionNotify: break;
209 return check_configure_change(0);
212 MWindow *mwindow = plugin->server->mwindow;
213 CWindowGUI *cwindow_gui = mwindow->cwindow->gui;
214 CWindowCanvas *canvas = cwindow_gui->canvas;
215 int cx, cy; cwindow_gui->get_relative_cursor(cx, cy);
216 cx -= mwindow->theme->ccanvas_x;
217 cy -= mwindow->theme->ccanvas_y;
220 if( cx < 0 || cx >= mwindow->theme->ccanvas_w ||
221 cy < 0 || cy >= mwindow->theme->ccanvas_h )
222 return check_configure_change(0);
225 switch( event->type ) {
227 if( dragging ) return check_configure_change(0);
228 if( event->xbutton.button == WHEEL_UP ) return threshold->wheel_event(1);
229 if( event->xbutton.button == WHEEL_DOWN ) return threshold->wheel_event(-1);
230 dragging = event->xbutton.state & ShiftMask ? -1 : 1;
233 if( !dragging ) return check_configure_change(0);
237 if( !dragging ) return check_configure_change(0);
240 return check_configure_change(0);
243 float cursor_x = cx, cursor_y = cy;
244 canvas->canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
245 int64_t position = plugin->get_source_position();
246 float projector_x, projector_y, projector_z;
247 Track *track = plugin->server->plugin->track;
248 int track_w = track->track_w, track_h = track->track_h;
249 track->automation->get_projector(
250 &projector_x, &projector_y, &projector_z,
251 position, PLAY_FORWARD);
252 projector_x += mwindow->edl->session->output_w / 2;
253 projector_y += mwindow->edl->session->output_h / 2;
254 float output_x = (cursor_x - projector_x) / projector_z + track_w / 2;
255 float output_y = (cursor_y - projector_y) / projector_z + track_h / 2;
256 point_x->update((int64_t)(output_x));
257 point_y->update((int64_t)(output_y));
258 CriKeyPoints &points = plugin->config.points;
261 switch( event->type ) {
263 int button_no = event->xbutton.button;
265 if( button_no == RIGHT_BUTTON ) {
266 hot_point = plugin->new_point();
267 CriKeyPoint *pt = points[hot_point];
268 pt->x = output_x; pt->y = output_y;
269 point_list->update(hot_point);
272 int sz = points.size();
273 if( hot_point < 0 && sz > 0 ) {
274 CriKeyPoint *pt = points[hot_point=0];
275 double dist = DISTANCE(output_x,output_y, pt->x,pt->y);
276 for( int i=1; i<sz; ++i ) {
278 double d = DISTANCE(output_x,output_y, pt->x,pt->y);
279 if( d >= dist ) continue;
280 dist = d; hot_point = i;
282 pt = points[hot_point];
283 float px = (pt->x - track_w / 2) * projector_z + projector_x;
284 float py = (pt->y - track_h / 2) * projector_z + projector_y;
285 dist = DISTANCE(px, py, cursor_x,cursor_y);
286 if( dist >= HANDLE_W ) hot_point = -1;
288 if( hot_point >= 0 && sz > 0 ) {
289 CriKeyPoint *pt = points[hot_point];
290 point_list->set_point(hot_point, PT_X, pt->x = output_x);
291 for( int i=0; i<sz; ++i ) {
293 pt->e = i==hot_point ? !pt->e : 0;
294 point_list->set_point(i, PT_E, pt->e ? "*" : "");
296 point_list->update_list(hot_point);
300 int hot_point = point_list->get_selection_number(0, 0);
301 if( hot_point >= 0 && hot_point < points.size() ) {
302 CriKeyPoint *pt = points[hot_point];
303 if( pt->x == output_x && pt->y == output_y ) break;
304 point_list->set_point(hot_point, PT_X, pt->x = output_x);
305 point_list->set_point(hot_point, PT_Y, pt->y = output_y);
306 point_x->update(pt->x);
307 point_y->update(pt->y);
308 point_list->update_list(hot_point);
314 switch( event->type ) {
316 float dx = output_x - last_x, dy = output_y - last_y;
317 int sz = points.size();
318 for( int i=0; i<sz; ++i ) {
319 CriKeyPoint *pt = points[i];
320 point_list->set_point(i, PT_X, pt->x += dx);
321 point_list->set_point(i, PT_Y, pt->y += dy);
323 int hot_point = point_list->get_selection_number(0, 0);
324 if( hot_point >= 0 && hot_point < sz ) {
325 CriKeyPoint *pt = points[hot_point];
326 point_x->update(pt->x);
327 point_y->update(pt->y);
328 point_list->update_list(hot_point);
334 last_x = output_x; last_y = output_y;
335 if( !grab_event_count() )
336 send_configure_change();
342 void CriKeyWindow::done_event(int result)
344 ungrab(client->server->mwindow->cwindow->gui);
347 CriKeyPointList::CriKeyPointList(CriKeyWindow *gui, CriKey *plugin, int x, int y)
348 : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT)
351 this->plugin = plugin;
352 titles[PT_E] = _("E"); widths[PT_E] = 50;
353 titles[PT_X] = _("X"); widths[PT_X] = 90;
354 titles[PT_Y] = _("Y"); widths[PT_Y] = 90;
355 titles[PT_T] = _("T"); widths[PT_T] = 70;
356 titles[PT_TAG] = _("Tag"); widths[PT_TAG] = 50;
358 CriKeyPointList::~CriKeyPointList()
362 void CriKeyPointList::clear()
364 for( int i=PT_SZ; --i>=0; )
365 cols[i].remove_all_objects();
368 int CriKeyPointList::column_resize_event()
370 for( int i=PT_SZ; --i>=0; )
371 widths[i] = get_column_width(i);
375 int CriKeyPointList::handle_event()
377 int hot_point = get_selection_number(0, 0);
378 const char *x_text = "", *y_text = "";
379 float t = plugin->config.threshold;
380 CriKeyPoints &points = plugin->config.points;
382 int sz = points.size();
383 if( hot_point >= 0 && sz > 0 ) {
384 if( get_cursor_x() < widths[0] ) {
386 int all_on = points[0]->e;
387 for( int i=1; i<sz && all_on; ++i ) all_on = points[i]->e;
388 int e = !all_on ? 1 : 0;
389 for( int i=0; i<sz; ++i ) points[i]->e = e;
390 points[hot_point]->e = 1;
393 points[hot_point]->e = !points[hot_point]->e;
395 x_text = gui->point_list->cols[PT_X].get(hot_point)->get_text();
396 y_text = gui->point_list->cols[PT_Y].get(hot_point)->get_text();
397 t = points[hot_point]->t;
399 gui->point_x->update(x_text);
400 gui->point_y->update(y_text);
401 gui->threshold->update(t);
403 gui->send_configure_change();
407 int CriKeyPointList::selection_changed()
413 void CriKeyPointList::new_point(const char *ep, const char *xp, const char *yp,
414 const char *tp, const char *tag)
416 cols[PT_E].append(new BC_ListBoxItem(ep));
417 cols[PT_X].append(new BC_ListBoxItem(xp));
418 cols[PT_Y].append(new BC_ListBoxItem(yp));
419 cols[PT_T].append(new BC_ListBoxItem(tp));
420 cols[PT_TAG].append(new BC_ListBoxItem(tag));
423 void CriKeyPointList::del_point(int i)
425 for( int sz1=cols[0].size()-1, c=PT_SZ; --c>=0; )
426 cols[c].remove_object_number(sz1-i);
429 void CriKeyPointList::set_point(int i, int c, float v)
431 char s[BCSTRLEN]; sprintf(s,"%0.4f",v);
434 void CriKeyPointList::set_point(int i, int c, const char *cp)
436 cols[c].get(i)->set_text(cp);
439 int CriKeyPointList::set_selected(int k)
441 CriKeyPoints &points = plugin->config.points;
442 int sz = points.size();
445 for( int i=0; i<sz; ++i ) points[i]->e = 0;
447 update_selection(&cols[0], k);
450 void CriKeyPointList::update_list(int k)
452 int xpos = get_xposition(), ypos = get_yposition();
453 if( k < 0 ) k = get_selection_number(0, 0);
454 update_selection(&cols[0], k);
455 BC_ListBox::update(&cols[0], &titles[0],&widths[0],PT_SZ, xpos,ypos,k);
458 void CriKeyPointList::update(int k)
461 CriKeyPoints &points = plugin->config.points;
462 int sz = points.size();
463 for( int i=0; i<sz; ++i ) {
464 CriKeyPoint *pt = points[i];
465 char etxt[BCSTRLEN]; sprintf(etxt,"%s", pt->e ? "*" : "");
466 char xtxt[BCSTRLEN]; sprintf(xtxt,"%0.4f", pt->x);
467 char ytxt[BCSTRLEN]; sprintf(ytxt,"%0.4f", pt->y);
468 char ttxt[BCSTRLEN]; sprintf(ttxt,"%0.4f", pt->t);
469 char ttag[BCSTRLEN]; sprintf(ttag,"%d", pt->tag);
470 new_point(etxt, xtxt, ytxt, ttxt, ttag);
472 if( k >= 0 && k < sz ) {
473 gui->point_x->update(gui->point_list->cols[PT_X].get(k)->get_text());
474 gui->point_y->update(gui->point_list->cols[PT_Y].get(k)->get_text());
475 plugin->config.selected = k;
481 void CriKeyWindow::update_gui()
483 draw_mode->update(plugin->config.draw_mode);
484 threshold->update(plugin->config.threshold);
485 drag->update(plugin->config.drag);
486 point_list->update(-1);
490 CriKeyThreshold::CriKeyThreshold(CriKeyWindow *gui, int x, int y, int w)
491 : BC_FSlider(x, y, 0, w, w, 0, 1, gui->plugin->config.threshold, 0)
494 set_precision(0.005);
495 set_pagination(0.01, 0.1);
498 int CriKeyThreshold::wheel_event(int v)
500 if( v > 0 ) increase_value();
501 else if( v < 0 ) decrease_value();
507 int CriKeyThreshold::handle_event()
509 float v = get_value();
510 gui->plugin->config.threshold = v;
511 int hot_point = gui->point_list->get_selection_number(0, 0);
512 if( hot_point >= 0 ) {
513 CriKeyPoints &points = gui->plugin->config.points;
514 CriKeyPoint *pt = points[hot_point];
515 pt->t = v; pt->e = 1;
516 gui->point_list->update(hot_point);
518 gui->send_configure_change();
523 CriKeyPointUp::CriKeyPointUp(CriKeyWindow *gui, int x, int y)
524 : BC_GenericButton(x, y, _("Up"))
528 CriKeyPointUp::~CriKeyPointUp()
532 int CriKeyPointUp::handle_event()
534 CriKeyPoints &points = gui->plugin->config.points;
535 int sz = points.size();
536 int hot_point = gui->point_list->get_selection_number(0, 0);
538 if( sz > 1 && hot_point > 0 ) {
539 CriKeyPoint *&pt0 = points[hot_point];
540 CriKeyPoint *&pt1 = points[--hot_point];
541 CriKeyPoint *t = pt0; pt0 = pt1; pt1 = t;
542 gui->point_list->update(hot_point);
544 gui->send_configure_change();
548 CriKeyPointDn::CriKeyPointDn(CriKeyWindow *gui, int x, int y)
549 : BC_GenericButton(x, y, _("Dn"))
553 CriKeyPointDn::~CriKeyPointDn()
557 int CriKeyPointDn::handle_event()
559 CriKeyPoints &points = gui->plugin->config.points;
560 int sz = points.size();
561 int hot_point = gui->point_list->get_selection_number(0, 0);
562 if( sz > 1 && hot_point < sz-1 ) {
563 CriKeyPoint *&pt0 = points[hot_point];
564 CriKeyPoint *&pt1 = points[++hot_point];
565 CriKeyPoint *t = pt0; pt0 = pt1; pt1 = t;
566 gui->point_list->update(hot_point);
568 gui->send_configure_change();
572 CriKeyDrag::CriKeyDrag(CriKeyWindow *gui, int x, int y)
573 : BC_CheckBox(x, y, gui->plugin->config.drag, _("Drag"))
577 int CriKeyDrag::handle_event()
579 CWindowGUI *cwindow_gui = gui->plugin->server->mwindow->cwindow->gui;
580 int value = get_value();
582 if( !gui->grab(cwindow_gui) ) {
588 gui->ungrab(cwindow_gui);
589 gui->plugin->config.drag = value;
590 gui->send_configure_change();
594 CriKeyNewPoint::CriKeyNewPoint(CriKeyWindow *gui, CriKey *plugin, int x, int y)
595 : BC_GenericButton(x, y, 80, _("New"))
598 this->plugin = plugin;
600 CriKeyNewPoint::~CriKeyNewPoint()
603 int CriKeyNewPoint::handle_event()
605 int k = plugin->new_point();
606 gui->point_list->update(k);
607 gui->send_configure_change();
611 CriKeyDelPoint::CriKeyDelPoint(CriKeyWindow *gui, CriKey *plugin, int x, int y)
612 : BC_GenericButton(x, y, 80, C_("Del"))
615 this->plugin = plugin;
617 CriKeyDelPoint::~CriKeyDelPoint()
620 int CriKeyDelPoint::handle_event()
622 int hot_point = gui->point_list->get_selection_number(0, 0);
623 CriKeyPoints &points = plugin->config.points;
624 if( hot_point >= 0 && hot_point < points.size() ) {
625 plugin->config.del_point(hot_point);
626 if( !points.size() ) plugin->new_point();
627 int sz = points.size();
628 if( hot_point >= sz && hot_point > 0 ) --hot_point;
629 gui->point_list->update(hot_point);
630 gui->send_configure_change();
635 CriKeyReset::CriKeyReset(CriKeyWindow *gui, CriKey *plugin, int x, int y)
636 : BC_GenericButton(x, y, _("Reset"))
639 this->plugin = plugin;
641 CriKeyReset::~CriKeyReset()
644 int CriKeyReset::handle_event()
646 CriKeyPoints &points = plugin->config.points;
647 points.remove_all_objects();
649 gui->point_list->update(0);
650 gui->send_configure_change();