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