1 | /* === S Y N F I G ========================================================= */ |
---|
2 | /*! \file gtkmm/instance.cpp |
---|
3 | ** \brief writeme |
---|
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 | ** Copyright (c) 2008, 2011 Carlos López |
---|
11 | ** Copyright (c) 2009 Nikita Kitaev |
---|
12 | ** Copyright (c) 2012 Konstantin Dmitriev |
---|
13 | ** |
---|
14 | ** This package is free software; you can redistribute it and/or |
---|
15 | ** modify it under the terms of the GNU General Public License as |
---|
16 | ** published by the Free Software Foundation; either version 2 of |
---|
17 | ** the License, or (at your option) any later version. |
---|
18 | ** |
---|
19 | ** This package is distributed in the hope that it will be useful, |
---|
20 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
21 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
22 | ** General Public License for more details. |
---|
23 | ** \endlegal |
---|
24 | */ |
---|
25 | /* ========================================================================= */ |
---|
26 | |
---|
27 | /* === H E A D E R S ======================================================= */ |
---|
28 | |
---|
29 | #ifdef USING_PCH |
---|
30 | # include "pch.h" |
---|
31 | #else |
---|
32 | #ifdef HAVE_CONFIG_H |
---|
33 | # include <config.h> |
---|
34 | #endif |
---|
35 | |
---|
36 | #include "instance.h" |
---|
37 | #include <cassert> |
---|
38 | |
---|
39 | #include <gtkmm/stock.h> |
---|
40 | #include <gtkmm/image.h> |
---|
41 | #include <gtkmm/menuitem.h> |
---|
42 | #include <gtkmm/imagemenuitem.h> |
---|
43 | #include <gtkmm/separatormenuitem.h> |
---|
44 | #include <gtkmm/button.h> |
---|
45 | |
---|
46 | #include <iostream> |
---|
47 | #include "canvasview.h" |
---|
48 | #include "app.h" |
---|
49 | #include <sigc++/sigc++.h> |
---|
50 | #include "docks/dock_toolbox.h" |
---|
51 | #include "onemoment.h" |
---|
52 | #include <synfig/savecanvas.h> |
---|
53 | |
---|
54 | #include "autorecover.h" |
---|
55 | #include <synfig/valuenodes/valuenode_composite.h> |
---|
56 | #include <synfig/valuenodes/valuenode_duplicate.h> |
---|
57 | #include "widgets/widget_waypointmodel.h" |
---|
58 | #include <gtkmm/actiongroup.h> |
---|
59 | #include "iconcontroller.h" |
---|
60 | #include "workarea.h" |
---|
61 | #include <sys/stat.h> |
---|
62 | #include <errno.h> |
---|
63 | #include <ETL/stringf> |
---|
64 | |
---|
65 | #include "general.h" |
---|
66 | |
---|
67 | #endif |
---|
68 | |
---|
69 | using namespace std; |
---|
70 | using namespace etl; |
---|
71 | using namespace synfig; |
---|
72 | using namespace studio; |
---|
73 | using namespace sigc; |
---|
74 | |
---|
75 | /* === M A C R O S ========================================================= */ |
---|
76 | |
---|
77 | /* === G L O B A L S ======================================================= */ |
---|
78 | |
---|
79 | int studio::Instance::instance_count_=0; |
---|
80 | |
---|
81 | /* === P R O C E D U R E S ================================================= */ |
---|
82 | |
---|
83 | /* === M E T H O D S ======================================================= */ |
---|
84 | |
---|
85 | Instance::Instance(synfig::Canvas::Handle canvas, etl::handle< synfig::FileContainerTemporary > container): |
---|
86 | synfigapp::Instance (canvas, container), |
---|
87 | canvas_tree_store_ (Gtk::TreeStore::create(canvas_tree_model)), |
---|
88 | history_tree_store_ (HistoryTreeStore::create(this)), |
---|
89 | undo_status_(false), |
---|
90 | redo_status_(false) |
---|
91 | { |
---|
92 | id_=instance_count_++; |
---|
93 | |
---|
94 | // Connect up all the signals |
---|
95 | signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles)); |
---|
96 | signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles))); |
---|
97 | signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status)); |
---|
98 | signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status)); |
---|
99 | |
---|
100 | signal_saved().connect( |
---|
101 | sigc::hide_return( |
---|
102 | sigc::ptr_fun( |
---|
103 | studio::AutoRecover::auto_backup |
---|
104 | ) |
---|
105 | ) |
---|
106 | ); |
---|
107 | |
---|
108 | refresh_canvas_tree(); |
---|
109 | } |
---|
110 | |
---|
111 | Instance::~Instance() |
---|
112 | { |
---|
113 | } |
---|
114 | |
---|
115 | int |
---|
116 | Instance::get_visible_canvases()const |
---|
117 | { |
---|
118 | int count(0); |
---|
119 | CanvasViewList::const_iterator iter; |
---|
120 | for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter) |
---|
121 | if((*iter)->get_visible()) |
---|
122 | count++; |
---|
123 | return count; |
---|
124 | } |
---|
125 | |
---|
126 | handle<Instance> |
---|
127 | Instance::create(synfig::Canvas::Handle canvas, etl::handle< synfig::FileContainerTemporary > container) |
---|
128 | { |
---|
129 | // Construct a new instance |
---|
130 | handle<Instance> instance(new Instance(canvas, container)); |
---|
131 | |
---|
132 | // Add the new instance to the application's instance list |
---|
133 | App::instance_list.push_back(instance); |
---|
134 | |
---|
135 | // Set up the instance with the default UI manager |
---|
136 | instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface()); |
---|
137 | |
---|
138 | // Signal the new instance |
---|
139 | App::signal_instance_created()(instance); |
---|
140 | |
---|
141 | // And then make sure that is has been selected |
---|
142 | App::set_selected_instance(instance); |
---|
143 | |
---|
144 | // Create the initial window for the root canvas |
---|
145 | instance->focus(canvas); |
---|
146 | |
---|
147 | return instance; |
---|
148 | } |
---|
149 | |
---|
150 | handle<CanvasView> |
---|
151 | Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas) |
---|
152 | { |
---|
153 | if(!canvas) |
---|
154 | return 0; |
---|
155 | |
---|
156 | while(canvas->is_inline()) |
---|
157 | canvas=canvas->parent(); |
---|
158 | |
---|
159 | CanvasViewList::iterator iter; |
---|
160 | |
---|
161 | for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++) |
---|
162 | if((*iter)->get_canvas()==canvas) |
---|
163 | return *iter; |
---|
164 | |
---|
165 | return CanvasView::create(this,canvas); |
---|
166 | } |
---|
167 | |
---|
168 | void |
---|
169 | Instance::focus(etl::handle<synfig::Canvas> canvas) |
---|
170 | { |
---|
171 | handle<CanvasView> canvas_view=find_canvas_view(canvas); |
---|
172 | assert(canvas_view); |
---|
173 | canvas_view->present(); |
---|
174 | } |
---|
175 | |
---|
176 | void |
---|
177 | Instance::set_undo_status(bool x) |
---|
178 | { |
---|
179 | undo_status_=x; |
---|
180 | App::dock_toolbox->update_tools(); |
---|
181 | signal_undo_redo_status_changed()(); |
---|
182 | } |
---|
183 | |
---|
184 | void |
---|
185 | Instance::set_redo_status(bool x) |
---|
186 | { |
---|
187 | redo_status_=x; |
---|
188 | App::dock_toolbox->update_tools(); |
---|
189 | signal_undo_redo_status_changed()(); |
---|
190 | } |
---|
191 | |
---|
192 | void |
---|
193 | studio::Instance::run_plugin(std::string plugin_path) |
---|
194 | { |
---|
195 | handle<synfigapp::UIInterface> uim = this->find_canvas_view(this->get_canvas())->get_ui_interface(); |
---|
196 | |
---|
197 | string message = strprintf(_("Do you realy want to run plugin for file \"%s\"?" ), |
---|
198 | this->get_canvas()->get_name().c_str()); |
---|
199 | |
---|
200 | string details = strprintf(_("This operation cannot be undone and all undo history will be cleared.")); |
---|
201 | |
---|
202 | int answer = uim->confirmation( |
---|
203 | message, |
---|
204 | details, |
---|
205 | _("Cancel"), |
---|
206 | _("Proceed"), |
---|
207 | synfigapp::UIInterface::RESPONSE_OK); |
---|
208 | |
---|
209 | if(answer == synfigapp::UIInterface::RESPONSE_OK){ |
---|
210 | |
---|
211 | OneMoment one_moment; |
---|
212 | |
---|
213 | Canvas::Handle canvas(this->get_canvas()); |
---|
214 | |
---|
215 | synfigapp::PluginLauncher launcher(canvas); |
---|
216 | |
---|
217 | Time cur_time; |
---|
218 | cur_time = canvas->get_time(); |
---|
219 | |
---|
220 | this->close(); |
---|
221 | |
---|
222 | if(canvas->count()!=1) |
---|
223 | { |
---|
224 | one_moment.hide(); |
---|
225 | App::dialog_message_1b( |
---|
226 | "ERROR", |
---|
227 | _("The plugin operation has failed."), |
---|
228 | _("This can be due to current file " |
---|
229 | "being referenced by another composition that is already open, " |
---|
230 | "or because of an internal error in Synfig Studio. Try closing " |
---|
231 | "any compositions that might reference this file and try again, " |
---|
232 | "or restart Synfig Studio."), |
---|
233 | _("Close")); |
---|
234 | |
---|
235 | one_moment.show(); |
---|
236 | |
---|
237 | } else { |
---|
238 | bool result; |
---|
239 | result = launcher.execute( plugin_path, App::get_base_path() ); |
---|
240 | if (!result){ |
---|
241 | one_moment.hide(); |
---|
242 | App::dialog_message_1b( |
---|
243 | "Error", |
---|
244 | launcher.get_output(), |
---|
245 | "details", |
---|
246 | _("Close")); |
---|
247 | |
---|
248 | one_moment.show(); |
---|
249 | |
---|
250 | } |
---|
251 | } |
---|
252 | |
---|
253 | |
---|
254 | canvas=0; |
---|
255 | |
---|
256 | App::open_as(launcher.get_result_path(),launcher.get_original_path()); |
---|
257 | |
---|
258 | |
---|
259 | etl::handle<Instance> new_instance = App::instance_list.back(); |
---|
260 | new_instance->inc_action_count(); // This file isn't saved! mark it as such |
---|
261 | |
---|
262 | // Restore time cursor position |
---|
263 | canvas = App::instance_list.back()->get_canvas(); |
---|
264 | etl::handle<synfigapp::CanvasInterface> new_canvas_interface(new_instance->find_canvas_interface(canvas)); |
---|
265 | new_canvas_interface->set_time(cur_time); |
---|
266 | |
---|
267 | } |
---|
268 | return; |
---|
269 | } |
---|
270 | |
---|
271 | |
---|
272 | bool |
---|
273 | studio::Instance::save_as(const synfig::String &file_name) |
---|
274 | { |
---|
275 | if(synfigapp::Instance::save_as(file_name)) |
---|
276 | { |
---|
277 | // after changing the filename, update the render settings with the new filename |
---|
278 | list<handle<CanvasView> >::iterator iter; |
---|
279 | for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++) |
---|
280 | (*iter)->render_settings.set_entry_filename(); |
---|
281 | App::add_recent_file(etl::handle<Instance>(this)); |
---|
282 | return true; |
---|
283 | } |
---|
284 | return false; |
---|
285 | } |
---|
286 | |
---|
287 | void |
---|
288 | studio::Instance::open() |
---|
289 | { |
---|
290 | App::dialog_open(get_file_name()); |
---|
291 | } |
---|
292 | |
---|
293 | Instance::Status |
---|
294 | studio::Instance::save() |
---|
295 | { |
---|
296 | // if we don't have a real filename yet then we need to ask where to save it |
---|
297 | if (!has_real_filename()) |
---|
298 | { |
---|
299 | if (dialog_save_as()) |
---|
300 | return STATUS_OK; |
---|
301 | else |
---|
302 | return STATUS_CANCEL; |
---|
303 | } |
---|
304 | |
---|
305 | if (synfigapp::Instance::save()) |
---|
306 | { |
---|
307 | App::add_recent_file(etl::handle<Instance>(this)); |
---|
308 | return STATUS_OK; |
---|
309 | } |
---|
310 | string msg(strprintf(_("Unable to save to '%s'"), get_file_name().c_str())); |
---|
311 | App::dialog_message_1b( |
---|
312 | "ERROR", |
---|
313 | msg.c_str(), |
---|
314 | "details", |
---|
315 | _("Close")); |
---|
316 | |
---|
317 | return STATUS_ERROR; |
---|
318 | } |
---|
319 | |
---|
320 | // the filename will be set to "Synfig Animation 1" or some such when first created |
---|
321 | // and will be changed to an absolute path once it has been saved |
---|
322 | // so if it still begins with "Synfig Animation " then we don't have a real filename yet |
---|
323 | bool |
---|
324 | studio::Instance::has_real_filename() |
---|
325 | { |
---|
326 | return get_file_name().find(App::custom_filename_prefix.c_str()) != 0; |
---|
327 | } |
---|
328 | |
---|
329 | bool |
---|
330 | studio::Instance::dialog_save_as() |
---|
331 | { |
---|
332 | string filename = get_file_name(); |
---|
333 | Canvas::Handle canvas(get_canvas()); |
---|
334 | |
---|
335 | { |
---|
336 | OneMoment one_moment; |
---|
337 | std::set<Node*>::iterator iter; |
---|
338 | for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter) |
---|
339 | { |
---|
340 | synfig::Node* node(*iter); |
---|
341 | for(;!node->parent_set.empty();node=*node->parent_set.begin()) |
---|
342 | { |
---|
343 | Layer::Handle parent_layer(dynamic_cast<Layer*>(node)); |
---|
344 | if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas()) |
---|
345 | { |
---|
346 | //! \todo Fix big involving "Save As" with referenced compositions |
---|
347 | string msg(strprintf(_("There is currently a bug when using \"SaveAs\"\n" |
---|
348 | "on a composition that is being referenced by other\n" |
---|
349 | "files that are currently open. Close these\n" |
---|
350 | "other files first before trying to use \"SaveAs\"."))); |
---|
351 | App::dialog_message_1b( |
---|
352 | "ERROR", |
---|
353 | msg.c_str(), |
---|
354 | "details", |
---|
355 | _("Close")); |
---|
356 | |
---|
357 | return false; |
---|
358 | } |
---|
359 | if(parent_layer) |
---|
360 | break; |
---|
361 | } |
---|
362 | } |
---|
363 | } |
---|
364 | |
---|
365 | if (has_real_filename()) |
---|
366 | filename = absolute_path(filename); |
---|
367 | |
---|
368 | // show the canvas' name if it has one, else its ID |
---|
369 | while (App::dialog_save_file((_("Please choose a file name") + |
---|
370 | String(" (") + |
---|
371 | (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) + |
---|
372 | ")"), |
---|
373 | filename, ANIMATION_DIR_PREFERENCE)) |
---|
374 | { |
---|
375 | // If the filename still has wildcards, then we should |
---|
376 | // continue looking for the file we want |
---|
377 | string base_filename = basename(filename); |
---|
378 | if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end()) |
---|
379 | continue; |
---|
380 | |
---|
381 | // if file extension is not recognized, then forced to .sifz |
---|
382 | if (filename_extension(filename) == "") |
---|
383 | filename+=".sifz"; |
---|
384 | |
---|
385 | // forced to .sifz, the below code is not need anymore |
---|
386 | try |
---|
387 | { |
---|
388 | String ext(filename_extension(filename)); |
---|
389 | // todo: ".sfg" literal and others |
---|
390 | if (ext != ".sif" && ext != ".sifz" && ext != ".sfg" && !App::dialog_message_2b( |
---|
391 | _("Unknown extension"), |
---|
392 | _("You have given the file name an extension which I do not recognize. " |
---|
393 | "Are you sure this is what you want?"), |
---|
394 | Gtk::MESSAGE_QUESTION, |
---|
395 | _("Cancel"), |
---|
396 | _("Sure")) |
---|
397 | ) |
---|
398 | continue; |
---|
399 | } |
---|
400 | catch(...) |
---|
401 | { |
---|
402 | continue; |
---|
403 | } |
---|
404 | |
---|
405 | { |
---|
406 | struct stat s; |
---|
407 | int stat_return = stat(filename.c_str(), &s); |
---|
408 | |
---|
409 | // if stat() fails with something other than 'file doesn't exist', there's been a real |
---|
410 | // error of some kind. let's give up now and ask for a new path. |
---|
411 | if (stat_return == -1 && errno != ENOENT) |
---|
412 | { |
---|
413 | perror(filename.c_str()); |
---|
414 | string msg(strprintf(_("Unable to check whether '%s' exists."), filename.c_str())); |
---|
415 | App::dialog_message_1b( |
---|
416 | "ERROR", |
---|
417 | msg.c_str(), |
---|
418 | "details", |
---|
419 | _("Close")); |
---|
420 | |
---|
421 | continue; |
---|
422 | } |
---|
423 | |
---|
424 | // If the file exists and the user doesn't want to overwrite it, keep prompting for a filename |
---|
425 | string message = strprintf(_("A file named \"%s\" already exists. " |
---|
426 | "Do you want to replace it?"), |
---|
427 | basename(filename).c_str()); |
---|
428 | |
---|
429 | string details = strprintf(_("The file already exists in \"%s\". " |
---|
430 | "Replacing it will overwrite its contents."), |
---|
431 | basename(dirname(filename)).c_str()); |
---|
432 | |
---|
433 | if ((stat_return == 0) && !App::dialog_message_2b( |
---|
434 | message, |
---|
435 | details, |
---|
436 | Gtk::MESSAGE_QUESTION, |
---|
437 | _("Use Another Name…"), |
---|
438 | _("Replace")) |
---|
439 | ) |
---|
440 | continue; |
---|
441 | } |
---|
442 | |
---|
443 | if(save_as(filename)) |
---|
444 | { |
---|
445 | synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1)); |
---|
446 | return true; |
---|
447 | } |
---|
448 | string msg(strprintf(_("Unable to save to '%s'"), filename.c_str())); |
---|
449 | App::dialog_message_1b( |
---|
450 | "ERROR", |
---|
451 | msg.c_str(), |
---|
452 | "details", |
---|
453 | _("Close")); |
---|
454 | } |
---|
455 | |
---|
456 | return false; |
---|
457 | } |
---|
458 | |
---|
459 | void |
---|
460 | Instance::update_all_titles() |
---|
461 | { |
---|
462 | list<handle<CanvasView> >::iterator iter; |
---|
463 | for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++) |
---|
464 | (*iter)->update_title(); |
---|
465 | } |
---|
466 | |
---|
467 | void |
---|
468 | Instance::close() |
---|
469 | { |
---|
470 | // This will increase the reference count so we don't get DELETED |
---|
471 | // until we are ready |
---|
472 | handle<Instance> me(this); |
---|
473 | |
---|
474 | /* |
---|
475 | We need to hide some panels when instance is closed. |
---|
476 | This is done to avoid the crash when two conditions met: |
---|
477 | 1) the list is scrolled down |
---|
478 | 2) user closes file |
---|
479 | */ |
---|
480 | handle<CanvasView> canvas_view=find_canvas_view(get_canvas()); |
---|
481 | Gtk::Widget* tree_view_keyframes = canvas_view->get_ext_widget("keyframes"); |
---|
482 | tree_view_keyframes->hide(); |
---|
483 | |
---|
484 | Gtk::Widget* tree_view_params = canvas_view->get_ext_widget("params"); |
---|
485 | tree_view_params->hide(); |
---|
486 | |
---|
487 | Gtk::Widget* tree_view_children = canvas_view->get_ext_widget("children"); |
---|
488 | tree_view_children->hide(); |
---|
489 | |
---|
490 | // Make sure we aren't selected as the current instance |
---|
491 | if(studio::App::get_selected_instance()==this) |
---|
492 | studio::App::set_selected_instance(0); |
---|
493 | |
---|
494 | // Turn-off/clean-up auto recovery |
---|
495 | studio::App::auto_recover->clear_backup(get_canvas()); |
---|
496 | |
---|
497 | // Remove us from the active instance list |
---|
498 | std::list<etl::handle<studio::Instance> >::iterator iter; |
---|
499 | for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++) |
---|
500 | if(*iter==this) |
---|
501 | break; |
---|
502 | assert(iter!=studio::App::instance_list.end()); |
---|
503 | if(iter!=studio::App::instance_list.end()) |
---|
504 | studio::App::instance_list.erase(iter); |
---|
505 | |
---|
506 | // Send out a signal that we are being deleted |
---|
507 | studio::App::signal_instance_deleted()(this); |
---|
508 | |
---|
509 | // Hide all of the canvas views |
---|
510 | for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++) |
---|
511 | (*iter)->hide(); |
---|
512 | |
---|
513 | // Consume pending events before deleting the canvas views |
---|
514 | while(studio::App::events_pending())studio::App::iteration(true); |
---|
515 | |
---|
516 | // Delete all of the canvas views |
---|
517 | canvas_view_list().clear(); |
---|
518 | |
---|
519 | // If there is another open instance to select, |
---|
520 | // go ahead and do so. If not, never mind. |
---|
521 | if(studio::App::instance_list.empty()) |
---|
522 | { |
---|
523 | studio::App::set_selected_canvas_view(0); |
---|
524 | studio::App::set_selected_instance(0); |
---|
525 | } |
---|
526 | else |
---|
527 | studio::App::instance_list.front()->canvas_view_list().front()->present(); |
---|
528 | } |
---|
529 | |
---|
530 | void |
---|
531 | Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas) |
---|
532 | { |
---|
533 | CanvasTreeModel canvas_tree_model; |
---|
534 | assert(canvas); |
---|
535 | |
---|
536 | row[canvas_tree_model.icon] = Gtk::Button().render_icon_pixbuf(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR); |
---|
537 | row[canvas_tree_model.id] = canvas->get_id(); |
---|
538 | row[canvas_tree_model.name] = canvas->get_name(); |
---|
539 | if(canvas->is_root()) |
---|
540 | row[canvas_tree_model.label] = basename(canvas->get_file_name()); |
---|
541 | else |
---|
542 | if(!canvas->get_id().empty()) |
---|
543 | row[canvas_tree_model.label] = canvas->get_id(); |
---|
544 | else |
---|
545 | if(!canvas->get_name().empty()) |
---|
546 | row[canvas_tree_model.label] = canvas->get_name(); |
---|
547 | else |
---|
548 | row[canvas_tree_model.label] = _("[Unnamed]"); |
---|
549 | |
---|
550 | row[canvas_tree_model.canvas] = canvas; |
---|
551 | row[canvas_tree_model.is_canvas] = true; |
---|
552 | row[canvas_tree_model.is_value_node] = false; |
---|
553 | |
---|
554 | { |
---|
555 | synfig::Canvas::Children::iterator iter; |
---|
556 | synfig::Canvas::Children &children(canvas->children()); |
---|
557 | |
---|
558 | for(iter=children.begin();iter!=children.end();iter++) |
---|
559 | insert_canvas(*(canvas_tree_store()->append(row.children())),*iter); |
---|
560 | } |
---|
561 | } |
---|
562 | |
---|
563 | void |
---|
564 | Instance::refresh_canvas_tree() |
---|
565 | { |
---|
566 | canvas_tree_store()->clear(); |
---|
567 | Gtk::TreeRow row = *(canvas_tree_store()->prepend()); |
---|
568 | insert_canvas(row,get_canvas()); |
---|
569 | } |
---|
570 | |
---|
571 | void |
---|
572 | Instance::dialog_cvs_commit() |
---|
573 | { |
---|
574 | calc_repository_info(); |
---|
575 | if(!in_repository()) |
---|
576 | { |
---|
577 | App::dialog_message_1b( |
---|
578 | "ERROR", |
---|
579 | _("You must first add this composition to the repository"), |
---|
580 | "details", |
---|
581 | _("Close")); |
---|
582 | |
---|
583 | return; |
---|
584 | } |
---|
585 | try |
---|
586 | { |
---|
587 | string message; |
---|
588 | |
---|
589 | if(synfigapp::Instance::get_action_count()) |
---|
590 | { |
---|
591 | if (!App::dialog_message_2b( |
---|
592 | _("CVS Commit"), |
---|
593 | _("This will save any changes you have made. Are you sure?"), |
---|
594 | Gtk::MESSAGE_QUESTION, |
---|
595 | _("Cancel"), |
---|
596 | _("Commit")) |
---|
597 | ) |
---|
598 | return; |
---|
599 | |
---|
600 | save(); |
---|
601 | } |
---|
602 | |
---|
603 | if(!is_modified()) |
---|
604 | { |
---|
605 | App::dialog_message_1b( |
---|
606 | "ERROR", |
---|
607 | _("The local copy of the file hasn't been changed since the last update. Nothing to commit!"), |
---|
608 | "details", |
---|
609 | _("Close")); |
---|
610 | |
---|
611 | return; |
---|
612 | } |
---|
613 | |
---|
614 | if(!App::dialog_entry(_("CVS Commit"), |
---|
615 | _("Log Message: "), |
---|
616 | message, |
---|
617 | _("Cancel"), |
---|
618 | _("Commit")) |
---|
619 | ) |
---|
620 | return; |
---|
621 | |
---|
622 | OneMoment one_moment; |
---|
623 | cvs_commit(message); |
---|
624 | } |
---|
625 | catch(...) |
---|
626 | { |
---|
627 | App::dialog_message_1b( |
---|
628 | "ERROR", |
---|
629 | _("An error has occurred when trying to COMMIT"), |
---|
630 | "details", |
---|
631 | _("Close")); |
---|
632 | } |
---|
633 | update_all_titles(); |
---|
634 | } |
---|
635 | |
---|
636 | void |
---|
637 | Instance::dialog_cvs_add() |
---|
638 | { |
---|
639 | calc_repository_info(); |
---|
640 | if(in_repository()) |
---|
641 | { |
---|
642 | App::dialog_message_1b( |
---|
643 | "ERROR", |
---|
644 | _("This composition has already been added to the repository"), |
---|
645 | "details", |
---|
646 | _("Close")); |
---|
647 | return; |
---|
648 | } |
---|
649 | try |
---|
650 | { |
---|
651 | string message; |
---|
652 | |
---|
653 | //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message)) |
---|
654 | // return; |
---|
655 | OneMoment one_moment; |
---|
656 | cvs_add(); |
---|
657 | } |
---|
658 | catch(...) |
---|
659 | { |
---|
660 | App::dialog_message_1b( |
---|
661 | "ERROR", |
---|
662 | _("An error has occurred when trying to ADD"), |
---|
663 | "details", |
---|
664 | _("Close")); |
---|
665 | } |
---|
666 | update_all_titles(); |
---|
667 | } |
---|
668 | |
---|
669 | void |
---|
670 | Instance::dialog_cvs_update() |
---|
671 | { |
---|
672 | calc_repository_info(); |
---|
673 | if(!in_repository()) |
---|
674 | { |
---|
675 | App::dialog_message_1b( |
---|
676 | "ERROR", |
---|
677 | _("This file is not under version control, so there is nothing to update from!"), |
---|
678 | "details", |
---|
679 | _("Close")); |
---|
680 | |
---|
681 | return; |
---|
682 | } |
---|
683 | if(!is_updated()) |
---|
684 | { |
---|
685 | App::dialog_message_1b( |
---|
686 | "INFO", |
---|
687 | _("This file is up-to-date"), |
---|
688 | "details", |
---|
689 | _("Close")); |
---|
690 | |
---|
691 | return; |
---|
692 | } |
---|
693 | |
---|
694 | try |
---|
695 | { |
---|
696 | String filename(get_file_name()); |
---|
697 | if(synfigapp::Instance::get_action_count()) |
---|
698 | { |
---|
699 | if (!App::dialog_message_2b( |
---|
700 | _("CVS Update"), |
---|
701 | _("This will save any changes you have made. Are you sure?"), |
---|
702 | Gtk::MESSAGE_QUESTION, |
---|
703 | _("Cancel"), |
---|
704 | _("Update")) |
---|
705 | ) |
---|
706 | return; |
---|
707 | |
---|
708 | save(); |
---|
709 | } |
---|
710 | OneMoment one_moment; |
---|
711 | time_t oldtime=get_original_timestamp(); |
---|
712 | cvs_update(); |
---|
713 | calc_repository_info(); |
---|
714 | // If something has been updated... |
---|
715 | if(oldtime!=get_original_timestamp()) |
---|
716 | { |
---|
717 | revert(); |
---|
718 | } |
---|
719 | } |
---|
720 | catch(...) |
---|
721 | { |
---|
722 | App::dialog_message_1b( |
---|
723 | "ERROR", |
---|
724 | _("An error has occurred when trying to UPDATE"), |
---|
725 | "details", |
---|
726 | _("Close")); |
---|
727 | } |
---|
728 | //update_all_titles(); |
---|
729 | } |
---|
730 | |
---|
731 | void |
---|
732 | Instance::dialog_cvs_revert() |
---|
733 | { |
---|
734 | calc_repository_info(); |
---|
735 | if(!in_repository()) |
---|
736 | { |
---|
737 | App::dialog_message_1b( |
---|
738 | "ERROR", |
---|
739 | _("This file is not under version control, so there is nothing to revert to!"), |
---|
740 | "details", |
---|
741 | _("Close")); |
---|
742 | return; |
---|
743 | } |
---|
744 | try |
---|
745 | { |
---|
746 | String filename(get_file_name()); |
---|
747 | |
---|
748 | if (!App::dialog_message_2b( |
---|
749 | _("CVS Revert"), |
---|
750 | _("This will abandon all changes you have made since the last time you " |
---|
751 | "performed a commit operation. This cannot be undone! " |
---|
752 | "Are you sure you want to do this?"), |
---|
753 | Gtk::MESSAGE_QUESTION, |
---|
754 | _("Cancel"), |
---|
755 | _("Revert")) |
---|
756 | ) |
---|
757 | return; |
---|
758 | |
---|
759 | OneMoment one_moment; |
---|
760 | |
---|
761 | // Remove the old file |
---|
762 | if(remove(get_file_name().c_str())!=0) |
---|
763 | { |
---|
764 | App::dialog_message_1b( |
---|
765 | "ERROR", |
---|
766 | _("Unable to remove previous version"), |
---|
767 | "details", |
---|
768 | _("Close")); |
---|
769 | |
---|
770 | return; |
---|
771 | } |
---|
772 | |
---|
773 | cvs_update(); |
---|
774 | revert(); |
---|
775 | } |
---|
776 | catch(...) |
---|
777 | { |
---|
778 | App::dialog_message_1b( |
---|
779 | "ERROR", |
---|
780 | _("An error has occurred when trying to UPDATE"), |
---|
781 | "details", |
---|
782 | _("Close")); |
---|
783 | } |
---|
784 | //update_all_titles(); |
---|
785 | } |
---|
786 | |
---|
787 | void |
---|
788 | Instance::revert() |
---|
789 | { |
---|
790 | OneMoment one_moment; |
---|
791 | |
---|
792 | String filename(get_file_name()); |
---|
793 | |
---|
794 | Canvas::Handle canvas(get_canvas()); |
---|
795 | |
---|
796 | close(); |
---|
797 | |
---|
798 | if(canvas->count()!=1) |
---|
799 | { |
---|
800 | one_moment.hide(); |
---|
801 | App::dialog_message_1b( |
---|
802 | "ERROR", |
---|
803 | _("The revert operation has failed."), |
---|
804 | _("This can be due to it being referenced by another composition" |
---|
805 | " that is already open, or because of an internal error " |
---|
806 | "in Synfig Studio. Try closing any compositions that " |
---|
807 | "might reference this composition and try again, or " |
---|
808 | "restart Synfig Studio."), |
---|
809 | _("Close")); |
---|
810 | |
---|
811 | one_moment.show(); |
---|
812 | } |
---|
813 | canvas=0; |
---|
814 | |
---|
815 | App::open(filename); |
---|
816 | } |
---|
817 | |
---|
818 | bool |
---|
819 | Instance::safe_revert() |
---|
820 | { |
---|
821 | if(synfigapp::Instance::get_action_count()) |
---|
822 | { |
---|
823 | if (!App::dialog_message_2b( |
---|
824 | _("Revert to saved"), |
---|
825 | _("You will lose any changes you have made since your last save." |
---|
826 | "Are you sure?"), |
---|
827 | Gtk::MESSAGE_QUESTION, |
---|
828 | _("Cancel"), |
---|
829 | _("Revert")) |
---|
830 | ) |
---|
831 | return false; |
---|
832 | } |
---|
833 | |
---|
834 | revert(); |
---|
835 | return true; |
---|
836 | } |
---|
837 | |
---|
838 | bool |
---|
839 | Instance::safe_close() |
---|
840 | { |
---|
841 | handle<CanvasView> canvas_view = find_canvas_view(get_canvas()); |
---|
842 | handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface(); |
---|
843 | |
---|
844 | // if the animation is currently playing, closing the window will cause a crash, |
---|
845 | // so don't allow it |
---|
846 | if (canvas_view->is_playing()) |
---|
847 | { |
---|
848 | canvas_view->present(); |
---|
849 | App::dialog_message_1b( |
---|
850 | "ERROR", |
---|
851 | _("The animation is currently playing so the window cannot be closed."), |
---|
852 | "details", |
---|
853 | _("Thanks!")); |
---|
854 | |
---|
855 | return false; |
---|
856 | } |
---|
857 | if(get_action_count()) |
---|
858 | do |
---|
859 | { |
---|
860 | string message = strprintf(_("Save changes to document \"%s\" before closing?"), |
---|
861 | basename(get_file_name()).c_str() ); |
---|
862 | |
---|
863 | string details = (_("If you don't save, changes from the last time you saved " |
---|
864 | "will be permanently lost.")); |
---|
865 | |
---|
866 | int answer=uim->yes_no_cancel( |
---|
867 | message, |
---|
868 | details, |
---|
869 | _("Close without Saving"), |
---|
870 | _("Cancel"), |
---|
871 | _("Save"), |
---|
872 | synfigapp::UIInterface::RESPONSE_YES |
---|
873 | ); |
---|
874 | |
---|
875 | if(answer == synfigapp::UIInterface::RESPONSE_YES){ |
---|
876 | enum Status status = save(); |
---|
877 | if (status == STATUS_OK) break; |
---|
878 | else if (status == STATUS_CANCEL) return false; |
---|
879 | } |
---|
880 | if(answer==synfigapp::UIInterface::RESPONSE_NO) |
---|
881 | break; |
---|
882 | if(answer==synfigapp::UIInterface::RESPONSE_CANCEL) |
---|
883 | return false; |
---|
884 | } while (true); |
---|
885 | |
---|
886 | if(is_modified()) |
---|
887 | { |
---|
888 | string message = strprintf(_("Commit changes of \"%s\" to the CVS repository?"), |
---|
889 | basename(get_file_name()).c_str()); |
---|
890 | |
---|
891 | string details = (_("If you don't commit, changes not yet on the CVS repository will " |
---|
892 | "be permanently lost.")); |
---|
893 | |
---|
894 | int answer=uim->yes_no_cancel( |
---|
895 | message, |
---|
896 | details, |
---|
897 | _("Close without Committing"), |
---|
898 | _("Cancel"), |
---|
899 | _("Commit…"), |
---|
900 | synfigapp::UIInterface::RESPONSE_YES |
---|
901 | ); |
---|
902 | |
---|
903 | if(answer==synfigapp::UIInterface::RESPONSE_YES) |
---|
904 | dialog_cvs_commit(); |
---|
905 | if(answer==synfigapp::UIInterface::RESPONSE_CANCEL) |
---|
906 | return false; |
---|
907 | } |
---|
908 | |
---|
909 | close(); |
---|
910 | |
---|
911 | return true; |
---|
912 | } |
---|
913 | |
---|
914 | void |
---|
915 | Instance::add_actions_to_group(const Glib::RefPtr<Gtk::ActionGroup>& action_group, synfig::String& ui_info, const synfigapp::Action::ParamList ¶m_list, synfigapp::Action::Category category)const |
---|
916 | { |
---|
917 | synfigapp::Action::CandidateList candidate_list; |
---|
918 | synfigapp::Action::CandidateList::iterator iter; |
---|
919 | |
---|
920 | candidate_list=compile_candidate_list(param_list,category); |
---|
921 | |
---|
922 | candidate_list.sort(); |
---|
923 | |
---|
924 | // if(candidate_list.empty()) |
---|
925 | // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__); |
---|
926 | |
---|
927 | for(iter=candidate_list.begin();iter!=candidate_list.end();++iter) |
---|
928 | { |
---|
929 | Gtk::StockID stock_id(get_action_stock_id(*iter)); |
---|
930 | |
---|
931 | if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN)) |
---|
932 | { |
---|
933 | action_group->add(Gtk::Action::create( |
---|
934 | "action-"+iter->name, |
---|
935 | stock_id, |
---|
936 | iter->local_name,iter->local_name |
---|
937 | ), |
---|
938 | sigc::bind( |
---|
939 | sigc::bind( |
---|
940 | sigc::mem_fun( |
---|
941 | *const_cast<studio::Instance*>(this), |
---|
942 | &studio::Instance::process_action |
---|
943 | ), |
---|
944 | param_list |
---|
945 | ), |
---|
946 | iter->name |
---|
947 | ) |
---|
948 | ); |
---|
949 | ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str()); |
---|
950 | } |
---|
951 | } |
---|
952 | } |
---|
953 | |
---|
954 | void |
---|
955 | Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const |
---|
956 | { |
---|
957 | synfigapp::Action::CandidateList candidate_list; |
---|
958 | synfigapp::Action::CandidateList::iterator iter; |
---|
959 | |
---|
960 | candidate_list=compile_candidate_list(param_list,category); |
---|
961 | |
---|
962 | candidate_list.sort(); |
---|
963 | |
---|
964 | if(candidate_list.empty()) |
---|
965 | synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__); |
---|
966 | |
---|
967 | for(iter=candidate_list.begin();iter!=candidate_list.end();++iter) |
---|
968 | { |
---|
969 | if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN)) |
---|
970 | { |
---|
971 | Gtk::MenuItem *item = Gtk::manage(new Gtk::ImageMenuItem( |
---|
972 | *Gtk::manage(new Gtk::Image(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU)), |
---|
973 | iter->local_name )); |
---|
974 | item->signal_activate().connect( |
---|
975 | sigc::bind( |
---|
976 | sigc::bind( |
---|
977 | sigc::mem_fun( |
---|
978 | *const_cast<studio::Instance*>(this), |
---|
979 | &studio::Instance::process_action ), |
---|
980 | param_list ), |
---|
981 | iter->name )); |
---|
982 | item->show_all(); |
---|
983 | menu->append(*item); |
---|
984 | } |
---|
985 | } |
---|
986 | } |
---|
987 | |
---|
988 | void |
---|
989 | Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,const synfigapp::Action::ParamList ¶m_list2,synfigapp::Action::Category category)const |
---|
990 | { |
---|
991 | synfigapp::Action::CandidateList candidate_list; |
---|
992 | synfigapp::Action::CandidateList candidate_list2; |
---|
993 | |
---|
994 | synfigapp::Action::CandidateList::iterator iter; |
---|
995 | |
---|
996 | candidate_list=compile_candidate_list(param_list,category); |
---|
997 | candidate_list2=compile_candidate_list(param_list2,category); |
---|
998 | |
---|
999 | candidate_list.sort(); |
---|
1000 | |
---|
1001 | if(candidate_list.empty()) |
---|
1002 | synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__); |
---|
1003 | if(candidate_list2.empty()) |
---|
1004 | synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__); |
---|
1005 | |
---|
1006 | // Separate out the candidate lists so that there are no conflicts |
---|
1007 | for(iter=candidate_list.begin();iter!=candidate_list.end();++iter) |
---|
1008 | { |
---|
1009 | synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name)); |
---|
1010 | if(iter2!=candidate_list2.end()) |
---|
1011 | candidate_list2.erase(iter2); |
---|
1012 | } |
---|
1013 | |
---|
1014 | for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter) |
---|
1015 | { |
---|
1016 | if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN)) |
---|
1017 | { |
---|
1018 | Gtk::MenuItem *item = Gtk::manage(new Gtk::ImageMenuItem( |
---|
1019 | *Gtk::manage(new Gtk::Image(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU)), |
---|
1020 | iter->local_name )); |
---|
1021 | item->signal_activate().connect( |
---|
1022 | sigc::bind( |
---|
1023 | sigc::bind( |
---|
1024 | sigc::mem_fun( |
---|
1025 | *const_cast<studio::Instance*>(this), |
---|
1026 | &studio::Instance::process_action ), |
---|
1027 | param_list2 ), |
---|
1028 | iter->name )); |
---|
1029 | item->show_all(); |
---|
1030 | menu->append(*item); |
---|
1031 | } |
---|
1032 | } |
---|
1033 | |
---|
1034 | for(iter=candidate_list.begin();iter!=candidate_list.end();++iter) |
---|
1035 | { |
---|
1036 | if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN)) |
---|
1037 | { |
---|
1038 | Gtk::MenuItem *item = Gtk::manage(new Gtk::ImageMenuItem( |
---|
1039 | *Gtk::manage(new Gtk::Image(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU)), |
---|
1040 | iter->local_name )); |
---|
1041 | item->signal_activate().connect( |
---|
1042 | sigc::bind( |
---|
1043 | sigc::bind( |
---|
1044 | sigc::mem_fun( |
---|
1045 | *const_cast<studio::Instance*>(this), |
---|
1046 | &studio::Instance::process_action ), |
---|
1047 | param_list ), |
---|
1048 | iter->name )); |
---|
1049 | item->show_all(); |
---|
1050 | menu->append(*item); |
---|
1051 | } |
---|
1052 | } |
---|
1053 | } |
---|
1054 | |
---|
1055 | void |
---|
1056 | Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list) |
---|
1057 | { |
---|
1058 | if (getenv("SYNFIG_DEBUG_ACTIONS")) |
---|
1059 | synfig::info("%s:%d process_action: '%s'", __FILE__, __LINE__, name.c_str()); |
---|
1060 | |
---|
1061 | assert(synfigapp::Action::book().count(name)); |
---|
1062 | |
---|
1063 | synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second); |
---|
1064 | |
---|
1065 | synfigapp::Action::Handle action(entry.factory()); |
---|
1066 | |
---|
1067 | if(!action) |
---|
1068 | { |
---|
1069 | synfig::error("Bad Action"); |
---|
1070 | return; |
---|
1071 | } |
---|
1072 | |
---|
1073 | action->set_param_list(param_list); |
---|
1074 | |
---|
1075 | synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab()); |
---|
1076 | synfigapp::Action::ParamVocab::const_iterator iter; |
---|
1077 | |
---|
1078 | for(iter=param_vocab.begin();iter!=param_vocab.end();++iter) |
---|
1079 | { |
---|
1080 | if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion())) |
---|
1081 | continue; |
---|
1082 | |
---|
1083 | // If the parameter is optionally user-supplied, |
---|
1084 | // and has not been already provided in the param_list, |
---|
1085 | // then we should go ahead and see if we can |
---|
1086 | // provide that data. |
---|
1087 | if(iter->get_user_supplied() && param_list.count(iter->get_name())==0) |
---|
1088 | { |
---|
1089 | switch(iter->get_type()) |
---|
1090 | { |
---|
1091 | case synfigapp::Action::Param::TYPE_STRING: |
---|
1092 | { |
---|
1093 | String str; |
---|
1094 | if(iter->get_value_provided()) |
---|
1095 | { |
---|
1096 | synfigapp::Action::Param param; |
---|
1097 | if (action->get_param(iter->get_name(),param)) |
---|
1098 | { |
---|
1099 | if(param.get_type()==synfigapp::Action::Param::TYPE_STRING) |
---|
1100 | str = param.get_string(); |
---|
1101 | } |
---|
1102 | } |
---|
1103 | String button2 = _("Export"); |
---|
1104 | String label = _("Name: "); |
---|
1105 | |
---|
1106 | // export and rename value dialog |
---|
1107 | if (entry.name == "ValueNodeRename") button2 = _("Rename"); |
---|
1108 | // set layer description dialog |
---|
1109 | if (entry.name == "LayerSetDesc") |
---|
1110 | { |
---|
1111 | button2 = _("Set"); |
---|
1112 | label = _("Description: "); |
---|
1113 | } |
---|
1114 | |
---|
1115 | if(!studio::App::dialog_entry(entry.local_name, |
---|
1116 | label, |
---|
1117 | //iter->get_local_name()+": "+iter->get_desc(), |
---|
1118 | str, |
---|
1119 | _("Cancel"), |
---|
1120 | button2)) |
---|
1121 | return; |
---|
1122 | action->set_param(iter->get_name(),str); |
---|
1123 | break; |
---|
1124 | } |
---|
1125 | default: |
---|
1126 | synfig::error("Unsupported user-supplied action parameter"); |
---|
1127 | return; |
---|
1128 | break; |
---|
1129 | } |
---|
1130 | } |
---|
1131 | } |
---|
1132 | |
---|
1133 | if(!action->is_ready()) |
---|
1134 | { |
---|
1135 | synfig::error("Action not ready"); |
---|
1136 | return; |
---|
1137 | } |
---|
1138 | |
---|
1139 | perform_action(action); |
---|
1140 | } |
---|
1141 | |
---|
1142 | void |
---|
1143 | Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location, bool bezier) |
---|
1144 | { |
---|
1145 | Gtk::Menu& parammenu(*menu); |
---|
1146 | synfigapp::ValueDesc value_desc2(value_desc); |
---|
1147 | etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas)); |
---|
1148 | |
---|
1149 | if(!canvas_interface) |
---|
1150 | return; |
---|
1151 | |
---|
1152 | Gtk::MenuItem *item = NULL; |
---|
1153 | |
---|
1154 | synfigapp::Action::ParamList param_list,param_list2; |
---|
1155 | param_list=canvas_interface->generate_param_list(value_desc); |
---|
1156 | param_list.add("origin",location); |
---|
1157 | |
---|
1158 | #ifdef BLINEPOINT_MENU_IS_VERTEX_MENU |
---|
1159 | if(value_desc.get_value_type()==type_bline_point && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())) |
---|
1160 | { |
---|
1161 | param_list2=canvas_interface->generate_param_list( |
---|
1162 | synfigapp::ValueDesc( |
---|
1163 | ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()) |
---|
1164 | ,ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()) |
---|
1165 | ->get_link_index_from_name("point") |
---|
1166 | ) |
---|
1167 | ); |
---|
1168 | param_list2.add("origin",location); |
---|
1169 | } |
---|
1170 | #endif // BLINEPOINT_MENU_IS_VERTEX_MENU |
---|
1171 | |
---|
1172 | // Populate the convert menu by looping through |
---|
1173 | // the ValueNode book and find the ones that are |
---|
1174 | // relevant. |
---|
1175 | |
---|
1176 | // show the 'Convert' sub-menu if this valuedesc is anything other than either: |
---|
1177 | // the 'Index' parameter of a Duplicate layer |
---|
1178 | // or |
---|
1179 | // a Duplicate ValueNode whose parent is not a (layer or ValueNode) |
---|
1180 | if (!((value_desc.parent_is_layer() && |
---|
1181 | value_desc.get_layer()->get_name() == "duplicate" && |
---|
1182 | value_desc.get_param_name() == "index") || |
---|
1183 | (value_desc.is_value_node() && |
---|
1184 | ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) && |
---|
1185 | !(value_desc.parent_is_layer() || |
---|
1186 | value_desc.parent_is_value_node())))) |
---|
1187 | { |
---|
1188 | Gtk::Menu *convert_menu=Gtk::manage(new Gtk::Menu()); |
---|
1189 | LinkableValueNode::Book::const_iterator iter; |
---|
1190 | for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter) |
---|
1191 | { |
---|
1192 | if(iter->second.check_type(value_desc.get_value_type())) |
---|
1193 | { |
---|
1194 | item = Gtk::manage(new Gtk::MenuItem(iter->second.local_name)); |
---|
1195 | item->signal_activate().connect( |
---|
1196 | sigc::hide_return( |
---|
1197 | sigc::bind( |
---|
1198 | sigc::bind( |
---|
1199 | sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert), |
---|
1200 | iter->first ), |
---|
1201 | value_desc ))); |
---|
1202 | item->show(); |
---|
1203 | convert_menu->append(*item); |
---|
1204 | } |
---|
1205 | } |
---|
1206 | |
---|
1207 | item = Gtk::manage(new Gtk::ImageMenuItem( |
---|
1208 | *manage(new Gtk::Image( |
---|
1209 | Gtk::StockID("gtk-convert"), |
---|
1210 | Gtk::ICON_SIZE_MENU )), |
---|
1211 | _("Convert") )); |
---|
1212 | item->set_submenu(*convert_menu); |
---|
1213 | item->show(); |
---|
1214 | parammenu.append(*item); |
---|
1215 | } |
---|
1216 | |
---|
1217 | // Interpolation menu: Show it only if |
---|
1218 | // the value description is constant or animated (layer parameter or exported) and not static |
---|
1219 | // and value is not canvas type |
---|
1220 | if (((value_desc.is_const() && !value_desc.get_static()) || value_desc.is_animated()) |
---|
1221 | && |
---|
1222 | value_desc.get_value_type()!= type_canvas |
---|
1223 | ) |
---|
1224 | { |
---|
1225 | Gtk::Menu *param_interpolation_menu=Gtk::manage(new Gtk::Menu()); |
---|
1226 | synfigapp::Action::ParamList param_list; |
---|
1227 | param_list.add("canvas", get_canvas()); |
---|
1228 | param_list.add("value_desc", value_desc); |
---|
1229 | param_list.add("canvas_interface",canvas_interface); |
---|
1230 | |
---|
1231 | /////// Default |
---|
1232 | param_list.add("new_value", INTERPOLATION_UNDEFINED); |
---|
1233 | item = Gtk::manage(new Gtk::MenuItem(_("Default"))); |
---|
1234 | item->signal_activate().connect( |
---|
1235 | sigc::bind( |
---|
1236 | sigc::bind( |
---|
1237 | sigc::mem_fun(*const_cast<studio::Instance*>(this),&studio::Instance::process_action), |
---|
1238 | param_list ), |
---|
1239 | "ValueDescSetInterpolation" )); |
---|
1240 | item->show(); |
---|
1241 | param_interpolation_menu->append(*item); |
---|
1242 | param_list.erase("new_value"); |
---|
1243 | |
---|
1244 | #define ADD_IMAGE_MENU_ITEM(Interpolation, StockId, Text) \ |
---|
1245 | param_list.add("new_value", Interpolation); \ |
---|
1246 | item = Gtk::manage(new Gtk::ImageMenuItem( \ |
---|
1247 | *Gtk::manage(new Gtk::Image(Gtk::StockID(StockId), Gtk::IconSize::from_name("synfig-small_icon"))), \ |
---|
1248 | _(Text) )); \ |
---|
1249 | item->signal_activate().connect( \ |
---|
1250 | sigc::bind( \ |
---|
1251 | sigc::bind( \ |
---|
1252 | sigc::mem_fun(*const_cast<studio::Instance*>(this),&studio::Instance::process_action), \ |
---|
1253 | param_list ), \ |
---|
1254 | "ValueDescSetInterpolation" )); \ |
---|
1255 | item->show_all(); \ |
---|
1256 | param_interpolation_menu->append(*item); \ |
---|
1257 | param_list.erase("new_value"); |
---|
1258 | |
---|
1259 | |
---|
1260 | ADD_IMAGE_MENU_ITEM(INTERPOLATION_TCB, "synfig-interpolation_type_tcb", _("TCB")); |
---|
1261 | ADD_IMAGE_MENU_ITEM(INTERPOLATION_LINEAR, "synfig-interpolation_type_linear", _("Linear")); |
---|
1262 | ADD_IMAGE_MENU_ITEM(INTERPOLATION_HALT, "synfig-interpolation_type_ease", _("Ease")); |
---|
1263 | ADD_IMAGE_MENU_ITEM(INTERPOLATION_CONSTANT, "synfig-interpolation_type_const", _("Constant")); |
---|
1264 | ADD_IMAGE_MENU_ITEM(INTERPOLATION_CLAMPED, "synfig-interpolation_type_clamped", _("Clamped")); |
---|
1265 | |
---|
1266 | #undef ADD_IMAGE_MENU_ITEM |
---|
1267 | |
---|
1268 | item = Gtk::manage(new Gtk::MenuItem(_("Interpolation"))); |
---|
1269 | item->set_submenu(*param_interpolation_menu); |
---|
1270 | item->show(); |
---|
1271 | parammenu.append(*item); |
---|
1272 | } |
---|
1273 | |
---|
1274 | synfigapp::Action::Category categories = synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE; |
---|
1275 | if (bezier) |
---|
1276 | categories = categories|synfigapp::Action::CATEGORY_BEZIER; |
---|
1277 | |
---|
1278 | const DuckList selected_ducks(find_canvas_view(canvas)->get_work_area()->get_selected_ducks()); |
---|
1279 | for(DuckList::const_iterator iter=selected_ducks.begin();iter!=selected_ducks.end();++iter) |
---|
1280 | { |
---|
1281 | synfigapp::ValueDesc selected_value_desc((*iter)->get_value_desc()); |
---|
1282 | if(selected_value_desc.is_valid() && value_desc != selected_value_desc) |
---|
1283 | param_list.add("selected_value_desc",selected_value_desc); |
---|
1284 | } |
---|
1285 | |
---|
1286 | if(param_list2.empty()) |
---|
1287 | add_actions_to_menu(¶mmenu, param_list,categories); |
---|
1288 | else |
---|
1289 | add_actions_to_menu(¶mmenu, param_list2,param_list,categories); |
---|
1290 | |
---|
1291 | if((value_desc2.get_value_type()==type_bline_point || value_desc2.get_value_type()==type_width_point) |
---|
1292 | && value_desc2.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc2.get_value_node())) |
---|
1293 | { |
---|
1294 | // the index=0 is position for widthpoint and vertex for blinepoint |
---|
1295 | value_desc2=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc2.get_value_node()),0); |
---|
1296 | } |
---|
1297 | |
---|
1298 | if(value_desc2.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc2.get_value_node())) |
---|
1299 | { |
---|
1300 | ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc2.get_value_node())); |
---|
1301 | |
---|
1302 | try |
---|
1303 | { |
---|
1304 | // try to find a waypoint at the current time - if we |
---|
1305 | // can't, we don't want the menu entry - an exception is thrown |
---|
1306 | WaypointList::iterator iter(value_node->find(canvas->get_time())); |
---|
1307 | std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set; |
---|
1308 | waypoint_set.insert(*iter); |
---|
1309 | |
---|
1310 | item = Gtk::manage(new Gtk::MenuItem(_("Edit Waypoint"))); |
---|
1311 | item->signal_activate().connect( |
---|
1312 | sigc::bind( |
---|
1313 | sigc::bind( |
---|
1314 | sigc::bind( |
---|
1315 | sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview), |
---|
1316 | -1 ), |
---|
1317 | waypoint_set ), |
---|
1318 | value_desc2 )); |
---|
1319 | item->show(); |
---|
1320 | parammenu.append(*item); |
---|
1321 | } |
---|
1322 | catch(...) |
---|
1323 | { } |
---|
1324 | } |
---|
1325 | //// Add here the rest of actions here for specific single value descriptions |
---|
1326 | // |
---|
1327 | // Specific actions for Widthpoints (Composite) |
---|
1328 | if(value_desc.get_value_type()==type_width_point && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())) |
---|
1329 | { |
---|
1330 | ValueNode_Composite::Handle wpoint_composite(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())); |
---|
1331 | synfigapp::Action::ParamList param_list; |
---|
1332 | param_list.add("canvas",canvas); |
---|
1333 | param_list.add("canvas_interface",canvas_interface); |
---|
1334 | param_list.add("time",canvas_interface->get_time()); |
---|
1335 | item = Gtk::manage(new Gtk::SeparatorMenuItem()); |
---|
1336 | item->show(); |
---|
1337 | parammenu.append(*item); |
---|
1338 | |
---|
1339 | |
---|
1340 | #define ADD_IMAGE_MENU_ITEM(Type, StockId, Text) \ |
---|
1341 | param_list.add("new_value", ValueBase((int)WidthPoint::Type)); \ |
---|
1342 | item = Gtk::manage(new Gtk::ImageMenuItem( \ |
---|
1343 | *Gtk::manage(new Gtk::Image(Gtk::StockID(StockId),Gtk::IconSize::from_name("synfig-small_icon"))), \ |
---|
1344 | _(Text) )); \ |
---|
1345 | item->signal_activate().connect( \ |
---|
1346 | sigc::bind( \ |
---|
1347 | sigc::bind( \ |
---|
1348 | sigc::mem_fun(*const_cast<studio::Instance*>(this),&studio::Instance::process_action), \ |
---|
1349 | param_list ), \ |
---|
1350 | "ValueDescSet" )); \ |
---|
1351 | item->show_all(); \ |
---|
1352 | parammenu.append(*item); \ |
---|
1353 | param_list.erase("new_value"); |
---|
1354 | |
---|
1355 | ////// Before ////////////////// |
---|
1356 | param_list.add("value_desc",synfigapp::ValueDesc(wpoint_composite, wpoint_composite->get_link_index_from_name("side_before"))); |
---|
1357 | |
---|
1358 | ADD_IMAGE_MENU_ITEM(TYPE_INTERPOLATE, "synfig-interpolate_interpolation", "Cusp Before: Interpolate") |
---|
1359 | ADD_IMAGE_MENU_ITEM(TYPE_ROUNDED, "synfig-rounded_interpolation", "Cusp Before: Rounded") |
---|
1360 | ADD_IMAGE_MENU_ITEM(TYPE_SQUARED, "synfig-squared_interpolation", "Cusp Before: Squared") |
---|
1361 | ADD_IMAGE_MENU_ITEM(TYPE_PEAK, "synfig-peak_interpolation", "Cusp Before: Peak") |
---|
1362 | ADD_IMAGE_MENU_ITEM(TYPE_FLAT, "synfig-flat_interpolation", "Cusp Before: Flat") |
---|
1363 | |
---|
1364 | /////// |
---|
1365 | item = Gtk::manage(new Gtk::SeparatorMenuItem()); |
---|
1366 | item->show(); |
---|
1367 | parammenu.append(*item); |
---|
1368 | |
---|
1369 | ////// After /////////////////////// |
---|
1370 | param_list.erase("value_desc"); |
---|
1371 | param_list.add("value_desc",synfigapp::ValueDesc(wpoint_composite, wpoint_composite->get_link_index_from_name("side_after"))); |
---|
1372 | |
---|
1373 | ADD_IMAGE_MENU_ITEM(TYPE_INTERPOLATE, "synfig-interpolate_interpolation", "Cusp After: Interpolate") |
---|
1374 | ADD_IMAGE_MENU_ITEM(TYPE_ROUNDED, "synfig-rounded_interpolation", "Cusp After: Rounded") |
---|
1375 | ADD_IMAGE_MENU_ITEM(TYPE_SQUARED, "synfig-squared_interpolation", "Cusp After: Squared") |
---|
1376 | ADD_IMAGE_MENU_ITEM(TYPE_PEAK, "synfig-peak_interpolation", "Cusp After: Peak") |
---|
1377 | ADD_IMAGE_MENU_ITEM(TYPE_FLAT, "synfig-flat_interpolation", "Cusp After: Flat") |
---|
1378 | |
---|
1379 | /////// |
---|
1380 | item = Gtk::manage(new Gtk::SeparatorMenuItem()); |
---|
1381 | item->show(); |
---|
1382 | parammenu.append(*item); |
---|
1383 | |
---|
1384 | /////// Set WIDTH to ZERO |
---|
1385 | param_list.erase("value_desc"); |
---|
1386 | param_list.erase("new_value"); |
---|
1387 | param_list.add("value_desc",synfigapp::ValueDesc(wpoint_composite, wpoint_composite->get_link_index_from_name("width"))); |
---|
1388 | param_list.add("new_value", ValueBase(Real(0.0))); |
---|
1389 | item = Gtk::manage(new Gtk::MenuItem(_("Set width to zero"))); |
---|
1390 | item->signal_activate().connect( |
---|
1391 | sigc::bind( |
---|
1392 | sigc::bind( |
---|
1393 | sigc::mem_fun(*const_cast<studio::Instance*>(this),&studio::Instance::process_action), |
---|
1394 | param_list ), |
---|
1395 | "ValueDescSet" )); |
---|
1396 | item->show(); |
---|
1397 | parammenu.append(*item); |
---|
1398 | |
---|
1399 | /////// Set WIDTH to DEFAULT |
---|
1400 | param_list.erase("new_value"); |
---|
1401 | param_list.add("value_desc",synfigapp::ValueDesc(wpoint_composite, wpoint_composite->get_link_index_from_name("width"))); |
---|
1402 | param_list.add("new_value", ValueBase(Real(1.0))); |
---|
1403 | item = Gtk::manage(new Gtk::MenuItem(_("Set width to default"))); |
---|
1404 | item->signal_activate().connect( |
---|
1405 | sigc::bind( |
---|
1406 | sigc::bind( |
---|
1407 | sigc::mem_fun(*const_cast<studio::Instance*>(this),&studio::Instance::process_action), |
---|
1408 | param_list ), |
---|
1409 | "ValueDescSet" )); |
---|
1410 | item->show(); |
---|
1411 | parammenu.append(*item); |
---|
1412 | } |
---|
1413 | } |
---|
1414 | |
---|
1415 | void |
---|
1416 | edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list) |
---|
1417 | { |
---|
1418 | etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface()); |
---|
1419 | |
---|
1420 | Gtk::Dialog dialog( |
---|
1421 | "Edit Multiple Waypoints", |
---|
1422 | true |
---|
1423 | ); |
---|
1424 | |
---|
1425 | Widget_WaypointModel widget_waypoint_model; |
---|
1426 | widget_waypoint_model.show(); |
---|
1427 | |
---|
1428 | dialog.get_vbox()->pack_start(widget_waypoint_model); |
---|
1429 | |
---|
1430 | dialog.add_button(_("Cancel"), 0); |
---|
1431 | dialog.add_button(_("Apply"), 1); |
---|
1432 | dialog.show(); |
---|
1433 | |
---|
1434 | if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial()) |
---|
1435 | return; |
---|
1436 | synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints")); |
---|
1437 | |
---|
1438 | std::list<synfigapp::ValueDesc>::iterator iter; |
---|
1439 | for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter) |
---|
1440 | { |
---|
1441 | synfigapp::ValueDesc value_desc(*iter); |
---|
1442 | |
---|
1443 | if(!value_desc.is_valid()) |
---|
1444 | continue; |
---|
1445 | |
---|
1446 | ValueNode_Animated::Handle value_node; |
---|
1447 | // Check if we are dealing with a BLinePoint or a WidthPoint value desc |
---|
1448 | // If so, then change the value desc to be the position or the point. |
---|
1449 | |
---|
1450 | if(value_desc.is_value_node() && value_desc.parent_is_linkable_value_node()) |
---|
1451 | { |
---|
1452 | synfig::ValueNode_Composite::Handle compo(synfig::ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())); |
---|
1453 | if(compo && compo->get_type() == type_width_point) |
---|
1454 | { |
---|
1455 | value_desc=synfigapp::ValueDesc(compo, compo->get_link_index_from_name("position")); |
---|
1456 | //value_node=ValueNode_Animated::Handle::cast_dynamic(compo->get_link(compo->get_link_index_from_name("position"))); |
---|
1457 | } |
---|
1458 | if(compo && compo->get_type() == type_bline_point) |
---|
1459 | { |
---|
1460 | value_desc=synfigapp::ValueDesc(compo, compo->get_link_index_from_name("point")); |
---|
1461 | //value_node=ValueNode_Animated::Handle::cast_dynamic(compo->get_link(compo->get_link_index_from_name("point"))); |
---|
1462 | } |
---|
1463 | } |
---|
1464 | |
---|
1465 | // If this value isn't a ValueNode_Animated, but |
---|
1466 | // it is somewhat constant, then go ahead and convert |
---|
1467 | // it to a ValueNode_Animated. |
---|
1468 | if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())) |
---|
1469 | { |
---|
1470 | ValueBase value; |
---|
1471 | if(value_desc.is_value_node()) |
---|
1472 | value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value(); |
---|
1473 | else |
---|
1474 | value=value_desc.get_value(); |
---|
1475 | |
---|
1476 | value_node=ValueNode_Animated::create(value,canvas_interface->get_time()); |
---|
1477 | |
---|
1478 | synfigapp::Action::Handle action; |
---|
1479 | |
---|
1480 | if(!value_desc.is_value_node()) |
---|
1481 | { |
---|
1482 | action=synfigapp::Action::create("ValueDescConnect"); |
---|
1483 | action->set_param("dest",value_desc); |
---|
1484 | action->set_param("src",ValueNode::Handle(value_node)); |
---|
1485 | } |
---|
1486 | else |
---|
1487 | { |
---|
1488 | action=synfigapp::Action::create("ValueNodeReplace"); |
---|
1489 | action->set_param("dest",value_desc.get_value_node()); |
---|
1490 | action->set_param("src",ValueNode::Handle(value_node)); |
---|
1491 | } |
---|
1492 | |
---|
1493 | action->set_param("canvas",canvas_view->get_canvas()); |
---|
1494 | action->set_param("canvas_interface",canvas_interface); |
---|
1495 | |
---|
1496 | if(!canvas_interface->get_instance()->perform_action(action)) |
---|
1497 | { |
---|
1498 | canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint")); |
---|
1499 | group.cancel(); |
---|
1500 | return; |
---|
1501 | } |
---|
1502 | } |
---|
1503 | else |
---|
1504 | { |
---|
1505 | if(value_desc.is_value_node()) |
---|
1506 | value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()); |
---|
1507 | } |
---|
1508 | |
---|
1509 | if(value_node) |
---|
1510 | { |
---|
1511 | synfigapp::Action::Handle action(synfigapp::Action::create("WaypointSetSmart")); |
---|
1512 | |
---|
1513 | if(!action) |
---|
1514 | { |
---|
1515 | canvas_view->get_ui_interface()->error(_("Unable to find WaypointSetSmart action")); |
---|
1516 | group.cancel(); |
---|
1517 | return; |
---|
1518 | } |
---|
1519 | |
---|
1520 | action->set_param("canvas",canvas_view->get_canvas()); |
---|
1521 | action->set_param("canvas_interface",canvas_interface); |
---|
1522 | action->set_param("value_node",ValueNode::Handle(value_node)); |
---|
1523 | action->set_param("time",canvas_interface->get_time()); |
---|
1524 | action->set_param("model",widget_waypoint_model.get_waypoint_model()); |
---|
1525 | |
---|
1526 | if(!canvas_interface->get_instance()->perform_action(action)) |
---|
1527 | { |
---|
1528 | canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint")); |
---|
1529 | group.cancel(); |
---|
1530 | return; |
---|
1531 | } |
---|
1532 | } |
---|
1533 | else |
---|
1534 | { |
---|
1535 | //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc")); |
---|
1536 | //group.cancel(); |
---|
1537 | //return; |
---|
1538 | } |
---|
1539 | |
---|
1540 | } |
---|
1541 | } |
---|
1542 | |
---|
1543 | void |
---|
1544 | Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list, const synfigapp::ValueDesc &value_desc) |
---|
1545 | { |
---|
1546 | etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas)); |
---|
1547 | |
---|
1548 | synfigapp::Action::ParamList param_list; |
---|
1549 | param_list=canvas_interface->generate_param_list(value_desc_list); |
---|
1550 | |
---|
1551 | add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE); |
---|
1552 | |
---|
1553 | // Add the edit waypoints option if that might be useful |
---|
1554 | if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start()) |
---|
1555 | { |
---|
1556 | Gtk::MenuItem *item = Gtk::manage(new Gtk::MenuItem(_("Edit Waypoints"))); |
---|
1557 | item->signal_activate().connect( |
---|
1558 | sigc::bind( |
---|
1559 | sigc::bind( |
---|
1560 | sigc::ptr_fun(&edit_several_waypoints), |
---|
1561 | value_desc_list ), |
---|
1562 | find_canvas_view(canvas) )); |
---|
1563 | item->show(); |
---|
1564 | menu->append(*item); |
---|
1565 | } |
---|
1566 | // add here the rest of specific actions for multiple selected value_descs |
---|
1567 | if (value_desc.is_valid()) |
---|
1568 | make_param_menu(menu,canvas,value_desc, 0.f, false); |
---|
1569 | } |
---|