Allow linking widths and tangents to positions on a bline. It also now works with...
[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, 2008 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 #include "general.h"
42
43 #endif
44
45 /* === U S I N G =========================================================== */
46
47 using namespace std;
48 using namespace etl;
49 using namespace synfig;
50 using namespace studio;
51
52 /* === M A C R O S ========================================================= */
53
54 /* === G L O B A L S ======================================================= */
55
56 /* === P R O C E D U R E S ================================================= */
57
58 /* === M E T H O D S ======================================================= */
59
60 RenderSettings::RenderSettings(Gtk::Window& parent, etl::handle<synfigapp::CanvasInterface> canvas_interface):
61         Gtk::Dialog(_("Render Settings"),parent,false,true),
62         canvas_interface_(canvas_interface),
63         adjustment_quality(3,0,9),
64         entry_quality(adjustment_quality,1,0),
65         adjustment_antialias(1,1,31),
66         entry_antialias(adjustment_antialias,1,0),
67         toggle_single_frame(_("Use _current frame"), true)
68 {
69         widget_rend_desc.show();
70         widget_rend_desc.signal_changed().connect(sigc::mem_fun(*this,&studio::RenderSettings::on_rend_desc_changed));
71         widget_rend_desc.set_rend_desc(canvas_interface_->get_canvas()->rend_desc());
72
73         canvas_interface->signal_rend_desc_changed().connect(sigc::mem_fun(*this,&RenderSettings::on_rend_desc_changed));
74
75         menu_target=manage(new class Gtk::Menu());
76
77         menu_target->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Auto"),
78                         sigc::bind(sigc::mem_fun(*this,&RenderSettings::set_target),String())
79                 ));
80
81         synfig::Target::Book::iterator iter;
82         synfig::Target::Book book(synfig::Target::book());
83
84         for(iter=book.begin();iter!=book.end();iter++)
85         {
86                 menu_target->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->first,
87                         sigc::bind(sigc::mem_fun(*this,&RenderSettings::set_target),iter->first)
88                 ));
89         }
90         optionmenu_target.set_menu(*menu_target);
91
92         optionmenu_target.set_history(0);
93
94         Gtk::Alignment *dialogPadding = manage(new Gtk::Alignment(0, 0, 1, 1));
95         dialogPadding->set_padding(12, 12, 12, 12);
96         get_vbox()->pack_start(*dialogPadding, false, false, 0);
97
98         Gtk::VBox *dialogBox = manage(new Gtk::VBox(false, 12));
99         dialogPadding->add(*dialogBox);
100
101         Gtk::Button *choose_button(manage(new class Gtk::Button(Gtk::StockID(_("Choose...")))));
102         choose_button->show();
103         choose_button->signal_clicked().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_choose_pressed));
104
105         Gtk::Frame *target_frame=manage(new Gtk::Frame(_("Target")));
106         target_frame->set_shadow_type(Gtk::SHADOW_NONE);
107         ((Gtk::Label *) target_frame->get_label_widget())->set_markup(_("<b>Target</b>"));
108         dialogBox->pack_start(*target_frame);
109         Gtk::Alignment *targetPadding = manage(new Gtk::Alignment(0, 0, 1, 1));
110         targetPadding->set_padding(6, 0, 24, 0);
111         target_frame->add(*targetPadding);
112
113         Gtk::Table *target_table = manage(new Gtk::Table(2, 3, false));
114         target_table->set_row_spacings(6);
115         target_table->set_col_spacings(12);
116         targetPadding->add(*target_table);
117
118         Gtk::Label *filenameLabel = manage(new Gtk::Label(_("_Filename"), true));
119         filenameLabel->set_alignment(0, 0.5);
120         filenameLabel->set_mnemonic_widget(entry_filename);
121         target_table->attach(*filenameLabel, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
122         target_table->attach(entry_filename, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
123         target_table->attach(*choose_button, 2, 3, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
124
125         Gtk::Label *targetLabel = manage(new Gtk::Label(_("_Target"), true));
126         targetLabel->set_alignment(0, 0.5);
127         targetLabel->set_mnemonic_widget(optionmenu_target);
128         target_table->attach(*targetLabel, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
129         target_table->attach(optionmenu_target, 1, 3, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
130
131         toggle_single_frame.signal_toggled().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_single_frame_toggle));
132
133         Gtk::Frame *settings_frame=manage(new Gtk::Frame(_("Settings")));
134         settings_frame->set_shadow_type(Gtk::SHADOW_NONE);
135         ((Gtk::Label *) settings_frame->get_label_widget())->set_markup(_("<b>Settings</b>"));
136         dialogBox->pack_start(*settings_frame);
137
138         Gtk::Alignment *settingsPadding = manage(new Gtk::Alignment(0, 0, 1, 1));
139         settingsPadding->set_padding(6, 0, 24, 0);
140         settings_frame->add(*settingsPadding);
141
142         Gtk::Table *settings_table=manage(new Gtk::Table(2,2,false));
143         settings_table->set_row_spacings(6);
144         settings_table->set_col_spacings(12);
145         settingsPadding->add(*settings_table);
146
147         Gtk::Label *qualityLabel = manage(new Gtk::Label(_("_Quality"), true));
148         qualityLabel->set_alignment(0, 0.5);
149         qualityLabel->set_mnemonic_widget(entry_quality);
150         settings_table->attach(*qualityLabel, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
151         settings_table->attach(entry_quality, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
152
153         Gtk::Label *antiAliasLabel = manage(new Gtk::Label(_("_Anti-Aliasing"), true));
154         antiAliasLabel->set_alignment(0, 0.5);
155         antiAliasLabel->set_mnemonic_widget(entry_antialias);
156         settings_table->attach(*antiAliasLabel, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
157         settings_table->attach(entry_antialias, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
158
159         toggle_single_frame.set_alignment(0, 0.5);
160         settings_table->attach(toggle_single_frame, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::SHRINK|Gtk::FILL, 0, 0);
161
162         dialogBox->pack_start(widget_rend_desc);
163
164
165         Gtk::Button *render_button(manage(new class Gtk::Button(Gtk::StockID("Render"))));
166         render_button->show();
167         add_action_widget(*render_button,1);
168         render_button->signal_clicked().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_render_pressed));
169
170         Gtk::Button *cancel_button(manage(new class Gtk::Button(Gtk::StockID("gtk-cancel"))));
171         cancel_button->show();
172         add_action_widget(*cancel_button,0);
173         cancel_button->signal_clicked().connect(sigc::mem_fun(*this, &studio::RenderSettings::on_cancel_pressed));
174
175         //set_default_response(1);
176
177         set_title(_("Render Settings")+String(" - ")+canvas_interface_->get_canvas()->get_name());
178
179         toggle_single_frame.set_active(false);
180         widget_rend_desc.enable_time_section();
181
182         set_entry_filename();
183
184         get_vbox()->show_all();
185 }
186
187 RenderSettings::~RenderSettings()
188 {
189 }
190
191 void
192 RenderSettings::set_entry_filename()
193 {
194         String filename(filename_sans_extension(canvas_interface_->get_canvas()->get_file_name()));
195
196         // if this isn't the root canvas, append (<canvasname>) to the filename
197         etl::handle<synfig::Canvas> canvas = canvas_interface_->get_canvas();
198         if (!canvas->is_root())
199         {
200                 if(canvas->get_name().empty())
201                         filename+=" ("+canvas->get_id()+')';
202                 else
203                         filename+=" ("+canvas->get_name()+')';
204         }
205
206         filename += ".png";
207
208         try
209         {
210                 entry_filename.set_text((filename));
211         }
212         catch(...)
213         {
214                 synfig::warning("Averted crash!");
215                 entry_filename.set_text("output.png");
216         }
217 }
218
219 void
220 RenderSettings::on_rend_desc_changed()
221 {
222         widget_rend_desc.set_rend_desc(canvas_interface_->get_canvas()->rend_desc());
223 }
224
225 void
226 RenderSettings::set_target(synfig::String name)
227 {
228         target_name=name;
229 }
230
231 void
232 RenderSettings::on_choose_pressed()
233 {
234         String filename=entry_filename.get_text();
235         if(App::dialog_save_file("Save Render As", filename, RENDER_DIR_PREFERENCE))
236                 entry_filename.set_text(filename);
237 }
238
239 void
240 RenderSettings::on_render_pressed()
241 {
242         String filename=entry_filename.get_text();
243         synfig::String calculated_target_name(target_name);
244
245         if(filename.empty())
246         {
247                 canvas_interface_->get_ui_interface()->error(_("You must supply a filename!"));
248                 return;
249         }
250
251         // If the target type is not yet defined,
252         // try to figure it out from the outfile.
253         if(calculated_target_name.empty())
254         {
255                 try
256                 {
257                         String ext(filename_extension(filename));
258                         if (ext.size()) ext=ext.substr(1); // skip initial '.'
259                         synfig::info("render target filename: '%s'; extension: '%s'", filename.c_str(), ext.c_str());
260                         if(Target::ext_book().count(ext))
261                         {
262                                 calculated_target_name=Target::ext_book()[ext];
263                                 synfig::info("'%s' is a known extension - using target '%s'", ext.c_str(), calculated_target_name.c_str());
264                         }
265                         else
266                         {
267                                 calculated_target_name=ext;
268                                 synfig::info("unknown extension");
269                         }
270                 }
271                 catch(std::runtime_error x)
272                 {
273                         canvas_interface_->get_ui_interface()->error(_("Unable to determine proper target from filename."));
274                         return;
275                 }
276         }
277
278         if(filename.empty() && calculated_target_name!="null")
279         {
280                 canvas_interface_->get_ui_interface()->error(_("A filename is required for this target"));
281                 return;
282         }
283
284         Target::Handle target=Target::create(calculated_target_name,filename);
285         if(!target)
286         {
287                 canvas_interface_->get_ui_interface()->error(_("Unable to create target for ")+filename);
288                 return;
289         }
290
291         hide();
292
293         target->set_canvas(canvas_interface_->get_canvas());
294         RendDesc rend_desc(widget_rend_desc.get_rend_desc());
295         rend_desc.set_antialias((int)adjustment_antialias.get_value());
296
297         // If we are to only render the current frame
298         if(toggle_single_frame.get_active())
299                 rend_desc.set_time(canvas_interface_->get_time());
300
301         target->set_rend_desc(&rend_desc);
302         target->set_quality((int)adjustment_quality.get_value());
303         if( !target->init() ){
304                 canvas_interface_->get_ui_interface()->error(_("Target initialization failure"));
305                 return;
306         }
307
308         canvas_interface_->get_ui_interface()->task(_("Rendering ")+filename);
309
310         if(async_renderer)
311         {
312                 async_renderer->stop();
313                 async_renderer.detach();
314         }
315         async_renderer=new AsyncRenderer(target);
316         async_renderer->signal_finished().connect( sigc::mem_fun(*this,&RenderSettings::on_finished));
317         async_renderer->start();
318         /*
319         if(!target->render(canvas_interface_->get_ui_interface().get()))
320         {
321                 canvas_interface_->get_ui_interface()->error(_("Render Failure"));
322                 canvas_interface_->get_ui_interface()->amount_complete(0,10000);
323                 return;
324         }
325
326         // Success!
327         canvas_interface_->get_ui_interface()->task(filename+_(" rendered successfully"));
328         canvas_interface_->get_ui_interface()->amount_complete(0,10000);
329         */
330         return;
331 }
332
333 void
334 RenderSettings::on_finished()
335 {
336         canvas_interface_->get_ui_interface()->task(_("File rendered successfully"));
337         canvas_interface_->get_ui_interface()->amount_complete(0,10000);
338 }
339
340 void
341 RenderSettings::on_cancel_pressed()
342 {
343         hide();
344 }
345
346 void
347 RenderSettings::on_single_frame_toggle()
348 {
349         if(toggle_single_frame.get_active())
350                 widget_rend_desc.disable_time_section();
351         else
352                 widget_rend_desc.enable_time_section();
353 }