update ffmpeg to 4.1, add sketcher plugin, crikey tweaks, titler colorpicker, keyfram...
authorGood Guy <[email protected]>
Mon, 12 Nov 2018 20:37:03 +0000 (13:37 -0700)
committerGood Guy <[email protected]>
Mon, 12 Nov 2018 20:37:03 +0000 (13:37 -0700)
34 files changed:
cinelerra-5.1/cinelerra/colorpicker.C
cinelerra-5.1/cinelerra/colorpicker.h
cinelerra-5.1/cinelerra/filexml.C
cinelerra-5.1/cinelerra/filexml.h
cinelerra-5.1/cinelerra/keyframe.C
cinelerra-5.1/cinelerra/keyframe.h
cinelerra-5.1/cinelerra/pluginclient.C
cinelerra-5.1/configure.ac
cinelerra-5.1/db/utils/Makefile
cinelerra-5.1/doc/shortcuts.html
cinelerra-5.1/ffmpeg/plugin.opts
cinelerra-5.1/guicast/bcpopupmenu.C
cinelerra-5.1/guicast/bctextbox.C
cinelerra-5.1/guicast/bctextbox.h
cinelerra-5.1/guicast/bcwindowbase.h
cinelerra-5.1/guicast/test9.C [new file with mode: 0644]
cinelerra-5.1/plugin_defs
cinelerra-5.1/plugins/Makefile
cinelerra-5.1/plugins/crikey/crikey.C
cinelerra-5.1/plugins/crikey/crikeywindow.C
cinelerra-5.1/plugins/crikey/crikeywindow.h
cinelerra-5.1/plugins/sketcher/Makefile [new file with mode: 0644]
cinelerra-5.1/plugins/sketcher/sketcher.C [new file with mode: 0644]
cinelerra-5.1/plugins/sketcher/sketcher.h [new file with mode: 0644]
cinelerra-5.1/plugins/sketcher/sketcherwindow.C [new file with mode: 0644]
cinelerra-5.1/plugins/sketcher/sketcherwindow.h [new file with mode: 0644]
cinelerra-5.1/plugins/titler/titlerwindow.C
cinelerra-5.1/plugins/titler/titlerwindow.h
cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch0 [moved from cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch0 with 100% similarity]
cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch1 [moved from cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch1 with 100% similarity]
cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch2 [moved from cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch2 with 100% similarity]
cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch3 [moved from cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch3 with 81% similarity]
cinelerra-5.1/thirdparty/src/ffmpeg-4.1.tar.xz [moved from cinelerra-5.1/thirdparty/src/ffmpeg-4.0.tar.xz with 52% similarity]
cinelerra-5.1/thirdparty/src/ffmpeg.git.patch3

index 1b40fcf0c38f8d00bca6f4b35972422918d71c3c..097bcec8949aa7bbc1b5b3f40a9ae5cf217a5f0d 100644 (file)
@@ -43,8 +43,8 @@ ColorPicker::ColorPicker(int do_alpha, const char *title)
        this->title = title;
        this->do_alpha = do_alpha;
        this->do_okcancel = 0;
-       this->output = BLACK;
-       this->alpha = 255;
+       this->output = this->orig_color = BLACK;
+       this->alpha = this->orig_alpha = 255;
 }
 
 ColorPicker::~ColorPicker()
@@ -63,6 +63,8 @@ void ColorPicker::start_window(int output, int alpha, int do_okcancel)
                }
                return;
        }
+       this->orig_color = output;
+       this->orig_alpha = alpha;
        this->output = output;
        this->alpha = alpha;
        this->do_okcancel = do_okcancel;
index 0ae021edc6a31696785a5cfc88a2876c70769b24..ed3f5a951f39e473942295ec464218651810e9fe 100644 (file)
@@ -69,6 +69,7 @@ public:
        void update_gui(int output, int alpha);
        BC_Window* new_gui();
 
+       int orig_color, orig_alpha;
        int output, alpha;
        int do_alpha, do_okcancel;
        const char *title;
index 8914ff4498b29412500936913104e40740fd8aee..8b7315ca9640288fd9b26b0380587bff3942b658 100644 (file)
@@ -46,6 +46,7 @@ XMLBuffer::XMLBuffer(long buf_size, long max_size, int del)
        lmt = bfr + bsz;
        isz = max_size;
        destroy = del;
+       share_lock = new Mutex("XMLBuffer::share_lock");
 }
 
 XMLBuffer::XMLBuffer(const char *buf, long buf_size, int del)
@@ -56,6 +57,7 @@ XMLBuffer::XMLBuffer(const char *buf, long buf_size, int del)
        lmt = inp = bfr+bsz;
        isz = bsz;
        destroy = del;
+       share_lock = new Mutex("XMLBuffer::share_lock");
 }
 
 XMLBuffer::XMLBuffer(long buf_size, char *buf, int del)
@@ -66,11 +68,13 @@ XMLBuffer::XMLBuffer(long buf_size, char *buf, int del)
        lmt = outp = bfr+bsz;
        isz = bsz;
        destroy = del;
+       share_lock = new Mutex("XMLBuffer::share_lock");
 }
 
 XMLBuffer::~XMLBuffer()
 {
        if( destroy ) delete [] bfr;
+       delete share_lock;
 }
 
 int XMLBuffer::demand(long len)
@@ -111,15 +115,13 @@ int XMLBuffer::read(char *bp, int len)
 
 void XMLBuffer::copy_from(XMLBuffer *xbuf)
 {
-       if( bsz != xbuf->bsz ) { delete [] bfr;  bfr = 0; }
-       if( !bfr ) bfr = new unsigned char[bsz = xbuf->bsz];
-       lmt = bfr + bsz;
-       long ilen = xbuf->otell(), olen = xbuf->itell();
-       inp = pos(ilen);
-       outp = pos(olen);
-       if( ilen > 0 )
-               memmove(bfr, xbuf->bfr, ilen);
-       destroy = xbuf->destroy;
+       xbuf->share_lock->lock("XMLBuffer::copy_from");
+       share_lock->lock("XMLBuffer::copy_from");
+       oseek(0);
+       write((const char*)xbuf->pos(0), xbuf->otell());
+       iseek(xbuf->itell());
+       xbuf->share_lock->unlock();
+       share_lock->unlock();
 }
 
 
@@ -382,6 +384,7 @@ FileXML::FileXML(int coded)
 FileXML::~FileXML()
 {
        if( !shared ) delete buffer;
+       else buffer->share_lock->unlock();
        delete [] output;
 }
 
@@ -633,9 +636,10 @@ int FileXML::set_shared_input(XMLBuffer *xbuf)
        strcpy(this->filename, "");
        delete buffer;
        buffer = xbuf;
+       xbuf->share_lock->lock("FileXML::set_shared_input");
        xbuf->iseek(0);
-       set_coding(coded);
        shared = 1;
+       set_coding(coded);
        return 0;
 }
 
@@ -644,9 +648,10 @@ int FileXML::set_shared_output(XMLBuffer *xbuf)
        strcpy(this->filename, "");
        delete buffer;
        buffer = xbuf;
+       xbuf->share_lock->lock("FileXML::set_shared_output");
        xbuf->oseek(0);
-       set_coding(coded);
        shared = 1;
+       set_coding(coded);
        return 0;
 }
 
index 70950384a5bda391a42dc20f0ad1c25c64d3c4b3..602a411e625ffe47ca54b72b2a5fb77dfb30b918 100644 (file)
@@ -27,6 +27,7 @@
 #include <limits.h>
 
 #include "arraylist.h"
+#include "mutex.h"
 #include "keyframe.inc"
 #include "filexml.inc"
 #include "sizes.h"
@@ -38,9 +39,11 @@ class XMLBuffer
        long bsz, isz;
        unsigned char *inp, *outp, *bfr, *lmt;
        int destroy;
+       Mutex *share_lock;
 
        int demand(long len);
        friend class KeyFrame;
+       friend class FileXML;
 public:
        XMLBuffer(long buf_size=0x1000, long max_size=LONG_MAX, int del=1);
        XMLBuffer(long buf_size, char *buf, int del=0); // writing
index faad855bec12fe610f6985c996ad930ff105b2fb..8d6a57557f86f881e8bde110ed4de3d1adb3ef5a 100644 (file)
@@ -37,6 +37,12 @@ KeyFrame::KeyFrame()
        xbuf = new XMLBuffer();
 }
 
+KeyFrame::KeyFrame(const char *buf, long len)
+ : Auto()
+{
+       xbuf = new XMLBuffer(buf, len, 0);
+}
+
 KeyFrame::KeyFrame(EDL *edl, KeyFrames *autos)
  : Auto(edl, (Autos*)autos)
 {
index 89edf1f984af931cf86edbabdd4aa91b086056ac..d766b915078dd72a382990f9c82e246bda1da386 100644 (file)
@@ -37,6 +37,7 @@ class KeyFrame : public Auto
        KeyFrame &operator =(KeyFrame &k) { return k; } //illegal
 public:
        KeyFrame();
+       KeyFrame(const char *buf, long len);
        KeyFrame(EDL *edl, KeyFrames *autos);
        virtual ~KeyFrame();
 
index 606f83d95af6ce007aa7d685d5a6fbb730761af3..4e2c29847694cedc6f6bafb4350856669562a51f 100644 (file)
 #include "track.h"
 #include "transportque.inc"
 
-
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
 #include <ctype.h>
 #include <errno.h>
-#include <string.h>
-
-
-
 
 
 PluginClientThread::PluginClientThread(PluginClient *client)
@@ -551,38 +551,38 @@ void PluginClient::load_defaults_xml()
        using_defaults = 1;
 //printf("PluginClient::load_defaults_xml %d %s\n", __LINE__, path);
 
-       KeyFrame temp_keyframe;
-       FILE *fp = fopen(path, "r");
-       if( fp ) {
-               struct stat st;  int fd = fileno(fp);
-               int64_t sz = !fstat(fd, &st) ? st.st_size : BCTEXTLEN;
-               char *data = temp_keyframe.get_data(sz+1);
-               int data_size = fread(data, 1, sz, fp);
-               if( data_size < 0 ) data_size = 0;
-               if( data_size > 0 ) {
-                       data[data_size] = 0;
-                       temp_keyframe.xbuf->oseek(data_size);
+       char *data = 0;
+       int64_t len = -1;
+       struct stat st;
+       int fd = open(path, O_RDONLY);
+       if( fd >= 0 && !fstat(fd, &st) ) {
+               int64_t sz = st.st_size;
+               data = new char[sz+1];
+               len = read(fd, data, sz);
+               close(fd);
+       }
+       if( data && len >= 0 ) {
+               data[len] = 0;
 // Get window extents
-                       int i = 0;
-                       for( int state=0; i<(data_size-8) && state>=0; ++i ) {
-                               if( !data[i] || data[i] == '<' ) break;
-                               if( !isdigit(data[i]) ) continue;
-                               if( !state ) {
-                                       window_x = atoi(data + i);
-                                       state = 1;
-                               }
-                               else {
-                                       window_y = atoi(data + i);
-                                       state = -1;
-                               }
-                               while( i<data_size && isdigit(data[i]) ) ++i;
+               int i = 0;
+               for( int state=0; i<len && state>=0; ++i ) {
+                       if( !data[i] || data[i] == '<' ) break;
+                       if( !isdigit(data[i]) ) continue;
+                       if( !state ) {
+                               window_x = atoi(data+i);
+                               state = 1;
+                       }
+                       else {
+                               window_y = atoi(data+i);
+                               state = -1;
                        }
-                       temp_keyframe.xbuf->iseek(i);
-                       read_data(&temp_keyframe);
+                       while( i<len && isdigit(data[i]) ) ++i;
                }
-
-               fclose(fp);
+               KeyFrame keyframe(data+i, len-i);
+               read_data(&keyframe);
        }
+       delete [] data;
+
        using_defaults = 0;
 //printf("PluginClient::load_defaults_xml %d %s\n", __LINE__, path);
 }
index 131116b593c8c4b12f58a36baba5eb49a5c0af3d..ab04c5edb1d1bbc959791e1eaeec74459d4109ad 100644 (file)
@@ -177,7 +177,7 @@ PKG_3RD([esound],[no],
   [ . ])
 
 PKG_3RD([ffmpeg],[yes],
-  [ffmpeg-4.0],
+  [ffmpeg-4.1],
   [ libavutil/libavutil.a \
     libavcodec/libavcodec.a \
     libpostproc/libpostproc.a \
index cd1a4973cfd416a809af81987e5e0347697e388d..c81fbe506ddca9cfb25f73cee0f58ddbbc46459f 100644 (file)
@@ -45,7 +45,10 @@ all: $(TARGETS)
 dbtv:  dbtv.C
        $(CXX) $(CXXFLAGS) $(LDFLAGS) dbtv.C $(LDLIBS) -lX11 -o $@
 
-XTV_MEDIA_OBJS := $(TOPDIR)/cinelerra/$(OBJDIR)/mediadb.o $(TOPDIR)/cinelerra/$(OBJDIR)/filexml.o
+XTV_MEDIA_OBJS := $(TOPDIR)/cinelerra/$(OBJDIR)/mediadb.o
+XTV_MEDIA_OBJS += $(TOPDIR)/cinelerra/$(OBJDIR)/filexml.o
+XTV_MEDIA_OBJS += $(TOPDIR)/guicast/$(OBJDIR)/mutex.o
+XTV_MEDIA_OBJS += $(TOPDIR)/guicast/$(OBJDIR)/bctrace.o
 
 XTV_LIBS := $(TOPDIR)/libzmpeg3/$(OBJDIR)/libzmpeg3.a
 XTV_LIBS += $(TOPDIR)/db/$(OBJDIR)/db.a
index 7d10898dbb42ab8f4c030ad803e42d622ff69122..eec60962a303c8e210d26e6dd2e0303907876583 100644 (file)
@@ -27,7 +27,7 @@
        <tr>
                <td height="29" align="left"><b><font face="Liberation Serif" size=5>Main Menu</font></b></td>
                <td align="left"><font face="Liberation Serif" size=3>(- is a checkbox)</font></td>
-               <td align="left" sdnum="1033;0;MM/DD/YY"><font face="Liberation Serif" size=3>06/26/2018 update</font></td>
+               <td align="left" sdnum="1033;0;MM/DD/YY"><font face="Liberation Serif" size=3>11/08/2018 update</font></td>
                <td align="left"><font face="Liberation Serif"><br></font></td>
        </tr>
        <tr>
                <td align="left"><font face="Liberation Serif" size=4>Double click</font></td>
                <td align="left"><font face="Liberation Serif" size=4>On fade/speed, synch video/audio ganged</font></td>
        </tr>
+       <tr>
+               <td height="26" align="left"><font face="Liberation Serif" size=4><br></font></td>
+               <td align="left"><font face="Liberation Serif" size=4><br></font></td>
+               <td align="left"><font face="Liberation Serif" size=4>Double MMB</font></td>
+               <td align="left"><font face="Liberation Serif" size=4>On fade/speed, select keyframe position</font></td>
+       </tr>
        <tr>
                <td height="26" align="left"><font face="Liberation Serif" size=4><br></font></td>
                <td align="left"><font face="Liberation Serif" size=4>'U'</font></td>
                <td align="left"><font face="Liberation Serif" size=4><br></font></td>
                <td align="left"><font face="Liberation Serif" size=4><br></font></td>
        </tr>
+       <tr>
+               <td height="26" align="left"><b><font face="Liberation Serif" size=4> Patchbay</b></font></td>
+               <td align="left"><font face="Liberation Serif" size=4>Shift/Move</font></td>
+               <td align="left"><font face="Liberation Serif" size=4>Hold LMB</font></td>
+               <td align="left"><font face="Liberation Serif" size=4>On Fade slider bar, sets gain to 100% or 0db</font></td>
+       </tr>
+       <tr>
+               <td height="26" align="left"><font face="Liberation Serif" size=4><br></font></td>
+               <td align="left"><font face="Liberation Serif" size=4><br></font></td>
+               <td align="left"><font face="Liberation Serif" size=4><br></font></td>
+               <td align="left"><font face="Liberation Serif" size=4><br></font></td>
+       </tr>
        <tr>
                <td height="26" align="left"><b><font face="Liberation Serif" size=4>Compositor</font></b></td>
                <td align="left"><font face="Liberation Serif" size=4><br></font></td>
index dca6523b21e09cdf79dea8674a56ebb204d68c3b..27fcfaed52d37d73f12771ee853a73df3636fe2d 100644 (file)
@@ -320,7 +320,6 @@ tlut2
 ; new in 4.0
 #acontrast Contrast=33
 #afir
-aiir
 #convolve
 #deconvolve
 drmeter
@@ -334,3 +333,34 @@ normalize
 setrange
 #unpremultiply
 vfrdet
+; new in 4.1
+acue
+#adeclick
+#adeclip
+aderivative
+afftdn
+aintegral
+amplify
+chromahold
+cue
+deblock
+fttdnoiz
+#graphmonitor
+greyedge
+#highshelf
+#lowshelf
+lut1d
+pal75bars
+pal100bars
+setparams
+#sinc
+tmix
+vibrance
+; broken in 4.1
+#acrossover
+#aiir
+#amultiply
+#bm3d
+#sr
+#xstack
+#agraphmonitor
index 4c639fe27d25890f96bd4289cc77c21c651ca569..2fb69d1cb2ecd8010a8b634a07691e8f6d0409d5 100644 (file)
@@ -53,11 +53,8 @@ BC_PopupMenu::BC_PopupMenu(int x,
        highlighted = popup_down = 0;
        menu_popup = 0;
        icon = 0;
-       if(margin >= 0)
-               this->margin = margin;
-       else
-               this->margin = BC_WindowBase::get_resources()->popupmenu_margin;
-
+       this->margin = margin >= 0 ? margin :
+               BC_WindowBase::get_resources()->popupmenu_margin;
        this->use_title = use_title;
        strcpy(this->text, text);
        for(int i = 0; i < TOTAL_IMAGES; i++)
@@ -80,6 +77,7 @@ BC_PopupMenu::BC_PopupMenu(int x,
        highlighted = popup_down = 0;
        menu_popup = 0;
        icon = 0;
+       this->margin = BC_WindowBase::get_resources()->popupmenu_margin;
        this->use_title = use_title;
        strcpy(this->text, text);
        for(int i = 0; i < TOTAL_IMAGES; i++)
index a7c8f5f7570ff1ce45d55bb65aaf1fded2da3007..c404877cced5fbcf885be1096435d7df3a5a21b9 100644 (file)
@@ -2686,12 +2686,8 @@ int BC_TumbleTextBoxText::button_press_event()
 
 
 BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
-               int64_t default_value,
-               int64_t min,
-               int64_t max,
-               int x,
-               int y,
-               int text_w)
+               int64_t default_value, int64_t min, int64_t max,
+               int x, int y, int text_w)
 {
        reset();
        this->x = x;
@@ -2707,12 +2703,8 @@ BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
 }
 
 BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
-               int default_value,
-               int min,
-               int max,
-               int x,
-               int y,
-               int text_w)
+               int default_value, int min, int max,
+               int x, int y, int text_w)
 {
        reset();
        this->x = x;
@@ -2728,12 +2720,8 @@ BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
 }
 
 BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
-               float default_value_f,
-               float min_f,
-               float max_f,
-               int x,
-               int y,
-               int text_w)
+               float default_value_f, float min_f, float max_f,
+               int x, int y, int text_w, int precision)
 {
        reset();
        this->x = x;
@@ -2742,9 +2730,9 @@ BC_TumbleTextBox::BC_TumbleTextBox(BC_WindowBase *parent_window,
        this->max_f = max_f;
        this->default_value_f = default_value_f;
        this->text_w = text_w;
+       this->precision = precision;
        this->parent_window = parent_window;
        use_float = 1;
-       precision = 4;
        increment = 1;
 }
 
index ffc5c0e429c0ca29d4f4b0d4154b38317a2ca02e..70c99003bc11a9704fa6f325d81d1cc718e2c9eb 100644 (file)
@@ -403,26 +403,14 @@ class BC_TumbleTextBox
 {
 public:
        BC_TumbleTextBox(BC_WindowBase *parent_window,
-               int64_t default_value,
-               int64_t min,
-               int64_t max,
-               int x,
-               int y,
-               int text_w);
+               int64_t default_value, int64_t min, int64_t max,
+               int x, int y, int text_w);
        BC_TumbleTextBox(BC_WindowBase *parent_window,
-               int default_value,
-               int min,
-               int max,
-               int x,
-               int y,
-               int text_w);
+               int default_value, int min, int max,
+               int x, int y, int text_w);
        BC_TumbleTextBox(BC_WindowBase *parent_window,
-               float default_value,
-               float min,
-               float max,
-               int x,
-               int y,
-               int text_w);
+               float default_value, float min, float max,
+               int x, int y, int text_w, int precision=4);
        virtual ~BC_TumbleTextBox();
 
        int create_objects();
