Fix a crash when running single-threaded and dragging the time slider.
[synfig.git] / synfig-studio / trunk / src / gtkmm / render.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file gtkmm/render.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "render.h"
34 #include "app.h"
35 #include <gtkmm/frame.h>
36 #include <gtkmm/alignment.h>
37 #include <synfig/target_scanline.h>
38 #include <synfig/canvas.h>
39 #include "asyncrenderer.h"
40
41 #endif
42
43 /* === U S I N G =========================================================== */
44
45 using namespace std;
46 using namespace etl;
47 using namespace synfig;
48 using namespace studio;
49
50 /* === M A C R O S ========================================================= */
51
52 /* === G L O B A L S ======================================================= */
53
54 /* === P R O C E D U R E S ================================================= */
55
56 /* === M E T H O D S ======================================================= */
57
58 RenderSettings::RenderSettings(Gtk::Window& parent,handle<synfigapp::CanvasInterface> canvas_interface):
59         Gtk::Dialog(_("Render Settings"),parent,false,true),
60         canvas_interface_(canvas_interface),
61         adjustment_quality(3,0,9),
62         entry_quality(adjustment_quality,1,0),
63         adjustment_antialias(1,1,31),
64         entry_antialias(adjustment_antialias,1,0),
65         toggle_single_frame(_("Use _current frame"), true)
66 {
67         widget_rend_desc.show();
68         widget_rend_desc.signal_changed().connect(sigc::mem_fun(*this,&studio::RenderSettings::on_rend_desc_changed));
69         widget_rend_desc.set_rend_desc(canvas_interface_->get_canvas()->rend_desc());
70
71         canvas_interface->signal_rend_desc_changed().connect(sigc::mem_fun(*this,&RenderSettings::on_rend_desc_changed));
72
73         menu_target=manage(new class Gtk::Menu());
74
75         menu_target->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Auto"),
76                         sigc::bind(sigc::mem_fun(*this,&RenderSettings::set_target),String())
77                 ));
78
79         synfig::Target::Book::iterator iter;
80         synfig::Target::Book book(synfig::Target::book());
81
82         for(iter=book.begin();iter!=book.end();iter++)
83         {
84                 menu_target->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->first,
85                         sigc::bind(sigc::mem_fun(*this,&RenderSettings::set_target),iter->first)
86                 ));
87         }
88         optionmenu_target.set_menu(*menu_target);
89
90         optionmenu_target.set_history(0);
91
92         Gtk::Alignment *dialogPadding = manage(new Gtk::Alignment(0, 0, 1, 1));
93         dialogPadding->set_padding(12, 12, 12, 12);
94         get_vbox()->pack_start(*dialogPadding, false, false, 0);
95
96         Gtk::VBox *dialogBox = manage(new Gtk::VBox(false, 12));
97         dialogPadding->add(*dialogBox);
98
99         Gtk::Button *choose_button(manage(new class Gtk::Button(Gtk::StockID(_("Choose...")))));
100         choose_button->show();
101         choose_button->signal_clicked().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_choose_pressed));
102
103         Gtk::Frame *target_frame=manage(new Gtk::Frame(_("Target")));
104         target_frame->set_shadow_type(Gtk::SHADOW_NONE);
105         ((Gtk::Label *) target_frame->get_label_widget())->set_markup(_("<b>Target</b>"));
106         dialogBox->pack_start(*target_frame);
107         Gtk::Alignment *targetPadding = manage(new Gtk::Alignment(0, 0, 1, 1));
108         targetPadding->set_padding(6, 0, 24, 0);
109         target_frame->add(*targetPadding);
110
111         Gtk::Table *target_table = manage(new Gtk::Table(2, 3, false));
112         target_table->set_row_spacings(6);
113         target_table->set_col_spacings(12);
114         targetPadding->add(*target_table);
115
116         Gtk::Label *filenameLabel = manage(new Gtk::Label(_("_Filename"), true));
117         filenameLabel->set_alignment(0, 0.5);
118         filenameLabel->set_mnemonic_widget(entry_filename);
119         target_table->attach(*filenameLabel, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
120         target_table->attach(entry_filename, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
121         target_table->attach(*choose_button, 2, 3, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
122
123         Gtk::Label *targetLabel = manage(new Gtk::Label(_("_Target"), true));
124         targetLabel->set_alignment(0, 0.5);
125         targetLabel->set_mnemonic_widget(optionmenu_target);
126         target_table->attach(*targetLabel, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
127         target_table->attach(optionmenu_target, 1, 3, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
128
129         toggle_single_frame.signal_toggled().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_single_frame_toggle));
130
131         Gtk::Frame *settings_frame=manage(new Gtk::Frame(_("Settings")));
132         settings_frame->set_shadow_type(Gtk::SHADOW_NONE);
133         ((Gtk::Label *) settings_frame->get_label_widget())->set_markup(_("<b>Settings</b>"));
134         dialogBox->pack_start(*settings_frame);
135
136         Gtk::Alignment *settingsPadding = manage(new Gtk::Alignment(0, 0, 1, 1));
137         settingsPadding->set_padding(6, 0, 24, 0);
138         settings_frame->add(*settingsPadding);
139
140         Gtk::Table *settings_table=manage(new Gtk::Table(2,2,false));
141         settings_table->set_row_spacings(6);
142         settings_table->set_col_spacings(12);
143         settingsPadding->add(*settings_table);
144
145         Gtk::Label *qualityLabel = manage(new Gtk::Label(_("_Quality"), true));
146         qualityLabel->set_alignment(0, 0.5);
147         qualityLabel->set_mnemonic_widget(entry_quality);
148         settings_table->attach(*qualityLabel, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
149         settings_table->attach(entry_quality, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
150
151         Gtk::Label *antiAliasLabel = manage(new Gtk::Label(_("_Anti-Aliasing"), true));
152         antiAliasLabel->set_alignment(0, 0.5);
153         antiAliasLabel->set_mnemonic_widget(entry_antialias);
154         settings_table->attach(*antiAliasLabel, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
155         settings_table->attach(entry_antialias, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
156
157         toggle_single_frame.set_alignment(0, 0.5);
158         settings_table->attach(toggle_single_frame, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
159
160         dialogBox->pack_start(widget_rend_desc);
161
162
163         Gtk::Button *render_button(manage(new class Gtk::Button(Gtk::StockID("Render"))));
164         render_button->show();
165         add_action_widget(*render_button,1);
166         render_button->signal_clicked().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_render_pressed));
167
168         Gtk::Button *cancel_button(manage(new class Gtk::Button(Gtk::StockID("gtk-cancel"))));
169         cancel_button->show();
170         add_action_widget(*cancel_button,0);
171         cancel_button->signal_clicked().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_cancel_pressed));
172
173         //set_default_response(1);
174
175         set_title(_("Render Settings")+String(" - ")+canvas_interface_->get_canvas()->get_name());
176
177
178         toggle_single_frame.set_active(true);
179         widget_rend_desc.disable_time_section();
180
181         set_entry_filename();
182
183         get_vbox()->show_all();
184 }
185
186 RenderSettings::~RenderSettings()
187 {
188 }
189
190 void
191 RenderSettings::set_entry_filename()
192 {
193         String filename(canvas_interface_->get_canvas()->get_file_name());
194
195         // if the basename of the filename has an extension, remove it
196         String base = basename(filename);
197         if(find(base.begin(),base.end(),'.')!=base.end())
198                 filename = String(filename.begin(), filename.begin()+filename.find_last_of('.'));
199
200         // if this isn't the root canvas, append (<canvasname>) to the filename
201         etl::handle<synfig::Canvas> canvas = canvas_interface_->get_canvas();
202         if (!canvas->is_root())
203                 if(canvas->get_name().empty())
204                         filename+=" ("+canvas->get_id()+')';
205                 else
206                         filename+=" ("+canvas->get_name()+')';
207
208         filename += ".png";
209
210         try
211         {
212                 entry_filename.set_text((filename));
213         }
214         catch(...)
215         {
216                 synfig::warning("Averted crash!");
217                 entry_filename.set_text("output.png");
218         }
219 }
220
221 void
222 RenderSettings::on_rend_desc_changed()
223 {
224         widget_rend_desc.set_rend_desc(canvas_interface_->get_canvas()->rend_desc());
225 }
226
227 void
228 RenderSettings::set_target(synfig::String name)
229 {
230         target_name=name;
231 }
232
233 void
234 RenderSettings::on_choose_pressed()
235 {
236         String filename=entry_filename.get_text();
237         if(App::dialog_save_file("Save Render As",filename))
238                 entry_filename.set_text(filename);
239 }
240
241 void
242 RenderSettings::on_render_pressed()
243 {
244         String filename=entry_filename.get_text();
245
246         if(filename.empty())
247         {
248                 canvas_interface_->get_ui_interface()->error(_("You must supply a filename!"));
249                 return;
250         }
251
252         // If the target type is not yet defined,
253         // try to figure it out from the outfile.
254         if(target_name.empty())
255         {
256                 try
257                 {
258                         String ext=String(find(filename.begin(),filename.end(),'.')+1,filename.end());
259                         if(Target::ext_book().count(ext))
260                                 target_name=Target::ext_book()[ext];
261                         else
262                                 target_name=ext;
263                 }
264                 catch(std::runtime_error x)
265                 {
266                         canvas_interface_->get_ui_interface()->error(_("Unable to determine proper target from filename."));
267                         return;
268                 }
269         }
270
271         if(filename.empty() && target_name!="null")
272         {
273                 canvas_interface_->get_ui_interface()->error(_("A filename is required for this target"));
274                 return;
275         }
276
277         Target::Handle target=Target::create(target_name,filename);
278         if(!target)
279         {
280                 canvas_interface_->get_ui_interface()->error(_("Unable to create target for ")+filename);
281                 return;
282         }
283
284         hide();
285
286         target->set_canvas(canvas_interface_->get_canvas());
287         RendDesc rend_desc(widget_rend_desc.get_rend_desc());
288         rend_desc.set_antialias((int)adjustment_antialias.get_value());
289
290         // If we are to only render the current frame
291         if(toggle_single_frame.get_active())
292                 rend_desc.set_time(canvas_interface_->get_time());
293
294         target->set_rend_desc(&rend_desc);
295         target->set_quality((int)adjustment_quality.get_value());
296         if( !target->init() ){
297                 canvas_interface_->get_ui_interface()->error(_("Target initialisation failure"));
298                 return;
299         }
300
301         canvas_interface_->get_ui_interface()->task(_("Rendering ")+filename);
302
303         if(async_renderer)
304         {
305                 async_renderer->stop();
306                 async_renderer.detach();
307         }
308         async_renderer=new AsyncRenderer(target);
309         async_renderer->signal_finished().connect( sigc::mem_fun(*this,&RenderSettings::on_finished));
310         async_renderer->start();
311         /*
312         if(!target->render(canvas_interface_->get_ui_interface().get()))
313         {
314                 canvas_interface_->get_ui_interface()->error(_("Render Failure"));
315                 canvas_interface_->get_ui_interface()->amount_complete(0,10000);
316                 return;
317         }
318
319         // Success!
320         canvas_interface_->get_ui_interface()->task(filename+_(" rendered successfully"));
321         canvas_interface_->get_ui_interface()->amount_complete(0,10000);
322         */
323         return;
324 }
325
326 void
327 RenderSettings::on_finished()
328 {
329         canvas_interface_->get_ui_interface()->task(_("File rendered successfully"));
330         canvas_interface_->get_ui_interface()->amount_complete(0,10000);
331 }
332
333 void
334 RenderSettings::on_cancel_pressed()
335 {
336         hide();
337 }
338
339 void
340 RenderSettings::on_single_frame_toggle()
341 {
342         if(toggle_single_frame.get_active())
343                 widget_rend_desc.disable_time_section();
344         else
345                 widget_rend_desc.enable_time_section();
346 }