Use conf.fatal for fatal configuration errors
[ardour.git] / gtk2_ardour / session_import_dialog.cc
1 /*
2  * Copyright (C) 2008-2009 Sakari Bergen <sakari.bergen@beatwaves.net>
3  * Copyright (C) 2009-2010 Carl Hetherington <carl@carlh.net>
4  * Copyright (C) 2009-2011 David Robillard <d@drobilla.net>
5  * Copyright (C) 2009-2017 Paul Davis <paul@linuxaudiosystems.com>
6  * Copyright (C) 2015-2018 Robin Gareus <robin@gareus.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include <gtkmm/messagedialog.h>
24 #include <gtkmm/stock.h>
25
26 #include "pbd/failed_constructor.h"
27
28 #include "ardour/audio_region_importer.h"
29 #include "ardour/audio_playlist_importer.h"
30 #include "ardour/audio_track_importer.h"
31 #include "ardour/filename_extensions.h"
32 #include "ardour/location_importer.h"
33 #include "ardour/tempo_map_importer.h"
34
35 #include "gtkmm2ext/utils.h"
36 #include "widgets/prompter.h"
37
38 #include "gui_thread.h"
39 #include "session_import_dialog.h"
40 #include "ui_config.h"
41
42 #include "pbd/i18n.h"
43
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
47 using namespace Gtk;
48
49 SessionImportDialog::SessionImportDialog (ARDOUR::Session* target) :
50   ArdourDialog (_("Import from Session")),
51   file_browse_button (_("Browse"))
52 {
53         set_session (target);
54
55         // File entry
56         file_entry.set_name ("ImportFileNameEntry");
57         file_entry.set_text ("/");
58         Gtkmm2ext::set_size_request_to_display_given_text (file_entry, X_("Kg/quite/a/reasonable/size/for/files/i/think"), 5, 8);
59
60         file_browse_button.set_name ("EditorGTKButton");
61         file_browse_button.signal_clicked().connect (sigc::mem_fun(*this, &SessionImportDialog::browse));
62
63         file_hbox.set_spacing (5);
64         file_hbox.set_border_width (5);
65         file_hbox.pack_start (file_entry, true, true);
66         file_hbox.pack_start (file_browse_button, false, false);
67
68         file_frame.add (file_hbox);
69         file_frame.set_border_width (5);
70         file_frame.set_name ("ImportFrom");
71         file_frame.set_label (_("Import from Session"));
72
73         get_vbox()->pack_start (file_frame, false, false);
74
75         // Session browser
76         session_tree = TreeStore::create (sb_cols);
77         session_browser.set_model (session_tree);
78
79         session_browser.set_name ("SessionBrowser");
80         session_browser.append_column (_("Elements"), sb_cols.name);
81         session_browser.append_column_editable (_("Import"), sb_cols.queued);
82         session_browser.get_column(0)->set_min_width (180);
83         session_browser.get_column(1)->set_min_width (40);
84         session_browser.get_column(1)->set_sizing (TREE_VIEW_COLUMN_AUTOSIZE);
85         if (UIConfiguration::instance().get_use_tooltips()) {
86                 session_browser.set_tooltip_column (3);
87         }
88
89         session_scroll.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC);
90         session_scroll.add (session_browser);
91         session_scroll.set_size_request (220, 400);
92
93         // Connect signals
94         CellRendererToggle *toggle = dynamic_cast<CellRendererToggle *> (session_browser.get_column_cell_renderer (1));
95         toggle->signal_toggled().connect(sigc::mem_fun (*this, &SessionImportDialog::update));
96         session_browser.signal_row_activated().connect(sigc::mem_fun (*this, &SessionImportDialog::show_info));
97
98         get_vbox()->pack_start (session_scroll, false, false);
99
100         // Buttons
101         cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL);
102         cancel_button->signal_clicked().connect (sigc::mem_fun (*this, &SessionImportDialog::end_dialog));
103         ok_button = add_button (_("Import"), RESPONSE_ACCEPT);
104         ok_button->signal_clicked().connect (sigc::mem_fun (*this, &SessionImportDialog::do_merge));
105
106         // prompt signals XXX: problem - handlers to be in the same thread since they return values
107         ElementImporter::Rename.connect_same_thread (connections, boost::bind (&SessionImportDialog::open_rename_dialog, this, _1, _2));
108         ElementImporter::Prompt.connect_same_thread (connections, boost::bind (&SessionImportDialog::open_prompt_dialog, this, _1));
109
110         // Finalize
111         show_all();
112 }
113
114 void
115 SessionImportDialog::load_session (const string& filename)
116 {
117         if (_session) {
118                 if (tree.read (filename)) {
119                         error << string_compose (_("Cannot load XML for session from %1"), filename) << endmsg;
120                         return;
121                 }
122                 boost::shared_ptr<AudioRegionImportHandler> region_handler (new AudioRegionImportHandler (tree, *_session));
123                 boost::shared_ptr<AudioPlaylistImportHandler> pl_handler (new AudioPlaylistImportHandler (tree, *_session, *region_handler));
124
125                 handlers.push_back (boost::static_pointer_cast<ElementImportHandler> (region_handler));
126                 handlers.push_back (boost::static_pointer_cast<ElementImportHandler> (pl_handler));
127                 handlers.push_back (HandlerPtr(new UnusedAudioPlaylistImportHandler (tree, *_session, *region_handler)));
128                 handlers.push_back (HandlerPtr(new AudioTrackImportHandler (tree, *_session, *pl_handler)));
129                 handlers.push_back (HandlerPtr(new LocationImportHandler (tree, *_session)));
130                 handlers.push_back (HandlerPtr(new TempoMapImportHandler (tree, *_session)));
131
132                 fill_list();
133
134                 if (ElementImportHandler::dirty()) {
135                         // Warn user
136                         string txt = _("Some elements had errors in them. Please see the log for details");
137                         MessageDialog msg (txt, false, MESSAGE_WARNING, BUTTONS_OK, true);
138                         msg.run();
139                 }
140         }
141 }
142
143 void
144 SessionImportDialog::fill_list ()
145 {
146         session_tree->clear();
147
148         // Loop through element types
149         for (HandlerList::iterator handler = handlers.begin(); handler != handlers.end(); ++handler) {
150                 TreeModel::iterator iter = session_tree->append();
151                 TreeModel::Row row = *iter;
152                 row[sb_cols.name] = (*handler)->get_info();
153                 row[sb_cols.queued] = false;
154                 row[sb_cols.element] = ElementPtr(); // "Null" pointer
155
156                 // Loop through elements
157                 ElementList &elements = (*handler)->elements;
158                 for (ElementList::iterator element = elements.begin(); element != elements.end(); ++element) {
159                         iter = session_tree->append(row.children());
160                         TreeModel::Row child = *iter;
161                         child[sb_cols.name] = (*element)->get_name();
162                         child[sb_cols.queued] = false;
163                         child[sb_cols.element] = *element;
164                         child[sb_cols.info] = (*element)->get_info();
165                 }
166         }
167 }
168
169 void
170 SessionImportDialog::browse ()
171 {
172         FileChooserDialog dialog(_("Import from session"), browse_action());
173         dialog.set_transient_for(*this);
174         dialog.set_filename (file_entry.get_text());
175
176         FileFilter session_filter;
177         session_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::statefile_suffix));
178         session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME));
179         dialog.add_filter (session_filter);
180         dialog.set_filter (session_filter);
181
182         dialog.add_button(Stock::CANCEL, RESPONSE_CANCEL);
183         dialog.add_button(Stock::OK, RESPONSE_OK);
184
185         int result = dialog.run();
186
187         if (result == RESPONSE_OK) {
188                 string filename = dialog.get_filename();
189
190                 if (filename.length()) {
191                         file_entry.set_text (filename);
192                         load_session (filename);
193                 }
194         }
195 }
196
197 void
198 SessionImportDialog::do_merge ()
199 {
200
201         // element types
202         TreeModel::Children types = session_browser.get_model()->children();
203         TreeModel::Children::iterator ti;
204         for (ti = types.begin(); ti != types.end(); ++ti) {
205                 // elements
206                 TreeModel::Children elements = ti->children();
207                 TreeModel::Children::iterator ei;
208                 for (ei = elements.begin(); ei != elements.end(); ++ei) {
209                         if ((*ei)[sb_cols.queued]) {
210                                 ElementPtr element = (*ei)[sb_cols.element];
211                                 element->move();
212                         }
213                 }
214         }
215
216         end_dialog();
217
218         if (ElementImportHandler::errors()) {
219                 // Warn user
220                 string txt = _("Some elements had errors in them. Please see the log for details");
221                 MessageDialog msg (txt, false, MESSAGE_WARNING, BUTTONS_OK, true);
222                 msg.run();
223         }
224 }
225
226
227 void
228 SessionImportDialog::update (string path)
229 {
230         TreeModel::iterator cell = session_browser.get_model()->get_iter (path);
231
232         // Select all elements if element type is selected
233         if (path.size() == 1) {
234                 {
235                         // Prompt user for verification
236                         string txt = _("This will select all elements of this type!");
237                         MessageDialog msg (txt, false, MESSAGE_QUESTION, BUTTONS_OK_CANCEL, true);
238                         if (msg.run() == RESPONSE_CANCEL) {
239                                 (*cell)[sb_cols.queued] = false;
240                                 return;
241                         }
242                 }
243
244                 TreeModel::Children elements = cell->children();
245                 TreeModel::Children::iterator ei;
246                 for (ei = elements.begin(); ei != elements.end(); ++ei) {
247                         ElementPtr element = (*ei)[sb_cols.element];
248                         if (element->prepare_move()) {
249                                 (*ei)[sb_cols.queued] = true;
250                         } else {
251                                 (*cell)[sb_cols.queued] = false; // Not all are selected
252                         }
253                 }
254                 return;
255         }
256
257         ElementPtr element = (*cell)[sb_cols.element];
258         if ((*cell)[sb_cols.queued]) {
259                 if (!element->prepare_move()) {
260                         (*cell)[sb_cols.queued] = false;
261                 }
262         } else {
263                 element->cancel_move();
264         }
265 }
266
267 void
268 SessionImportDialog::show_info(const TreeModel::Path& path, TreeViewColumn*)
269 {
270         if (path.size() == 1) {
271                 return;
272         }
273
274         TreeModel::iterator cell = session_browser.get_model()->get_iter (path);
275         string info = (*cell)[sb_cols.info];
276
277         MessageDialog msg (info, false, MESSAGE_INFO, BUTTONS_OK, true);
278         msg.run();
279 }
280
281 void
282 SessionImportDialog::end_dialog ()
283 {
284         hide_all();
285
286         set_modal (false);
287         ok_button->set_sensitive(true);
288 }
289
290 std::pair<bool, string>
291 SessionImportDialog::open_rename_dialog (string text, string name)
292 {
293         ArdourWidgets::Prompter prompter(true);
294         string new_name;
295
296         prompter.set_name ("Prompter");
297         prompter.add_button (Stock::SAVE, RESPONSE_ACCEPT);
298         prompter.set_prompt (text);
299         prompter.set_initial_text (name);
300
301         if (prompter.run() == RESPONSE_ACCEPT) {
302                 prompter.get_result (new_name);
303                 if (new_name.length()) {
304                         name = new_name;
305                 }
306                 return std::make_pair (true, new_name);
307         }
308         return std::make_pair (false, new_name);
309 }
310
311 bool
312 SessionImportDialog::open_prompt_dialog (string text)
313 {
314         MessageDialog msg (text, false, MESSAGE_QUESTION, BUTTONS_OK_CANCEL, true);
315         if (msg.run() == RESPONSE_OK) {
316                 return true;
317         }
318         return false;
319 }