svg plugin upgrades, filepng improvements
[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
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 <errno.h>
33
34 #include "empty_svg.h"
35
36 struct fifo_struct {
37         int pid;
38 // 1 = update from client, 2 = client closes, 3 = quit
39         int action;
40 };
41
42 SvgWin::SvgWin(SvgMain *client)
43  : PluginClientWindow(client, 420, 210, 420, 210, 1)
44 {
45         this->client = client;
46         this->editing = 0;
47 }
48
49 SvgWin::~SvgWin()
50 {
51 }
52
53 void SvgWin::create_objects()
54 {
55         BC_Title *title;
56         int x0 = 10, y = 10;
57
58         add_tool(title = new BC_Title(x0, y, _("Out X:")));
59         int x1 = x0 + title->get_w() + 10;
60         out_x = new SvgCoord(this, client, x1, y, &client->config.out_x);
61         out_x->create_objects();
62         int x2 = x1 + out_x->get_w() + 15;
63         add_tool(title = new BC_Title(x2, y, _("Out W:")));
64         int x3 = x2 + title->get_w() + 10;
65         out_w = new SvgCoord(this, client, x3, y, &client->config.out_w);
66         out_w->create_objects();
67         y += out_x->get_h() + 5;
68
69         add_tool(new BC_Title(x0, y, _("Out Y:")));
70         out_y = new SvgCoord(this, client, x1, y, &client->config.out_y);
71         out_y->create_objects();
72         add_tool(title = new BC_Title(x2, y, _("Out H:")));
73         out_h = new SvgCoord(this, client, x3, y, &client->config.out_h);
74         out_h->create_objects();
75         y += out_y->get_h() + 20;
76
77         add_tool(title = new BC_Title(x0, y, _("DPI:")));
78         dpi = new DpiValue(this, client, x1, y, &client->config.dpi);
79         dpi->create_objects();
80         add_tool(dpi_button = new DpiButton(this, client, x2, y));
81         dpi_button->create_objects();
82         y += dpi->get_h() + 20;
83
84         add_tool(svg_file_title = new BC_Title(x0, y, client->config.svg_file));
85         y += svg_file_title->get_h() + 5;
86         struct stat st;
87         int64_t ms_time = stat(client->config.svg_file, &st) ? 0 :
88                 st.st_mtim.tv_sec*1000 + st.st_mtim.tv_nsec/1000000;
89         char mtime[BCSTRLEN];  mtime[0] = 0;
90         if( ms_time > 0 ) {
91                 time_t tm = ms_time/1000;
92                 ctime_r(&tm ,mtime);
93         }
94         add_tool(svg_file_mstime = new BC_Title(x0, y, mtime));
95         y += svg_file_mstime->get_h() + 15;
96
97         y = get_h() - NewSvgButton::calculate_h() - 5;
98         add_tool(new_svg_button = new NewSvgButton(client, this, x0, y));
99         y = get_h() - EditSvgButton::calculate_h() - 5;
100         add_tool(edit_svg_button = new EditSvgButton(client, this, x0+300, y));
101
102         show_window();
103         flush();
104 }
105
106 int SvgWin::close_event()
107 {
108         edit_svg_button->stop();
109         set_done(1);
110         return 1;
111 }
112
113 int SvgWin::hide_window(int flush)
114 {
115         edit_svg_button->stop();
116         return BC_WindowBase::hide_window(flush);
117 }
118
119
120 void SvgWin::update_gui(SvgConfig &config)
121 {
122         lock_window("SvgWin::update_gui");
123         out_x->update(config.out_x);
124         out_y->update(config.out_y);
125         out_w->update(config.out_w);
126         out_h->update(config.out_h);
127         dpi->update(config.dpi);
128         svg_file_title->update(config.svg_file);
129         char mtime[BCSTRLEN];  mtime[0] = 0;
130         if( config.ms_time > 0 ) {
131                 time_t tm = config.ms_time/1000;
132                 ctime_r(&tm ,mtime);
133         }
134         svg_file_mstime->update(mtime);
135         unlock_window();
136 }
137
138 static void flicker(BC_GenericButton *btn, int n, int clr)
139 {
140         int color = btn->get_color();
141         while( --n >= 0 ) {
142                 btn->text_color(clr);   btn->draw_face(1);
143                 btn->sync_display();    usleep(100000);
144                 btn->text_color(color); btn->draw_face(1);
145                 btn->sync_display();    usleep(100000);
146         }
147 }
148
149 SvgCoord::SvgCoord(SvgWin *win, SvgMain *client, int x, int y, float *value)
150  : BC_TumbleTextBox(win, *value, (float)0, (float)3000, x, y, 100)
151 {
152 //printf("SvgWidth::SvgWidth %f\n", client->config.w);
153         this->client = client;
154         this->win = win;
155         this->value = value;
156 }
157
158 SvgCoord::~SvgCoord()
159 {
160 }
161
162 int SvgCoord::handle_event()
163 {
164         *value = atof(get_text());
165         client->send_configure_change();
166         return 1;
167 }
168
169 NewSvgButton::NewSvgButton(SvgMain *client, SvgWin *window, int x, int y)
170  : BC_GenericButton(x, y, _("New/Open SVG..."))
171 {
172         this->client = client;
173         this->window = window;
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(this, 5, RED);
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                 NewSvgWindow *new_window = new NewSvgWindow(client, window, directory);
211                 new_window->create_objects();
212                 new_window->update_filter("*.svg");
213                 result = new_window->run_window();
214                 const char *filepath = new_window->get_path(0);
215                 strcpy(filename, filepath);
216                 delete new_window;
217                 if( result || !filepath || !*filepath ) {
218                         window->editing_lock.lock();
219                         window->editing = 0;
220                         window->editing_lock.unlock();
221                         return;              // cancel or no filename given
222                 }
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         struct stat st;
245         client->config.ms_time = stat(filename, &st) ? 0 :
246                 st.st_mtim.tv_sec*1000 + st.st_mtim.tv_nsec/1000000;
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 EditSvgButton::EditSvgButton(SvgMain *client, SvgWin *window, int x, int y)
258  : BC_GenericButton(x, y, _("Edit")), Thread(1)
259 {
260         this->client = client;
261         this->window = window;
262         fh_fifo = -1;
263 }
264
265 EditSvgButton::~EditSvgButton()
266 {
267         stop();
268 }
269
270 void EditSvgButton::stop()
271 {
272         if( running() ) {
273                 if( fh_fifo >= 0 ) {
274                         struct fifo_struct fifo_buf;
275                         fifo_buf.pid = getpid();
276                         fifo_buf.action = 3;
277                         write(fh_fifo, &fifo_buf, sizeof(fifo_buf));
278                 }
279         }
280         join();
281 }
282
283 int EditSvgButton::handle_event()
284 {
285         
286         window->editing_lock.lock();
287         if( !window->editing && client->config.svg_file[0] != 0 ) {
288                 window->editing = 1;
289                 window->editing_lock.unlock();
290                 start();
291         }
292         else {
293                 flicker(this, 5, RED);
294                 window->editing_lock.unlock();
295         }
296         return 1;
297 }
298
299 void EditSvgButton::run()
300 {
301 // ======================================= get path from user
302         char filename_png[1024];
303         char filename_fifo[1024];
304         strcpy(filename_png, client->config.svg_file);
305         strcat(filename_png, ".png");
306         remove(filename_png);
307         strcpy(filename_fifo, filename_png);
308         strcat(filename_fifo, ".fifo"); 
309         remove(filename_fifo);
310         if( !mkfifo(filename_fifo, S_IRWXU) &&
311             (fh_fifo = ::open(filename_fifo, O_RDWR+O_NONBLOCK)) >= 0 ) {
312                 SvgInkscapeThread inkscape_thread(this);
313                 inkscape_thread.start();
314                 int done = 0;
315                 while( inkscape_thread.running() && !done ) {
316                         struct stat st;
317                         int64_t ms_time = stat(client->config.svg_file, &st) ? 0 :
318                                 st.st_mtim.tv_sec*1000 + st.st_mtim.tv_nsec/1000000;
319                         if( client->config.ms_time != ms_time ) {
320                                 client->config.ms_time = ms_time;
321                                 client->send_configure_change();
322                         }
323                         // select(fh_fifo+1,rds,0,ers,tmo) does not work here
324                         Timer::delay(200);
325                         struct fifo_struct fifo_buf; fifo_buf.action = 1;
326                         int ret = read(fh_fifo, &fifo_buf, sizeof(fifo_buf));
327                         if( ret < 0 ) {
328                                 if( errno == EAGAIN ) continue;
329                                 perror("fifo");
330                                 break;
331                         }
332                         if( ret != sizeof(fifo_buf) ) continue;
333                         switch( fifo_buf.action ) {
334                         case 1: break;
335                         case 2: printf(_("Inkscape has exited\n"));
336                                 break;
337                         case 3: printf(_("Plugin window has closed\n"));
338                                 done = 1;
339                                 break;
340                         }
341                 }
342         }
343         else
344                 perror(_("Error opening fifo file"));
345         remove(filename_fifo); // fifo destroyed on last close
346         ::close(fh_fifo);
347         window->editing_lock.lock();
348         window->editing = 0;
349         window->editing_lock.unlock();
350         struct stat st;
351         client->config.ms_time = stat(client->config.svg_file, &st) ? 0 :
352                 st.st_mtim.tv_sec*1000 + st.st_mtim.tv_nsec/1000000;
353         client->send_configure_change();
354 }
355
356 SvgInkscapeThread::SvgInkscapeThread(EditSvgButton *edit)
357  : Thread(1)
358 {
359         this->edit = edit;;
360 }
361
362 SvgInkscapeThread::~SvgInkscapeThread()
363 {
364         cancel();
365         join();
366 }
367
368 void SvgInkscapeThread::run()
369 {
370 // Runs the inkscape
371         char command[1024];
372         sprintf(command, "inkscape --with-gui %s", edit->client->config.svg_file);
373         printf(_("Running external SVG editor: %s\n"), command);
374
375         enable_cancel();
376         system(command);
377         printf(_("External SVG editor finished\n"));
378         struct fifo_struct fifo_buf;
379         fifo_buf.pid = getpid();
380         fifo_buf.action = 2;
381         write(edit->fh_fifo, &fifo_buf, sizeof(fifo_buf));
382         disable_cancel();
383
384         return;
385 }
386
387
388
389 NewSvgWindow::NewSvgWindow(SvgMain *client, SvgWin *window, char *init_directory)
390  : BC_FileBox(0,
391         BC_WindowBase::get_resources()->filebox_h / 2,
392         init_directory,
393         _("SVG Plugin: Pick SVG file"),
394         _("Open an existing SVG file or create a new one"))
395 {
396         this->window = window;
397 }
398
399 NewSvgWindow::~NewSvgWindow() {}
400
401
402 DpiValue::DpiValue(SvgWin *win, SvgMain *client, int x, int y, float *value)
403  : BC_TumbleTextBox(win, *value, (float)10, (float)1000, x, y, 100)
404 {
405 //printf("SvgWidth::SvgWidth %f\n", client->config.w);
406         this->client = client;
407         this->win = win;
408         this->value = value;
409 }
410
411 DpiValue::~DpiValue()
412 {
413 }
414
415 int DpiValue::handle_event()
416 {
417         *value = atof(get_text());
418         return 1;
419 }
420
421
422 DpiButton::DpiButton( SvgWin *window, SvgMain *client, int x, int y)
423  : BC_GenericButton(x, y, _("update dpi"))
424 {
425         this->client = client;
426         this->window = window;
427 }
428
429 DpiButton::~DpiButton()
430 {
431 }
432
433 int DpiButton::handle_event()
434 {
435         client->send_configure_change();
436         return 1;
437 };
438