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