load fn from resources, del kfrm speed update, svg exec cmd/file win fixes, undo...
[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);
217                 delete new_window;  new_window = 0;
218                 if( result || !filepath || !*filepath ) {
219                         window->editing_lock.lock();
220                         window->editing = 0;
221                         window->editing_lock.unlock();
222                         return;              // cancel or no filename given
223                 }
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                                 unsigned long size = sizeof(empty_svg) - 4;
237                                 fwrite(empty_svg+4, size,  1, out);
238                                 fclose(out);
239                                 result = 0;
240                         }
241                 }
242         } while(result);        // file doesn't exist so repeat
243
244         strcpy(client->config.svg_file, filename);
245         client->config.ms_time = 0;
246         window->update_gui(client->config);
247         client->send_configure_change();
248
249         window->editing_lock.lock();
250         window->editing = 0;
251         window->editing_lock.unlock();
252
253         return;
254 }
255
256 void NewSvgButton::stop()
257 {
258         if( new_window ) {
259                 new_window->set_done(1);
260         }
261         join();
262 }
263
264 EditSvgButton::EditSvgButton(SvgMain *client, SvgWin *window, int x, int y)
265  : BC_GenericButton(x, y, _("Edit")), Thread(1)
266 {
267         this->client = client;
268         this->window = window;
269         fh_fifo = -1;
270 }
271
272 EditSvgButton::~EditSvgButton()
273 {
274         stop();
275 }
276
277 void EditSvgButton::stop()
278 {
279         if( running() ) {
280                 if( fh_fifo >= 0 ) {
281                         struct fifo_struct fifo_buf;
282                         fifo_buf.pid = getpid();
283                         fifo_buf.action = 3;
284                         write(fh_fifo, &fifo_buf, sizeof(fifo_buf));
285                 }
286         }
287         join();
288 }
289
290 int EditSvgButton::handle_event()
291 {
292
293         window->editing_lock.lock();
294         if( !window->editing && client->config.svg_file[0] != 0 ) {
295                 window->editing = 1;
296                 window->editing_lock.unlock();
297                 start();
298         }
299         else {
300                 flicker();
301                 window->editing_lock.unlock();
302         }
303         return 1;
304 }
305
306 void EditSvgButton::run()
307 {
308 // ======================================= get path from user
309         char filename_png[1024];
310         char filename_fifo[1024];
311         strcpy(filename_png, client->config.svg_file);
312         strcat(filename_png, ".png");
313         remove(filename_png);
314         strcpy(filename_fifo, filename_png);
315         strcat(filename_fifo, ".fifo");
316         remove(filename_fifo);
317         if( !mkfifo(filename_fifo, S_IRWXU) &&
318             (fh_fifo = ::open(filename_fifo, O_RDWR+O_NONBLOCK)) >= 0 ) {
319                 SvgInkscapeThread inkscape_thread(this);
320                 inkscape_thread.start();
321                 int done = 0;
322                 while( inkscape_thread.running() && !done ) {
323                         struct stat st;
324                         int64_t ms_time = stat(client->config.svg_file, &st) ? 0 :
325                                 st.st_mtim.tv_sec*1000 + st.st_mtim.tv_nsec/1000000;
326                         if( client->config.ms_time != ms_time ) {
327                                 client->config.ms_time = ms_time;
328                                 client->send_configure_change();
329                         }
330                         // select(fh_fifo+1,rds,0,ers,tmo) does not work here
331                         Timer::delay(200);
332                         struct fifo_struct fifo_buf; fifo_buf.action = 1;
333                         int ret = read(fh_fifo, &fifo_buf, sizeof(fifo_buf));
334                         if( ret < 0 ) {
335                                 if( errno == EAGAIN ) continue;
336                                 perror("fifo");
337                                 break;
338                         }
339                         if( ret != sizeof(fifo_buf) ) continue;
340                         switch( fifo_buf.action ) {
341                         case 1: break;
342                         case 2: printf(_("Inkscape has exited\n"));
343                                 break;
344                         case 3: printf(_("Plugin window has closed\n"));
345                                 done = 1;
346                                 break;
347                         }
348                 }
349         }
350         else
351                 perror(_("Error opening fifo file"));
352         remove(filename_fifo); // fifo destroyed on last close
353         ::close(fh_fifo);
354         window->editing_lock.lock();
355         window->editing = 0;
356         window->editing_lock.unlock();
357         struct stat st;
358         client->config.ms_time = stat(client->config.svg_file, &st) ? 0 :
359                 st.st_mtim.tv_sec*1000 + st.st_mtim.tv_nsec/1000000;
360         client->send_configure_change();
361 }
362
363 SvgInkscapeThread::SvgInkscapeThread(EditSvgButton *edit)
364  : Thread(1)
365 {
366         this->edit = edit;;
367 }
368
369 SvgInkscapeThread::~SvgInkscapeThread()
370 {
371         cancel();
372         join();
373 }
374
375 static int exec_command(char* const*argv)
376 {
377         pid_t pid = vfork();
378         if( pid < 0 ) return -1;
379         if( pid > 0 ) {
380                 int stat = 0;
381                 waitpid(pid, &stat, 0);
382                 if( stat ) {
383                         char msg[BCTEXTLEN];
384                         sprintf(msg, "%s: error exit status %d", argv[0], stat);
385                         MainError::show_error(msg);
386                 }
387                 return 0;
388         }
389         execvp(argv[0], &argv[0]);
390         return -1;
391 }
392
393 void SvgInkscapeThread::run()
394 {
395 // Runs the inkscape
396         char command[1024];
397         snprintf(command, sizeof(command),
398                 "inkscape --with-gui %s", edit->client->config.svg_file);
399         printf(_("Running external SVG editor: %s\n"), command);
400         char *const argv[] = {
401                 (char*) "incscape",
402                 (char*)"--with-gui",
403                 edit->client->config.svg_file,
404                 0,
405         };
406
407         enable_cancel();
408         exec_command(argv);
409         printf(_("External SVG editor finished\n"));
410         struct fifo_struct fifo_buf;
411         fifo_buf.pid = getpid();
412         fifo_buf.action = 2;
413         write(edit->fh_fifo, &fifo_buf, sizeof(fifo_buf));
414         disable_cancel();
415
416         return;
417 }
418
419
420 NewSvgWindow::NewSvgWindow(SvgMain *client, SvgWin *window, char *init_directory)
421  : BC_FileBox(0,
422         BC_WindowBase::get_resources()->filebox_h / 2,
423         init_directory,
424         _("SVG Plugin: Pick SVG file"),
425         _("Open an existing SVG file or create a new one"))
426 {
427         this->window = window;
428 }
429
430 NewSvgWindow::~NewSvgWindow() {}
431
432
433 DpiValue::DpiValue(SvgWin *win, SvgMain *client, int x, int y, float *value)
434  : BC_TumbleTextBox(win, *value, (float)10, (float)1000, x, y, 100)
435 {
436 //printf("SvgWidth::SvgWidth %f\n", client->config.w);
437         this->client = client;
438         this->win = win;
439         this->value = value;
440 }
441
442 DpiValue::~DpiValue()
443 {
444 }
445
446 int DpiValue::handle_event()
447 {
448         *value = atof(get_text());
449         return 1;
450 }
451
452
453 DpiButton::DpiButton( SvgWin *window, SvgMain *client, int x, int y)
454  : BC_GenericButton(x, y, _("update dpi"))
455 {
456         this->client = client;
457         this->window = window;
458 }
459
460 DpiButton::~DpiButton()
461 {
462 }
463
464 int DpiButton::handle_event()
465 {
466         client->send_configure_change();
467         return 1;
468 };
469