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