index ca73b39e61c696d37648cf3243243475ece56daa..84cb089608cf642ea9c2d19fdef65ff720f4ff6e 100644 (file)
@@ -310,6 +310,7 @@ public:
        int get_dragging();
        wchar_t* get_wkeystring(int *length = 0);
        int get_keypress();
+       int get_keysym() { return keysym; }
 #ifdef X_HAVE_UTF8_STRING
        char* get_keypress_utf8();
 #endif
diff --git a/cinelerra-5.1/guicast/test9.C b/cinelerra-5.1/guicast/test9.C
new file mode 100644 (file)
index 0000000..a777d7f
--- /dev/null
@@ -0,0 +1,117 @@
+#include "bcsignals.h"
+#include "guicast.h"
+#include "pys_icon_png.h"
+
+class TestList : public BC_ListBox
+{
+public:
+       TestList(int x, int y, int w, int h, 
+               ArrayList<BC_ListBoxItem*> *items);
+       int handle_event();
+       int selection_changed();
+};
+
+TestList::TestList(int x, int y, int w, int h, 
+               ArrayList<BC_ListBoxItem*> *items)
+ : BC_ListBox(x, y, w, h, LISTBOX_TEXT, items,
+       0, 0, 1, 0, 0, LISTBOX_SINGLE, ICON_LEFT, 0)
+{
+}
+
+int TestList::handle_event()
+{
+       printf("handle_event\n");
+       return 1;
+}
+
+int TestList::selection_changed()
+{
+       BC_ListBoxItem *item = get_selection(0, 0);
+       printf("selection_changed %s\n", !item ? "<nul>" : item->get_text());
+       return 1;
+}
+
+class TestWindow : public BC_Window
+{
+public:
+       TestWindow() : BC_Window("Test9", 0, 0, 320, 240) {};
+       void create_objects();
+       int keypress_event();
+       BC_ListBox *list;
+       ArrayList<BC_ListBoxItem*> items;
+};
+
+void TestWindow::create_objects()
+{
+       lock_window("AWindowRemovePluginGUI::create_objects");
+       set_color(BLACK);
+       set_font(LARGEFONT);
+       int x = 10, y = 20;
+       draw_text(x, y, "Hello world");
+       y += 25;
+       BC_Button *ok_button = new BC_OKButton(this);
+       add_subwindow(ok_button);
+       BC_Button *cancel_button = new BC_CancelButton(this);
+       add_subwindow(cancel_button);
+       BC_ListBoxItem *thing;
+       ArrayList<BC_ListBoxItem*> *sublist;
+       items.append(thing = new BC_ListBoxItem("thing 1"));
+       VFrame *pys_icon = new VFramePng(pys_icon_png);
+       thing->set_icon_vframe(pys_icon);
+       int pw = pys_icon->get_w(), ph = pys_icon->get_h();
+       BC_Pixmap *pys_picon = new BC_Pixmap(this, pw, ph);
+       pys_picon->draw_vframe(pys_icon, 0, 0, pw, pw, 0, 0);
+       thing->set_icon(pys_picon);
+       sublist = thing->new_sublist(1);
+       BC_ListBoxItem *fish, *cat, *hat;
+       sublist->append(fish = new BC_ListBoxItem("fish"));
+       ArrayList<BC_ListBoxItem*> *fish_list = fish->new_sublist(1);
+       fish_list->append(new BC_ListBoxItem("green"));
+       fish_list->append(new BC_ListBoxItem("eggs"));
+       fish_list->append(new BC_ListBoxItem("ham"));
+       sublist->append(cat = new BC_ListBoxItem("cat"));
+       ArrayList<BC_ListBoxItem*> *cat_list = cat->new_sublist(1);
+       cat_list->append(new BC_ListBoxItem("videos"));
+       sublist->append(hat = new BC_ListBoxItem("hat"));
+       ArrayList<BC_ListBoxItem*> *hat_list = hat->new_sublist(1);
+       hat_list->append(new BC_ListBoxItem("bonnet"));
+       hat_list->append(new BC_ListBoxItem("cap"));
+       hat_list->append(new BC_ListBoxItem("sombrero"));
+       items.append(thing = new BC_ListBoxItem("thing 2"));
+       int lw = get_w()-x-10, lh = ok_button->get_y() - y - 5;
+       add_subwindow(list = new TestList(x, y, lw, lh, &items));
+       show_window();
+       unlock_window();
+}
+
+int TestWindow::keypress_event()
+{
+       switch( get_keypress() ) {
+       case 'v':
+               switch( list->get_format() ) {
+               case LISTBOX_TEXT:
+                       list->update_format(LISTBOX_ICONS, 1);
+                       break;
+               case LISTBOX_ICONS:
+                       list->update_format(LISTBOX_ICONS_PACKED, 1);
+                       break;
+               case LISTBOX_ICONS_PACKED:
+                       list->update_format(LISTBOX_ICON_LIST, 1);
+                       break;
+               case LISTBOX_ICON_LIST:
+                       list->update_format(LISTBOX_TEXT, 1);
+                       break;
+               }
+               break;
+       }
+       return 1;
+}
+
+int main()
+{
+       new BC_Signals;
+       TestWindow window;
+       window.create_objects();
+       window.run_window();
+}
+
index 9d94dd2054c92dc6f774d52e34a261e6e28d2b2c..a5f8b45a56bba55f26c2cb06fa461ab94c0d6358 100644 (file)
@@ -92,6 +92,7 @@ video := \
        rumbler \
        scale \
        scaleratio \
+       sketcher \
        seltempavg \
        shapewipe \
        sharpen \
index 125de335d7d99f25897e627b0bb7ee31c7ad9566..7833156e57de64056e053514729e13a5a7912f99 100644 (file)
@@ -117,6 +117,7 @@ DIRS = $(OPENCV_OBJS) \
        rotate \
        rumbler \
        scale \
+       sketcher \
        shapewipe \
        sharpen \
        shiftinterlace \
index 2f22fed98adf620db3442ef8221a76c8e42b2bb9..46d2402f2f58e0f630328bad62876d2b3a3b9083 100644 (file)
@@ -334,11 +334,12 @@ void CriKey::read_data(KeyFrame *keyframe)
 void CriKey::update_gui()
 {
        if( !thread ) return;
-       if( !load_configuration() ) return;
        thread->window->lock_window("CriKey::update_gui");
        CriKeyWindow *window = (CriKeyWindow*)thread->window;
-       window->update_gui();
-       window->flush();
+       if( load_configuration() ) {
+               window->update_gui();
+               window->flush();
+       }
        thread->window->unlock_window();
 }
 
index eaaa0b5ca3c01c33269688d22ee172c289fc7ff8..8d6d8644a86924179fffe5915cc7514212d0714f 100644 (file)
@@ -129,6 +129,8 @@ CriKeyWindow::CriKeyWindow(CriKey *plugin)
 
 CriKeyWindow::~CriKeyWindow()
 {
+       delete point_x;
+       delete point_y;
 }
 
 void CriKeyWindow::create_objects()
@@ -192,21 +194,23 @@ void CriKeyWindow::send_configure_change()
        pending_config = 0;
        plugin->send_configure_change();
 }
-int CriKeyWindow::check_configure_change(int ret)
+
+int CriKeyWindow::grab_event(XEvent *event)
 {
+       int ret = do_grab_event(event);
        if( pending_config && !grab_event_count() )
                send_configure_change();
        return ret;
 }
 
