Add select all / deselect all to timespan section of export dialogue (#4273).
[ardour.git] / gtk2_ardour / export_timespan_selector.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 "export_timespan_selector.h"
22
23 #include "ardour_ui.h"
24
25 #include "ardour/tempo.h"
26 #include "ardour/location.h"
27 #include "ardour/types.h"
28 #include "ardour/session.h"
29 #include "ardour/export_handler.h"
30 #include "ardour/export_timespan.h"
31
32 #include "pbd/enumwriter.h"
33 #include "pbd/convert.h"
34
35 #include <sstream>
36 #include <iomanip>
37
38 #include "i18n.h"
39
40 using namespace Glib;
41 using namespace ARDOUR;
42 using namespace PBD;
43 using std::string;
44
45 ExportTimespanSelector::ExportTimespanSelector (ARDOUR::Session * session, ProfileManagerPtr manager) :
46         manager (manager),
47         time_format_label (_("Show Times as:"), Gtk::ALIGN_LEFT)
48 {
49         set_session (session);
50
51         option_hbox.pack_start (time_format_label, false, false, 0);
52         option_hbox.pack_start (time_format_combo, false, false, 6);
53
54         Gtk::Button* b = manage (new Gtk::Button (_("Select All")));
55         b->signal_clicked().connect (
56                 sigc::bind (
57                         sigc::mem_fun (*this, &ExportTimespanSelector::set_selection_state_of_all_timespans), true
58                         )
59                 );
60         option_hbox.pack_start (*b, false, false, 6);
61         
62         b = manage (new Gtk::Button (_("Deselect All")));
63         b->signal_clicked().connect (
64                 sigc::bind (
65                         sigc::mem_fun (*this, &ExportTimespanSelector::set_selection_state_of_all_timespans), false
66                         )
67                 );
68         option_hbox.pack_start (*b, false, false, 6);
69
70         range_scroller.add (range_view);
71
72         pack_start (option_hbox, false, false, 0);
73         pack_start (range_scroller, true, true, 6);
74
75         /*** Combo boxes ***/
76
77         Gtk::TreeModel::iterator iter;
78         Gtk::TreeModel::Row row;
79
80         /* Time format combo */
81
82         time_format_list = Gtk::ListStore::create (time_format_cols);
83         time_format_combo.set_model (time_format_list);
84         time_format_combo.set_name ("PaddedButton");
85
86         iter = time_format_list->append();
87         row = *iter;
88         row[time_format_cols.format] = ExportProfileManager::Timecode;
89         row[time_format_cols.label] = _("Timecode");
90
91         iter = time_format_list->append();
92         row = *iter;
93         row[time_format_cols.format] = ExportProfileManager::MinSec;
94         row[time_format_cols.label] = _("Minutes:Seconds");
95
96         iter = time_format_list->append();
97         row = *iter;
98         row[time_format_cols.format] = ExportProfileManager::BBT;
99         row[time_format_cols.label] = _("Bars:Beats");
100
101         time_format_combo.pack_start (time_format_cols.label);
102         time_format_combo.set_active (0);
103
104         time_format_combo.signal_changed().connect (sigc::mem_fun (*this, &ExportTimespanSelector::change_time_format));
105
106         /* Range view */
107
108         range_list = Gtk::ListStore::create (range_cols);
109         range_view.set_model (range_list);
110         range_view.set_headers_visible (true);
111 }
112
113 ExportTimespanSelector::~ExportTimespanSelector ()
114 {
115
116 }
117
118 void
119 ExportTimespanSelector::add_range_to_selection (ARDOUR::Location const * loc)
120 {
121         ExportTimespanPtr span = _session->get_export_handler()->add_timespan();
122
123         std::string id;
124         if (loc == state->session_range.get()) {
125                 id = "session";
126         } else if (loc == state->selection_range.get()) {
127                 id = "selection";
128         } else {
129                 id = loc->id().to_s();
130         }
131
132         span->set_range (loc->start(), loc->end());
133         span->set_name (loc->name());
134         span->set_range_id (id);
135         state->timespans->push_back (span);
136 }
137
138 void
139 ExportTimespanSelector::set_time_format_from_state ()
140 {
141         Gtk::TreeModel::Children::iterator tree_it;
142         for (tree_it = time_format_list->children().begin(); tree_it != time_format_list->children().end(); ++tree_it) {
143                 if (tree_it->get_value (time_format_cols.format) == state->time_format) {
144                         time_format_combo.set_active (tree_it);
145                 }
146         }
147 }
148
149 void
150 ExportTimespanSelector::sync_with_manager ()
151 {
152         state = manager->get_timespans().front();
153         fill_range_list ();
154         CriticalSelectionChanged();
155 }
156
157 void
158 ExportTimespanSelector::change_time_format ()
159 {
160         state->time_format = time_format_combo.get_active()->get_value (time_format_cols.format);
161
162         for (Gtk::ListStore::Children::iterator it = range_list->children().begin(); it != range_list->children().end(); ++it) {
163                 Location * location = it->get_value (range_cols.location);
164                 it->set_value (range_cols.label, construct_label (location));
165                 it->set_value (range_cols.length, construct_length (location));
166         }
167 }
168
169 std::string
170 ExportTimespanSelector::construct_label (ARDOUR::Location const * location) const
171 {
172         std::string label;
173         std::string start;
174         std::string end;
175
176         framepos_t start_frame = location->start();
177         framepos_t end_frame = location->end();
178
179         switch (state->time_format) {
180           case AudioClock::BBT:
181                 start = bbt_str (start_frame);
182                 end = bbt_str (end_frame);
183                 break;
184
185           case AudioClock::Timecode:
186                 start = timecode_str (start_frame);
187                 end = timecode_str (end_frame);
188                 break;
189
190           case AudioClock::MinSec:
191                 start = ms_str (start_frame);
192                 end = ms_str (end_frame);
193                 break;
194
195           case AudioClock::Frames:
196                 start = to_string (start_frame, std::dec);
197                 end = to_string (end_frame, std::dec);
198                 break;
199         }
200
201         // label += _("from ");
202
203         // label += "<span color=\"#7fff7f\">";
204         label += start;
205 //      label += "</span>";
206
207         label += _(" to ");
208
209 //      label += "<span color=\"#7fff7f\">";
210         label += end;
211 //      label += "</span>";
212
213         return label;
214 }
215
216 std::string
217 ExportTimespanSelector::construct_length (ARDOUR::Location const * location) const
218 {
219         if (location->length() == 0) {
220                 return "";
221         }
222
223         std::stringstream s;
224
225         switch (state->time_format) {
226         case AudioClock::BBT:
227                 s << bbt_str (location->length ());
228                 break;
229
230         case AudioClock::Timecode:
231         {
232                 Timecode::Time tc;
233                 _session->timecode_duration (location->length(), tc);
234                 tc.print (s);
235                 break;
236         }
237
238         case AudioClock::MinSec:
239                 s << ms_str (location->length ());
240                 break;
241
242         case AudioClock::Frames:
243                 s << location->length ();
244                 break;
245         }
246
247         return s.str ();
248 }
249
250
251 std::string
252 ExportTimespanSelector::bbt_str (framepos_t frames) const
253 {
254         if (!_session) {
255                 return "Error!";
256         }
257
258         std::ostringstream oss;
259         Timecode::BBT_Time time;
260         _session->bbt_time (frames, time);
261
262         print_padded (oss, time);
263         return oss.str ();
264 }
265
266 std::string
267 ExportTimespanSelector::timecode_str (framecnt_t frames) const
268 {
269         if (!_session) {
270                 return "Error!";
271         }
272
273         std::ostringstream oss;
274         Timecode::Time time;
275
276         _session->timecode_time (frames, time);
277
278         oss << std::setfill('0') << std::right <<
279           std::setw(2) <<
280           time.hours << ":" <<
281           std::setw(2) <<
282           time.minutes << ":" <<
283           std::setw(2) <<
284           time.seconds << ":" <<
285           std::setw(2) <<
286           time.frames;
287
288         return oss.str();
289 }
290
291 std::string
292 ExportTimespanSelector::ms_str (framecnt_t frames) const
293 {
294         if (!_session) {
295                 return "Error!";
296         }
297
298         std::ostringstream oss;
299         framecnt_t left;
300         int hrs;
301         int mins;
302         int secs;
303         int sec_promilles;
304
305         left = frames;
306         hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
307         left -= (framecnt_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
308         mins = (int) floor (left / (_session->frame_rate() * 60.0f));
309         left -= (framecnt_t) floor (mins * _session->frame_rate() * 60.0f);
310         secs = (int) floor (left / (float) _session->frame_rate());
311         left -= (framecnt_t) floor (secs * _session->frame_rate());
312         sec_promilles = (int) (left * 1000 / (float) _session->frame_rate() + 0.5);
313
314         oss << std::setfill('0') << std::right <<
315           std::setw(2) <<
316           hrs << ":" <<
317           std::setw(2) <<
318           mins << ":" <<
319           std::setw(2) <<
320           secs << "." <<
321           std::setw(3) <<
322           sec_promilles;
323
324         return oss.str();
325 }
326
327 void
328 ExportTimespanSelector::update_range_name (std::string const & path, std::string const & new_text)
329 {
330         Gtk::TreeStore::iterator it = range_list->get_iter (path);
331         it->get_value (range_cols.location)->set_name (new_text);
332
333         CriticalSelectionChanged();
334 }
335
336 void
337 ExportTimespanSelector::set_selection_state_of_all_timespans (bool s)
338 {
339         for (Gtk::ListStore::Children::iterator it = range_list->children().begin(); it != range_list->children().end(); ++it) {
340                 it->set_value (range_cols.selected, s);
341         }
342 }
343
344 /*** ExportTimespanSelectorSingle ***/
345
346 ExportTimespanSelectorSingle::ExportTimespanSelectorSingle (ARDOUR::Session * session, ProfileManagerPtr manager, std::string range_id) :
347         ExportTimespanSelector (session, manager),
348         range_id (range_id)
349 {
350         range_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_NEVER);
351         range_view.append_column_editable (_("Range"), range_cols.name);
352
353         if (Gtk::CellRendererText * renderer = dynamic_cast<Gtk::CellRendererText *> (range_view.get_column_cell_renderer (0))) {
354                 renderer->signal_edited().connect (sigc::mem_fun (*this, &ExportTimespanSelectorSingle::update_range_name));
355         }
356
357         Gtk::CellRendererText * label_render = Gtk::manage (new Gtk::CellRendererText());
358         Gtk::TreeView::Column * label_col = Gtk::manage (new Gtk::TreeView::Column (_("Time Span"), *label_render));
359         label_col->add_attribute (label_render->property_markup(), range_cols.label);
360         range_view.append_column (*label_col);
361
362         range_view.append_column (_("Length"), range_cols.length);
363 }
364
365 void
366 ExportTimespanSelectorSingle::fill_range_list ()
367 {
368         if (!state) { return; }
369
370         std::string id;
371         if (!range_id.compare (X_("session"))) {
372                 id = state->session_range->id().to_s();
373         } else if (!range_id.compare (X_("selection"))) {
374                 id = state->selection_range->id().to_s();
375         } else {
376                 id = range_id;
377         }
378
379         range_list->clear();
380         state->timespans->clear();
381
382         Gtk::TreeModel::iterator iter;
383         Gtk::TreeModel::Row row;
384         for (LocationList::const_iterator it = state->ranges->begin(); it != state->ranges->end(); ++it) {
385
386                 if (!(*it)->id().to_s().compare (id)) {
387                         iter = range_list->append();
388                         row = *iter;
389
390                         row[range_cols.location] = *it;
391                         row[range_cols.selected] = true;
392                         row[range_cols.name] = (*it)->name();
393                         row[range_cols.label] = construct_label (*it);
394                         row[range_cols.length] = construct_length (*it);
395
396                         add_range_to_selection (*it);
397
398                         break;
399                 }
400         }
401
402         set_time_format_from_state();
403 }
404
405 /*** ExportTimespanSelectorMultiple ***/
406
407 ExportTimespanSelectorMultiple::ExportTimespanSelectorMultiple (ARDOUR::Session * session, ProfileManagerPtr manager) :
408   ExportTimespanSelector (session, manager)
409 {
410         range_scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
411         range_view.append_column_editable ("", range_cols.selected);
412         range_view.append_column_editable (_("Range"), range_cols.name);
413
414         if (Gtk::CellRendererToggle * renderer = dynamic_cast<Gtk::CellRendererToggle *> (range_view.get_column_cell_renderer (0))) {
415                 renderer->signal_toggled().connect (sigc::hide (sigc::mem_fun (*this, &ExportTimespanSelectorMultiple::update_selection)));
416         }
417         if (Gtk::CellRendererText * renderer = dynamic_cast<Gtk::CellRendererText *> (range_view.get_column_cell_renderer (1))) {
418                 renderer->signal_edited().connect (sigc::mem_fun (*this, &ExportTimespanSelectorMultiple::update_range_name));
419         }
420
421         Gtk::CellRendererText * label_render = Gtk::manage (new Gtk::CellRendererText());
422         Gtk::TreeView::Column * label_col = Gtk::manage (new Gtk::TreeView::Column (_("Time Span"), *label_render));
423         label_col->add_attribute (label_render->property_markup(), range_cols.label);
424         range_view.append_column (*label_col);
425
426         range_view.append_column (_("Length"), range_cols.length);
427 }
428
429 void
430 ExportTimespanSelectorMultiple::fill_range_list ()
431 {
432         if (!state) { return; }
433
434         range_list->clear();
435
436         Gtk::TreeModel::iterator iter;
437         Gtk::TreeModel::Row row;
438         for (LocationList::const_iterator it = state->ranges->begin(); it != state->ranges->end(); ++it) {
439
440                 iter = range_list->append();
441                 row = *iter;
442
443                 row[range_cols.location] = *it;
444                 row[range_cols.selected] = false;
445                 row[range_cols.name] = (*it)->name();
446                 row[range_cols.label] = construct_label (*it);
447                 row[range_cols.length] = construct_length (*it);
448         }
449
450         set_selection_from_state ();
451 }
452
453 void
454 ExportTimespanSelectorMultiple::set_selection_from_state ()
455 {
456         Gtk::TreeModel::Children::iterator tree_it;
457
458         for (TimespanList::iterator it = state->timespans->begin(); it != state->timespans->end(); ++it) {
459                 string id = (*it)->range_id();
460                 for (tree_it = range_list->children().begin(); tree_it != range_list->children().end(); ++tree_it) {
461                         Location * loc = tree_it->get_value (range_cols.location);
462
463                         if ((!id.compare ("session") && loc == state->session_range.get()) ||
464                             (!id.compare ("selection") && loc == state->selection_range.get()) ||
465                             (!id.compare (loc->id().to_s()))) {
466                                 tree_it->set_value (range_cols.selected, true);
467                         }
468                 }
469         }
470
471         set_time_format_from_state();
472 }
473
474 void
475 ExportTimespanSelectorMultiple::update_selection ()
476 {
477         update_timespans ();
478         CriticalSelectionChanged ();
479 }
480
481 void
482 ExportTimespanSelectorMultiple::update_timespans ()
483 {
484         state->timespans->clear();
485
486         for (Gtk::TreeStore::Children::iterator it = range_list->children().begin(); it != range_list->children().end(); ++it) {
487                 if (it->get_value (range_cols.selected)) {
488                         add_range_to_selection (it->get_value (range_cols.location));
489                 }
490         }
491 }
492