Add back 2 patches for histogram and overlayframe that are working correctly and...
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / svg / svgwin.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include "svgwin.h"
22 #include "filexml.h"
23 #include "language.h"
24 #include "mainerror.h"
25
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <sys/types.h>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34
35 #include <errno.h>
36
37 struct fifo_struct {
38         int pid;
39 // 1 = update from client, 2 = client closes, 3 = quit
40         int action;
41 };
42
43 SvgWin::SvgWin(SvgMain *client)
44  : PluginClientWindow(client, xS(420), yS(210), xS(420), yS(210), 1)
45 {
46         this->client = client;
47         this->editing = 0;
48 }
49
50 SvgWin::~SvgWin()
51 {
52 }
53
54 void SvgWin::create_objects()
55 {
56         int xs10 = xS(10), xs15 = xS(15);
57         int ys5 = yS(5), ys10 = yS(10), ys20 = yS(20), ys15 = yS(15);
58         BC_Title *title;
59         int x0 = xs10, y = ys10;
60
61         add_tool(title = new BC_Title(x0, y, _("Out X:")));
62         int x1 = x0 + title->get_w() + xs10;
63         out_x = new SvgCoord(this, client, x1, y, &client->config.out_x);
64         out_x->create_objects();
65         int x2 = x1 + out_x->get_w() + xs15;
66         add_tool(title = new BC_Title(x2, y, _("Out W:")));
67         int x3 = x2 + title->get_w() + xs10;
68         out_w = new SvgCoord(this, client, x3, y, &client->config.out_w);
69         out_w->create_objects();
70         y += out_x->get_h() + ys5;
71
72         add_tool(new BC_Title(x0, y, _("Out Y:")));
73         out_y = new SvgCoord(this, client, x1, y, &client->config.out_y);
74         out_y->create_objects();
75         add_tool(title = new BC_Title(x2, y, _("Out H:")));
76         out_h = new SvgCoord(this, client, x3, y, &client->config.out_h);
77         out_h->create_objects();
78         y += out_y->get_h() + ys20;
79
80         add_tool(title = new BC_Title(x0, y, _("DPI:")));
81         dpi = new DpiValue(this, client, x1, y, &client->config.dpi);
82         dpi->create_objects();
83         add_tool(dpi_button = new DpiButton(this, client, x2, y));
84         dpi_button->create_objects();
85         y += dpi->get_h() + ys20;
86
87         add_tool(svg_file_title = new BC_Title(x0, y, client->config.svg_file));
88         y += svg_file_title->get_h() + ys5;
89         struct stat st;
90         int64_t ms_time = stat(client->config.svg_file, &st) ? 0 :
91                 st.st_mtim.tv_sec*1000 + st.st_mtim.tv_nsec/1000000;
92         char mtime[BCSTRLEN];  mtime[0] = 0;
93         if( ms_time > 0 ) {
94                 time_t tm = ms_time/1000;
95                 ctime_r(&tm ,mtime);
96         }
97         add_tool(svg_file_mstime = new BC_Title(x0, y, mtime));
98         y += svg_file_mstime->get_h() + ys15;
99
100         y = get_h() - NewSvgButton::calculate_h() - ys5;
101         add_tool(new_svg_button = new NewSvgButton(client, this, x0, y));
102         y = get_h() - EditSvgButton::calculate_h() - ys5;
103         add_tool(edit_svg_button = new EditSvgButton(client, this, x0+xS(300), y));
104
105         show_window();
106         flush();
107 }
108
109 int SvgWin::close_event()
110 {
111         new_svg_button->stop();
112         edit_svg_button->stop();
113         set_done(1);
114         return 1;
115 }
116
117 int SvgWin::hide_window(int flush)
118 {
119         new_svg_button->stop();
120         edit_svg_button->stop();
121         return BC_WindowBase::hide_window(flush);
122 }
123
124
125 void SvgWin::update_gui(SvgConfig &config)
126 {
127         lock_window("SvgWin::update_gui");
128         out_x->update(config.out_x);
129         out_y->update(config.out_y);
130         out_w->update(config.out_w);
131         out_h->update(config.out_h);
132         dpi->update(config.dpi);
133         svg_file_title->update(config.svg_file);
134         char mtime[BCSTRLEN];  mtime[0] = 0;
135         if( config.ms_time > 0 ) {
136                 time_t tm = config.ms_time/1000;
137                 ctime_r(&tm ,mtime);
138         }
139         svg_file_mstime->update(mtime);
140         unlock_window();
141 }
142
143 SvgCoord::SvgCoord(SvgWin *win, SvgMain *client, int x, int y, float *value)
144  : BC_TumbleTextBox(win, *value, (float)0, (float)3000, x, y, xS(100))
145 {
146 //printf("SvgWidth::SvgWidth %f\n", client->config.w);
147         this->client = client;
148         this->win = win;
149         this->value = value;
150 }
151
152 SvgCoord::~SvgCoord()
153 {
154 }
155
156 int SvgCoord::handle_event()
157 {
158         *value = atof(get_text());
159         client->send_configure_change();
160         return 1;
161 }
162
163 NewSvgButton::NewSvgButton(SvgMain *client, SvgWin *window, int x, int y)
164  : BC_GenericButton(x, y, _("New/Open SVG..."))
165 {
166         this->client = client;
167         this->window = window;
168         new_window = 0;
169 }
170 NewSvgButton::~NewSvgButton()
171 {
172         stop();
173 }
174
175
176 int NewSvgButton::handle_event()
177 {
178         window->editing_lock.lock();
179         if( !window->editing ) {
180                 window->editing = 1;
181                 window->editing_lock.unlock();
182                 start();
183         }
184         else {
185                 flicker();
186                 window->editing_lock.unlock();
187         }
188
189         return 1;
190 }
191
192 void NewSvgButton::run()
193 {
194 // ======================================= get path from user
195         int result;
196 //printf("NewSvgButton::run 1\n");
197         result = 1;
198         char filename[1024];
199         strncpy(filename, client->config.svg_file, sizeof(filename));
200 // Loop until file is chosen
201         do {
202                 char directory[1024];
203                 strncpy(directory, client->config.svg_file, sizeof(directory));
204                 char *cp = strrchr(directory, '/');
205                 if( cp ) *cp = 0;
206                 if( !directory[0] ) {
207                         char *cp = getenv("HOME");
208                         if( cp ) strncpy(directory, cp, sizeof(directory));
209                 }
210                 new_window = new NewSvgWindow(client, window, directory);
211                 new_window->create_objects();
212                 new_window->lock_window("NewSvgButton::run");
213                 new_window->update_filter("*.svg");
214                 new_window->unlock_window();
215                 result = new_window->run_window();
216                 const char *filepath = new_window->get_path(0);
217                 strcpy(filename, filepath ? filepath : "" );
218                 delete new_window;  new_window = 0;
219                 window->editing_lock.lock();
220                 if( result || !filename[0] )
221                         window->editing = 0;
222                 window->editing_lock.unlock();
223                 if( !window->editing ) return;              // cancel or no filename given
224
225 // Extend the filename with .svg
226                 if( strlen(filename) < 4 ||
227                         strcasecmp(&filename[strlen(filename) - 4], ".svg") ) {
228                         strcat(filename, ".svg");
229                 }
230
231                 if( !access(filename, R_OK) )
232                         result = 0;
233                 else {
234                         FILE *out = fopen(filename,"w");
235                         if( out ) {
236                                 extern unsigned char _binary_new_svg_start[];
237                                 extern unsigned char _binary_new_svg_end[];
238                                 unsigned int *ip = (unsigned int *)_binary_new_svg_start;
239                                 unsigned int hdr_sz = *ip++;
240                                 unsigned char *dp = (unsigned char *)ip + hdr_sz;
241                                 unsigned char *ep = _binary_new_svg_end;
242                                 fwrite(dp, ep - dp,  1, out);
243                                 fclose(out);
244                                 result = 0;
245                         }
246                 }
247         } while(result);        // file doesn't exist so repeat
248
249         strcpy(client->config.svg_file, filename);
250         client->config.ms_time = 0;
251         window->update_gui(client->config);
252         client->send_configure_change();
253
254         window->editing_lock.lock();
255         window->editing = 0;
256         window->editing_lock.unlock();
257
258         return;
259 }
260
261 void NewSvgButton::stop()
262 {
263         if( new_window ) {
264                 new_window->set_done(1);
265         }
266         join();
267 }
268
269 EditSvgButton::EditSvgButton(SvgMain *client, SvgWin *window, int x, int y)
270  : BC_GenericButton(x, y, _("Edit")), Thread(1)
271 {
272         this->client = client;
273         this->window = window;
274         fh_fifo = -1;
275 }
276
277 EditSvgButton::~EditSvgButton()
278 {
279         stop();
280 }
281
282 void EditSvgButton::stop()
283 {
284         if( running() ) {
285                 if( fh_fifo >= 0 ) {
286                         struct fifo_struct fifo_buf;
287                         fifo_buf.pid = getpid();
288                         fifo_buf.action = 3;
289                         write(fh_fifo, &fifo_buf, sizeof(fifo_buf));
290                 }
291         }
292         join();
293 }
294
295 int EditSvgButton::handle_event()
296 {
297
298         window->editing_lock.lock();
299         if( !window->editing && client->config.svg_file[0] != 0 ) {
300                 window->editing = 1;
301                 window->editing_lock.unlock();
302                 start();
303         }
304         else {
305                 flicker();
306                 window->editing_lock.unlock();
307         }
308         return 1;
309 }
310
311 void EditSvgButton::run()
312 {
313 // ======================================= get path from user
314         char filename_png[1024];
315         char filename_fifo[1024];
316         strcpy(filename_png, client->config.svg_file);
317         strcat(filename_png, ".png");
318         remove(filename_png);
319         strcpy(filename_fifo, filename_png);
320         strcat(filename_fifo, ".fifo");
321         remove(filename_fifo);
322         if( !mkfifo(filename_fifo, S_IRWXU) &&
323             (fh_fifo = ::open(filename_fifo, O_RDWR+O_NONBLOCK)) >= 0 ) {
324                 SvgInkscapeThread inkscape_thread(this);
325                 inkscape_thread.start();
326                 int done = 0;
327                 while( inkscape_thread.running() && !done ) {
328                         struct stat st;
329                         int64_t ms_time = stat(client->config.svg_file, &st) ? 0 :
330                                 st.st_mtim.tv_sec*1000 + st.st_mtim.tv_nsec/1000000;
331                         if( client->config.ms_time != ms_time ) {
332                                 client->config.ms_time = ms_time;
333                                 client->send_configure_change();
334                         }
335                         // select(fh_fifo+1,rds,0,ers,tmo) does not work here
336                         Timer::delay(200);
337                         struct fifo_struct fifo_buf; fifo_buf.action = 1;
338                         int ret = read(fh_fifo, &fifo_buf, sizeof(fifo_buf));
339                         if( ret < 0 ) {
340                                 if( errno == EAGAIN ) continue;
341                                 perror("fifo");
342                                 break;
343                         }
344                         if( ret != sizeof(fifo_buf) ) continue;
345                         switch( fifo_buf.action ) {
346                         case 1: break;
347                         case 2: // printf(_("Inkscape has exited\n"));
348                                 break;
349                         case 3: // printf(_("Plugin window has closed\n"));
350                                 done = 1;
351                                 break;
352                         }
353                 }
354         }
355         else
356                 perror(_("Error opening fifo file"));
357         remove(filename_fifo); // fifo destroyed on last close
358         ::close(fh_fifo);
359         window->editing_lock.lock();
360         window->editing = 0;
361         window->editing_lock.unlock();
362         struct stat st;
363         client->config.ms_time = stat(client->config.svg_file, &st) ? 0 :
364                 st.st_mtim.tv_sec*1000 + st.st_mtim.tv_nsec/1000000;
365         client->send_configure_change();
366 }
367
368 SvgInkscapeThread::SvgInkscapeThread(EditSvgButton *edit)
369  : Thread(1)
370 {
371         this->edit = edit;;
372 }
373
374 SvgInkscapeThread::~SvgInkscapeThread()
375 {
376         cancel();
377         join();
378 }
379
380 static int exec_command(char* const*argv)
381 {
382         pid_t pid = vfork();
383         if( pid < 0 ) return -1;
384         if( pid > 0 ) {
385                 int stat = 0;
386                 waitpid(pid, &stat, 0);
387                 if( stat ) {
388                         char msg[BCTEXTLEN];
389                         sprintf(msg, "%s: error exit status %d", argv[0], stat);
390                         MainError::show_error(msg);
391                 }
392                 return 0;
393         }
394         execvp(argv[0], &argv[0]);
395         return -1;
396 }
397
398 void SvgInkscapeThread::run()
399 {
400 // Runs the inkscape
401         char command[1024];
402         snprintf(command, sizeof(command),
403                 "inkscape --with-gui %s", edit->client->config.svg_file);
404         printf(_("Running external SVG editor: %s\n"), command);
405         char *const argv[] = {
406                 (char*) "inkscape",
407                 (char*)"--with-gui",
408                 edit->client->config.svg_file,
409                 0,
410         };
411
412         enable_cancel();
413         exec_command(argv);
414         printf(_("External SVG editor finished\n"));
415         struct fifo_struct fifo_buf;
416         fifo_buf.pid = getpid();
417         fifo_buf.action = 2;
418         write(edit->fh_fifo, &fifo_buf, sizeof(fifo_buf));
419         disable_cancel();
420
421         return;
422 }
423
424
425 NewSvgWindow::NewSvgWindow(SvgMain *client, SvgWin *window, char *init_directory)
426  : BC_FileBox(0,
427         BC_WindowBase::get_resources()->filebox_h / 2,
428         init_directory,
429         _("SVG Plugin: Pick SVG file"),
430         _("Open an existing SVG file or create a new one"))
431 {
432         this->window = window;
433 }
434
435 NewSvgWindow::~NewSvgWindow() {}
436
437
438 DpiValue::DpiValue(SvgWin *win, SvgMain *client, int x, int y, float *value)
439  : BC_TumbleTextBox(win, *value, 10.f, 1000.f, x, y, xS(100), 2)
440 {
441 //printf("SvgWidth::SvgWidth %f\n", client->config.w);
442         this->client = client;
443         this->win = win;
444         this->value = value;
445 }
446
447 DpiValue::~DpiValue()
448 {
449 }
450
451 int DpiValue::handle_event()
452 {
453         *value = atof(get_text());
454         return 1;
455 }
456
457
458 DpiButton::DpiButton( SvgWin *window, SvgMain *client, int x, int y)
459  : BC_GenericButton(x, y, _("update dpi"))
460 {
461         this->client = client;
462         this->window = window;
463 }
464
465 DpiButton::~DpiButton()
466 {
467 }
468
469 int DpiButton::handle_event()
470 {
471         client->send_configure_change();
472         return 1;
473 };
474