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