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