-int CriKeyWindow::grab_event(XEvent *event)
+int CriKeyWindow::do_grab_event(XEvent *event)
 {
        switch( event->type ) {
        case ButtonPress: break;
        case ButtonRelease: break;
        case MotionNotify: break;
        default:
-               return check_configure_change(0);
+               return 0;
        }
 
        MWindow *mwindow = plugin->server->mwindow;
@@ -219,25 +223,25 @@ int CriKeyWindow::grab_event(XEvent *event)
        if( !dragging ) {
                if( cx < 0 || cx >= mwindow->theme->ccanvas_w ||
                    cy < 0 || cy >= mwindow->theme->ccanvas_h )
-                       return check_configure_change(0);
+                       return 0;
        }
 
        switch( event->type ) {
        case ButtonPress:
-               if( dragging ) return check_configure_change(0);
+               if( dragging ) return 0;
                if( event->xbutton.button == WHEEL_UP )  return threshold->wheel_event(1);
                if( event->xbutton.button == WHEEL_DOWN ) return threshold->wheel_event(-1);
                dragging = event->xbutton.state & ShiftMask ? -1 : 1;
                break;
        case ButtonRelease:
-               if( !dragging ) return check_configure_change(0);
+               if( !dragging ) return 0;
                dragging = 0;
                return 1;
        case MotionNotify:
-               if( !dragging ) return check_configure_change(0);
+               if( !dragging ) return 0;
                break;
        default:
-               return check_configure_change(0);
+               return 0;
        }
 
        float cursor_x = cx, cursor_y = cy;
@@ -288,6 +292,7 @@ int CriKeyWindow::grab_event(XEvent *event)
                        if( hot_point >= 0 && sz > 0 ) {
                                CriKeyPoint *pt = points[hot_point];
                                point_list->set_point(hot_point, PT_X, pt->x = output_x);
+                               point_list->set_point(hot_point, PT_Y, pt->y = output_y);
                                for( int i=0; i<sz; ++i ) {
                                        pt = points[i];
                                        pt->e = i==hot_point ? !pt->e : 0;
@@ -332,10 +337,7 @@ int CriKeyWindow::grab_event(XEvent *event)
        }
 
        last_x = output_x;  last_y = output_y;
-       if( !grab_event_count() ) 
-               send_configure_change();
-       else
-               pending_config = 1;
+       pending_config = 1;
        return 1;
 }
 
index 7338d2dd03f8b1996777e43df0ddf2e155f4b44a..3e897f80e0b22fd71ac23d6fe582773ecd0baff8 100644 (file)
@@ -203,8 +203,8 @@ public:
        void update_gui();
        void start_color_thread();
        int grab_event(XEvent *event);
+       int do_grab_event(XEvent *event);
        void done_event(int result);
-       int check_configure_change(int ret);
        void send_configure_change();
 
        CriKey *plugin;
diff --git a/cinelerra-5.1/plugins/sketcher/Makefile b/cinelerra-5.1/plugins/sketcher/Makefile
new file mode 100644 (file)
index 0000000..a53e62f
--- /dev/null
@@ -0,0 +1,13 @@
+include ../../plugin_defs
+
+OBJS := \
+       $(OBJDIR)/sketcher.o \
+       $(OBJDIR)/sketcherwindow.o
+
+PLUGIN = sketcher
+
+include ../../plugin_config
+
+$(OBJDIR)/sketcher.o: sketcher.C
+$(OBJDIR)/sketcherwindow.o: sketcherwindow.C
+
diff --git a/cinelerra-5.1/plugins/sketcher/sketcher.C b/cinelerra-5.1/plugins/sketcher/sketcher.C
new file mode 100644 (file)
index 0000000..b9d729c
--- /dev/null
@@ -0,0 +1,652 @@
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2015 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include<stdio.h>
+#include<stdint.h>
+#include<math.h>
+#include<string.h>
+
+#include "arraylist.h"
+#include "bccmodels.h"
+#include "bccolors.h"
+#include "clip.h"
+#include "edlsession.h"
+#include "filexml.h"
+#include "sketcher.h"
+#include "sketcherwindow.h"
+#include "language.h"
+#include "vframe.h"
+
+void SketcherPoint::init(int id, int x, int y)
+{
+       this->id = id;
+       this->x = x;    this->y = y;
+}
+SketcherPoint::SketcherPoint(int id)
+{
+       init(id, 0, 0);
+}
+SketcherPoint::SketcherPoint(int id, int x, int y)
+{
+       init(id, x, y);
+}
+SketcherPoint::~SketcherPoint()
+{
+}
+SketcherPoint::SketcherPoint(SketcherPoint &pt)
+{
+       copy_from(pt);
+}
+int SketcherPoint::equivalent(SketcherPoint &that)
+{
+       return this->id == that.id &&
+               this->x == that.x &&
+               this->y == that.y ? 1 : 0;
+}
+void SketcherPoint::copy_from(SketcherPoint &that)
+{
+       this->id = that.id;
+       this->x = that.x;    this->y = that.y;
+}
+void SketcherPoint::save_data(FileXML &output)
+{
+       char point[BCSTRLEN];
+       sprintf(point,"/POINT_%d",id);
+       output.tag.set_title(point+1);
+       output.tag.set_property("X", x);
+       output.tag.set_property("Y", y);
+       output.append_tag();
+       output.tag.set_title(point+0);
+       output.append_tag();
+       output.append_newline();
+}
+void SketcherPoint::read_data(FileXML &input)
+{
+       id = atoi(input.tag.get_title() + 6);
+       x = input.tag.get_property("X", 0.f);
+       y = input.tag.get_property("Y", 0.f);
+}
+
+void SketcherCurve::init(int id, int ty, int radius, int pen, int color)
+{
+       this->id = id;
+       this->ty = ty;
+       this->radius = radius;
+       this->pen = pen;
+       this->color = color;
+}
+SketcherCurve::SketcherCurve(int id)
+{
+       init(id, 0, 1, 0, BLACK);
+}
+SketcherCurve::SketcherCurve(int id, int ty,  int radius,int pen, int color)
+{
+       init(id, ty, radius, pen, color);
+}
+SketcherCurve::~SketcherCurve()
+{
+}
+SketcherCurve::SketcherCurve(SketcherCurve &cv)
+{
+       copy_from(cv);
+}
+int SketcherCurve::equivalent(SketcherCurve &that)
+{
+       if( this->id != that.id ) return 0;
+       if( this->ty != that.ty ) return 0;
+       if( this->radius != that.radius ) return 0;
+       if( this->pen != that.pen ) return 0;
+       if( this->color != that.color ) return 0;
+       int n = this->points.size();
+       if( n != that.points.size() ) return 0;
+       for( int i=0; i<n; ++i ) {
+               if( !points[i]->equivalent(*that.points[i]) ) return 0;
+       }
+       return 1;
+}
+void SketcherCurve::copy_from(SketcherCurve &that)
+{
+       this->id = that.id;
+       this->ty = that.ty;
+       this->radius = that.radius;
+       this->pen = that.pen;
+       this->color = that.color;
+       int m = points.size(), n = that.points.size();
+       while( m > n ) points.remove_object_number(--m);
+       while( m < n ) { points.append(new SketcherPoint());  ++m; }
+       for( int i=0; i<n; ++i ) points[i]->copy_from(*that.points[i]);
+}
+void SketcherCurve::save_data(FileXML &output)
+{
+       this->ty = ty;
+       this->pen = pen;  this->color = color;
+       char curve[BCSTRLEN];
+       sprintf(curve,"/CURVE_%d",id);
+       output.tag.set_title(curve+1);
+       output.tag.set_property("TYPE", ty);
+       output.tag.set_property("RADIUS", radius);
+       output.tag.set_property("PEN", pen);
+       output.tag.set_property("COLOR", color);
+       output.append_tag();
+       output.append_newline();
+       for( int i=0,n=points.size(); i<n; ++i )
+               points[i]->save_data(output);
+       output.tag.set_title(curve+0);
+       output.append_tag();
+       output.append_newline();
+}
+void SketcherCurve::read_data(FileXML &input)
+{
+       id = atoi(input.tag.get_title() + 6);
+       ty = input.tag.get_property("TYPE", 0);
+       radius = input.tag.get_property("RADIUS", 1.);
+       pen = input.tag.get_property("PEN", 0);
+       color = input.tag.get_property("COLOR", BLACK);
+}
+
+int Sketcher::new_curve(int ty, int radius, int pen, int color)
+{
+       SketcherCurves &curves = config.curves;
+       int k = curves.size(), id = 1;
+       for( int i=k; --i>=0; ) {
+               int n = config.curves[i]->id;
+               if( n >= id ) id = n + 1;
+       }
+       SketcherCurve *cv = new SketcherCurve(id, ty, radius, pen, color);
+       curves.append(cv);
+       config.cv_selected = k;
+       return k;
+}
+
+int Sketcher::new_curve()
+{
+       return new_curve(0, 1, 0, BLACK);
+}
+
+int Sketcher::new_point(SketcherCurve *cv, int x, int y)
+{
+       int k = cv->points.size(), id = 1;
+       for( int i=k; --i>=0; ) {
+               int n = cv->points[i]->id;
+               if( n >= id ) id = n + 1;
+       }
+       SketcherPoint *pt = new SketcherPoint(id, x, y);
+       cv->points.append(pt);
+       return k;
+}
+
+int Sketcher::new_point()
+{
+       int ci = config.cv_selected;
+       if( ci < 0 || ci >= config.curves.size() )
+               return -1;
+       SketcherCurve *cv = config.curves[ci];
+       EDLSession *session = get_edlsession();
+       int x = !session ? 0.f : session->output_w / 2.f;
+       int y = !session ? 0.f : session->output_h / 2.f;
+       return new_point(cv, x, y);
+}
+
+REGISTER_PLUGIN(Sketcher)
+
+SketcherConfig::SketcherConfig()
+{
+       drag = 1;
+       cv_selected = 0;
+       pt_selected = 0;
+}
+SketcherConfig::~SketcherConfig()
+{
+}
+
+int SketcherConfig::equivalent(SketcherConfig &that)
+{
+       if( this->drag != that.drag ) return 0;
+       if( this->cv_selected != that.cv_selected ) return 0;
+       if( this->pt_selected != that.pt_selected ) return 0;
+       if( this->curves.size() != that.curves.size() ) return 0;
+       for( int i=0, n=curves.size(); i<n; ++i ) {
+               if( !curves[i]->equivalent(*that.curves[i]) ) return 0;
+       }
+       return 1;
+}
+
+void SketcherConfig::copy_from(SketcherConfig &that)
+{
+       this->drag = that.drag;
+       this->cv_selected = that.cv_selected;
+       this->pt_selected = that.pt_selected;
+       int m = curves.size(), n = that.curves.size();
+       while( m > n ) curves.remove_object_number(--m);
+       while( m < n ) { curves.append(new SketcherCurve());  ++m; }
+       for( int i=0; i<n; ++i ) curves[i]->copy_from(*that.curves[i]);
+}
+
+void SketcherConfig::interpolate(SketcherConfig &prev, SketcherConfig &next,
+               long prev_frame, long next_frame, long current_frame)
+{
+       this->cv_selected = prev.cv_selected;
+       this->pt_selected = prev.pt_selected;
+       this->drag = prev.drag;
+
+       double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+       double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
+
+       curves.remove_all_objects();
+       int prev_cv_sz = prev.curves.size();
+       int next_cv_sz = next.curves.size();
+       for( int i=0; i<prev_cv_sz; ++i ) {
+               SketcherCurve *pcv = prev.curves[i], *ncv = 0;
+               SketcherCurve *cv = curves.append(new SketcherCurve());
+               int k = next_cv_sz;  // associated by id in next
+               while( --k >= 0 && pcv->id != (ncv=next.curves[k])->id );
+               if( k >= 0 ) {
+                       cv->id = pcv->id;
+                       cv->ty = pcv->ty;
+                       cv->radius = pcv->radius;
+                       cv->pen = pcv->pen;
+                       cv->color = pcv->color;
+                       int prev_pt_sz = pcv->points.size(), next_pt_sz = ncv->points.size();
+                       for( int j=0; j<prev_pt_sz; ++j ) {
+                               SketcherPoint &pt = *pcv->points[j], *nt = 0;
+                               k = next_pt_sz;  // associated by id in next
+                               while( --k >= 0 && pt.id != (nt=ncv->points[k])->id );
+                               int x = pt.x, y = pt.y;
+                               if( k >= 0 ) {
+                                       x = x * prev_scale + nt->x * next_scale;
+                                       y = y * prev_scale + nt->y * next_scale;
+                               }
+                               cv->points.append(new SketcherPoint(pt.id, x, y));
+                       }
+               }
+               else
+                       cv->copy_from(*pcv);
+       }
+}
+
+void SketcherConfig::limits()
+{
+}
+
+
+Sketcher::Sketcher(PluginServer *server)
+ : PluginVClient(server)
+{
+}
+
+Sketcher::~Sketcher()
+{
+}
+
+const char* Sketcher::plugin_title() { return N_("Sketcher"); }
+int Sketcher::is_realtime() { return 1; }
+
+NEW_WINDOW_MACRO(Sketcher, SketcherWindow);
+LOAD_CONFIGURATION_MACRO(Sketcher, SketcherConfig)
+
+void Sketcher::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+// cause data to be stored directly in text
+       output.set_shared_output(keyframe->xbuf);
+
+       output.tag.set_title("SKETCHER");
+       output.tag.set_property("DRAG", config.drag);
+       output.tag.set_property("CURVE_SELECTED", config.cv_selected);
+       output.tag.set_property("POINT_SELECTED", config.pt_selected);
+       output.append_tag();
+       output.append_newline();
+       for( int i=0,n=config.curves.size(); i<n; ++i ) {
+               config.curves[i]->save_data(output);
+       }
+       output.tag.set_title("/SKETCHER");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+}
+
+void Sketcher::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+       input.set_shared_input(keyframe->xbuf);
+       config.curves.remove_all_objects();
+       int result = 0;
+       SketcherCurve *cv = 0;
+
+       while( !(result=input.read_tag()) ) {
+               if( input.tag.title_is("SKETCHER") ) {
+                       config.drag = input.tag.get_property("DRAG", config.drag);
+                       config.cv_selected = input.tag.get_property("CV_SELECTED", 0);
+                       config.pt_selected = input.tag.get_property("PT_SELECTED", 0);
+               }
+               else if( !strncmp(input.tag.get_title(),"CURVE_",6) ) {
+                       cv = new SketcherCurve();
+                       cv->read_data(input);
+                       config.curves.append(cv);
+               }
+               else if( !strncmp(input.tag.get_title(),"/CURVE_",7) )
+                       cv = 0;
+               else if( !strncmp(input.tag.get_title(),"POINT_",6) ) {
+                       if( cv ) {
+                               SketcherPoint *pt = new SketcherPoint();
+                               pt->read_data(input);
+                               cv->points.append(pt);
+                       }
+                       else
+                               printf("Sketcher::read_data: no curve for point\n");
+               }
+       }
+
+       if( !config.curves.size() ) {
+               new_curve(0, 1, 0, BLACK);
+       }
+       config.limits();
+}
+
+void Sketcher::update_gui()
+{
+       if( !thread ) return;
+       thread->window->lock_window("Sketcher::update_gui");
+       if( load_configuration() ) {
+               SketcherWindow *window = (SketcherWindow*)thread->window;
+               window->update_gui();
+               window->flush();
+       }
+       thread->window->unlock_window();
+}
+
+void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color, int d)
+{
+       int r = d/2+1, x = pt->x, y = pt->y;
+       vfrm->draw_smooth(x-r,y+0, x-r, y-r, x+0,y-r);
+       vfrm->draw_smooth(x+0,y-r, x+r, y-r, x+r,y+0);
+       vfrm->draw_smooth(x+r,y+0, x+r, y+r, x+0,y+r);
+       vfrm->draw_smooth(x+0,y+r, x-r, y+r, x-r,y+0);
+       vfrm->set_pixel_color(color);
+       vfrm->draw_x(pt->x, pt->y, d);
+}
+void Sketcher::draw_point(VFrame *vfrm, SketcherPoint *pt, int color)
+{
+       draw_point(vfrm, pt, color, bmax(w,h)/200 + 2);
+}
+
+
+int SketcherPenSquare::draw_pixel(int x, int y)
+{
+       vfrm->draw_line(x-n, y, x+n, y);
+       for( int i=-n; i<n; ++i )
+               vfrm->draw_line(x-n, y+i, x+n, y+i);
+       return 0;
+}
+int SketcherPenPlus::draw_pixel(int x, int y)
+{
+       if( n > 1 ) {
+               vfrm->draw_line(x-n, y, x+n, y);
+               vfrm->draw_line(x, y-n, x, y+n);
+       }
+       else
+               vfrm->draw_pixel(x, y);
+       return 0;
+}
+int SketcherPenSlant::draw_pixel(int x, int y)
+{
+       vfrm->draw_line(x-n,   y+n,   x+n,   y-n);
+       vfrm->draw_line(x-n+1, y+n,   x+n+1, y-n);
+       vfrm->draw_line(x-n,   y+n+1, x+n,   y-n+1);
+       return 0;
+}
+int SketcherPenXlant::draw_pixel(int x, int y)
+{
+       vfrm->draw_line(x-n,   y+n,   x+n,   y-n);
+       vfrm->draw_line(x-n+1, y+n,   x+n+1, y-n);
+       vfrm->draw_line(x-n,   y+n+1, x+n,   y-n+1);
+       vfrm->draw_line(x-n,   y-n,   x+n,   y+n);
+       vfrm->draw_line(x-n+1, y-n,   x+n+1, y+n);
+       vfrm->draw_line(x-n,   y-n+1, x+n,   y-n+1);
+       return 0;
+}
+
+
+VFrame *SketcherCurve::new_vpen(VFrame *out)
+{
+       switch( pen ) {
+       case PEN_SQUARE: return new SketcherPenSquare(out, radius);
+       case PEN_PLUS:   return new SketcherPenPlus(out, radius);
+       case PEN_SLANT:  return new SketcherPenSlant(out, radius);
+       case PEN_XLANT:  return new SketcherPenXlant(out, radius);
+       }
+       return 0;
+}
+
+void SketcherCurve::draw_line(VFrame *out)
+{
+       SketcherPoint *pt0 = points[0];
+       VFrame *vpen = new_vpen(out);
+       out->set_pixel_color(color);
+       int n = points.size();
+       if( n >= 2 ) {
+               for( int pi=1; pi<n; ++pi ) {
+                       SketcherPoint *pt1 = points[pi];
+                       vpen->draw_line(pt0->x, pt0->y, pt1->x, pt1->y);
+                       pt0 = pt1;
+               }
+       }
+       else
+               vpen->draw_pixel(pt0->x, pt0->y);
+       delete vpen;
+}
+
+/*
+# python
+from sympy import *
+var("x,y, ax,ay, bx,by, cx,cy, dx,dy")
+
+var("abdx,abdy, acdx,acdy, bddx,bddy, cddx,cddy");
+abdx = bx-ax;  abdy = by-ay;
+acdx = cx-ax;  acdy = cy-ay;
+bddx = dx-bx;  bddy = dy-by;
+cddx = dx-cx;  cddy = dy-cy;
+
+var("xc,yc, xd,yd, sx,sy");
+xc = (bx+dx)/2;  yc = (by+dy)/2;
+xd = cx-xc;      yd = cy-yc;
+ax = xc-xd;      ay = yc-yd;
+# line thru b with slope (c-a) intersects line thru c with slope (d-b)
+sx = solve(((x - bx) * acdy/acdx + by) - ((x - cx) * bddy/bddx + cy),x)
+sy = solve(((y - by) * acdx/acdy + bx) - ((y - cy) * bddx/bddy + cx),y)
+
+var("zx,zy, zdx,zdy, sx,sy, px,py, qx,qy");
+# point z = (b+c)/2
+zx = (bx+cx)/2;  zy = (by+cy)/2;
+zdx = (abdx+cddx)/2;  zdy = (abdy+cddy)/2;
+# line thru z with slope (d-a) intersects line thru b with slope (c-a)
+px = solve(((x-zx)*zdy/zdx + zy) - ((x-bx) * acdy/acdx + by),x);
+py = solve(((y-zy)*zdx/zdy + zx) - ((y-by) * acdx/acdy + bx),y);
+# line thru z with slope (c-a + d-b)/2 intersects line thru c with slope (d-b)
+qx = solve(((x-zx)*zdy/zdx + zy) - ((x-cx) * bddy/bddx + cy),x);
+qy = solve(((y-zy)*zdx/zdy + zx) - ((y-cy) * bddx/bddy + cx),y);
+*/
+
+static void smooth_sxy(
+       float ax, float ay, float bx, float by,
+       float cx, float cy, float dx, float dy,
+       float &sx, float &sy)
+{
+       float acdx = cx-ax, acdy = cy-ay;
+       float bddx = dx-bx, bddy = dy-by;
+       float d = acdx*bddy - acdy*bddx;
+       if( fabsf(d) < 1 ) d = 1;
+       sx = (acdx*bddx*by - acdx*bddx*cy + acdx*bddy*cx - acdy*bddx*bx) / d;
+       sy = (acdx*bddy*by - acdy*bddx*cy - acdy*bddy*bx + acdy*bddy*cx) / d;
+       bclamp(sx, -32767.f, 32767.f);
+       bclamp(sy, -32767.f, 32767.f);
+}
+
+static void smooth_pxy(
+       float ax, float ay, float bx, float by,
+       float cx, float cy, float dx, float dy,
+       float &px, float &py)
+{
+       float abdx = bx - ax, abdy = by - ay;
+       float acdx = cx - ax, acdy = cy - ay;
+       float cddx = dx - cx, cddy = dy - cy;
+       float d = (2*(abdx*acdy - abdy*acdx - acdx*cddy + acdy*cddx));
+       if( fabsf(d) < 1 ) d = 1;
+       px = (-abdx*acdx*by + abdx*acdx*cy + 2*abdx*acdy*bx - abdy*acdx*bx - abdy*acdx*cx -
+               acdx*bx*cddy - acdx*by*cddx + acdx*cddx*cy - acdx*cddy*cx + 2*acdy*bx*cddx) / d;
+       py = (abdx*acdy*by + abdx*acdy*cy - 2*abdy*acdx*by + abdy*acdy*bx - abdy*acdy*cx -
+                2*acdx*by*cddy + acdy*bx*cddy + acdy*by*cddx + acdy*cddx*cy - acdy*cddy*cx) / d;
+       bclamp(px, -32767.f, 32767.f);
+       bclamp(py, -32767.f, 32767.f);
+}
+static void smooth_qxy(
+       float ax, float ay, float bx, float by,
+       float cx, float cy, float dx, float dy,
+       float &qx, float &qy)
+{
+       float abdx = bx - ax, abdy = by - ay;
+       float bddx = dx - bx, bddy = dy - by;
+       float cddx = dx - cx, cddy = dy - cy;
+       float d = (2*(abdx*bddy - abdy*bddx - bddx*cddy + bddy*cddx));
+       if( fabsf(d) < 1 ) d = 1;
+       qx = (abdx*bddx*by - abdx*bddx*cy + 2*abdx*bddy*cx - abdy*bddx*bx - abdy*bddx*cx -
+               bddx*bx*cddy + bddx*by*cddx - bddx*cddx*cy - bddx*cddy*cx + 2*bddy*cddx*cx) / d;
+       qy = (abdx*bddy*by + abdx*bddy*cy - 2*abdy*bddx*cy - abdy*bddy*bx + abdy*bddy*cx -
+               2*bddx*cddy*cy - bddy*bx*cddy + bddy*by*cddx + bddy*cddx*cy + bddy*cddy*cx) / d;
+       bclamp(qx, -32767.f, 32767.f);
+       bclamp(qy, -32767.f, 32767.f);
+}
+
+
+static int convex(float ax,float ay, float bx,float by,
+                 float cx,float cy, float dx,float dy)
+{
+       float abdx = bx - ax, abdy = by - ay;
+       float acdx = cx - ax, acdy = cy - ay;
+       float bcdx = cx - bx, bcdy = cy - by;
+       float bddx = dx - bx, bddy = dy - by;
+       float abc = abdx*acdy - abdy*acdx;
+       float bcd = bcdx*bddy - bcdy*bddx;
+       float v = abc * bcd;
+       return !v ? 0 : v>0 ? 1 : -1;
+}
+
+void SketcherCurve::draw_smooth(VFrame *out)
+{
+       VFrame *vpen = new_vpen(out);
+       out->set_pixel_color(color);
+       int n = points.size();
+       if( !n ) return;
+       if( n > 2 ) {
+               SketcherPoint *pt0 = points[0], *pt1 = points[1], *pt2 = points[2];
+               float bx = pt0->x, by = pt0->y;
+               float cx = pt1->x, cy = pt1->y;
+               float dx = pt2->x, dy = pt2->y;
+               float xc = (bx+dx)/2.f, yc = (by+dy)/2.f;
+               float xd = cx - xc, yd = cy - yc;
+               float ax = xc - xd, ay = yc - yd;
+               float sx, sy;
+               for( int pi=0,n2=n-2; pi<n2; ++pi ) {
+                       float dx = points[pi+2]->x, dy = points[pi+2]->y;
+                       if( convex(ax,ay, bx,by, cx,cy, dx,dy) >= 0 ) {
+                               smooth_sxy(ax,ay, bx,by, cx,cy, dx,dy, sx, sy);
+                               vpen->draw_smooth(bx,by, sx,sy, cx,cy);
+                       }
+                       else {
+                               float zx = (bx+cx)/2.f, zy = (by+cy)/2.f;
+                               smooth_pxy(ax,ay, bx,by, cx,cy, dx,dy, sx,sy);
+                               vpen->draw_smooth(bx,by, sx,sy, zx,zy);
+                               smooth_qxy(ax,ay, bx,by, cx,cy, dx,dy, sx,sy);
+                               vpen->draw_smooth(zx,zy, sx,sy, cx,cy);
+                       }
+                       ax = bx;  ay = by;
+                       bx = cx;  by = cy;
+                       cx = dx;  cy = dy;
+               }
+               xc = (ax+cx)/2.f; yc = (ay+cy)/2.f;
+               xd = bx - xc, yd = by - yc;
+               dx = xc - xd, dy = yc - yd;
+               smooth_sxy(ax, ay, bx, by, cx, cy, dx, dy, sx, sy);
+               vpen->draw_smooth(bx, by, sx, sy, cx, cy);
+       }
+       else if( n == 2 ) {
+               SketcherPoint *pt0 = points[0], *pt1 = points[1];
+               vpen->draw_line(pt0->x, pt0->y, pt1->x, pt1->y);
+       }
+       else if( n > 0 ) {
+               SketcherPoint *pt0 = points[0];
+               vpen->draw_pixel(pt0->x, pt0->y);
+       }
+       delete vpen;
+}
+
+int Sketcher::process_realtime(VFrame *input, VFrame *output)
+{
+       this->input = input;  this->output = output;
+       w = output->get_w();  h = output->get_h();
+       if( input != output ) output->transfer_from(input);
+
+       load_configuration();
+
+       for( int ci=0, n=config.curves.size(); ci<n; ++ci ) {
+               SketcherCurve *cv = config.curves[ci];
+               int m = cv->points.size();
+               if( !m ) continue;
+               switch( cv->ty ) {
+               case TYP_OFF:
+                       break;
+               case TYP_SKETCHER:
+                       cv->draw_line(output);
+                       break;
+               case TYP_SMOOTH:
+                       cv->draw_smooth(output);
+                       break;
+               }
+       }
+
+       if( config.drag ) {
+               for( int ci=0, n=config.curves.size(); ci<n; ++ci ) {
+                       SketcherCurve *cv = config.curves[ci];
+                       for( int pi=0,m=cv->points.size(); pi<m; ++pi )
+                               draw_point(output, cv->points[pi], cv->color);
+               }
+       }
+
+       return 0;
+}
+
+void SketcherCurves::dump()
+{
+       for( int i=0; i<size(); ++i ) {
+               SketcherCurve *cv = get(i);
+               printf("Curve %d, id=%d, ty=%s, r=%d, pen=%s, color=%02x%02x%02x\n",
+                       i, cv->id, cv_type[cv->ty], cv->radius, cv_pen[cv->pen],
+                       (cv->color>>16)&0xff, (cv->color>>8)&0xff, (cv->color>>0)&0xff);
+               cv->points.dump();
+       }
+}
+void SketcherPoints::dump()
+{
+       for( int i=0; i<size(); ++i ) {
+               SketcherPoint *pt = get(i);
+               printf("  Pt %d, id=%d, x=%d, y=%d\n", i, pt->id, pt->x, pt->y);
+       }
+}
+
diff --git a/cinelerra-5.1/plugins/sketcher/sketcher.h b/cinelerra-5.1/plugins/sketcher/sketcher.h
new file mode 100644 (file)
index 0000000..d0429cb
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+
+#ifndef __SKETCHERS_H__
+#define __SKETCHERS_H__
+
+#include "pluginvclient.h"
+
+class Sketcher;
+
+enum { PT_ID, PT_X, PT_Y, PT_SZ };
+enum { CV_ID, CV_TY, CV_RAD, CV_PEN, CV_CLR, CV_SZ };
+enum { TYP_OFF, TYP_SKETCHER, TYP_SMOOTH, TYP_SZ };
+enum { PEN_SQUARE, PEN_PLUS, PEN_SLANT, PEN_XLANT, PEN_SZ };
+
+class SketcherVPen : public VFrame
+{
+public:
+       SketcherVPen(VFrame *vfrm, int n)
+        : VFrame(vfrm->get_data(), -1, vfrm->get_y()-vfrm->get_data(),
+           vfrm->get_u()-vfrm->get_data(), vfrm->get_v()-vfrm->get_data(),
+           vfrm->get_w(), vfrm->get_h(), vfrm->get_color_model(),
+           vfrm->get_bytes_per_line()) {
+               this->vfrm = vfrm;  this->n = n;
+       }
+       virtual int draw_pixel(int x, int y) = 0;
+       VFrame *vfrm;
+       int n;
+};
+
+class SketcherPenSquare : public SketcherVPen
+{
+public:
+       SketcherPenSquare(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {}
+       int draw_pixel(int x, int y);
+};
+class SketcherPenPlus : public SketcherVPen
+{
+public:
+       SketcherPenPlus(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {}
+       int draw_pixel(int x, int y);
+};
+class SketcherPenSlant : public SketcherVPen
+{
+public:
+       SketcherPenSlant(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {}
+       int draw_pixel(int x, int y);
+};
+class SketcherPenXlant : public SketcherVPen
+{
+public:
+       SketcherPenXlant(VFrame *vfrm, int n) : SketcherVPen(vfrm, n) {}
+       int draw_pixel(int x, int y);
+};
+
+
+class SketcherPoint
+{
+public:
+       int id;
+       int x, y;
+
+       void init(int id, int x, int y);
+       SketcherPoint(int id, int x, int y);
+       SketcherPoint(int id=-1);
+       SketcherPoint(SketcherPoint &pt);
+       ~SketcherPoint();
+       int equivalent(SketcherPoint &that);
+       void copy_from(SketcherPoint &that);
+       void save_data(FileXML &output);
+       void read_data(FileXML &input);
+};
+class SketcherPoints : public ArrayList<SketcherPoint *>
+{
+public:
+       SketcherPoints() {}
+       ~SketcherPoints() { remove_all_objects(); }
+       void dump();
+};
+
+#define cv_type SketcherCurve::types
+#define cv_pen SketcherCurve::pens
+
+class SketcherCurve
+{
+public:
+       int id, ty, radius, pen, color;
+       static const char *types[TYP_SZ];
+       static const char *pens[PEN_SZ];
+
+       SketcherPoints points;
+
+       void init(int id, int ty, int radius, int pen, int color);
+       SketcherCurve(int id, int ty, int radius, int pen, int color);
+       SketcherCurve(int id=-1);
+       ~SketcherCurve();
+       SketcherCurve(SketcherCurve &cv);
+       int equivalent(SketcherCurve &that);
+       void copy_from(SketcherCurve &that);
+       void save_data(FileXML &output);
+       void read_data(FileXML &input);
+       VFrame *new_vpen(VFrame *out);
+       void draw_line(VFrame *out);
+       void draw_smooth(VFrame *out);
+};
+class SketcherCurves : public ArrayList<SketcherCurve *>
+{
+public:
+       SketcherCurves() {}
+       ~SketcherCurves() { remove_all_objects(); }
+       void dump();
+};
+
+class SketcherConfig
+{
+public:
+       SketcherConfig();
+       ~SketcherConfig();
+
+       SketcherCurves curves;
+       int equivalent(SketcherConfig &that);
+       void copy_from(SketcherConfig &that);
+       void interpolate(SketcherConfig &prev, SketcherConfig &next,
+               long prev_frame, long next_frame, long current_frame);
+       void limits();
+
+       int drag;
+       int cv_selected, pt_selected;
+};
+
+class Sketcher : public PluginVClient
+{
+public:
+       Sketcher(PluginServer *server);
+       ~Sketcher();
+// required for all realtime plugins
+       PLUGIN_CLASS_MEMBERS2(SketcherConfig)
+       int is_realtime();
+       void update_gui();
+       void save_data(KeyFrame *keyframe);
+       void read_data(KeyFrame *keyframe);
+       int new_curve(int ty, int radius, int pen, int color);
+       int new_curve();
+       int new_point(SketcherCurve *cv, int x, int y);
+       int new_point();
+       int process_realtime(VFrame *input, VFrame *output);
+       static void draw_point(VFrame *vfrm, SketcherPoint *pt, int color, int d);
+       void draw_point(VFrame *vfrm, SketcherPoint *pt, int color);
+
+       VFrame *input, *output;
+       int w, h, color_model, bpp, comp;
+       int is_yuv, is_float;
+};
+
+#endif
diff --git a/cinelerra-5.1/plugins/sketcher/sketcherwindow.C b/cinelerra-5.1/plugins/sketcher/sketcherwindow.C
new file mode 100644 (file)
index 0000000..5e55f29
--- /dev/null
@@ -0,0 +1,1218 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2014 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "automation.h"
+#include "bcdisplayinfo.h"
+#include "clip.h"
+#include "sketcher.h"
+#include "sketcherwindow.h"
+#include "cstrdup.h"
+#include "cwindow.h"
+#include "cwindowgui.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "keys.h"
+#include "language.h"
+#include "mainerror.h"
+#include "mwindow.h"
+#include "plugin.h"
+#include "pluginserver.h"
+#include "theme.h"
+#include "track.h"
+
+#define COLOR_W 30
+#define COLOR_H 30
+
+const char *SketcherCurve::types[] = {
+       N_("off"),
+       N_("line"),
+       N_("smooth"),
+};
+const char *SketcherCurve::pens[] = {
+       N_("box"),
+       N_("+"),
+       N_("/"),
+       N_("X"),
+};
+
+SketcherCurveTypeItem::SketcherCurveTypeItem(int ty)
+ : BC_MenuItem(_(cv_type[ty]))
+{
+       this->ty = ty;
+}
+int SketcherCurveTypeItem::handle_event()
+{
+       SketcherCurveType *popup = (SketcherCurveType*)get_popup_menu();
+       popup->update(ty);
+       SketcherWindow *gui = popup->gui;
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci >= 0 && ci < config.curves.size() ) {
+               SketcherCurve *cv = config.curves[ci];
+               cv->ty = ty;
+               gui->curve_list->update(ci);
+               gui->send_configure_change();
+       }
+       return 1;
+}
+
+SketcherCurveType::SketcherCurveType(SketcherWindow *gui, int x, int y, int ty)
+ : BC_PopupMenu(x,y,64,_(cv_type[ty]))
+{
+       this->gui = gui;
+}
+void SketcherCurveType::create_objects()
+{
+       int n = sizeof(cv_type)/sizeof(cv_type[0]);
+       for( int ty=0; ty<n; ++ty )
+               add_item(new SketcherCurveTypeItem(ty));
+}
+void SketcherCurveType::update(int ty)
+{
+       set_text(_(cv_type[ty]));
+}
+
+
+SketcherCurvePenItem::SketcherCurvePenItem(int pen)
+ : BC_MenuItem(_(cv_pen[pen]))
+{
+       this->pen = pen;
+}
+int SketcherCurvePenItem::handle_event()
+{
+       SketcherCurvePen *popup = (SketcherCurvePen*)get_popup_menu();
+       popup->update(pen);
+       SketcherWindow *gui = popup->gui;
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci >= 0 && ci < config.curves.size() ) {
+               SketcherCurve *cv = config.curves[ci];
+               cv->pen = pen;
+               gui->curve_list->update(ci);
+               gui->send_configure_change();
+       }
+       return 1;
+}
+
+SketcherCurvePen::SketcherCurvePen(SketcherWindow *gui, int x, int y, int pen)
+ : BC_PopupMenu(x,y,72,_(cv_pen[pen]))
+{
+       this->gui = gui;
+}
+void SketcherCurvePen::create_objects()
+{
+       int n = sizeof(cv_pen)/sizeof(cv_pen[0]);
+       for( int pen=0; pen<n; ++pen )
+               add_item(new SketcherCurvePenItem(pen));
+}
+void SketcherCurvePen::update(int pen)
+{
+       set_text(_(cv_pen[pen]));
+}
+
+
+SketcherCurveColor::SketcherCurveColor(SketcherWindow *gui, int x, int y, int w)
+ : BC_Button(x, y, w, vframes)
+{
+       this->gui = gui;
+       this->color = BLACK;
+       for( int i=0; i<3; ++i ) {
+               vframes[i] = new VFrame(w, w, BC_RGBA8888);
+               vframes[i]->clear_frame();
+       }
+}
+
+SketcherCurveColor::~SketcherCurveColor()
+{
+       for( int i=0; i<3; ++i )
+               delete vframes[i];
+}
+
+void SketcherCurveColor::set_color(int color)
+{
+       this->color = color;
+       int r = (color>>16) & 0xff;
+       int g = (color>>8) & 0xff;
+       int b = (color>>0) & 0xff;
+       for( int i=0; i<3; ++i ) {
+               VFrame *vframe = vframes[i];
+               int ww = vframe->get_w(), hh = vframe->get_h();
+               int cx = (ww+1)/2, cy = hh/2;
+               double cc = (cx*cx + cy*cy) / 4.;
+               uint8_t *bp = vframe->get_data(), *dp = bp;
+               uint8_t *ep = dp + vframe->get_data_size();
+               int rr = r, gg = g, bb = b;
+               int bpl = vframe->get_bytes_per_line();
+               switch( i ) {
+               case BUTTON_UP:
+                       break;
+               case BUTTON_UPHI:
+                       if( (rr+=48) > 0xff ) rr = 0xff;
+                       if( (gg+=48) > 0xff ) gg = 0xff;
+                       if( (bb+=48) > 0xff ) bb = 0xff;
+                       break;
+               case BUTTON_DOWNHI:
+                       if( (rr-=48) < 0x00 ) rr = 0x00;
+                       if( (gg-=48) < 0x00 ) gg = 0x00;
+                       if( (bb-=48) < 0x00 ) bb = 0x00;
+                       break;
+               }
+               while( dp < ep ) {
+                       int yy = (dp-bp) / bpl, xx = ((dp-bp) % bpl) >> 2;
+                       int dy = cy - yy, dx = cx - xx;
+                       double s = dx*dx + dy*dy - cc;
+                       double ss = s < 0 ? 1 : s >= cc ? 0 : 1 - s/cc;
+                       int aa = ss * 0xff;
+                       *dp++ = rr; *dp++ = gg; *dp++ = bb; *dp++ = aa;
+               }
+       }
+       set_images(vframes);
+}
+
+void SketcherCurveColor::update_gui(int color)
+{
+       set_color(color);
+       draw_face();
+}
+
+int SketcherCurveColor::handle_event()
+{
+       gui->start_color_thread(this);
+       return 1;
+}
+
+SketcherCurveColorPicker::SketcherCurveColorPicker(SketcherWindow *gui, SketcherCurveColor *color_button)
+ : ColorPicker(0, _("Color"))
+{
+       this->gui = gui;
+       this->color_button = color_button;
+       this->color = 0;
+       color_update = new SketcherCurveColorThread(this);
+}
+
+SketcherCurveColorPicker::~SketcherCurveColorPicker()
+{
+       delete color_update;
+}
+
+void SketcherCurveColorPicker::start(int color)
+{
+       start_window(color, 0, 1);
+       color_update->start();
+}
+
+void SketcherCurveColorPicker::handle_done_event(int result)
+{
+       color_update->stop();
+       gui->lock_window("SketcherCurveColorPicker::handle_done_event");
+       if( result ) color = orig_color;
+       color_button->update_gui(color);
+       gui->unlock_window();
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci >= 0 && ci < config.curves.size() ) {
+               SketcherCurve *cv = config.curves[ci];
+               cv->color = color;
+               gui->curve_list->update(ci);
+               gui->send_configure_change();
+       }
+}
+
+int SketcherCurveColorPicker::handle_new_color(int color, int alpha)
+{
+       this->color = color;
+       color_update->update_lock->unlock();
+       return 1;
+}
+
+void SketcherCurveColorPicker::update_gui()
+{
+       gui->lock_window("SketcherCurveColorPicker::update_gui");
+       color_button->update_gui(color);
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci >= 0 ) {
+               SketcherCurve *cv = config.curves[ci];
+               cv->color = color;
+               gui->curve_list->update(ci);
+               gui->send_configure_change();
+       }
+       gui->unlock_window();
+}
+
+SketcherCurveColorThread::SketcherCurveColorThread(SketcherCurveColorPicker *color_picker)
+ : Thread(1, 0, 0)
+{
+       this->color_picker = color_picker;
+       this->update_lock = new Condition(0,"SketcherCurveColorThread::update_lock");
+       done = 1;
+}
+
+SketcherCurveColorThread::~SketcherCurveColorThread()
+{
+       stop();
+       delete update_lock;
+}
+
+void SketcherCurveColorThread::start()
+{
+       if( done ) {
+               done = 0;
+               Thread::start();
+       }
+}
+
+void SketcherCurveColorThread::stop()
+{
+       if( !done ) {
+               done = 1;
+               update_lock->unlock();
+               join();
+       }
+}
+
+void SketcherCurveColorThread::run()
+{
+       while( !done ) {
+               update_lock->lock("SketcherCurveColorThread::run");
+               if( done ) break;
+               color_picker->update_gui();
+       }
+}
+
+
+SketcherNum::SketcherNum(SketcherWindow *gui, int x, int y, int output,
+               int mn, int mx)
+ : BC_TumbleTextBox(gui, output, mn, mx, x, y, 64)
+{
+       this->gui = gui;
+       set_increment(1);
+}
+
+SketcherNum::~SketcherNum()
+{
+}
+
+int SketcherPointX::handle_event()
+{
+       if( !SketcherNum::handle_event() ) return 0;
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci >= 0 && ci < config.curves.size() ) {
+               SketcherCurve *cv = config.curves[ci];
+               SketcherPointList *point_list = gui->point_list;
+               int hot_point = point_list->get_selection_number(0, 0);
+               SketcherPoints &points = cv->points;
+               if( hot_point >= 0 && hot_point < points.size() ) {
+                       int v = atoi(get_text());
+                       points[hot_point]->x = v;
+                       point_list->set_point(hot_point, PT_X, v);
+                       point_list->update_list(hot_point);
+                       gui->send_configure_change();
+               }
+       }
+       return 1;
+}
+int SketcherPointY::handle_event()
+{
+       if( !SketcherNum::handle_event() ) return 0;
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci >= 0 && ci < config.curves.size() ) {
+               SketcherCurve *cv = config.curves[ci];
+               SketcherPointList *point_list = gui->point_list;
+               int hot_point = point_list->get_selection_number(0, 0);
+               SketcherPoints &points = cv->points;
+               if( hot_point >= 0 && hot_point < points.size() ) {
+                       int v = atoi(get_text());
+                       points[hot_point]->y = v;
+                       point_list->set_point(hot_point, PT_Y, v);
+                       point_list->update_list(hot_point);
+                       gui->send_configure_change();
+               }
+       }
+       return 1;
+}
+
+int SketcherCurveRadius::handle_event()
+{
+       if( !SketcherNum::handle_event() ) return 0;
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci >= 0 && ci < config.curves.size() ) {
+               SketcherCurve *cv = config.curves[ci];
+               int v = atoi(get_text());
+               cv->radius = v;
+               gui->curve_list->update(ci);
+               gui->send_configure_change();
+       }
+       return 1;
+}
+
+
+SketcherWindow::SketcherWindow(Sketcher *plugin)
+ : PluginClientWindow(plugin, 380, 580, 380, 580, 0)
+{
+       this->plugin = plugin;
+       this->title_type = 0; this->curve_type = 0;
+       this->title_pen = 0;  this->curve_pen = 0;
+       this->title_color = 0; this->curve_color = 0;
+       this->color_picker = 0; this->new_points = 0;
+       this->new_curve = 0;  this->del_curve = 0;
+       this->curve_up = 0;   this->curve_dn = 0;
+       this->title_x = 0;    this->point_x = 0;
+       this->title_y = 0;    this->point_y = 0;
+       this->new_point = 0;  this->del_point = 0;
+       this->point_up = 0;   this->point_dn = 0;
+       this->drag = 0;       this->dragging = 0;
+       this->last_x = 0;     this->last_y = 0;
+       this->point_list = 0; this->pending_config = 0;
+}
+
+SketcherWindow::~SketcherWindow()
+{
+       delete curve_radius;
+       delete point_x;
+       delete point_y;
+       delete color_picker;
+}
+
+void SketcherWindow::create_objects()
+{
+       int x = 10, y = 10, x1, y1;
+       int margin = plugin->get_theme()->widget_border;
+       BC_Title *title;
+       int ci = plugin->config.cv_selected;
+       if( ci < 0 || ci >= plugin->config.curves.size() )
+               ci = plugin->new_curve(0, 1, 0, BLACK);
+       SketcherCurve *cv = plugin->config.curves[ci];
+       add_subwindow(reset_curves = new SketcherResetCurves(this, plugin, x1=x, y+3));
+       x1 += reset_curves->get_w() + 2*margin;
+       const char *curve_text = _("Curve");
+       add_subwindow(title_radius = new BC_Title(x1, y, _("Width:")));
+       x1 += title_radius->get_w() + margin;
+       curve_radius = new SketcherCurveRadius(this, x1, y, cv->radius);
+       curve_radius->create_objects();
+       y += reset_curves->get_h() + 2*margin;
+       x1 = get_w()-x - BC_Title::calculate_w(this, curve_text, LARGEFONT);
+       y1 = y-margin - BC_Title::calculate_h(this, curve_text, LARGEFONT);
+       add_subwindow(title = new BC_Title(x1, y1, curve_text, LARGEFONT,
+               get_resources()->menu_highlighted_fontcolor));
+       add_subwindow(curve_list = new SketcherCurveList(this, plugin, x, y));
+       y += curve_list->get_h() + margin;
+       add_subwindow(title_type = new BC_Title(x, y, _("Type:")));
+       x1 = x + title_type->get_w() + margin;
+       add_subwindow(curve_type = new SketcherCurveType(this, x1, y, cv->ty));
+       curve_type->create_objects();
+       x1 += curve_type->get_w() + margin;
+       add_subwindow(new_curve = new SketcherNewCurve(this, plugin, x1, y));
+       x1 += new_curve->get_w() + margin;
+       add_subwindow(curve_up = new SketcherCurveUp(this, x1, y));
+       x1 += curve_up->get_w() + 2*margin;
+       add_subwindow(title_color = new BC_Title(x1, y, _("Color:")));
+       y += curve_type->get_h() + margin;
+
+       add_subwindow(title_pen = new BC_Title(x, y, _("Pen:")));
+       x1 = x + title_pen->get_w() + margin;
+       add_subwindow(curve_pen = new SketcherCurvePen(this, x1, y, cv->pen));
+       curve_pen->create_objects();
+       x1 += curve_pen->get_w() + margin;
+       add_subwindow(del_curve = new SketcherDelCurve(this, plugin, x1, y));
+       x1 += del_curve->get_w() + margin;
+       add_subwindow(curve_dn = new SketcherCurveDn(this, x1, y));
+       x1 += curve_dn->get_w() + 2*margin;
+       add_subwindow(curve_color = new SketcherCurveColor(this, x1, y, COLOR_W));
+       curve_color->create_objects();
+       curve_color->set_color(cv->color);
+       curve_color->draw_face();
+       y += COLOR_H + margin;
+       curve_list->update(ci);
+
+       BC_Bar *bar;
+       add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
+       y += bar->get_h() + 2*margin;
+
+       int pi = plugin->config.pt_selected;
+       SketcherPoint *pt = pi >= 0 && pi < cv->points.size() ? cv->points[pi] : 0;
+       add_subwindow(reset_points = new SketcherResetPoints(this, plugin, x1=x, y+3));
+       x1 += reset_points->get_w() + 2*margin;
+       add_subwindow(drag = new SketcherDrag(this, x1, y));
+       y += drag->get_h() + margin;
+       if( plugin->config.drag ) {
+               if( !grab(plugin->server->mwindow->cwindow->gui) )
+                       eprintf("drag enabled, but compositor already grabbed\n");
+       }
+       const char *point_text = _("Point");
+       x1 = get_w()-x - BC_Title::calculate_w(this, point_text, LARGEFONT);
+       y1 = y-margin - BC_Title::calculate_h(this, point_text, LARGEFONT);
+       add_subwindow(title = new BC_Title(x1, y1, point_text, LARGEFONT,
+               get_resources()->menu_highlighted_fontcolor));
+       add_subwindow(point_list = new SketcherPointList(this, plugin, x, y));
+
+       y += point_list->get_h() + margin;
+       add_subwindow(title_x = new BC_Title(x, y, _("X:")));
+       x1 = x + title_x->get_w() + margin;
+       point_x = new SketcherPointX(this, x1, y, !pt ? 0.f : pt->x);
+       point_x->create_objects();
+       x1 += point_x->get_w() + 2*margin;
+       add_subwindow(new_point = new SketcherNewPoint(this, plugin, x1, y));
+       x1 += new_point->get_w() + margin;
+       add_subwindow(point_up = new SketcherPointUp(this, x1, y));
+       y += point_x->get_h() + margin;
+       add_subwindow(title_y = new BC_Title(x, y, _("Y:")));
+       x1 = x + title_y->get_w() + margin;
+       point_y = new SketcherPointY(this, x1, y, !pt ? 0.f : pt->y);
+       point_y->create_objects();
+       x1 += point_y->get_w() + 2*margin;
+       add_subwindow(del_point = new SketcherDelPoint(this, plugin, x1, y));
+       x1 += del_point->get_w() + margin;
+       add_subwindow(point_dn = new SketcherPointDn(this, x1, y));
+       y += point_y->get_h() + margin + 10;
+       point_list->update(pi);
+
+       add_subwindow(notes0 = new BC_Title(x, y,
+                _("\n"
+                  "LMB=\n"
+                  "Alt+LMB=\n"
+                  "MMB=\n"
+                  "DEL=\n")));
+       add_subwindow(notes1 = new BC_Title(x+80, y,
+                _("     No Shift\n"
+                  "select point\n"
+                  "drag curve\n"
+                  "next curve type\n"
+                  "deletes point\n")));
+       add_subwindow(notes2 = new BC_Title(x+200, y,
+                _("             Shift\n"
+                  "append new points\n"
+                  "drag image\n"
+                  "append new curve\n"
+                  "delete curve\n")));
+       show_window(1);
+}
+
+void SketcherWindow::send_configure_change()
+{
+       pending_config = 0;
+       plugin->send_configure_change();
+}
+
+int SketcherWindow::grab_event(XEvent *event)
+{
+       int ret = do_grab_event(event);
+       if( pending_config && !grab_event_count() )
+               send_configure_change();
+       return ret;
+}
+
+int SketcherWindow::do_grab_event(XEvent *event)
+{
+       switch( event->type ) {
+       case ButtonPress: break;
+       case ButtonRelease: break;
+       case MotionNotify: break;
+       case KeyPress:
+               if( keysym_lookup(event) > 0 ) {
+                       switch( get_keysym() ) {
+                       case XK_Delete:
+                               return (event->xkey.state & ShiftMask) ?
+                                       del_curve->handle_event() :
+                                       del_point->handle_event() ;
+                       }
+               } // fall thru
+       default:
+               return 0;
+       }
+
+       MWindow *mwindow = plugin->server->mwindow;
+       CWindowGUI *cwindow_gui = mwindow->cwindow->gui;
+       CWindowCanvas *canvas = cwindow_gui->canvas;
+       int cx, cy;  cwindow_gui->get_relative_cursor(cx, cy);
+       cx -= mwindow->theme->ccanvas_x;
+       cy -= mwindow->theme->ccanvas_y;
+
+       if( !dragging ) {
+               if( cx < 0 || cx >= mwindow->theme->ccanvas_w ||
+                   cy < 0 || cy >= mwindow->theme->ccanvas_h )
+                       return 0;
+       }
+
+       switch( event->type ) {
+       case ButtonPress:
+               if( dragging ) return 0;
+               dragging = event->xbutton.state & Mod1Mask ? -1 : 1; // alt_down
+               break;
+       case ButtonRelease:
+       case MotionNotify:
+               if( !dragging ) return 0;
+               break;
+       default:
+               return 0;
+       }
+
+
+       int ci = plugin->config.cv_selected;
+       if( ci < 0 || ci >= plugin->config.curves.size() )
+               return 1;
+
+       float cursor_x = cx, cursor_y = cy;
+       canvas->canvas_to_output(mwindow->edl, 0, cursor_x, cursor_y);
+       int64_t position = plugin->get_source_position();
+       float projector_x, projector_y, projector_z;
+       Track *track = plugin->server->plugin->track;
+       int track_w = track->track_w, track_h = track->track_h;
+       track->automation->get_projector(
+               &projector_x, &projector_y, &projector_z,
+               position, PLAY_FORWARD);
+       projector_x += mwindow->edl->session->output_w / 2;
+       projector_y += mwindow->edl->session->output_h / 2;
+       float output_x = (cursor_x - projector_x) / projector_z + track_w / 2;
+       float output_y = (cursor_y - projector_y) / projector_z + track_h / 2;
+       SketcherCurve *cv = plugin->config.curves[ci];
+       SketcherPoints &points = cv->points;
+       int state = event->xmotion.state;
+
+       switch( event->type ) {
+       case ButtonPress: {
+               if( dragging < 0 ) break;
+               int hot_point = -1;
+               int button_no = event->xbutton.button;
+               if( button_no == LEFT_BUTTON ) {
+// create new point string
+                       if( (state & ShiftMask) ) {
+                               ++new_points;
+                               hot_point = plugin->new_point(cv, output_x, output_y);
+                               point_list->update(hot_point);
+                       }
+                       else {
+// select point
+                               int sz = points.size();
+                               int last_point = hot_point;
+                               if( sz > 0 ) {
+                                       SketcherPoint *pt = points[hot_point=0];
+                                       double dist = DISTANCE(output_x,output_y, pt->x,pt->y);
+                                       for( int i=1; i<sz; ++i ) {
+                                               pt = points[i];
+                                               double d = DISTANCE(output_x,output_y, pt->x,pt->y);
+                                               if( d >= dist ) continue;
+                                               dist = d;  hot_point = i;
+                                       }
+                                       pt = points[hot_point];
+                                       float px = (pt->x - track_w / 2) * projector_z + projector_x;
+                                       float py = (pt->y - track_h / 2) * projector_z + projector_y;
+                                       dist = DISTANCE(px, py, cursor_x,cursor_y);
+                                       if( dist >= HANDLE_W ) hot_point = -1;
+                               }
+                               if( hot_point != last_point ) {
+                                       SketcherPoint *pt = 0;
+                                       if( hot_point >= 0 && hot_point < sz ) {
+                                               pt = points[hot_point];
+                                               point_list->set_point(hot_point, PT_X, pt->x = output_x);
+                                               point_list->set_point(hot_point, PT_Y, pt->y = output_y);
+                                       }
+                                       point_list->update_list(hot_point);
+                                       point_x->update(pt ? pt->x : 0.f);
+                                       point_y->update(pt ? pt->y : 0.f);
+                               }
+                       }
+               }
+               else if( button_no == MIDDLE_BUTTON ) {
+                       if( (state & ShiftMask) ) {
+                               int ci = plugin->new_curve(cv->ty, cv->radius, cv->pen, cv->color);
+                               curve_list->update(ci);
+                               point_list->update(-1);
+                       }
+                       else {
+                               int ty = cv->ty + 1;
+                               if( ty >= TYP_SZ ) ty = 0;
+                               cv->ty = ty;
+                               curve_type->update(ty);
+                               curve_list->update(ci);
+                       }
+               }
+               break; }
+       case MotionNotify: {
+               int hot_point = point_list->get_selection_number(0, 0);
+               if( dragging < 0 ) {
+                       SketcherCurves &curves = plugin->config.curves;
+                       int dx = round(output_x - last_x);
+                       int dy = round(output_y - last_y);
+                       int mnc = (state & ShiftMask) || ci<0 ? 0 : ci;
+                       int mxc = (state & ShiftMask) ? curves.size() : ci+1;
+                       for( int i=mnc; i<mxc; ++i ) {
+                               SketcherCurve *crv = plugin->config.curves[i];
+                               int pts = crv->points.size();
+                               for( int k=0; k<pts; ++k ) {
+                                       SketcherPoint *pt = crv->points[k];
+                                       pt->x += dx;  pt->y += dy;
+                               }
+                       }
+                       SketcherPoint *pt = hot_point >= 0 && hot_point < points.size() ?
+                               points[hot_point] : 0;
+                       point_x->update(pt ? pt->x : 0.f);
+                       point_y->update(pt ? pt->y : 0.f);
+                       point_list->update(hot_point);
+                       break;
+               }
+               if( (state & Button1Mask) ) {
+                       SketcherPoint *pt = hot_point >= 0 && hot_point < points.size() ?
+                               points[hot_point] : 0;
+                       if( pt && pt->x == output_x && pt->y == output_y ) break;
+                       if( new_points ) {
+                               if( pt ) {
+                                       float frac_w = DISTANCE(pt->x, pt->y, output_x, output_y) / get_w();
+                                       if( frac_w < 0.01 ) break; // 1 percent w
+                               }
+                               if( (state & ShiftMask) ) {
+                                       ++new_points;
+                                       hot_point = plugin->new_point(cv, output_x, output_y);
+                                       point_list->update(hot_point);
+                               }
+                       }
+                       else if( pt ) {
+                               point_list->set_point(hot_point, PT_X, pt->x = output_x);
+                               point_list->set_point(hot_point, PT_Y, pt->y = output_y);
+                               point_list->update_list(hot_point);
+                               point_x->update(pt->x);
+                               point_y->update(pt->y);
+                       }
+               }
+               break; }
+       case ButtonRelease: {
+               new_points = 0;
+               dragging = 0;
+               break; }
+       }
+
+       last_x = output_x;  last_y = output_y;
+       pending_config = 1;
+       return 1;
+}
+
+int SketcherWindow::keypress_event()
+{
+       int key = get_keypress();
+       switch( key ) {
+       case DELETE: return shift_down() ?
+                       del_curve->handle_event() :
+                       del_point->handle_event() ;
+       }
+       return 0;
+}
+
+void SketcherWindow::done_event(int result)
+{
+       ungrab(client->server->mwindow->cwindow->gui);
+}
+
+void SketcherWindow::start_color_thread(SketcherCurveColor *color_button)
+{
+       unlock_window();
+       delete color_picker;
+       color_picker = new SketcherCurveColorPicker(this, color_button);
+       int color = BLACK, ci = plugin->config.cv_selected;
+       if( ci >= 0 && ci < plugin->config.curves.size() ) {
+               SketcherCurve *cv = plugin->config.curves[ci];
+               color = cv->color;
+       }
+       color_picker->start(color);
+       lock_window("SketcherWindow::start_color_thread");
+}
+
+
+SketcherCurveList::SketcherCurveList(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT)
+{
+       this->gui = gui;
+       this->plugin = plugin;
+       titles[CV_ID] = _("id");  widths[CV_ID] = 64;
+       titles[CV_TY] = _("type");  widths[CV_TY] = 64;
+       titles[CV_RAD] = _("radius");  widths[CV_RAD] = 64;
+       titles[CV_PEN] = _("pen");  widths[CV_PEN] = 64;
+       titles[CV_CLR] = _("color");  widths[CV_CLR] = 64;
+}
+SketcherCurveList::~SketcherCurveList()
+{
+       clear();
+}
+void SketcherCurveList::clear()
+{
+       for( int i=CV_SZ; --i>=0; )
+               cols[i].remove_all_objects();
+}
+
+int SketcherCurveList::column_resize_event()
+{
+       for( int i=CV_SZ; --i>=0; )
+               widths[i] = get_column_width(i);
+       return 1;
+}
+
+int SketcherCurveList::handle_event()
+{
+       int ci = get_selection_number(0, 0);
+       set_selected(ci);
+       gui->point_list->update(0);
+       gui->send_configure_change();
+       return 1;
+}
+
+int SketcherCurveList::selection_changed()
+{
+       handle_event();
+       return 1;
+}
+
+void SketcherCurveList::set_selected(int k)
+{
+       int ci = -1;
+       if( k >= 0 && k < plugin->config.curves.size() ) {
+               SketcherCurve *cv = plugin->config.curves[k];
+               gui->curve_type->update(cv->ty);
+               gui->curve_radius->update(cv->radius);
+               gui->curve_pen->update(cv->pen);
+               gui->curve_color->update_gui(cv->color);
+               ci = k;
+       }
+       plugin->config.cv_selected = ci;
+       update_selection(&cols[0], ci);
+       update_list(-1);
+}
+
+void SketcherCurveList::update_list(int k)
+{
+       int xpos = get_xposition(), ypos = get_yposition();
+       if( k < 0 ) k = get_selection_number(0, 0);
+       update_selection(&cols[0], k);
+       BC_ListBox::update(&cols[0], &titles[0],&widths[0],CV_SZ, xpos,ypos,k);
+       center_selection();
+}
+
+void SketcherCurveList::update(int k)
+{
+       clear();
+       SketcherCurves &curves = plugin->config.curves;
+       int sz = curves.size();
+       for( int i=0; i<sz; ++i ) {
+               SketcherCurve *cv = curves[i];
+               char itxt[BCSTRLEN];  sprintf(itxt,"%d", cv->id);
+               char ttxt[BCSTRLEN];  sprintf(ttxt,"%s", cv_type[cv->ty]);
+               char rtxt[BCSTRLEN];  sprintf(rtxt,"%d", cv->radius);
+               char ptxt[BCSTRLEN];  sprintf(ptxt,"%s", cv_pen[cv->pen]);
+               int color = cv->color;
+               int r = (color>>16)&0xff, g = (color>>8)&0xff, b = (color>>0)&0xff;
+               char ctxt[BCSTRLEN];  sprintf(ctxt,"#%02x%02x%02x", r, g, b);
+               add_curve(itxt, ttxt, rtxt, ptxt, ctxt);
+       }
+       set_selected(k);
+}
+
+void SketcherCurveList::add_curve(const char *id, const char *type,
+               const char *radius, const char *pen, const char *color)
+{
+       cols[CV_ID].append(new BC_ListBoxItem(id));
+       cols[CV_TY].append(new BC_ListBoxItem(type));
+       cols[CV_RAD].append(new BC_ListBoxItem(radius));
+       cols[CV_PEN].append(new BC_ListBoxItem(pen));
+       cols[CV_CLR].append(new BC_ListBoxItem(color));
+}
+
+SketcherNewCurve::SketcherNewCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, 64, _("New"))
+{
+       this->gui = gui;
+       this->plugin = plugin;
+}
+SketcherNewCurve::~SketcherNewCurve()
+{
+}
+int SketcherNewCurve::handle_event()
+{
+       int ty = 1, pen = 0, color = 0, radius = 1;
+       int ci = plugin->config.cv_selected;
+       if( ci >= 0 && ci < plugin->config.curves.size() ) {
+               SketcherCurve *cv = plugin->config.curves[ci];
+               ty = cv->ty;  pen = cv->pen;
+               color = cv->color;  radius = cv->radius;
+       }
+       int k = plugin->new_curve(ty, radius, pen, color);
+       gui->curve_list->update(k);
+       gui->point_list->update(-1);
+       gui->send_configure_change();
+       return 1;
+}
+
+SketcherDelCurve::SketcherDelCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, 64, C_("Del"))
+{
+       this->gui = gui;
+       this->plugin = plugin;
+}
+SketcherDelCurve::~SketcherDelCurve()
+{
+}
+int SketcherDelCurve::handle_event()
+{
+       int hot_curve = gui->curve_list->get_selection_number(0, 0);
+       SketcherCurves &curves = plugin->config.curves;
+       if( hot_curve >= 0 && hot_curve < curves.size() ) {
+               curves.remove_object_number(hot_curve);
+               if( --hot_curve < 0 )
+                       hot_curve = plugin->new_curve(0, 1, 0, BLACK);
+               gui->curve_list->update(hot_curve);
+               gui->point_list->update(-1);
+               gui->send_configure_change();
+       }
+       return 1;
+}
+
+SketcherCurveUp::SketcherCurveUp(SketcherWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Up"))
+{
+       this->gui = gui;
+}
+SketcherCurveUp::~SketcherCurveUp()
+{
+}
+
+int SketcherCurveUp::handle_event()
+{
+       SketcherCurves &curves = gui->plugin->config.curves;
+       int hot_curve = gui->curve_list->get_selection_number(0, 0);
+
+       if( hot_curve > 0 && hot_curve < curves.size() ) {
+               SketcherCurve *&cv0 = curves[hot_curve];
+               SketcherCurve *&cv1 = curves[--hot_curve];
+               SketcherCurve *t = cv0;  cv0 = cv1;  cv1 = t;
+               gui->curve_list->update(hot_curve);
+       }
+       gui->send_configure_change();
+       return 1;
+}
+
+SketcherCurveDn::SketcherCurveDn(SketcherWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Dn"))
+{
+       this->gui = gui;
+}
+SketcherCurveDn::~SketcherCurveDn()
+{
+}
+
+int SketcherCurveDn::handle_event()
+{
+       SketcherCurves &curves = gui->plugin->config.curves;
+       int hot_curve = gui->curve_list->get_selection_number(0, 0);
+
+       if( hot_curve >= 0 && hot_curve < curves.size()-1 ) {
+               SketcherCurve *&cv0 = curves[hot_curve];
+               SketcherCurve *&cv1 = curves[++hot_curve];
+               SketcherCurve *t = cv0;  cv0 = cv1;  cv1 = t;
+               gui->curve_list->update(hot_curve);
+       }
+       gui->send_configure_change();
+       return 1;
+}
+
+
+SketcherPointList::SketcherPointList(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_ListBox(x, y, 360, 130, LISTBOX_TEXT)
+{
+       this->gui = gui;
+       this->plugin = plugin;
+       titles[PT_X] = _("X");    widths[PT_X] = 90;
+       titles[PT_Y] = _("Y");    widths[PT_Y] = 90;
+       titles[PT_ID] = _("ID");  widths[PT_ID] = 50;
+}
+SketcherPointList::~SketcherPointList()
+{
+       clear();
+}
+void SketcherPointList::clear()
+{
+       for( int i=PT_SZ; --i>=0; )
+               cols[i].remove_all_objects();
+}
+
+int SketcherPointList::column_resize_event()
+{
+       for( int i=PT_SZ; --i>=0; )
+               widths[i] = get_column_width(i);
+       return 1;
+}
+
+int SketcherPointList::handle_event()
+{
+       int pi = get_selection_number(0, 0);
+       set_selected(pi);
+       gui->send_configure_change();
+       return 1;
+}
+
+int SketcherPointList::selection_changed()
+{
+       handle_event();
+       return 1;
+}
+
+void SketcherPointList::add_point(const char *id, const char *xp, const char *yp)
+{
+       cols[PT_ID].append(new BC_ListBoxItem(id));
+       cols[PT_X].append(new BC_ListBoxItem(xp));
+       cols[PT_Y].append(new BC_ListBoxItem(yp));
+}
+
+void SketcherPointList::set_point(int i, int c, int v)
+{
+       char stxt[BCSTRLEN];
+       sprintf(stxt,"%d",v);
+       set_point(i,c,stxt);
+}
+void SketcherPointList::set_point(int i, int c, const char *cp)
+{
+       cols[c].get(i)->set_text(cp);
+}
+
+void SketcherPointList::set_selected(int k)
+{
+       SketcherPoint *pt = 0;
+       int ci = plugin->config.cv_selected, pi = -1;
+       if( ci >= 0 && ci < plugin->config.curves.size() ) {
+               SketcherCurve *cv = plugin->config.curves[ci];
+               pt = k >= 0 && k < cv->points.size() ? cv->points[pi=k] : 0;
+       }
+       gui->point_x->update(pt ? pt->x : 0.f);
+       gui->point_y->update(pt ? pt->y : 0.f);
+       plugin->config.pt_selected = pi;
+       update_selection(&cols[0], pi);
+       update_list(k);
+}
+void SketcherPointList::update_list(int k)
+{
+       int xpos = get_xposition(), ypos = get_yposition();
+       if( k < 0 ) k = get_selection_number(0, 0);
+       update_selection(&cols[0], k);
+       BC_ListBox::update(&cols[0], &titles[0],&widths[0],PT_SZ, xpos,ypos,k);
+       center_selection();
+}
+void SketcherPointList::update(int k)
+{
+       clear();
+       int ci = plugin->config.cv_selected, sz = 0;
+       if( ci >= 0 && ci < plugin->config.curves.size() ) {
+               SketcherCurve *cv = plugin->config.curves[ci];
+               SketcherPoints &points = cv->points;
+               sz = points.size();
+               for( int i=0; i<sz; ++i ) {
+                       SketcherPoint *pt = points[i];
+                       char itxt[BCSTRLEN];  sprintf(itxt,"%d", pt->id);
+                       char xtxt[BCSTRLEN];  sprintf(xtxt,"%d", pt->x);
+                       char ytxt[BCSTRLEN];  sprintf(ytxt,"%d", pt->y);
+                       add_point(itxt, xtxt, ytxt);
+               }
+       }
+       set_selected(k);
+}
+
+void SketcherWindow::update_gui()
+{
+       SketcherConfig &config = plugin->config;
+       int ci = config.cv_selected;
+       int pi = config.pt_selected;
+       curve_list->update(ci);
+       point_list->update(pi);
+       SketcherCurve *cv = ci >= 0 ? config.curves[ci] : 0;
+       curve_radius->update(cv ? cv->radius : 1);
+       curve_type->update(cv ? cv->ty : TYP_OFF);
+       curve_pen->update(cv ? cv->pen : PEN_SQUARE);
+       curve_color->set_color(cv ? cv->color : BLACK);
+       SketcherPoint *pt = pi >= 0 ? cv->points[pi] : 0;
+       point_x->update(pt ? pt->x : 0);
+       point_y->update(pt ? pt->y : 0);
+       drag->update(plugin->config.drag);
+}
+
+
+SketcherPointUp::SketcherPointUp(SketcherWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Up"))
+{
+       this->gui = gui;
+}
+SketcherPointUp::~SketcherPointUp()
+{
+}
+
+int SketcherPointUp::handle_event()
+{
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci < 0 || ci >= config.curves.size() )
+               return 1;
+       SketcherCurve *cv = config.curves[ci];
+       SketcherPoints &points = cv->points;
+       int sz = points.size();
+       int hot_point = gui->point_list->get_selection_number(0, 0);
+
+       if( sz > 1 && hot_point > 0 ) {
+               SketcherPoint *&pt0 = points[hot_point];
+               SketcherPoint *&pt1 = points[--hot_point];
+               SketcherPoint *t = pt0;  pt0 = pt1;  pt1 = t;
+               gui->point_list->update(hot_point);
+       }
+       gui->send_configure_change();
+       return 1;
+}
+
+SketcherPointDn::SketcherPointDn(SketcherWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Dn"))
+{
+       this->gui = gui;
+}
+SketcherPointDn::~SketcherPointDn()
+{
+}
+
+int SketcherPointDn::handle_event()
+{
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci < 0 || ci >= config.curves.size() )
+               return 1;
+       SketcherCurve *cv = config.curves[ci];
+       SketcherPoints &points = cv->points;
+       int sz = points.size();
+       int hot_point = gui->point_list->get_selection_number(0, 0);
+       if( sz > 1 && hot_point < sz-1 ) {
+               SketcherPoint *&pt0 = points[hot_point];
+               SketcherPoint *&pt1 = points[++hot_point];
+               SketcherPoint *t = pt0;  pt0 = pt1;  pt1 = t;
+               gui->point_list->update(hot_point);
+       }
+       gui->send_configure_change();
+       return 1;
+}
+
+SketcherDrag::SketcherDrag(SketcherWindow *gui, int x, int y)
+ : BC_CheckBox(x, y, gui->plugin->config.drag, _("Drag"))
+{
+       this->gui = gui;
+}
+int SketcherDrag::handle_event()
+{
+       CWindowGUI *cwindow_gui = gui->plugin->server->mwindow->cwindow->gui;
+       int value = get_value();
+       if( value ) {
+               if( !gui->grab(cwindow_gui) ) {
+                       update(value = 0);
+                       flicker(10,50);
+               }
+       }
+       else
+               gui->ungrab(cwindow_gui);
+       gui->plugin->config.drag = value;
+       gui->send_configure_change();
+       return 1;
+}
+
+SketcherNewPoint::SketcherNewPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, 64, _("New"))
+{
+       this->gui = gui;
+       this->plugin = plugin;
+}
+SketcherNewPoint::~SketcherNewPoint()
+{
+}
+int SketcherNewPoint::handle_event()
+{
+       int k = plugin->new_point();
+       gui->point_list->update(k);
+       gui->send_configure_change();
+       return 1;
+}
+
+SketcherDelPoint::SketcherDelPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, 64, C_("Del"))
+{
+       this->gui = gui;
+       this->plugin = plugin;
+}
+SketcherDelPoint::~SketcherDelPoint()
+{
+}
+int SketcherDelPoint::handle_event()
+{
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci >= 0 && ci < config.curves.size() ) {
+               SketcherCurve *cv = config.curves[ci];
+               SketcherPoints &points = cv->points;
+               int hot_point = gui->point_list->get_selection_number(0, 0);
+               if( hot_point >= 0 && hot_point < points.size() ) {
+                       points.remove_object_number(hot_point);
+                       gui->point_list->update(--hot_point);
+                       gui->send_configure_change();
+               }
+       }
+       return 1;
+}
+
+SketcherResetCurves::SketcherResetCurves(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, _("Reset"))
+{
+       this->gui = gui;
+       this->plugin = plugin;
+}
+SketcherResetCurves::~SketcherResetCurves()
+{
+}
+int SketcherResetCurves::handle_event()
+{
+       SketcherConfig &config = plugin->config;
+       config.curves.remove_all_objects();
+       int ci = plugin->new_curve(0, 1, 0, BLACK);
+       gui->curve_list->update(ci);
+       gui->point_list->update(-1);
+       gui->send_configure_change();
+       return 1;
+}
+
+SketcherResetPoints::SketcherResetPoints(SketcherWindow *gui, Sketcher *plugin, int x, int y)
+ : BC_GenericButton(x, y, _("Reset"))
+{
+       this->gui = gui;
+       this->plugin = plugin;
+}
+SketcherResetPoints::~SketcherResetPoints()
+{
+}
+int SketcherResetPoints::handle_event()
+{
+       SketcherConfig &config = gui->plugin->config;
+       int ci = config.cv_selected;
+       if( ci >= 0 && ci < config.curves.size() ) {
+               SketcherCurve *cv = config.curves[ci];
+               cv->points.remove_all_objects();
+               gui->point_list->update(-1);
+               gui->send_configure_change();
+       }
+       return 1;
+}
+
diff --git a/cinelerra-5.1/plugins/sketcher/sketcherwindow.h b/cinelerra-5.1/plugins/sketcher/sketcherwindow.h
new file mode 100644 (file)
index 0000000..a0cc4ae
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2015 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __CRIKEYWINDOW_H__
+#define __CRIKEYWINDOW_H__
+
+#include "guicast.h"
+#include "colorpicker.h"
+
+class Sketcher;
+class SketcherNum;
+class SketcherCurveTypeItem;
+class SketcherCurveType;
+class SketcherCurvePenItem;
+class SketcherCurvePen;
+class SketcherCurveColor;
+class SketcherCurveColorPicker;
+class SketcherCurveColorThread;
+class SketcherNewCurve;
+class SketcherDelCurve;
+class SketcherCurveUp;
+class SketcherCurveDn;
+class SketcherCurveRadius;
+class SketcherCurveList;
+class SketcherPointX;
+class SketcherPointY;
+class SketcherDrag;
+class SketcherPointList;
+class SketcherNewPoint;
+class SketcherDelPoint;
+class SketcherPointUp;
+class SketcherPointDn;
+class SketcherResetCurves;
+class SketcherResetPoints;
+class SketcherWindow;
+
+class SketcherNum : public BC_TumbleTextBox
+{
+public:
+       SketcherWindow *gui;
+
+       SketcherNum(SketcherWindow *gui, int x, int y, int output,
+               int mn=-32767, int mx=32767);
+       ~SketcherNum();
+       int update(int v) { return BC_TumbleTextBox::update((int64_t)v); }
+};
+
+class SketcherCurveTypeItem : public BC_MenuItem
+{
+public:
+       SketcherCurveTypeItem(int ty);
+       int handle_event();
+       int ty;
+};
+
+class SketcherCurveType : public BC_PopupMenu
+{
+public:
+       SketcherCurveType(SketcherWindow *gui, int x, int y, int ty);
+       void create_objects();
+       void update(int ty);
+
+       SketcherWindow *gui;
+};
+
+class SketcherCurvePenItem : public BC_MenuItem
+{
+public:
+       SketcherCurvePenItem(int pen);
+       int handle_event();
+       int pen;
+};
+
+class SketcherCurvePen : public BC_PopupMenu
+{
+public:
+       SketcherCurvePen(SketcherWindow *gui, int x, int y, int pen);
+       void create_objects();
+       void update(int ty);
+
+       SketcherWindow *gui;
+};
+
+class SketcherCurveColor : public BC_Button
+{
+public:
+       SketcherCurveColor(SketcherWindow *gui, int x, int y, int w);
+       ~SketcherCurveColor();
+
+       void set_color(int color);
+       void update_gui(int color);
+       int handle_event();
+
+       int color;
+       VFrame *vframes[3];
+       SketcherWindow *gui;
+};
+
+class SketcherCurveColorPicker : public ColorPicker
+{
+public:
+       SketcherCurveColorPicker(SketcherWindow *gui, SketcherCurveColor *curve_color);
+       ~SketcherCurveColorPicker();
+       void start(int color);
+       int handle_new_color(int color, int alpha);
+       void update_gui();
+       void handle_done_event(int result);
+
+       SketcherWindow *gui;
+       int color;
+       SketcherCurveColor *color_button;
+       SketcherCurveColorThread *color_update;
+};
+
+class SketcherCurveColorThread : public Thread
+{
+public:
+       SketcherCurveColorThread(SketcherCurveColorPicker *color_picker);
+       ~SketcherCurveColorThread();
+
+       void start();
+       void stop();
+       void run();
+
+       SketcherCurveColorPicker *color_picker;
+       int done;
+       Condition *update_lock;
+};
+
+class SketcherNewCurve : public BC_GenericButton
+{
+public:
+       SketcherNewCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+       ~SketcherNewCurve();
+
+       int handle_event();
+
+       SketcherWindow *gui;
+       Sketcher *plugin;
+};
+
+class SketcherDelCurve : public BC_GenericButton
+{
+public:
+       SketcherDelCurve(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+       ~SketcherDelCurve();
+
+       int handle_event();
+
+       Sketcher *plugin;
+       SketcherWindow *gui;
+};
+
+class SketcherCurveUp : public BC_GenericButton
+{
+public:
+       SketcherCurveUp(SketcherWindow *gui, int x, int y);
+       ~SketcherCurveUp();
+
+       int handle_event();
+
+       SketcherWindow *gui;
+};
+
+class SketcherCurveDn : public BC_GenericButton
+{
+public:
+       SketcherCurveDn(SketcherWindow *gui, int x, int y);
+       ~SketcherCurveDn();
+
+       int handle_event();
+
+       SketcherWindow *gui;
+};
+
+class SketcherCurveRadius : public SketcherNum
+{
+public:
+       SketcherCurveRadius(SketcherWindow *gui, int x, int y, float output)
+        : SketcherNum(gui, x, y, output, 0, 255) {}
+       ~SketcherCurveRadius() {}
+
+       int handle_event();
+};
+
+class SketcherCurveList : public BC_ListBox
+{
+public:
+       SketcherCurveList(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+       ~SketcherCurveList();
+
+       int handle_event();
+       int selection_changed();
+       int column_resize_event();
+       ArrayList<BC_ListBoxItem*> cols[CV_SZ];
+       void clear();
+       void add_curve(const char *id, const char *type,
+               const char *radius, const char *pen, const char *color);
+       void del_curve(int i);
+       void set_selected(int k);
+       void update(int k);
+       void update_list(int k);
+
+       SketcherWindow *gui;
+       Sketcher *plugin;
+       const char *titles[CV_SZ];
+       int widths[CV_SZ];
+};
+
+
+class SketcherPointX : public SketcherNum
+{
+public:
+       SketcherPointX(SketcherWindow *gui, int x, int y, float output)
+        : SketcherNum(gui, x, y, output) {}
+       ~SketcherPointX() {}
+
+       int handle_event();
+};
+class SketcherPointY : public SketcherNum
+{
+public:
+       SketcherPointY(SketcherWindow *gui, int x, int y, float output)
+        : SketcherNum(gui, x, y, output) {}
+       ~SketcherPointY() {}
+
+       int handle_event();
+};
+
+
+class SketcherDrag : public BC_CheckBox
+{
+public:
+       SketcherDrag(SketcherWindow *gui, int x, int y);
+
+       int handle_event();
+       SketcherWindow *gui;
+};
+
+
+class SketcherPointList : public BC_ListBox
+{
+public:
+       SketcherPointList(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+       ~SketcherPointList();
+
+       int handle_event();
+       int selection_changed();
+       int column_resize_event();
+       ArrayList<BC_ListBoxItem*> cols[PT_SZ];
+       void clear();
+       void add_point(const char *id, const char *xp, const char *yp);
+       void set_point(int i, int c, int v);
+       void set_point(int i, int c, const char *cp);
+       void set_selected(int k);
+       void update(int k);
+       void update_list(int k);
+
+
+       SketcherWindow *gui;
+       Sketcher *plugin;
+       const char *titles[PT_SZ];
+       int widths[PT_SZ];
+};
+
+class SketcherNewPoint : public BC_GenericButton
+{
+public:
+       SketcherNewPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+       ~SketcherNewPoint();
+
+       int handle_event();
+
+       SketcherWindow *gui;
+       Sketcher *plugin;
+};
+
+class SketcherDelPoint : public BC_GenericButton
+{
+public:
+       SketcherDelPoint(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+       ~SketcherDelPoint();
+
+       int handle_event();
+
+       Sketcher *plugin;
+       SketcherWindow *gui;
+};
+
+class SketcherPointUp : public BC_GenericButton
+{
+public:
+       SketcherPointUp(SketcherWindow *gui, int x, int y);
+       ~SketcherPointUp();
+
+       int handle_event();
+
+       SketcherWindow *gui;
+};
+
+class SketcherPointDn : public BC_GenericButton
+{
+public:
+       SketcherPointDn(SketcherWindow *gui, int x, int y);
+       ~SketcherPointDn();
+
+       int handle_event();
+
+       SketcherWindow *gui;
+};
+
+class SketcherResetCurves : public BC_GenericButton
+{
+public:
+       SketcherResetCurves(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+       ~SketcherResetCurves();
+
+       int handle_event();
+
+       Sketcher *plugin;
+       SketcherWindow *gui;
+};
+
+class SketcherResetPoints : public BC_GenericButton
+{
+public:
+       SketcherResetPoints(SketcherWindow *gui, Sketcher *plugin, int x, int y);
+       ~SketcherResetPoints();
+
+       int handle_event();
+
+       Sketcher *plugin;
+       SketcherWindow *gui;
+};
+
+
+class SketcherWindow : public PluginClientWindow
+{
+public:
+       SketcherWindow(Sketcher *plugin);
+       ~SketcherWindow();
+
+       void create_objects();
+       void update_gui();
+       void start_color_thread(SketcherCurveColor *curve_color);
+       int grab_event(XEvent *event);
+       int do_grab_event(XEvent *event);
+       void done_event(int result);
+       void send_configure_change();
+       int keypress_event();
+
+       Sketcher *plugin;
+
+       BC_Title *title_type, *title_pen;
+       BC_Title *title_color, *title_radius;
+       SketcherCurveType *curve_type;
+       SketcherCurvePen *curve_pen;
+       SketcherCurveColor *curve_color;
+       SketcherCurveColorPicker *color_picker;
+       SketcherNewCurve *new_curve;
+       SketcherDelCurve *del_curve;
+       SketcherCurveUp *curve_up;
+       SketcherCurveDn *curve_dn;
+       SketcherCurveRadius *curve_radius;
+       SketcherCurveList *curve_list;
+       SketcherResetCurves *reset_curves;
+
+       BC_Title *title_x, *title_y;
+       SketcherPointX *point_x;
+       SketcherPointY *point_y;
+       SketcherNewPoint *new_point;
+       SketcherDelPoint *del_point;
+       SketcherPointUp *point_up;
+       SketcherPointDn *point_dn;
+       int dragging, pending_config;
+       int new_points;
+       float last_x, last_y;
+       SketcherDrag *drag;
+       SketcherPointList *point_list;
+       SketcherResetPoints *reset_points;
+       BC_Title *notes0, *notes1, *notes2;
+};
+#endif
+
index bddb7993bdb845162de47d773e6454d01b847865..c34e29038840f23773e554c3456c43d3279e64e7 100644 (file)
@@ -785,7 +785,7 @@ TitleColorButton::TitleColorButton(TitleMain *client, TitleWindow *window, int x
 int TitleColorButton::handle_event()
 {
        window->color_thread->start_window(client->config.color,
-               client->config.alpha);
+               client->config.alpha, 1);
        return 1;
 }
 TitleOutlineColorButton::TitleOutlineColorButton(TitleMain *client, TitleWindow *window, int x, int y)
@@ -797,7 +797,7 @@ TitleOutlineColorButton::TitleOutlineColorButton(TitleMain *client, TitleWindow
 int TitleOutlineColorButton::handle_event()
 {
        window->outline_color_thread->start_window(client->config.outline_color,
-               client->config.outline_alpha);
+               client->config.outline_alpha, 1);
        return 1;
 }
 
@@ -1234,6 +1234,17 @@ int TitleColorThread::handle_new_color(int output, int alpha)
        return 1;
 }
 
+void TitleColorThread::handle_done_event(int result)
+{
+       if( result ) {
+               client->config.color = orig_color;
+               client->config.alpha = orig_alpha;
+               handle_new_color(orig_color, orig_alpha);
+               window->update_color();
+               window->send_configure_change();
+       }
+}
+
 TitleDrag::TitleDrag(TitleMain *client, TitleWindow *window, int x, int y)
  : DragCheckBox(client->server->mwindow, x, y, _("Drag"), &client->config.drag,
                client->config.title_x, client->config.title_y,
@@ -1528,9 +1539,10 @@ int TitleColorPopup::activate()
 }
 void TitleColorPopup::handle_done_event(int result)
 {
-       if( result ) return;
-       char txt[BCSTRLEN];  sprintf(txt, "<%s #%06x>", _(KW_COLOR), color_value);
-       window->insert_ibeam(txt);
+       if( !result ) {
+               char txt[BCSTRLEN];  sprintf(txt, "<%s #%06x>", _(KW_COLOR), color_value);
+               window->insert_ibeam(txt);
+       }
 }
 
 TitlePngPopup::TitlePngPopup(TitleMain *client, TitleWindow *window)
index 99cd3e18588a780b4ea35b6b48294388655136e7..4dcaa6ddd8c6e9216424ab17a2fc1623f7064c66 100644 (file)
@@ -503,6 +503,7 @@ class TitleColorThread : public ColorPicker
 public:
        TitleColorThread(TitleMain *client, TitleWindow *window, int is_outline);
        virtual int handle_new_color(int output, int alpha);
+       void handle_done_event(int result);
        TitleMain *client;
        TitleWindow *window;
        int is_outline;
similarity index 81%
rename from cinelerra-5.1/thirdparty/src/ffmpeg-4.0.patch3
rename to cinelerra-5.1/thirdparty/src/ffmpeg-4.1.patch3
index 3d64695a357199150f6ee790baa8da79d75e1f99..6e2ebbcd5a217f1d9da08fe8cfcb92ddf43f6e4c 100644 (file)
@@ -1,6 +1,6 @@
 diff -urN a/libavformat/avformat.h b/libavformat/avformat.h
---- a/libavformat/avformat.h   2018-04-20 04:02:57.000000000 -0600
-+++ b/libavformat/avformat.h   2018-04-24 11:02:20.777232001 -0600
+--- a/libavformat/avformat.h   2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/avformat.h   2018-11-08 07:25:17.066799941 -0700
 @@ -487,6 +487,9 @@
                                          The user or muxer can override this through
                                          AVFormatContext.avoid_negative_ts
@@ -22,8 +22,8 @@ diff -urN a/libavformat/avformat.h b/libavformat/avformat.h
      int flags;
  
 diff -urN a/libavformat/dv.c b/libavformat/dv.c
---- a/libavformat/dv.c 2018-04-13 17:34:28.000000000 -0600
-+++ b/libavformat/dv.c 2018-04-24 11:02:20.778232001 -0600
+--- a/libavformat/dv.c 2018-11-01 12:34:26.000000000 -0600
++++ b/libavformat/dv.c 2018-11-08 07:25:17.066799941 -0700
 @@ -632,6 +632,7 @@
  AVInputFormat ff_dv_demuxer = {
      .name           = "dv",
@@ -33,9 +33,9 @@ diff -urN a/libavformat/dv.c b/libavformat/dv.c
      .read_probe     = dv_probe,
      .read_header    = dv_read_header,
 diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c
---- a/libavformat/matroskadec.c        2018-04-20 04:02:57.000000000 -0600
-+++ b/libavformat/matroskadec.c        2018-04-24 11:02:20.779232001 -0600
-@@ -4026,6 +4026,7 @@
+--- a/libavformat/matroskadec.c        2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/matroskadec.c        2018-11-08 07:25:17.067799930 -0700
+@@ -4030,6 +4030,7 @@
  AVInputFormat ff_matroska_demuxer = {
      .name           = "matroska,webm",
      .long_name      = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
@@ -43,7 +43,7 @@ diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c
      .extensions     = "mkv,mk3d,mka,mks",
      .priv_data_size = sizeof(MatroskaDemuxContext),
      .read_probe     = matroska_probe,
-@@ -4039,6 +4040,7 @@
+@@ -4043,6 +4044,7 @@
  AVInputFormat ff_webm_dash_manifest_demuxer = {
      .name           = "webm_dash_manifest",
      .long_name      = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"),
@@ -52,9 +52,9 @@ diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c
      .read_header    = webm_dash_manifest_read_header,
      .read_packet    = webm_dash_manifest_read_packet,
 diff -urN a/libavformat/utils.c b/libavformat/utils.c
---- a/libavformat/utils.c      2018-04-20 04:02:58.000000000 -0600
-+++ b/libavformat/utils.c      2018-04-24 11:02:20.780232001 -0600
-@@ -2471,6 +2471,13 @@
+--- a/libavformat/utils.c      2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/utils.c      2018-11-08 07:25:17.069799908 -0700
+@@ -2472,6 +2472,13 @@
          return seek_frame_byte(s, stream_index, timestamp, flags);
      }
  
similarity index 52%
rename from cinelerra-5.1/thirdparty/src/ffmpeg-4.0.tar.xz
rename to cinelerra-5.1/thirdparty/src/ffmpeg-4.1.tar.xz
index 38e95dc074dc6300824a9c7af097ea48e98348de..9ce41056be5453a002593a51bf41fac412810f1f 100644 (file)
Binary files a/cinelerra-5.1/thirdparty/src/ffmpeg-4.0.tar.xz and b/cinelerra-5.1/thirdparty/src/ffmpeg-4.1.tar.xz differ
index 3d64695a357199150f6ee790baa8da79d75e1f99..6e2ebbcd5a217f1d9da08fe8cfcb92ddf43f6e4c 100644 (file)
@@ -1,6 +1,6 @@
 diff -urN a/libavformat/avformat.h b/libavformat/avformat.h
---- a/libavformat/avformat.h   2018-04-20 04:02:57.000000000 -0600
-+++ b/libavformat/avformat.h   2018-04-24 11:02:20.777232001 -0600
+--- a/libavformat/avformat.h   2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/avformat.h   2018-11-08 07:25:17.066799941 -0700
 @@ -487,6 +487,9 @@
                                          The user or muxer can override this through
                                          AVFormatContext.avoid_negative_ts
@@ -22,8 +22,8 @@ diff -urN a/libavformat/avformat.h b/libavformat/avformat.h
      int flags;
  
 diff -urN a/libavformat/dv.c b/libavformat/dv.c
---- a/libavformat/dv.c 2018-04-13 17:34:28.000000000 -0600
-+++ b/libavformat/dv.c 2018-04-24 11:02:20.778232001 -0600
+--- a/libavformat/dv.c 2018-11-01 12:34:26.000000000 -0600
++++ b/libavformat/dv.c 2018-11-08 07:25:17.066799941 -0700
 @@ -632,6 +632,7 @@
  AVInputFormat ff_dv_demuxer = {
      .name           = "dv",
@@ -33,9 +33,9 @@ diff -urN a/libavformat/dv.c b/libavformat/dv.c
      .read_probe     = dv_probe,
      .read_header    = dv_read_header,
 diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c
---- a/libavformat/matroskadec.c        2018-04-20 04:02:57.000000000 -0600
-+++ b/libavformat/matroskadec.c        2018-04-24 11:02:20.779232001 -0600
-@@ -4026,6 +4026,7 @@
+--- a/libavformat/matroskadec.c        2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/matroskadec.c        2018-11-08 07:25:17.067799930 -0700
+@@ -4030,6 +4030,7 @@
  AVInputFormat ff_matroska_demuxer = {
      .name           = "matroska,webm",
      .long_name      = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
@@ -43,7 +43,7 @@ diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c
      .extensions     = "mkv,mk3d,mka,mks",
      .priv_data_size = sizeof(MatroskaDemuxContext),
      .read_probe     = matroska_probe,
-@@ -4039,6 +4040,7 @@
+@@ -4043,6 +4044,7 @@
  AVInputFormat ff_webm_dash_manifest_demuxer = {
      .name           = "webm_dash_manifest",
      .long_name      = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"),
@@ -52,9 +52,9 @@ diff -urN a/libavformat/matroskadec.c b/libavformat/matroskadec.c
      .read_header    = webm_dash_manifest_read_header,
      .read_packet    = webm_dash_manifest_read_packet,
 diff -urN a/libavformat/utils.c b/libavformat/utils.c
---- a/libavformat/utils.c      2018-04-20 04:02:58.000000000 -0600
-+++ b/libavformat/utils.c      2018-04-24 11:02:20.780232001 -0600
-@@ -2471,6 +2471,13 @@
+--- a/libavformat/utils.c      2018-11-05 16:22:26.000000000 -0700
++++ b/libavformat/utils.c      2018-11-08 07:25:17.069799908 -0700
+@@ -2472,6 +2472,13 @@
          return seek_frame_byte(s, stream_index, timestamp, flags);
      }