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