make kfrm share_lock recursive, dial back sketcher clamps
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / sketcher / sketcherwindow.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 "sketcher.h"
25 #include "sketcherwindow.h"
26 #include "cstrdup.h"
27 #include "cwindow.h"
28 #include "cwindowgui.h"
29 #include "edl.h"
30 #include "edlsession.h"
31 #include "keys.h"
32 #include "language.h"
33 #include "mainerror.h"
34 #include "mwindow.h"
35 #include "plugin.h"
36 #include "pluginserver.h"
37 #include "theme.h"
38 #include "track.h"
39
40 #define COLOR_W 30
41 #define COLOR_H 30
42
43 const char *SketcherCurve::types[] = {
44         N_("off"),
45         N_("line"),
46         N_("smooth"),
47 };
48 const char *SketcherCurve::pens[] = {
49         N_("box"),
50         N_("+"),
51         N_("/"),
52         N_("X"),
53 };
54
55 SketcherCurveTypeItem::SketcherCurveTypeItem(int ty)
56  : BC_MenuItem(_(cv_type[ty]))
57 {
58         this->ty = ty;
59 }
60 int SketcherCurveTypeItem::handle_event()
61 {
62         SketcherCurveType *popup = (SketcherCurveType*)get_popup_menu();
63         popup->update(ty);
64         SketcherWindow *gui = popup->gui;
65         SketcherConfig &config = gui->plugin->config;
66         int ci = config.cv_selected;
67         if( ci >= 0 && ci < config.curves.size() ) {
68                 SketcherCurve *cv = config.curves[ci];
69                 cv->ty = ty;
70                 gui->curve_list->update(ci);
71                 gui->send_configure_change();
72         }
73         return 1;
74 }
75
76 SketcherCurveType::SketcherCurveType(SketcherWindow *gui, int x, int y, int ty)
77  : BC_PopupMenu(x,y,64,_(cv_type[ty]))
78 {
79         this->gui = gui;
80 }
81 void SketcherCurveType::create_objects()
82 {
83         int n = sizeof(cv_type)/sizeof(cv_type[0]);
84         for( int ty=0; ty<n; ++ty )
85                 add_item(new SketcherCurveTypeItem(ty));
86 }
87 void SketcherCurveType::update(int ty)
88 {
89         set_text(_(cv_type[ty]));
90 }
91
92
93 SketcherCurvePenItem::SketcherCurvePenItem(int pen)
94  : BC_MenuItem(_(cv_pen[pen]))
95 {
96         this->pen = pen;
97 }
98 int SketcherCurvePenItem::handle_event()
99 {
100         SketcherCurvePen *popup = (SketcherCurvePen*)get_popup_menu();
101         popup->update(pen);
102         SketcherWindow *gui = popup->gui;
103         SketcherConfig &config = gui->plugin->config;
104         int ci = config.cv_selected;
105         if( ci >= 0 && ci < config.curves.size() ) {
106                 SketcherCurve *cv = config.curves[ci];
107                 cv->pen = pen;
108                 gui->curve_list->update(ci);
109                 gui->send_configure_change();
110         }
111         return 1;
112 }
113
114 SketcherCurvePen::SketcherCurvePen(SketcherWindow *gui, int x, int y, int pen)
115  : BC_PopupMenu(x,y,72,_(cv_pen[pen]))
116 {
117         this->gui = gui;
118 }
119 void SketcherCurvePen::create_objects()
120 {
121         int n = sizeof(cv_pen)/sizeof(cv_pen[0]);
122         for( int pen=0; pen<n; ++pen )
123                 add_item(new SketcherCurvePenItem(pen));
124 }
125 void SketcherCurvePen::update(int pen)
126 {
127         set_text(_(cv_pen[pen]));
128 }
129
130
131 SketcherCurveColor::SketcherCurveColor(SketcherWindow *gui, int x, int y, int w)
132  : BC_Button(x, y, w, vframes)
133 {
134         this->gui = gui;
135         this->color = BLACK;
136         for( int i=0; i<3; ++i ) {
137                 vframes[i] = new VFrame(w, w, BC_RGBA8888);
138                 vframes[i]->clear_frame();
139         }
140 }
141
142 SketcherCurveColor::~SketcherCurveColor()
143 {
144         for( int i=0; i<3; ++i )
145                 delete vframes[i];
146 }
147
148 void SketcherCurveColor::set_color(int color)
149 {
150         this->color = color;
151         int r = (color>>16) & 0xff;
152         int g = (color>>8) & 0xff;
153         int b = (color>>0) & 0xff;
154         for( int i=0; i<3; ++i ) {
155                 VFrame *vframe = vframes[i];
156                 int ww = vframe->get_w(), hh = vframe->get_h();
157                 int cx = (ww+1)/2, cy = hh/2;
158                 double cc = (cx*cx + cy*cy) / 4.;
159                 uint8_t *bp = vframe->get_data(), *dp = bp;
160                 uint8_t *ep = dp + vframe->get_data_size();
161                 int rr = r, gg = g, bb = b;
162                 int bpl = vframe->get_bytes_per_line();
163                 switch( i ) {
164                 case BUTTON_UP:
165                         break;
166                 case BUTTON_UPHI:
167                         if( (rr+=48) > 0xff ) rr = 0xff;
168                         if( (gg+=48) > 0xff ) gg = 0xff;
169                         if( (bb+=48) > 0xff ) bb = 0xff;
170                         break;
171                 case BUTTON_DOWNHI:
172                         if( (rr-=48) < 0x00 ) rr = 0x00;
173                         if( (gg-=48) < 0x00 ) gg = 0x00;
174                         if( (bb-=48) < 0x00 ) bb = 0x00;
175                         break;
176                 }
177                 while( dp < ep ) {
178                         int yy = (dp-bp) / bpl, xx = ((dp-bp) % bpl) >> 2;
179                         int dy = cy - yy, dx = cx - xx;
180                         double s = dx*dx + dy*dy - cc;
181                         double ss = s < 0 ? 1 : s >= cc ? 0 : 1 - s/cc;
182                         int aa = ss * 0xff;
183                         *dp++ = rr; *dp++ = gg; *dp++ = bb; *dp++ = aa;
184                 }
185         }
186         set_images(vframes);
187 }
188
189 void SketcherCurveColor::update_gui(int color)
190 {
191         set_color(color);
192         draw_face();
193 }
194
195 int SketcherCurveColor::handle_event()
196 {
197         gui->start_color_thread(this);
198         return 1;
199 }
200
201 SketcherCurveColorPicker::SketcherCurveColorPicker(SketcherWindow *gui, SketcherCurveColor *color_button)
202  : ColorPicker(0, _("Color"))
203 {
204         this->gui = gui;
205         this->color_button = color_button;
206         this->color = 0;
207         color_update = new SketcherCurveColorThread(this);
208 }
209
210 SketcherCurveColorPicker::~SketcherCurveColorPicker()
211 {
212         delete color_update;
213 }
214
215 void SketcherCurveColorPicker::start(int color)
216 {
217         start_window(color, 0, 1);
218         color_update->start();
219 }
220
221 void SketcherCurveColorPicker::handle_done_event(int result)
222 {
223         color_update->stop();
224         gui->lock_window("SketcherCurveColorPicker::handle_done_event");
225         if( result ) color = orig_color;
226         color_button->update_gui(color);
227         gui->unlock_window();
228         SketcherConfig &config = gui->plugin->config;
229         int ci = config.cv_selected;
230         if( ci >= 0 && ci < config.curves.size() ) {
231                 SketcherCurve *cv = config.curves[ci];
232                 cv->color = color;
233                 gui->curve_list->update(ci);
234                 gui->send_configure_change();
235         }
236 }
237
238 int SketcherCurveColorPicker::handle_new_color(int color, int alpha)
239 {
240         this->color = color;
241         color_update->update_lock->unlock();
242         return 1;
243 }
244
245 void SketcherCurveColorPicker::update_gui()
246 {
247         gui->lock_window("SketcherCurveColorPicker::update_gui");
248         color_button->update_gui(color);
249         SketcherConfig &config = gui->plugin->config;
250         int ci = config.cv_selected;
251         if( ci >= 0 ) {
252                 SketcherCurve *cv = config.curves[ci];
253                 cv->color = color;
254                 gui->curve_list->update(ci);
255                 gui->send_configure_change();
256         }
257         gui->unlock_window();
258 }
259
260 SketcherCurveColorThread::SketcherCurveColorThread(SketcherCurveColorPicker *color_picker)
261  : Thread(1, 0, 0)
262 {
263         this->color_picker = color_picker;
264         this->update_lock = new Condition(0,"SketcherCurveColorThread::update_lock");
265         done = 1;
266 }
267
268 SketcherCurveColorThread::~SketcherCurveColorThread()
269 {
270         stop();
271         delete update_lock;
272 }
273
274 void SketcherCurveColorThread::start()
275 {
276         if( done ) {
277                 done = 0;
278                 Thread::start();
279         }
280 }
281
282 void SketcherCurveColorThread::stop()
283 {
284         if( !done ) {
285                 done = 1;
286                 update_lock->unlock();
287                 join();
288         }
289 }
290
291 void SketcherCurveColorThread::run()
292 {
293         while( !done ) {
294                 update_lock->lock("SketcherCurveColorThread::run");
295                 if( done ) break;
296                 color_picker->update_gui();
297         }
298 }
299
300
301 SketcherNum::SketcherNum(SketcherWindow *gui, int x, int y, int output,
302                 int mn, int mx)
303  : BC_TumbleTextBox(gui, output, mn, mx, x, y, 64)
304 {
305         this->gui = gui;
306         set_increment(1);
307 }
308
309 SketcherNum::~SketcherNum()
310 {
311 }
312
313 int SketcherPointX::handle_event()
314 {
315         if( !SketcherNum::handle_event() ) return 0;
316         SketcherConfig &config = gui->plugin->config;
317         int ci = config.cv_selected;
318         if( ci >= 0 && ci < config.curves.size() ) {
319                 SketcherCurve *cv = config.curves[ci];
320                 SketcherPointList *point_list = gui->point_list;
321                 int hot_point = point_list->get_selection_number(0, 0);
322                 SketcherPoints &points = cv->points;
323                 if( hot_point >= 0 && hot_point < points.size() ) {
324                         int v = atoi(get_text());
325                         points[hot_point]->x = v;
326                         point_list->set_point(hot_point, PT_X, v);
327                         point_list->update_list(hot_point);
328                         gui->send_configure_change();
329                 }
330         }
331         return 1;
332 }
333 int SketcherPointY::handle_event()
334 {
335         if( !SketcherNum::handle_event() ) return 0;
336         SketcherConfig &config = gui->plugin->config;
337         int ci = config.cv_selected;
338         if( ci >= 0 && ci < config.curves.size() ) {
339                 SketcherCurve *cv = config.curves[ci];
340                 SketcherPointList *point_list = gui->point_list;
341                 int hot_point = point_list->get_selection_number(0, 0);
342                 SketcherPoints &points = cv->points;
343                 if( hot_point >= 0 && hot_point < points.size() ) {
344                         int v = atoi(get_text());
345                         points[hot_point]->y = v;
346                         point_list->set_point(hot_point, PT_Y, v);
347                         point_list->update_list(hot_point);
348                         gui->send_configure_change();
349                 }
350         }
351         return 1;
352 }
353
354 int SketcherCurveRadius::handle_event()
355 {
356         if( !SketcherNum::handle_event() ) return 0;
357         SketcherConfig &config = gui->plugin->config;
358         int ci = config.cv_selected;
359         if( ci >= 0 && ci < config.curves.size() ) {
360                 SketcherCurve *cv = config.curves[ci];
361                 int v = atoi(get_text());
362                 cv->radius = v;
363                 gui->curve_list->update(ci);
364                 gui->send_configure_change();
365         }
366         return 1;
367 }
368
369
370 SketcherWindow::SketcherWindow(Sketcher *plugin)
371  : PluginClientWindow(plugin, 380, 580, 380, 580, 0)
372 {
373         this->plugin = plugin;
374         this->title_type = 0; this->curve_type = 0;
375         this->title_pen = 0;  this->curve_pen = 0;
376         this->title_color = 0; this->curve_color = 0;
377         this->color_picker = 0; this->new_points = 0;
378         this->new_curve = 0;  this->del_curve = 0;
379         this->curve_up = 0;   this->curve_dn = 0;
380         this->title_x = 0;    this->point_x = 0;
381         this->title_y = 0;    this->point_y = 0;
382         this->new_point = 0;  this->del_point = 0;
383         this->point_up = 0;   this->point_dn = 0;
384         this->drag = 0;       this->dragging = 0;
385         this->last_x = 0;     this->last_y = 0;
386         this->point_list = 0; this->pending_config = 0;
387 }
388
389 SketcherWindow::~SketcherWindow()
390 {
391         delete curve_radius;
392         delete point_x;
393         delete point_y;
394         delete color_picker;
395 }
396
397 void SketcherWindow::create_objects()
398 {
399         int x = 10, y = 10, x1, y1;
400         int margin = plugin->get_theme()->widget_border;
401         BC_Title *title;
402         int ci = plugin->config.cv_selected;
403         if( ci < 0 || ci >= plugin->config.curves.size() )
404                 ci = plugin->new_curve(0, 1, 0, BLACK);
405         SketcherCurve *cv = plugin->config.curves[ci];
406         add_subwindow(reset_curves = new SketcherResetCurves(this, plugin, x1=x, y+3));
407         x1 += reset_curves->get_w() + 2*margin;
408         const char *curve_text = _("Curve");
409         add_subwindow(title_radius = new BC_Title(x1, y, _("Width:")));
410         x1 += title_radius->get_w() + margin;
411         curve_radius = new SketcherCurveRadius(this, x1, y, cv->radius);
412         curve_radius->create_objects();
413         y += reset_curves->get_h() + 2*margin;
414         x1 = get_w()-x - BC_Title::calculate_w(this, curve_text, LARGEFONT);
415         y1 = y-margin - BC_Title::calculate_h(this, curve_text, LARGEFONT);
416         add_subwindow(title = new BC_Title(x1, y1, curve_text, LARGEFONT,
417                 get_resources()->menu_highlighted_fontcolor));
418         add_subwindow(curve_list = new SketcherCurveList(this, plugin, x, y));
419         y += curve_list->get_h() + margin;
420         add_subwindow(title_type = new BC_Title(x, y, _("Type:")));
421         x1 = x + title_type->get_w() + margin;
422         add_subwindow(curve_type = new SketcherCurveType(this, x1, y, cv->ty));
423         curve_type->create_objects();
424         x1 += curve_type->get_w() + margin;
425         add_subwindow(new_curve = new SketcherNewCurve(this, plugin, x1, y));
426         x1 += new_curve->get_w() + margin;
427         add_subwindow(curve_up = new SketcherCurveUp(this, x1, y));
428         x1 += curve_up->get_w() + 2*margin;
429         add_subwindow(title_color = new BC_Title(x1, y, _("Color:")));
430         y += curve_type->get_h() + margin;
431
432         add_subwindow(title_pen = new BC_Title(x, y, _("Pen:")));
433         x1 = x + title_pen->get_w() + margin;
434         add_subwindow(curve_pen = new SketcherCurvePen(this, x1, y, cv->pen));
435         curve_pen->create_objects();
436         x1 += curve_pen->get_w() + margin;
437         add_subwindow(del_curve = new SketcherDelCurve(this, plugin, x1, y));
438         x1 += del_curve->get_w() + margin;
439         add_subwindow(curve_dn = new SketcherCurveDn(this, x1, y));
440         x1 += curve_dn->get_w() + 2*margin;
441         add_subwindow(curve_color = new SketcherCurveColor(this, x1, y, COLOR_W));
442         curve_color->create_objects();
443         curve_color->set_color(cv->color);
444         curve_color->draw_face();
445         y += COLOR_H + margin;
446         curve_list->update(ci);
447
448         BC_Bar *bar;
449         add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
450         y += bar->get_h() + 2*margin;
451
452         int pi = plugin->config.pt_selected;
453         SketcherPoint *pt = pi >= 0 && pi < cv->points.size() ? cv->points[pi] : 0;
454         add_subwindow(reset_points = new SketcherResetPoints(this, plugin, x1=x, y+3));
455         x1 += reset_points->get_w() + 2*margin;
456         add_subwindow(drag = new SketcherDrag(this, x1, y));
457         y += drag->get_h() + margin;
458         if( plugin->config.drag ) {
459                 if( !grab(plugin->server->mwindow->cwindow->gui) )
460                         eprintf("drag enabled, but compositor already grabbed\n");
461         }
462         const char *point_text = _("Point");
463         x1 = get_w()-x - BC_Title::calculate_w(this, point_text, LARGEFONT);
464         y1 = y-margin - BC_Title::calculate_h(this, point_text, LARGEFONT);
465         add_subwindow(title = new BC_Title(x1, y1, point_text, LARGEFONT,
466                 get_resources()->menu_highlighted_fontcolor));
467         add_subwindow(point_list = new SketcherPointList(this, plugin, x, y));
468
469         y += point_list->get_h() + margin;
470         add_subwindow(title_x = new BC_Title(x, y, _("X:")));
471         x1 = x + title_x->get_w() + margin;
472         point_x = new SketcherPointX(this, x1, y, !pt ? 0.f : pt->x);
473         point_x->create_objects();
474         x1 += point_x->get_w() + 2*margin;
475         add_subwindow(new_point = new SketcherNewPoint(this, plugin, x1, y));
476         x1 += new_point->get_w() + margin;
477         add_subwindow(point_up = new SketcherPointUp(this, x1, y));
478         y += point_x->get_h() + margin;
479         add_subwindow(title_y = new BC_Title(x, y, _("Y:")));
480         x1 = x + title_y->get_w() + margin;
481         point_y = new SketcherPointY(this, x1, y, !pt ? 0.f : pt->y);
482         point_y->create_objects();
483         x1 += point_y->get_w() + 2*margin;
484         add_subwindow(del_point = new SketcherDelPoint(this, plugin, x1, y));
485         x1 += del_point->get_w() + margin;
486         add_subwindow(point_dn = new SketcherPointDn(this, x1, y));
487         y += point_y->get_h() + margin + 10;
488         point_list->update(pi);
489
490         add_subwindow(notes0 = new BC_Title(x, y,
491                  _("\n"
492                    "LMB=\n"
493                    "Alt+LMB=\n"
494                    "MMB=\n"
495                    "DEL=\n")));
496         add_subwindow(notes1 = new BC_Title(x+80, y,
497                  _("     No Shift\n"
498                    "select point\n"
499                    "drag curve\n"
500                    "next curve type\n"
501                    "deletes point\n")));
502         add_subwindow(notes2 = new BC_Title(x+200, y,
503                  _("             Shift\n"
504                    "append new points\n"
505                    "drag image\n"
506                    "append new curve\n"
507                    "delete curve\n")));
508         show_window(1);
509 }
510
511 void SketcherWindow::send_configure_change()
512 {
513         pending_config = 0;
514         plugin->send_configure_change();
515 }
516
517 int SketcherWindow::grab_event(XEvent *event)
518 {
519         int ret = do_grab_event(event);
520         if( pending_config && !grab_event_count() )
521                 send_configure_change();
522         return ret;
523 }
524
525 int SketcherWindow::do_grab_event(XEvent *event)
526 {
527         switch( event->type ) {
528         case ButtonPress: break;
529         case ButtonRelease: break;
530         case MotionNotify: break;
531         case KeyPress:
532                 if( keysym_lookup(event) > 0 ) {
533                         switch( get_keysym() ) {
534                         case XK_Delete:
535                                 return (event->xkey.state & ShiftMask) ?
536                                         del_curve->handle_event() :
537                                         del_point->handle_event() ;
538                         }
539                 } // fall thru
540         default:
541                 return 0;
542         }
543
544         MWindow *mwindow = plugin->server->mwindow;
545         CWindowGUI *cwindow_gui = mwindow->cwindow->gui;
546         CWindowCanvas *canvas = cwindow_gui->canvas;
547         int cx, cy;  cwindow_gui->get_relative_cursor(cx, cy);
548         cx -= mwindow->theme->ccanvas_x;
549         cy -= mwindow->theme->ccanvas_y;
550
551         if( !dragging ) {
552                 if( cx < 0 || cx >= mwindow->theme->ccanvas_w ||
553                     cy < 0 || cy >= mwindow->theme->ccanvas_h )
554                         return 0;
555         }
556
557         switch( event->type ) {
558         case ButtonPress:
559                 if( dragging ) return 0;
560                 dragging = event->xbutton.state & Mod1Mask ? -1 : 1; // alt_down
561                 break;
562         case ButtonRelease:
563         case MotionNotify:
564                 if( !dragging ) return 0;
565                 break;
566         default:
567                 return 0;
568         }
569
570
571         int ci = plugin->config.cv_selected;
572         if( ci < 0 || ci >= plugin->config.curves.size() )
573                 return 1;
574
575         float cursor_x = cx, cursor_y = cy;
576         canvas->canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
577         int64_t position = plugin->get_source_position();
578         float projector_x, projector_y, projector_z;
579         Track *track = plugin->server->plugin->track;
580         int track_w = track->track_w, track_h = track->track_h;
581         track->automation->get_projector(
582                 &projector_x, &projector_y, &projector_z,
583                 position, PLAY_FORWARD);
584         projector_x += mwindow->edl->session->output_w / 2;
585         projector_y += mwindow->edl->session->output_h / 2;
586         float output_x = (cursor_x - projector_x) / projector_z + track_w / 2;
587         float output_y = (cursor_y - projector_y) / projector_z + track_h / 2;
588         SketcherCurve *cv = plugin->config.curves[ci];
589         SketcherPoints &points = cv->points;
590         int state = event->xmotion.state;
591
592         switch( event->type ) {
593         case ButtonPress: {
594                 if( dragging < 0 ) break;
595                 int hot_point = -1;
596                 int button_no = event->xbutton.button;
597                 if( button_no == LEFT_BUTTON ) {
598 // create new point string
599                         if( (state & ShiftMask) ) {
600                                 ++new_points;
601                                 hot_point = plugin->new_point(cv, output_x, output_y);
602                                 point_list->update(hot_point);
603                         }
604                         else {
605 // select point
606                                 int sz = points.size();
607                                 int last_point = hot_point;
608                                 if( sz > 0 ) {
609                                         SketcherPoint *pt = points[hot_point=0];
610                                         double dist = DISTANCE(output_x,output_y, pt->x,pt->y);
611                                         for( int i=1; i<sz; ++i ) {
612                                                 pt = points[i];
613                                                 double d = DISTANCE(output_x,output_y, pt->x,pt->y);
614                                                 if( d >= dist ) continue;
615                                                 dist = d;  hot_point = i;
616                                         }
617                                         pt = points[hot_point];
618                                         float px = (pt->x - track_w / 2) * projector_z + projector_x;
619                                         float py = (pt->y - track_h / 2) * projector_z + projector_y;
620                                         dist = DISTANCE(px, py, cursor_x,cursor_y);
621                                         if( dist >= HANDLE_W ) hot_point = -1;
622                                 }
623                                 if( hot_point != last_point ) {
624                                         SketcherPoint *pt = 0;
625                                         if( hot_point >= 0 && hot_point < sz ) {
626                                                 pt = points[hot_point];
627                                                 point_list->set_point(hot_point, PT_X, pt->x = output_x);
628                                                 point_list->set_point(hot_point, PT_Y, pt->y = output_y);
629                                         }
630                                         point_list->update_list(hot_point);
631                                         point_x->update(pt ? pt->x : 0.f);
632                                         point_y->update(pt ? pt->y : 0.f);
633                                 }
634                         }
635                 }
636                 else if( button_no == MIDDLE_BUTTON ) {
637                         if( (state & ShiftMask) ) {
638                                 int ci = plugin->new_curve(cv->ty, cv->radius, cv->pen, cv->color);
639                                 curve_list->update(ci);
640                                 point_list->update(-1);
641                         }
642                         else {
643                                 int ty = cv->ty + 1;
644                                 if( ty >= TYP_SZ ) ty = 0;
645                                 cv->ty = ty;
646                                 curve_type->update(ty);
647                                 curve_list->update(ci);
648                         }
649                 }
650                 break; }
651         case MotionNotify: {
652                 int hot_point = point_list->get_selection_number(0, 0);
653                 if( dragging < 0 ) {
654                         SketcherCurves &curves = plugin->config.curves;
655                         int dx = round(output_x - last_x);
656                         int dy = round(output_y - last_y);
657                         int mnc = (state & ShiftMask) || ci<0 ? 0 : ci;
658                         int mxc = (state & ShiftMask) ? curves.size() : ci+1;
659                         for( int i=mnc; i<mxc; ++i ) {
660                                 SketcherCurve *crv = plugin->config.curves[i];
661                                 int pts = crv->points.size();
662                                 for( int k=0; k<pts; ++k ) {
663                                         SketcherPoint *pt = crv->points[k];
664                                         pt->x += dx;  pt->y += dy;
665                                 }
666                         }
667                         SketcherPoint *pt = hot_point >= 0 && hot_point < points.size() ?
668                                 points[hot_point] : 0;
669                         point_x->update(pt ? pt->x : 0.f);
670                         point_y->update(pt ? pt->y : 0.f);
671                         point_list->update(hot_point);
672                         break;
673                 }
674                 if( (state & Button1Mask) ) {
675                         SketcherPoint *pt = hot_point >= 0 && hot_point < points.size() ?
676                                 points[hot_point] : 0;
677                         if( pt && pt->x == output_x && pt->y == output_y ) break;
678                         if( new_points ) {
679                                 if( pt ) {
680                                         float frac_w = DISTANCE(pt->x, pt->y, output_x, output_y) / get_w();
681                                         if( frac_w < 0.01 ) break; // 1 percent w
682                                 }
683                                 if( (state & ShiftMask) ) {
684                                         ++new_points;
685                                         hot_point = plugin->new_point(cv, output_x, output_y);
686                                         point_list->update(hot_point);
687                                 }
688                         }
689                         else if( pt ) {
690                                 point_list->set_point(hot_point, PT_X, pt->x = output_x);
691                                 point_list->set_point(hot_point, PT_Y, pt->y = output_y);
692                                 point_list->update_list(hot_point);
693                                 point_x->update(pt->x);
694                                 point_y->update(pt->y);
695                         }
696                 }
697                 break; }
698         case ButtonRelease: {
699                 new_points = 0;
700                 dragging = 0;
701                 break; }
702         }
703
704         last_x = output_x;  last_y = output_y;
705         pending_config = 1;
706         return 1;
707 }
708
709 int SketcherWindow::keypress_event()
710 {
711         int key = get_keypress();
712         switch( key ) {
713         case DELETE: return shift_down() ?
714                         del_curve->handle_event() :
715                         del_point->handle_event() ;
716         }
717         return 0;
718 }
719
720 void SketcherWindow::done_event(int result)
721 {
722         ungrab(client->server->mwindow->cwindow->gui);
723 }
724
725 void SketcherWindow::start_color_thread(SketcherCurveColor *color_button)
726 {
727         unlock_window();
728         delete color_picker;
729         color_picker = new SketcherCurveColorPicker(this, color_button);
730         int color = BLACK, ci = plugin->config.cv_selected;
731         if( ci >= 0 && ci < plugin->config.curves.size() ) {
732                 SketcherCurve *cv = plugin->config.curves[ci];
733                 color = cv->color;
734         }
735         color_picker->start(color);
736         lock_window("SketcherWindow::start_color_thread");
737 }
738
739
740 SketcherCurveList::SketcherCurveList(SketcherWindow *gui, Sketcher *plugin, int x, int y)
741  : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT)
742 {
743         this->gui = gui;
744         this->plugin = plugin;
745         titles[CV_ID] = _("id");  widths[CV_ID] = 64;
746         titles[CV_TY] = _("type");  widths[CV_TY] = 64;
747         titles[CV_RAD] = _("radius");  widths[CV_RAD] = 64;
748         titles[CV_PEN] = _("pen");  widths[CV_PEN] = 64;
749         titles[CV_CLR] = _("color");  widths[CV_CLR] = 64;
750 }
751 SketcherCurveList::~SketcherCurveList()
752 {
753         clear();
754 }
755 void SketcherCurveList::clear()
756 {
757         for( int i=CV_SZ; --i>=0; )
758                 cols[i].remove_all_objects();
759 }
760
761 int SketcherCurveList::column_resize_event()
762 {
763         for( int i=CV_SZ; --i>=0; )
764                 widths[i] = get_column_width(i);
765         return 1;
766 }
767
768 int SketcherCurveList::handle_event()
769 {
770         int ci = get_selection_number(0, 0);
771         set_selected(ci);
772         gui->point_list->update(0);
773         gui->send_configure_change();
774         return 1;
775 }
776
777 int SketcherCurveList::selection_changed()
778 {
779         handle_event();
780         return 1;
781 }
782
783 void SketcherCurveList::set_selected(int k)
784 {
785         int ci = -1;
786         if( k >= 0 && k < plugin->config.curves.size() ) {
787                 SketcherCurve *cv = plugin->config.curves[k];
788                 gui->curve_type->update(cv->ty);
789                 gui->curve_radius->update(cv->radius);
790                 gui->curve_pen->update(cv->pen);
791                 gui->curve_color->update_gui(cv->color);
792                 ci = k;
793         }
794         plugin->config.cv_selected = ci;
795         update_selection(&cols[0], ci);
796         update_list(-1);
797 }
798
799 void SketcherCurveList::update_list(int k)
800 {
801         int xpos = get_xposition(), ypos = get_yposition();
802         if( k < 0 ) k = get_selection_number(0, 0);
803         update_selection(&cols[0], k);
804         BC_ListBox::update(&cols[0], &titles[0],&widths[0],CV_SZ, xpos,ypos,k);
805         center_selection();
806 }
807
808 void SketcherCurveList::update(int k)
809 {
810         clear();
811         SketcherCurves &curves = plugin->config.curves;
812         int sz = curves.size();
813         for( int i=0; i<sz; ++i ) {
814                 SketcherCurve *cv = curves[i];
815                 char itxt[BCSTRLEN];  sprintf(itxt,"%d", cv->id);
816                 char ttxt[BCSTRLEN];  sprintf(ttxt,"%s", cv_type[cv->ty]);
817                 char rtxt[BCSTRLEN];  sprintf(rtxt,"%d", cv->radius);
818                 char ptxt[BCSTRLEN];  sprintf(ptxt,"%s", cv_pen[cv->pen]);
819                 int color = cv->color;
820                 int r = (color>>16)&0xff, g = (color>>8)&0xff, b = (color>>0)&0xff;
821                 char ctxt[BCSTRLEN];  sprintf(ctxt,"#%02x%02x%02x", r, g, b);
822                 add_curve(itxt, ttxt, rtxt, ptxt, ctxt);
823         }
824         set_selected(k);
825 }
826
827 void SketcherCurveList::add_curve(const char *id, const char *type,
828                 const char *radius, const char *pen, const char *color)
829 {
830         cols[CV_ID].append(new BC_ListBoxItem(id));
831         cols[CV_TY].append(new BC_ListBoxItem(type));
832         cols[CV_RAD].append(new BC_ListBoxItem(radius));
833         cols[CV_PEN].append(new BC_ListBoxItem(pen));
834         cols[CV_CLR].append(new BC_ListBoxItem(color));
835 }
836
837 SketcherNewCurve::SketcherNewCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y)
838  : BC_GenericButton(x, y, 64, _("New"))
839 {
840         this->gui = gui;
841         this->plugin = plugin;
842 }
843 SketcherNewCurve::~SketcherNewCurve()
844 {
845 }
846 int SketcherNewCurve::handle_event()
847 {
848         int ty = 1, pen = 0, color = 0, radius = 1;
849         int ci = plugin->config.cv_selected;
850         if( ci >= 0 && ci < plugin->config.curves.size() ) {
851                 SketcherCurve *cv = plugin->config.curves[ci];
852                 ty = cv->ty;  pen = cv->pen;
853                 color = cv->color;  radius = cv->radius;
854         }
855         int k = plugin->new_curve(ty, radius, pen, color);
856         gui->curve_list->update(k);
857         gui->point_list->update(-1);
858         gui->send_configure_change();
859         return 1;
860 }
861
862 SketcherDelCurve::SketcherDelCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y)
863  : BC_GenericButton(x, y, 64, C_("Del"))
864 {
865         this->gui = gui;
866         this->plugin = plugin;
867 }
868 SketcherDelCurve::~SketcherDelCurve()
869 {
870 }
871 int SketcherDelCurve::handle_event()
872 {
873         int hot_curve = gui->curve_list->get_selection_number(0, 0);
874         SketcherCurves &curves = plugin->config.curves;
875         if( hot_curve >= 0 && hot_curve < curves.size() ) {
876                 curves.remove_object_number(hot_curve);
877                 if( --hot_curve < 0 )
878                         hot_curve = plugin->new_curve(0, 1, 0, BLACK);
879                 gui->curve_list->update(hot_curve);
880                 gui->point_list->update(-1);
881                 gui->send_configure_change();
882         }
883         return 1;
884 }
885
886 SketcherCurveUp::SketcherCurveUp(SketcherWindow *gui, int x, int y)
887  : BC_GenericButton(x, y, _("Up"))
888 {
889         this->gui = gui;
890 }
891 SketcherCurveUp::~SketcherCurveUp()
892 {
893 }
894
895 int SketcherCurveUp::handle_event()
896 {
897         SketcherCurves &curves = gui->plugin->config.curves;
898         int hot_curve = gui->curve_list->get_selection_number(0, 0);
899
900         if( hot_curve > 0 && hot_curve < curves.size() ) {
901                 SketcherCurve *&cv0 = curves[hot_curve];
902                 SketcherCurve *&cv1 = curves[--hot_curve];
903                 SketcherCurve *t = cv0;  cv0 = cv1;  cv1 = t;
904                 gui->curve_list->update(hot_curve);
905         }
906         gui->send_configure_change();
907         return 1;
908 }
909
910 SketcherCurveDn::SketcherCurveDn(SketcherWindow *gui, int x, int y)
911  : BC_GenericButton(x, y, _("Dn"))
912 {
913         this->gui = gui;
914 }
915 SketcherCurveDn::~SketcherCurveDn()
916 {
917 }
918
919 int SketcherCurveDn::handle_event()
920 {
921         SketcherCurves &curves = gui->plugin->config.curves;
922         int hot_curve = gui->curve_list->get_selection_number(0, 0);
923
924         if( hot_curve >= 0 && hot_curve < curves.size()-1 ) {
925                 SketcherCurve *&cv0 = curves[hot_curve];
926                 SketcherCurve *&cv1 = curves[++hot_curve];
927                 SketcherCurve *t = cv0;  cv0 = cv1;  cv1 = t;
928                 gui->curve_list->update(hot_curve);
929         }
930         gui->send_configure_change();
931         return 1;
932 }
933
934
935 SketcherPointList::SketcherPointList(SketcherWindow *gui, Sketcher *plugin, int x, int y)
936  : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT)
937 {
938         this->gui = gui;
939         this->plugin = plugin;
940         titles[PT_X] = _("X");    widths[PT_X] = 90;
941         titles[PT_Y] = _("Y");    widths[PT_Y] = 90;
942         titles[PT_ID] = _("ID");  widths[PT_ID] = 50;
943 }
944 SketcherPointList::~SketcherPointList()
945 {
946         clear();
947 }
948 void SketcherPointList::clear()
949 {
950         for( int i=PT_SZ; --i>=0; )
951                 cols[i].remove_all_objects();
952 }
953
954 int SketcherPointList::column_resize_event()
955 {
956         for( int i=PT_SZ; --i>=0; )
957                 widths[i] = get_column_width(i);
958         return 1;
959 }
960
961 int SketcherPointList::handle_event()
962 {
963         int pi = get_selection_number(0, 0);
964         set_selected(pi);
965         gui->send_configure_change();
966         return 1;
967 }
968
969 int SketcherPointList::selection_changed()
970 {
971         handle_event();
972         return 1;
973 }
974
975 void SketcherPointList::add_point(const char *id, const char *xp, const char *yp)
976 {
977         cols[PT_ID].append(new BC_ListBoxItem(id));
978         cols[PT_X].append(new BC_ListBoxItem(xp));
979         cols[PT_Y].append(new BC_ListBoxItem(yp));
980 }
981
982 void SketcherPointList::set_point(int i, int c, int v)
983 {
984         char stxt[BCSTRLEN];
985         sprintf(stxt,"%d",v);
986         set_point(i,c,stxt);
987 }
988 void SketcherPointList::set_point(int i, int c, const char *cp)
989 {
990         cols[c].get(i)->set_text(cp);
991 }
992
993 void SketcherPointList::set_selected(int k)
994 {
995         SketcherPoint *pt = 0;
996         int ci = plugin->config.cv_selected, pi = -1;
997         if( ci >= 0 && ci < plugin->config.curves.size() ) {
998                 SketcherCurve *cv = plugin->config.curves[ci];
999                 pt = k >= 0 && k < cv->points.size() ? cv->points[pi=k] : 0;
1000         }
1001         gui->point_x->update(pt ? pt->x : 0.f);
1002         gui->point_y->update(pt ? pt->y : 0.f);
1003         plugin->config.pt_selected = pi;
1004         update_selection(&cols[0], pi);
1005         update_list(k);
1006 }
1007 void SketcherPointList::update_list(int k)
1008 {
1009         int xpos = get_xposition(), ypos = get_yposition();
1010         if( k < 0 ) k = get_selection_number(0, 0);
1011         update_selection(&cols[0], k);
1012         BC_ListBox::update(&cols[0], &titles[0],&widths[0],PT_SZ, xpos,ypos,k);
1013         center_selection();
1014 }
1015 void SketcherPointList::update(int k)
1016 {
1017         clear();
1018         int ci = plugin->config.cv_selected, sz = 0;
1019         if( ci >= 0 && ci < plugin->config.curves.size() ) {
1020                 SketcherCurve *cv = plugin->config.curves[ci];
1021                 SketcherPoints &points = cv->points;
1022                 sz = points.size();
1023                 for( int i=0; i<sz; ++i ) {
1024                         SketcherPoint *pt = points[i];
1025                         char itxt[BCSTRLEN];  sprintf(itxt,"%d", pt->id);
1026                         char xtxt[BCSTRLEN];  sprintf(xtxt,"%d", pt->x);
1027                         char ytxt[BCSTRLEN];  sprintf(ytxt,"%d", pt->y);
1028                         add_point(itxt, xtxt, ytxt);
1029                 }
1030         }
1031         set_selected(k);
1032 }
1033
1034 void SketcherWindow::update_gui()
1035 {
1036         SketcherConfig &config = plugin->config;
1037         int ci = config.cv_selected;
1038         int pi = config.pt_selected;
1039         curve_list->update(ci);
1040         point_list->update(pi);
1041         SketcherCurve *cv = ci >= 0 ? config.curves[ci] : 0;
1042         curve_radius->update(cv ? cv->radius : 1);
1043         curve_type->update(cv ? cv->ty : TYP_OFF);
1044         curve_pen->update(cv ? cv->pen : PEN_SQUARE);
1045         curve_color->set_color(cv ? cv->color : BLACK);
1046         SketcherPoint *pt = pi >= 0 ? cv->points[pi] : 0;
1047         point_x->update(pt ? pt->x : 0);
1048         point_y->update(pt ? pt->y : 0);
1049         drag->update(plugin->config.drag);
1050 }
1051
1052
1053 SketcherPointUp::SketcherPointUp(SketcherWindow *gui, int x, int y)
1054  : BC_GenericButton(x, y, _("Up"))
1055 {
1056         this->gui = gui;
1057 }
1058 SketcherPointUp::~SketcherPointUp()
1059 {
1060 }
1061
1062 int SketcherPointUp::handle_event()
1063 {
1064         SketcherConfig &config = gui->plugin->config;
1065         int ci = config.cv_selected;
1066         if( ci < 0 || ci >= config.curves.size() )
1067                 return 1;
1068         SketcherCurve *cv = config.curves[ci];
1069         SketcherPoints &points = cv->points;
1070         int sz = points.size();
1071         int hot_point = gui->point_list->get_selection_number(0, 0);
1072
1073         if( sz > 1 && hot_point > 0 ) {
1074                 SketcherPoint *&pt0 = points[hot_point];
1075                 SketcherPoint *&pt1 = points[--hot_point];
1076                 SketcherPoint *t = pt0;  pt0 = pt1;  pt1 = t;
1077                 gui->point_list->update(hot_point);
1078         }
1079         gui->send_configure_change();
1080         return 1;
1081 }
1082
1083 SketcherPointDn::SketcherPointDn(SketcherWindow *gui, int x, int y)
1084  : BC_GenericButton(x, y, _("Dn"))
1085 {
1086         this->gui = gui;
1087 }
1088 SketcherPointDn::~SketcherPointDn()
1089 {
1090 }
1091
1092 int SketcherPointDn::handle_event()
1093 {
1094         SketcherConfig &config = gui->plugin->config;
1095         int ci = config.cv_selected;
1096         if( ci < 0 || ci >= config.curves.size() )
1097                 return 1;
1098         SketcherCurve *cv = config.curves[ci];
1099         SketcherPoints &points = cv->points;
1100         int sz = points.size();
1101         int hot_point = gui->point_list->get_selection_number(0, 0);
1102         if( sz > 1 && hot_point < sz-1 ) {
1103                 SketcherPoint *&pt0 = points[hot_point];
1104                 SketcherPoint *&pt1 = points[++hot_point];
1105                 SketcherPoint *t = pt0;  pt0 = pt1;  pt1 = t;
1106                 gui->point_list->update(hot_point);
1107         }
1108         gui->send_configure_change();
1109         return 1;
1110 }
1111
1112 SketcherDrag::SketcherDrag(SketcherWindow *gui, int x, int y)
1113  : BC_CheckBox(x, y, gui->plugin->config.drag, _("Drag"))
1114 {
1115         this->gui = gui;
1116 }
1117 int SketcherDrag::handle_event()
1118 {
1119         CWindowGUI *cwindow_gui = gui->plugin->server->mwindow->cwindow->gui;
1120         int value = get_value();
1121         if( value ) {
1122                 if( !gui->grab(cwindow_gui) ) {
1123                         update(value = 0);
1124                         flicker(10,50);
1125                 }
1126         }
1127         else
1128                 gui->ungrab(cwindow_gui);
1129         gui->plugin->config.drag = value;
1130         gui->send_configure_change();
1131         return 1;
1132 }
1133
1134 SketcherNewPoint::SketcherNewPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y)
1135  : BC_GenericButton(x, y, 64, _("New"))
1136 {
1137         this->gui = gui;
1138         this->plugin = plugin;
1139 }
1140 SketcherNewPoint::~SketcherNewPoint()
1141 {
1142 }
1143 int SketcherNewPoint::handle_event()
1144 {
1145         int k = plugin->new_point();
1146         gui->point_list->update(k);
1147         gui->send_configure_change();
1148         return 1;
1149 }
1150
1151 SketcherDelPoint::SketcherDelPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y)
1152  : BC_GenericButton(x, y, 64, C_("Del"))
1153 {
1154         this->gui = gui;
1155         this->plugin = plugin;
1156 }
1157 SketcherDelPoint::~SketcherDelPoint()
1158 {
1159 }
1160 int SketcherDelPoint::handle_event()
1161 {
1162         SketcherConfig &config = gui->plugin->config;
1163         int ci = config.cv_selected;
1164         if( ci >= 0 && ci < config.curves.size() ) {
1165                 SketcherCurve *cv = config.curves[ci];
1166                 SketcherPoints &points = cv->points;
1167                 int hot_point = gui->point_list->get_selection_number(0, 0);
1168                 if( hot_point >= 0 && hot_point < points.size() ) {
1169                         points.remove_object_number(hot_point);
1170                         gui->point_list->update(--hot_point);
1171                         gui->send_configure_change();
1172                 }
1173         }
1174         return 1;
1175 }
1176
1177 SketcherResetCurves::SketcherResetCurves(SketcherWindow *gui, Sketcher *plugin, int x, int y)
1178  : BC_GenericButton(x, y, _("Reset"))
1179 {
1180         this->gui = gui;
1181         this->plugin = plugin;
1182 }
1183 SketcherResetCurves::~SketcherResetCurves()
1184 {
1185 }
1186 int SketcherResetCurves::handle_event()
1187 {
1188         SketcherConfig &config = plugin->config;
1189         config.curves.remove_all_objects();
1190         int ci = plugin->new_curve(0, 1, 0, BLACK);
1191         gui->curve_list->update(ci);
1192         gui->point_list->update(-1);
1193         gui->send_configure_change();
1194         return 1;
1195 }
1196
1197 SketcherResetPoints::SketcherResetPoints(SketcherWindow *gui, Sketcher *plugin, int x, int y)
1198  : BC_GenericButton(x, y, _("Reset"))
1199 {
1200         this->gui = gui;
1201         this->plugin = plugin;
1202 }
1203 SketcherResetPoints::~SketcherResetPoints()
1204 {
1205 }
1206 int SketcherResetPoints::handle_event()
1207 {
1208         SketcherConfig &config = gui->plugin->config;
1209         int ci = config.cv_selected;
1210         if( ci >= 0 && ci < config.curves.size() ) {
1211                 SketcherCurve *cv = config.curves[ci];
1212                 cv->points.remove_all_objects();
1213                 gui->point_list->update(-1);
1214                 gui->send_configure_change();
1215         }
1216         return 1;
1217 }
1218