Fix some UI doxygen warnings
[ardour.git] / gtk2_ardour / ardour_ui_session.cc
1 /*
2  * Copyright (C) 2005-2007 Doug McLain <doug@nostar.net>
3  * Copyright (C) 2005-2017 Tim Mayberry <mojofunk@gmail.com>
4  * Copyright (C) 2005-2019 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2005 Karsten Wiese <fzuuzf@googlemail.com>
6  * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
7  * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
8  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
9  * Copyright (C) 2008-2010 Sakari Bergen <sakari.bergen@beatwaves.net>
10  * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
11  * Copyright (C) 2013-2015 Colin Fletcher <colin.m.fletcher@googlemail.com>
12  * Copyright (C) 2013-2016 John Emmas <john@creativepost.co.uk>
13  * Copyright (C) 2013-2016 Nick Mainsbridge <mainsbridge@gmail.com>
14  * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
15  * Copyright (C) 2015 AndrĂ© Nusser <andre.nusser@googlemail.com>
16  * Copyright (C) 2016-2018 Len Ovens <len@ovenwerks.net>
17  * Copyright (C) 2017 Johannes Mueller <github@johannes-mueller.org>
18  *
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 2 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License along
30  * with this program; if not, write to the Free Software Foundation, Inc.,
31  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32  */
33
34 #ifdef WAF_BUILD
35 #include "gtk2ardour-config.h"
36 #include "gtk2ardour-version.h"
37 #endif
38
39 #include <gtkmm/progressbar.h>
40 #include <gtkmm/stock.h>
41
42 #include "pbd/basename.h"
43 #include "pbd/localtime_r.h"
44 #include "pbd/unwind.h"
45
46 #include "gtkmm2ext/application.h"
47
48 #include "widgets/prompter.h"
49
50 #include "ardour/audioengine.h"
51 #include "ardour/filename_extensions.h"
52 #include "ardour/profile.h"
53 #include "ardour/session.h"
54 #include "ardour/session_utils.h"
55 #include "ardour/session_state_utils.h"
56 #include "ardour/session_directory.h"
57
58 #include "ardour_ui.h"
59 #include "engine_dialog.h"
60 #include "missing_plugin_dialog.h"
61 #include "opts.h"
62 #include "public_editor.h"
63 #include "save_as_dialog.h"
64 #include "session_dialog.h"
65 #include "session_archive_dialog.h"
66 #include "timers.h"
67 #include "utils.h"
68
69 #ifdef WINDOWS_VST_SUPPORT
70 #include <fst.h>
71 #endif
72
73 #include "pbd/i18n.h"
74
75 using namespace ARDOUR;
76 using namespace ARDOUR_UI_UTILS;
77 using namespace PBD;
78 using namespace Gtk;
79 using namespace std;
80 using namespace ArdourWidgets;
81
82 bool
83 ARDOUR_UI::ask_about_loading_existing_session (const std::string& session_path)
84 {
85         std::string str = string_compose (_("This session\n%1\nalready exists. Do you want to open it?"), session_path);
86
87         MessageDialog msg (str,
88                            false,
89                            Gtk::MESSAGE_WARNING,
90                            Gtk::BUTTONS_YES_NO,
91                            true);
92
93
94         msg.set_name (X_("OpenExistingDialog"));
95         msg.set_title (_("Open Existing Session"));
96         msg.set_wmclass (X_("existing_session"), PROGRAM_NAME);
97         msg.set_position (Gtk::WIN_POS_CENTER);
98         pop_back_splash (msg);
99
100         switch (msg.run()) {
101         case RESPONSE_YES:
102                 return true;
103                 break;
104         }
105         return false;
106 }
107
108 int
109 ARDOUR_UI::build_session_from_dialog (SessionDialog& sd, const std::string& session_path, const std::string& session_name)
110 {
111         BusProfile bus_profile;
112
113         if (nsm) {
114                 bus_profile.master_out_channels = 2;
115         } else if ( Profile->get_mixbus()) {
116                 bus_profile.master_out_channels = 2;
117         } else {
118                 /* get settings from advanced section of NSD */
119                 bus_profile.master_out_channels = (uint32_t) sd.master_channel_count();
120         }
121
122         // NULL profile: no master, no monitor
123         if (build_session (session_path, session_name, bus_profile.master_out_channels > 0 ? &bus_profile : NULL)) {
124                 return -1;
125         }
126
127         return 0;
128 }
129
130 void
131 ARDOUR_UI::load_from_application_api (const std::string& path)
132 {
133         /* OS X El Capitan (and probably later) now somehow passes the command
134            line arguments to an app via the openFile delegate protocol. Ardour
135            already does its own command line processing, and having both
136            pathways active causes crashes. So, if the command line was already
137            set, do nothing here.
138         */
139
140         if (!ARDOUR_COMMAND_LINE::session_name.empty()) {
141                 return;
142         }
143
144         ARDOUR_COMMAND_LINE::session_name = path;
145
146         /* Cancel SessionDialog if it's visible to make OSX delegates work.
147          *
148          * ARDOUR_UI::starting connects app->ShouldLoad signal and then shows a SessionDialog
149          * race-condition:
150          *  - ShouldLoad does not arrive in time, ARDOUR_COMMAND_LINE::session_name is empty:
151          *    -> ARDOUR_UI::get_session_parameters starts a SessionDialog.
152          *  - ShouldLoad signal arrives, this function is called and sets ARDOUR_COMMAND_LINE::session_name
153          *    -> SessionDialog is not displayed
154          */
155
156         if (_session_dialog) {
157                 std::string session_name = basename_nosuffix (ARDOUR_COMMAND_LINE::session_name);
158                 std::string session_path = path;
159                 if (Glib::file_test (session_path, Glib::FILE_TEST_IS_REGULAR)) {
160                         session_path = Glib::path_get_dirname (session_path);
161                 }
162                 // signal the existing dialog in ARDOUR_UI::get_session_parameters()
163                 _session_dialog->set_provided_session (session_name, session_path);
164                 _session_dialog->response (RESPONSE_NONE);
165                 _session_dialog->hide();
166                 return;
167         }
168
169         int rv;
170         if (Glib::file_test (path, Glib::FILE_TEST_IS_DIR)) {
171                 /* /path/to/foo => /path/to/foo, foo */
172                 rv = load_session (path, basename_nosuffix (path));
173         } else {
174                 /* /path/to/foo/foo.ardour => /path/to/foo, foo */
175                 rv =load_session (Glib::path_get_dirname (path), basename_nosuffix (path));
176         }
177
178         // if load_session fails -> pop up SessionDialog.
179         if (rv) {
180                 ARDOUR_COMMAND_LINE::session_name = "";
181
182                 if (get_session_parameters (true, false)) {
183                         exit (EXIT_FAILURE);
184                 }
185         }
186 }
187
188 /** @param quit_on_cancel true if exit() should be called if the user clicks `cancel' in the new session dialog */
189 int
190 ARDOUR_UI::get_session_parameters (bool quit_on_cancel, bool should_be_new, string load_template)
191 {
192         string session_name;
193         string session_path;
194         string template_name;
195         int ret = -1;
196         bool likely_new = false;
197         bool cancel_not_quit;
198
199         /* deal with any existing DIRTY session now, rather than later. don't
200          * treat a non-dirty session this way, so that it stays visible
201          * as we bring up the new session dialog.
202          */
203
204         if (_session && ARDOUR_UI::instance()->video_timeline) {
205                 ARDOUR_UI::instance()->video_timeline->sync_session_state();
206         }
207
208         /* if there is already a session, relabel the button
209            on the SessionDialog so that we don't Quit directly
210         */
211         cancel_not_quit = (_session != 0) && !quit_on_cancel;
212
213         if (_session && _session->dirty()) {
214                 if (unload_session (false)) {
215                         /* unload cancelled by user */
216                         return 0;
217                 }
218                 ARDOUR_COMMAND_LINE::session_name = "";
219         }
220
221         if (!load_template.empty()) {
222                 should_be_new = true;
223                 template_name = load_template;
224         }
225
226         session_path = ARDOUR_COMMAND_LINE::session_name;
227
228         if (!session_path.empty()) {
229
230                 if (Glib::file_test (session_path.c_str(), Glib::FILE_TEST_EXISTS)) {
231
232                         session_name = basename_nosuffix (ARDOUR_COMMAND_LINE::session_name);
233
234                         if (Glib::file_test (session_path.c_str(), Glib::FILE_TEST_IS_REGULAR)) {
235                                 /* session/snapshot file, change path to be dir */
236                                 session_path = Glib::path_get_dirname (session_path);
237                         }
238                 } else {
239
240                         /* session (file or folder) does not exist ... did the
241                          * user give us a path or just a name?
242                          */
243
244                         if (session_path.find (G_DIR_SEPARATOR) == string::npos) {
245                                 /* user gave session name with no path info, use
246                                    default session folder.
247                                 */
248                                 session_name = ARDOUR_COMMAND_LINE::session_name;
249                                 session_path = Glib::build_filename (Config->get_default_session_parent_dir (), session_name);
250                         } else {
251                                 session_name = basename_nosuffix (ARDOUR_COMMAND_LINE::session_name);
252                         }
253                 }
254         }
255
256         SessionDialog session_dialog (should_be_new, session_name, session_path, load_template, cancel_not_quit);
257
258         _session_dialog = &session_dialog;
259         while (ret != 0) {
260
261                 if (!ARDOUR_COMMAND_LINE::session_name.empty()) {
262
263                         /* if they named a specific statefile, use it, otherwise they are
264                            just giving a session folder, and we want to use it as is
265                            to find the session.
266                         */
267
268                         string::size_type suffix = ARDOUR_COMMAND_LINE::session_name.find (statefile_suffix);
269
270                         if (suffix != string::npos) {
271                                 session_path = Glib::path_get_dirname (ARDOUR_COMMAND_LINE::session_name);
272                                 session_name = ARDOUR_COMMAND_LINE::session_name.substr (0, suffix);
273                                 session_name = Glib::path_get_basename (session_name);
274                         } else {
275                                 session_path = ARDOUR_COMMAND_LINE::session_name;
276                                 session_name = Glib::path_get_basename (ARDOUR_COMMAND_LINE::session_name);
277                         }
278                 } else {
279                         session_path = "";
280                         session_name = "";
281                         session_dialog.clear_given ();
282                 }
283
284                 if (session_name.empty()) {
285                         /* need the dialog to get the name (at least) from the user */
286                         switch (session_dialog.run()) {
287                         case RESPONSE_ACCEPT:
288                                 break;
289                         case RESPONSE_NONE:
290                                 /* this is used for async * app->ShouldLoad(). */
291                                 continue; // while loop
292                                 break;
293                         default:
294                                 if (quit_on_cancel) {
295                                         ARDOUR_UI::finish ();
296                                         Gtkmm2ext::Application::instance()->cleanup();
297                                         ARDOUR::cleanup ();
298                                         pthread_cancel_all ();
299                                         return -1; // caller is responsible to call exit()
300                                 } else {
301                                         return ret;
302                                 }
303                         }
304
305                         session_dialog.hide ();
306                 }
307
308                 /* if we run the startup dialog again, offer more than just "new session" */
309
310                 should_be_new = false;
311
312                 session_name = session_dialog.session_name (likely_new);
313                 session_path = session_dialog.session_folder ();
314
315                 if (nsm) {
316                         likely_new = true;
317                 }
318
319                 if (!likely_new) {
320                         int rv = ARDOUR::inflate_session (session_name,
321                                         Config->get_default_session_parent_dir(), session_path, session_name);
322                         if (rv < 0) {
323                                 MessageDialog msg (session_dialog,
324                                         string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
325                                 msg.run ();
326                                 continue;
327                         }
328                         else if (rv == 0) {
329                                 session_dialog.set_provided_session (session_name, session_path);
330                         }
331                 }
332
333                 // XXX check archive, inflate
334                 string::size_type suffix = session_name.find (statefile_suffix);
335
336                 if (suffix != string::npos) {
337                         session_name = session_name.substr (0, suffix);
338                 }
339
340                 /* this shouldn't happen, but we catch it just in case it does */
341
342                 if (session_name.empty()) {
343                         continue;
344                 }
345
346                 if (session_dialog.use_session_template()) {
347                         template_name = session_dialog.session_template_name();
348                         _session_is_new = true;
349                 }
350
351                 if (session_name[0] == G_DIR_SEPARATOR ||
352 #ifdef PLATFORM_WINDOWS
353                    (session_name.length() > 3 && session_name[1] == ':' && session_name[2] == G_DIR_SEPARATOR)
354 #else
355                    (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == G_DIR_SEPARATOR) ||
356                    (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == G_DIR_SEPARATOR)
357 #endif
358                 )
359                 {
360
361                         /* absolute path or cwd-relative path specified for session name: infer session folder
362                            from what was given.
363                         */
364
365                         session_path = Glib::path_get_dirname (session_name);
366                         session_name = Glib::path_get_basename (session_name);
367
368                 } else {
369
370                         session_path = session_dialog.session_folder();
371
372                         char illegal = Session::session_name_is_legal (session_name);
373
374                         if (illegal) {
375                                 MessageDialog msg (session_dialog,
376                                                    string_compose (_("To ensure compatibility with various systems\n"
377                                                                      "session names may not contain a '%1' character"),
378                                                                    illegal));
379                                 msg.run ();
380                                 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
381                                 continue;
382                         }
383                 }
384
385                 if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
386
387
388                         if (likely_new && !nsm) {
389
390                                 std::string existing = Glib::build_filename (session_path, session_name);
391
392                                 if (!ask_about_loading_existing_session (existing)) {
393                                         ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
394                                         continue;
395                                 }
396                         }
397
398                         _session_is_new = false;
399
400                 } else {
401
402                         if (!likely_new) {
403                                 pop_back_splash (session_dialog);
404                                 MessageDialog msg (string_compose (_("There is no existing session at \"%1\""), session_path));
405                                 msg.run ();
406                                 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
407                                 continue;
408                         }
409
410                         char illegal = Session::session_name_is_legal(session_name);
411
412                         if (illegal) {
413                                 pop_back_splash (session_dialog);
414                                 MessageDialog msg (session_dialog, string_compose(_("To ensure compatibility with various systems\n"
415                                                                                     "session names may not contain a '%1' character"), illegal));
416                                 msg.run ();
417                                 ARDOUR_COMMAND_LINE::session_name = ""; // cancel that
418                                 continue;
419                         }
420
421                         _session_is_new = true;
422                 }
423
424                 if (!template_name.empty() && template_name.substr (0, 11) == "urn:ardour:") {
425
426                         ret = build_session_from_dialog (session_dialog, session_path, session_name);
427                         meta_session_setup (template_name.substr (11));
428
429                 } else if (likely_new && template_name.empty()) {
430
431                         ret = build_session_from_dialog (session_dialog, session_path, session_name);
432
433                 } else {
434
435                         ret = load_session (session_path, session_name, template_name);
436
437                         if (ret == -2) {
438                                 /* not connected to the AudioEngine, so quit to avoid an infinite loop */
439                                 exit (EXIT_FAILURE);
440                         }
441
442                         /* clear this to avoid endless attempts to load the
443                            same session.
444                         */
445
446                         ARDOUR_COMMAND_LINE::session_name = "";
447                 }
448         }
449
450         _session_dialog = NULL;
451
452         return ret;
453 }
454
455 void
456 ARDOUR_UI::close_session()
457 {
458         if (!check_audioengine (_main_window)) {
459                 return;
460         }
461
462         if (unload_session (true)) {
463                 return;
464         }
465
466         ARDOUR_COMMAND_LINE::session_name = "";
467
468         if (get_session_parameters (true, false)) {
469                 exit (EXIT_FAILURE);
470         }
471 }
472
473
474 /** @param snap_name Snapshot name (without .ardour suffix).
475  *  @return -2 if the load failed because we are not connected to the AudioEngine.
476  */
477 int
478 ARDOUR_UI::load_session (const std::string& path, const std::string& snap_name, std::string mix_template)
479 {
480         /* load_session calls flush_pending() which allows
481          * GUI interaction and potentially loading another session
482          * (that was easy via snapshot sidebar).
483          * Recursing into load_session() from load_session() and recusive
484          * event loops causes all kind of crashes.
485          */
486         assert (!session_load_in_progress);
487         if (session_load_in_progress) {
488                 return -1;
489         }
490         PBD::Unwinder<bool> lsu (session_load_in_progress, true);
491
492         Session *new_session;
493         int unload_status;
494         int retval = -1;
495
496         if (_session) {
497                 unload_status = unload_session ();
498
499                 if (unload_status < 0) {
500                         goto out;
501                 } else if (unload_status > 0) {
502                         retval = 0;
503                         goto out;
504                 }
505         }
506
507         loading_message (string_compose (_("Please wait while %1 loads your session"), PROGRAM_NAME));
508
509         try {
510                 new_session = new Session (*AudioEngine::instance(), path, snap_name, 0, mix_template);
511         }
512
513         /* this one is special */
514
515         catch (AudioEngine::PortRegistrationFailure const& err) {
516
517                 MessageDialog msg (err.what(),
518                                    true,
519                                    Gtk::MESSAGE_INFO,
520                                    Gtk::BUTTONS_CLOSE);
521
522                 msg.set_title (_("Port Registration Error"));
523                 msg.set_secondary_text (_("Click the Close button to try again."));
524                 msg.set_position (Gtk::WIN_POS_CENTER);
525                 pop_back_splash (msg);
526                 msg.present ();
527
528                 int response = msg.run ();
529
530                 msg.hide ();
531
532                 switch (response) {
533                 case RESPONSE_CANCEL:
534                         exit (EXIT_FAILURE);
535                 default:
536                         break;
537                 }
538                 goto out;
539         }
540         catch (SessionException const& e) {
541                 MessageDialog msg (string_compose(
542                                            _("Session \"%1 (snapshot %2)\" did not load successfully:\n%3"),
543                                            path, snap_name, e.what()),
544                                    true,
545                                    Gtk::MESSAGE_INFO,
546                                    BUTTONS_OK);
547
548                 msg.set_title (_("Loading Error"));
549                 msg.set_position (Gtk::WIN_POS_CENTER);
550                 pop_back_splash (msg);
551                 msg.present ();
552
553                 dump_errors (cerr);
554
555                 (void) msg.run ();
556                 msg.hide ();
557
558                 goto out;
559         }
560         catch (...) {
561
562                 MessageDialog msg (string_compose(
563                                            _("Session \"%1 (snapshot %2)\" did not load successfully."),
564                                            path, snap_name),
565                                    true,
566                                    Gtk::MESSAGE_INFO,
567                                    BUTTONS_OK);
568
569                 msg.set_title (_("Loading Error"));
570                 msg.set_position (Gtk::WIN_POS_CENTER);
571                 pop_back_splash (msg);
572                 msg.present ();
573
574                 dump_errors (cerr);
575
576                 (void) msg.run ();
577                 msg.hide ();
578
579                 goto out;
580         }
581
582         {
583                 list<string> const u = new_session->unknown_processors ();
584                 if (!u.empty()) {
585                         MissingPluginDialog d (_session, u);
586                         d.run ();
587                 }
588         }
589
590         if (!new_session->writable()) {
591                 MessageDialog msg (_("This session has been opened in read-only mode.\n\nYou will not be able to record or save."),
592                                    true,
593                                    Gtk::MESSAGE_INFO,
594                                    BUTTONS_OK);
595
596                 msg.set_title (_("Read-only Session"));
597                 msg.set_position (Gtk::WIN_POS_CENTER);
598                 pop_back_splash (msg);
599                 msg.present ();
600                 (void) msg.run ();
601                 msg.hide ();
602         }
603
604
605         /* Now the session been created, add the transport controls */
606         new_session->add_controllable(roll_controllable);
607         new_session->add_controllable(stop_controllable);
608         new_session->add_controllable(goto_start_controllable);
609         new_session->add_controllable(goto_end_controllable);
610         new_session->add_controllable(auto_loop_controllable);
611         new_session->add_controllable(play_selection_controllable);
612         new_session->add_controllable(rec_controllable);
613
614         set_session (new_session);
615
616         if (_session) {
617                 _session->set_clean ();
618         }
619
620 #ifdef WINDOWS_VST_SUPPORT
621         fst_stop_threading();
622 #endif
623
624         {
625                 Timers::TimerSuspender t;
626                 flush_pending (10);
627         }
628
629 #ifdef WINDOWS_VST_SUPPORT
630         fst_start_threading();
631 #endif
632         retval = 0;
633
634         if (!mix_template.empty ()) {
635                 /* if mix_template is given, assume this is a new session */
636                 string metascript = Glib::build_filename (mix_template, "template.lua");
637                 meta_session_setup (metascript);
638         }
639
640
641   out:
642         /* For successful session load the splash is hidden by ARDOUR_UI::first_idle,
643          * which is queued by set_session().
644          * If session-loading fails we hide it explicitly.
645          * This covers both cases in a central place.
646          */
647         if (retval) {
648                 hide_splash ();
649         }
650         return retval;
651 }
652
653 int
654 ARDOUR_UI::build_session (const std::string& path, const std::string& snap_name, BusProfile* bus_profile)
655 {
656         Session *new_session;
657         int x;
658
659         x = unload_session ();
660
661         if (x < 0) {
662                 return -1;
663         } else if (x > 0) {
664                 return 0;
665         }
666
667         _session_is_new = true;
668
669         try {
670                 new_session = new Session (*AudioEngine::instance(), path, snap_name, bus_profile);
671         }
672
673         catch (SessionException const& e) {
674                 cerr << "Here are the errors associated with this failed session:\n";
675                 dump_errors (cerr);
676                 cerr << "---------\n";
677                 MessageDialog msg (string_compose(_("Could not create session in \"%1\": %2"), path, e.what()));
678                 msg.set_title (_("Loading Error"));
679                 msg.set_position (Gtk::WIN_POS_CENTER);
680                 pop_back_splash (msg);
681                 msg.run ();
682                 return -1;
683         }
684         catch (...) {
685                 cerr << "Here are the errors associated with this failed session:\n";
686                 dump_errors (cerr);
687                 cerr << "---------\n";
688                 MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path));
689                 msg.set_title (_("Loading Error"));
690                 msg.set_position (Gtk::WIN_POS_CENTER);
691                 pop_back_splash (msg);
692                 msg.run ();
693                 return -1;
694         }
695
696         /* Give the new session the default GUI state, if such things exist */
697
698         XMLNode* n;
699         n = Config->instant_xml (X_("Editor"));
700         if (n) {
701                 n->remove_nodes_and_delete ("Selection"); // no not apply selection to new sessions.
702                 new_session->add_instant_xml (*n, false);
703         }
704         n = Config->instant_xml (X_("Mixer"));
705         if (n) {
706                 new_session->add_instant_xml (*n, false);
707         }
708
709         n = Config->instant_xml (X_("Preferences"));
710         if (n) {
711                 new_session->add_instant_xml (*n, false);
712         }
713
714         /* Put the playhead at 0 and scroll fully left */
715         n = new_session->instant_xml (X_("Editor"));
716         if (n) {
717                 n->set_property (X_("playhead"), X_("0"));
718                 n->set_property (X_("left-frame"), X_("0"));
719         }
720
721         set_session (new_session);
722
723         new_session->save_state(new_session->name());
724
725         return 0;
726 }
727
728 /** Ask the user for the name of a new snapshot and then take it.
729  */
730
731 void
732 ARDOUR_UI::snapshot_session (bool switch_to_it)
733 {
734         if (switch_to_it && _session->dirty()) {
735                 vector<string> actions;
736                 actions.push_back (_("Abort saving snapshot"));
737                 actions.push_back (_("Don't save now, just snapshot"));
738                 actions.push_back (_("Save it first"));
739                 switch (ask_about_saving_session(actions)) {
740                         case -1:
741                                 return;
742                                 break;
743                         case 1:
744                                 if (save_state_canfail ("")) {
745                                         MessageDialog msg (_main_window,
746                                                         string_compose (_("\
747 %1 was unable to save your session.\n\n\
748 If you still wish to proceed, please use the\n\n\
749 \"Don't save now\" option."), PROGRAM_NAME));
750                                         pop_back_splash(msg);
751                                         msg.run ();
752                                         return;
753                                 }
754                                 /* fallthrough */
755                         case 0:
756                                 _session->remove_pending_capture_state ();
757                                 break;
758                 }
759         }
760
761         Prompter prompter (true);
762         prompter.set_name ("Prompter");
763         prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
764         if (switch_to_it) {
765                 prompter.set_title (_("Snapshot and switch"));
766                 prompter.set_prompt (_("New session name"));
767         } else {
768                 prompter.set_title (_("Take Snapshot"));
769                 prompter.set_prompt (_("Name of new snapshot"));
770         }
771
772         if (switch_to_it) {
773                 prompter.set_initial_text (_session->snap_name());
774         } else {
775                 Glib::DateTime tm (g_date_time_new_now_local ());
776                 prompter.set_initial_text (tm.format ("%FT%H.%M.%S"));
777         }
778
779         bool finished = false;
780         while (!finished) {
781                 switch (prompter.run()) {
782                 case RESPONSE_ACCEPT:
783                 {
784                         finished = process_snapshot_session_prompter (prompter, switch_to_it);
785                         break;
786                 }
787
788                 default:
789                         finished = true;
790                         break;
791                 }
792         }
793 }
794
795 /** Ask the user for a new session name and then rename the session to it.
796  */
797
798 void
799 ARDOUR_UI::rename_session ()
800 {
801         if (!_session) {
802                 return;
803         }
804
805         Prompter prompter (true);
806         string name;
807
808         prompter.set_name ("Prompter");
809         prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
810         prompter.set_title (_("Rename Session"));
811         prompter.set_prompt (_("New session name"));
812
813   again:
814         switch (prompter.run()) {
815         case RESPONSE_ACCEPT:
816         {
817                 prompter.get_result (name);
818
819                 bool do_rename = (name.length() != 0);
820
821                 if (do_rename) {
822                         char illegal = Session::session_name_is_legal (name);
823
824                         if (illegal) {
825                                 MessageDialog msg (string_compose (_("To ensure compatibility with various systems\n"
826                                                                      "session names may not contain a '%1' character"), illegal));
827                                 msg.run ();
828                                 goto again;
829                         }
830
831                         switch (_session->rename (name)) {
832                         case -1: {
833                                 MessageDialog msg (_("That name is already in use by another directory/folder. Please try again."));
834                                 msg.set_position (WIN_POS_MOUSE);
835                                 msg.run ();
836                                 goto again;
837                                 break;
838                         }
839                         case 0:
840                                 break;
841                         default: {
842                                 MessageDialog msg (_("Renaming this session failed.\nThings could be seriously messed up at this point"));
843                                 msg.set_position (WIN_POS_MOUSE);
844                                 msg.run ();
845                                 break;
846                         }
847                         }
848                 }
849
850                 break;
851         }
852
853         default:
854                 break;
855         }
856 }
857
858 bool
859 ARDOUR_UI::save_as_progress_update (float fraction, int64_t cnt, int64_t total, Gtk::Label* label, Gtk::ProgressBar* bar)
860 {
861         char buf[256];
862
863         snprintf (buf, sizeof (buf), _("Copied %" PRId64 " of %" PRId64), cnt, total);
864
865         label->set_text (buf);
866         bar->set_fraction (fraction);
867
868         /* process events, redraws, etc. */
869
870         while (gtk_events_pending()) {
871                 gtk_main_iteration ();
872         }
873
874         return true; /* continue with save-as */
875 }
876
877 void
878 ARDOUR_UI::save_session_as ()
879 {
880         if (!_session) {
881                 return;
882         }
883
884         if (_session->dirty()) {
885                 vector<string> actions;
886                 actions.push_back (_("Abort save-as"));
887                 actions.push_back (_("Don't save now, just save-as"));
888                 actions.push_back (_("Save it first"));
889                 switch (ask_about_saving_session(actions)) {
890                         case -1:
891                                 return;
892                                 break;
893                         case 1:
894                                 if (save_state_canfail ("")) {
895                                         MessageDialog msg (_main_window,
896                                                         string_compose (_("\
897 %1 was unable to save your session.\n\n\
898 If you still wish to proceed, please use the\n\n\
899 \"Don't save now\" option."), PROGRAM_NAME));
900                                         pop_back_splash(msg);
901                                         msg.run ();
902                                         return;
903                                 }
904                                 /* fallthrough */
905                         case 0:
906                                 _session->remove_pending_capture_state ();
907                                 break;
908                 }
909         }
910
911         if (!save_as_dialog) {
912                 save_as_dialog = new SaveAsDialog;
913         }
914
915         save_as_dialog->set_name (_session->name());
916
917         int response = save_as_dialog->run ();
918
919         save_as_dialog->hide ();
920
921         switch (response) {
922         case Gtk::RESPONSE_OK:
923                 break;
924         default:
925                 return;
926         }
927
928
929         Session::SaveAs sa;
930
931         sa.new_parent_folder = save_as_dialog->new_parent_folder ();
932         sa.new_name = save_as_dialog->new_name ();
933         sa.switch_to = save_as_dialog->switch_to();
934         sa.copy_media = save_as_dialog->copy_media();
935         sa.copy_external = save_as_dialog->copy_external();
936         sa.include_media = save_as_dialog->include_media ();
937
938         /* Only bother with a progress dialog if we're going to copy
939            media into the save-as target. Without that choice, this
940            will be very fast because we're only talking about a few kB's to
941            perhaps a couple of MB's of data.
942         */
943
944         ArdourDialog progress_dialog (_("Save As"), true);
945         ScopedConnection c;
946
947         if (sa.include_media && sa.copy_media) {
948
949                 Gtk::Label* label = manage (new Gtk::Label());
950                 Gtk::ProgressBar* progress_bar = manage (new Gtk::ProgressBar ());
951
952                 progress_dialog.get_vbox()->pack_start (*label);
953                 progress_dialog.get_vbox()->pack_start (*progress_bar);
954                 label->show ();
955                 progress_bar->show ();
956
957                 /* this signal will be emitted from within this, the calling thread,
958                  * after every file is copied. It provides information on percentage
959                  * complete (in terms of total data to copy), the number of files
960                  * copied so far, and the total number to copy.
961                  */
962
963                 sa.Progress.connect_same_thread (c, boost::bind (&ARDOUR_UI::save_as_progress_update, this, _1, _2, _3, label, progress_bar));
964
965                 progress_dialog.show_all ();
966                 progress_dialog.present ();
967         }
968
969         if (_session->save_as (sa)) {
970                 /* ERROR MESSAGE */
971                 MessageDialog msg (string_compose (_("Save As failed: %1"), sa.failure_message));
972                 msg.run ();
973         }
974
975         /* the logic here may seem odd: why isn't the condition sa.switch_to ?
976          * the trick is this: if the new session was copy with media included,
977          * then Session::save_as() will have already done a neat trick to avoid
978          * us having to unload and load the new state. But if the media was not
979          * included, then this is required (it avoids us having to otherwise
980          * drop all references to media (sources).
981          */
982
983         if (!sa.include_media && sa.switch_to) {
984                 unload_session (false);
985                 load_session (sa.final_session_folder_name, sa.new_name);
986         }
987 }
988
989 void
990 ARDOUR_UI::archive_session ()
991 {
992         if (!_session) {
993                 return;
994         }
995
996         time_t n;
997         time (&n);
998         Glib::DateTime gdt (Glib::DateTime::create_now_local (n));
999
1000         SessionArchiveDialog sad;
1001         sad.set_name (_session->name() + gdt.format ("_%F_%H%M%S"));
1002         int response = sad.run ();
1003
1004         if (response != Gtk::RESPONSE_OK) {
1005                 sad.hide ();
1006                 return;
1007         }
1008
1009         if (_session->archive_session (sad.target_folder(), sad.name(), sad.encode_option (), sad.compression_level (), sad.only_used_sources (), &sad)) {
1010                 MessageDialog msg (_("Session Archiving failed."));
1011                 msg.run ();
1012         }
1013 }
1014
1015 void
1016 ARDOUR_UI::quick_snapshot_session (bool switch_to_it)
1017 {
1018                 char timebuf[128];
1019                 time_t n;
1020                 struct tm local_time;
1021
1022                 time (&n);
1023                 localtime_r (&n, &local_time);
1024                 strftime (timebuf, sizeof(timebuf), "%FT%H.%M.%S", &local_time);
1025                 if (switch_to_it && _session->dirty ()) {
1026                         save_state_canfail ("");
1027                 }
1028
1029                 save_state (timebuf, switch_to_it);
1030 }
1031
1032
1033 bool
1034 ARDOUR_UI::process_snapshot_session_prompter (Prompter& prompter, bool switch_to_it)
1035 {
1036         string snapname;
1037
1038         prompter.get_result (snapname);
1039
1040         bool do_save = (snapname.length() != 0);
1041
1042         if (do_save) {
1043                 char illegal = Session::session_name_is_legal(snapname);
1044                 if (illegal) {
1045                         MessageDialog msg (string_compose (_("To ensure compatibility with various systems\n"
1046                                                              "snapshot names may not contain a '%1' character"), illegal));
1047                         msg.run ();
1048                         return false;
1049                 }
1050         }
1051
1052         vector<std::string> p;
1053         get_state_files_in_directory (_session->session_directory().root_path(), p);
1054         vector<string> n = get_file_names_no_extension (p);
1055
1056         if (find (n.begin(), n.end(), snapname) != n.end()) {
1057
1058                 do_save = overwrite_file_dialog (prompter,
1059                                                  _("Confirm Snapshot Overwrite"),
1060                                                  _("A snapshot already exists with that name. Do you want to overwrite it?"));
1061         }
1062
1063         if (do_save) {
1064                 save_state (snapname, switch_to_it);
1065         }
1066         else {
1067                 return false;
1068         }
1069
1070         return true;
1071 }
1072
1073
1074 void
1075 ARDOUR_UI::open_session ()
1076 {
1077         if (!check_audioengine (_main_window)) {
1078                 return;
1079         }
1080
1081         /* ardour sessions are folders */
1082         Gtk::FileChooserDialog open_session_selector(_("Open Session"), FILE_CHOOSER_ACTION_OPEN);
1083         open_session_selector.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1084         open_session_selector.add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
1085         open_session_selector.set_default_response(Gtk::RESPONSE_ACCEPT);
1086
1087         if (_session) {
1088                 string session_parent_dir = Glib::path_get_dirname(_session->path());
1089                 open_session_selector.set_current_folder(session_parent_dir);
1090         } else {
1091                 open_session_selector.set_current_folder(Config->get_default_session_parent_dir());
1092         }
1093
1094         Gtkmm2ext::add_volume_shortcuts (open_session_selector);
1095         try {
1096                 /* add_shortcut_folder throws an exception if the folder being added already has a shortcut */
1097                 string default_session_folder = Config->get_default_session_parent_dir();
1098                 open_session_selector.add_shortcut_folder (default_session_folder);
1099         }
1100         catch (Glib::Error const& e) {
1101                 std::cerr << "open_session_selector.add_shortcut_folder() threw Glib::Error " << e.what() << std::endl;
1102         }
1103
1104         FileFilter session_filter;
1105         session_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::statefile_suffix));
1106         session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME));
1107         open_session_selector.add_filter (session_filter);
1108
1109         FileFilter archive_filter;
1110         archive_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::session_archive_suffix));
1111         archive_filter.set_name (_("Session Archives"));
1112
1113         open_session_selector.add_filter (archive_filter);
1114
1115         open_session_selector.set_filter (session_filter);
1116
1117         int response = open_session_selector.run();
1118         open_session_selector.hide ();
1119
1120         if (response == Gtk::RESPONSE_CANCEL) {
1121                 return;
1122         }
1123
1124         string session_path = open_session_selector.get_filename();
1125         string path, name;
1126         bool isnew;
1127
1128         if (session_path.length() > 0) {
1129                 int rv = ARDOUR::inflate_session (session_path,
1130                                 Config->get_default_session_parent_dir(), path, name);
1131                 if (rv == 0) {
1132                         _session_is_new = false;
1133                         load_session (path, name);
1134                 }
1135                 else if (rv < 0) {
1136                         MessageDialog msg (_main_window,
1137                                         string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
1138                         msg.run ();
1139                 }
1140                 else if (ARDOUR::find_session (session_path, path, name, isnew) == 0) {
1141                         _session_is_new = isnew;
1142                         load_session (path, name);
1143                 }
1144         }
1145 }
1146
1147 void
1148 ARDOUR_UI::open_recent_session ()
1149 {
1150         bool can_return = (_session != 0);
1151
1152         SessionDialog recent_session_dialog;
1153
1154         while (true) {
1155
1156                 ResponseType r = (ResponseType) recent_session_dialog.run ();
1157
1158                 switch (r) {
1159                 case RESPONSE_ACCEPT:
1160                         break;
1161                 default:
1162                         if (can_return) {
1163                                 recent_session_dialog.hide();
1164                                 return;
1165                         } else {
1166                                 exit (EXIT_FAILURE);
1167                         }
1168                 }
1169
1170                 recent_session_dialog.hide();
1171
1172                 bool should_be_new;
1173
1174                 std::string path = recent_session_dialog.session_folder();
1175                 std::string state = recent_session_dialog.session_name (should_be_new);
1176
1177                 if (should_be_new == true) {
1178                         continue;
1179                 }
1180
1181                 _session_is_new = false;
1182
1183                 if (load_session (path, state) == 0) {
1184                         break;
1185                 }
1186
1187                 can_return = false;
1188         }
1189 }
1190
1191 int
1192 ARDOUR_UI::ask_about_saving_session (const vector<string>& actions)
1193 {
1194         ArdourDialog window (_("Unsaved Session"));
1195         Gtk::HBox dhbox;  // the hbox for the image and text
1196         Gtk::Label  prompt_label;
1197         Gtk::Image* dimage = manage (new Gtk::Image(Stock::DIALOG_WARNING,  Gtk::ICON_SIZE_DIALOG));
1198
1199         string msg;
1200
1201         assert (actions.size() >= 3);
1202
1203         window.add_button (actions[0], RESPONSE_REJECT);
1204         window.add_button (actions[1], RESPONSE_APPLY);
1205         window.add_button (actions[2], RESPONSE_ACCEPT);
1206
1207         window.set_default_response (RESPONSE_ACCEPT);
1208
1209         Gtk::Button noquit_button (msg);
1210         noquit_button.set_name ("EditorGTKButton");
1211
1212         string prompt;
1213
1214         if (_session->snap_name() == _session->name()) {
1215                 prompt = string_compose(_("The session \"%1\"\nhas not been saved.\n\nAny changes made this time\nwill be lost unless you save it.\n\nWhat do you want to do?"),
1216                                         _session->snap_name());
1217         } else {
1218                 prompt = string_compose(_("The snapshot \"%1\"\nhas not been saved.\n\nAny changes made this time\nwill be lost unless you save it.\n\nWhat do you want to do?"),
1219                                         _session->snap_name());
1220         }
1221
1222         prompt_label.set_text (prompt);
1223         prompt_label.set_name (X_("PrompterLabel"));
1224         prompt_label.set_alignment(ALIGN_LEFT, ALIGN_TOP);
1225
1226         dimage->set_alignment(ALIGN_CENTER, ALIGN_TOP);
1227         dhbox.set_homogeneous (false);
1228         dhbox.pack_start (*dimage, false, false, 5);
1229         dhbox.pack_start (prompt_label, true, false, 5);
1230         window.get_vbox()->pack_start (dhbox);
1231
1232         window.set_name (_("Prompter"));
1233         window.set_modal (true);
1234         window.set_resizable (false);
1235
1236         dhbox.show();
1237         prompt_label.show();
1238         dimage->show();
1239         window.show();
1240         window.present ();
1241
1242         ResponseType r = (ResponseType) window.run();
1243
1244         window.hide ();
1245
1246         switch (r) {
1247         case RESPONSE_ACCEPT: // save and get out of here
1248                 return 1;
1249         case RESPONSE_APPLY:  // get out of here
1250                 return 0;
1251         default:
1252                 break;
1253         }
1254
1255         return -1;
1256 }
1257
1258
1259 void
1260 ARDOUR_UI::save_session_at_its_request (std::string snapshot_name)
1261 {
1262         if (_session) {
1263                 _session->save_state (snapshot_name);
1264         }
1265 }
1266
1267 gint
1268 ARDOUR_UI::autosave_session ()
1269 {
1270         if (g_main_depth() > 1) {
1271                 /* inside a recursive main loop,
1272                    give up because we may not be able to
1273                    take a lock.
1274                 */
1275                 return 1;
1276         }
1277
1278         if (!Config->get_periodic_safety_backups()) {
1279                 return 1;
1280         }
1281
1282         if (_session) {
1283                 _session->maybe_write_autosave();
1284         }
1285
1286         return 1;
1287 }