0f02a1580ec05d782f1f11468b2a4ad71a15a7d6
[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 ARDOUR;
40 using namespace PBD;
41
42 ExportTimespanSelector::ExportTimespanSelector () :
43   time_format_label (_("Show Times as:"), Gtk::ALIGN_LEFT)
44 {
45
46         option_hbox.pack_start (time_format_label, false, false, 0);
47         option_hbox.pack_start (time_format_combo, false, false, 6);
48         
49         range_scroller.add (range_view);
50         range_scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
51         
52         pack_start (option_hbox, false, false, 0);
53         pack_start (range_scroller, true, true, 6);
54         
55         /*** Combo boxes ***/
56         
57         Gtk::TreeModel::iterator iter;
58         Gtk::TreeModel::Row row;
59         
60         /* Time format combo */
61         
62         time_format_list = Gtk::ListStore::create (time_format_cols);
63         time_format_combo.set_model (time_format_list);
64         
65         iter = time_format_list->append();
66         row = *iter;
67         row[time_format_cols.format] = ExportProfileManager::SMPTE;
68         row[time_format_cols.label] = X_("Timecode");
69         
70         iter = time_format_list->append();
71         row = *iter;
72         row[time_format_cols.format] = ExportProfileManager::MinSec;
73         row[time_format_cols.label] = _("Minutes:Seconds");
74         
75         iter = time_format_list->append();
76         row = *iter;
77         row[time_format_cols.format] = ExportProfileManager::BBT;
78         row[time_format_cols.label] = _("Bars:Beats");
79         
80         time_format_combo.pack_start (time_format_cols.label);
81         time_format_combo.set_active (0);
82         
83         time_format_combo.signal_changed().connect (sigc::mem_fun (*this, &ExportTimespanSelector::change_time_format));
84         
85         /* Range view */
86         
87         range_list = Gtk::ListStore::create (range_cols);
88         range_view.set_model (range_list);
89         range_view.set_headers_visible (false);
90         
91         range_view.append_column_editable ("", range_cols.selected);
92         range_view.append_column_editable ("", range_cols.name);
93         
94         Gtk::CellRendererText * label_render = Gtk::manage (new Gtk::CellRendererText());
95         Gtk::TreeView::Column * label_col = Gtk::manage (new Gtk::TreeView::Column ("", *label_render));
96         label_col->add_attribute (label_render->property_markup(), range_cols.label);
97         range_view.append_column (*label_col);
98
99         if (Gtk::CellRendererToggle * renderer = dynamic_cast<Gtk::CellRendererToggle *> (range_view.get_column_cell_renderer (0))) {
100                 renderer->signal_toggled().connect (sigc::hide (sigc::mem_fun (*this, &ExportTimespanSelector::update_selection)));
101         }
102         if (Gtk::CellRendererText * renderer = dynamic_cast<Gtk::CellRendererText *> (range_view.get_column_cell_renderer (1))) {
103                 renderer->signal_edited().connect (sigc::mem_fun (*this, &ExportTimespanSelector::update_range_name));
104         }
105         
106 }
107
108 ExportTimespanSelector::~ExportTimespanSelector ()
109 {
110
111 }
112
113 void
114 ExportTimespanSelector::set_state (ARDOUR::ExportProfileManager::TimespanStatePtr const state_, ARDOUR::Session * session_)
115 {
116         state = state_;
117         session = session_;
118
119         fill_range_list ();
120         set_selection_from_state ();
121         
122         CriticalSelectionChanged();
123 }
124
125 void
126 ExportTimespanSelector::select_one_range (Glib::ustring id)
127 {
128         if (!state) { return; }
129         
130         range_view.remove_column (*range_view.get_column (0));
131         
132         Glib::ustring real_id;
133         
134         if (!id.compare (X_("session"))) {
135                 real_id = state->session_range->id().to_s();
136         } else if (!id.compare (X_("selection"))) {
137                 real_id = state->selection_range->id().to_s();
138         } else {
139                 real_id = id;
140         }
141         
142         for (Gtk::ListStore::Children::iterator it = range_list->children().begin(); it != range_list->children().end();) {
143                 if (!it->get_value (range_cols.location)->id().to_s().compare (real_id)) {
144                         it->set_value (range_cols.selected, true);
145                         ++it;
146                 } else {
147                         Gtk::ListStore::Children::iterator temp = it++;
148                         range_list->erase (temp);
149                 }
150         }
151         
152         int x_offset, y_offset, width, height;
153         Gtk::CellRenderer * renderer = *range_view.get_column(0)->get_cell_renderers().begin();
154         renderer->get_size (range_view, x_offset, y_offset, width, height);
155         range_scroller.set_size_request (-1, height);
156         range_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_NEVER);
157         
158         update_selection();
159 }
160
161 void
162 ExportTimespanSelector::fill_range_list ()
163 {
164         range_list->clear();
165
166         Gtk::TreeModel::iterator iter;
167         Gtk::TreeModel::Row row;
168         for (LocationList::const_iterator it = state->ranges->begin(); it != state->ranges->end(); ++it) {
169                 iter = range_list->append();
170                 row = *iter;
171                 
172                 row[range_cols.location] = *it;
173                 row[range_cols.selected] = false;
174                 row[range_cols.name] = (*it)->name();
175                 row[range_cols.label] = construct_label (*it);
176         }
177 }
178
179 void
180 ExportTimespanSelector::set_selection_from_state ()
181 {
182         Gtk::TreeModel::Children::iterator tree_it;
183         
184         for (TimespanList::iterator it = state->timespans->begin(); it != state->timespans->end(); ++it) {
185                 ustring id = (*it)->range_id();
186                 for (tree_it = range_list->children().begin(); tree_it != range_list->children().end(); ++tree_it) {
187                         Location * loc = tree_it->get_value (range_cols.location);
188                         
189                         if ((!id.compare ("session") && loc == state->session_range.get()) ||
190                             (!id.compare ("selection") && loc == state->selection_range.get()) ||
191                             (!id.compare (loc->id().to_s()))) {
192                                 tree_it->set_value (range_cols.selected, true);
193                         }
194                 }
195         }
196         
197         for (tree_it = time_format_list->children().begin(); tree_it != time_format_list->children().end(); ++tree_it) {
198                 if (tree_it->get_value (time_format_cols.format) == state->time_format) {
199                         time_format_combo.set_active (tree_it);
200                 }
201         }
202 }
203
204 void
205 ExportTimespanSelector::update_selection ()
206 {
207         update_timespans ();
208         CriticalSelectionChanged ();
209 }
210
211 void
212 ExportTimespanSelector::update_timespans ()
213 {
214         state->timespans->clear();
215         
216         TimespanPtr span;
217         HandlerPtr handler = session->get_export_handler();
218         
219         for (Gtk::TreeStore::Children::iterator it = range_list->children().begin(); it != range_list->children().end(); ++it) {
220                 if (it->get_value (range_cols.selected)) {
221                         span = handler->add_timespan();
222                         Location * loc = it->get_value (range_cols.location);
223                         
224                         Glib::ustring id;
225                         if (loc == state->session_range.get()) {
226                                 id = "session";
227                         } else if (loc == state->selection_range.get()) {
228                                 id = "selection";
229                         } else {
230                                 id = loc->id().to_s();
231                         }
232                         
233                         span->set_range (loc->start(), loc->end());
234                         span->set_name (loc->name());
235                         span->set_range_id (id);
236                         state->timespans->push_back (span);
237                 }
238         }
239 }
240
241 void
242 ExportTimespanSelector::change_time_format ()
243 {
244         state->time_format = time_format_combo.get_active()->get_value (time_format_cols.format);
245
246         for (Gtk::ListStore::Children::iterator it = range_list->children().begin(); it != range_list->children().end(); ++it) {
247                 Location * location = it->get_value (range_cols.location);
248                 it->set_value (range_cols.label, construct_label (location));
249         }
250 }
251
252 Glib::ustring
253 ExportTimespanSelector::construct_label (ARDOUR::Location const * location)
254 {
255         Glib::ustring label;
256         Glib::ustring start;
257         Glib::ustring end;
258         
259         nframes_t start_frame = location->start();
260         nframes_t end_frame = location->end();
261         
262         switch (state->time_format) {
263           case AudioClock::BBT:
264                 start = bbt_str (start_frame);
265                 end = bbt_str (end_frame);
266                 break;
267         
268           case AudioClock::SMPTE:
269                 start = smpte_str (start_frame);
270                 end = smpte_str (end_frame);
271                 break;
272         
273           case AudioClock::MinSec:
274                 start = ms_str (start_frame);
275                 end = ms_str (end_frame);
276                 break;
277         
278           case AudioClock::Frames:
279                 start = to_string (start_frame, std::dec);
280                 end = to_string (end_frame, std::dec);
281                 break;
282         
283           case AudioClock::Off:
284                 break;
285         }
286         
287         // label += _("from ");
288         
289         // label += "<span color=\"#7fff7f\">";
290         label += start;
291 //      label += "</span>";
292         
293         label += _(" to ");
294         
295 //      label += "<span color=\"#7fff7f\">";
296         label += end;
297 //      label += "</span>";
298         
299         return label;
300 }
301
302
303 Glib::ustring
304 ExportTimespanSelector::bbt_str (nframes_t frames)
305 {
306         if (!session) {
307                 return "Error!";
308         }
309         
310         std::ostringstream oss;
311         BBT_Time time;
312         
313         session->bbt_time (frames, time);
314         
315         oss << std::setfill('0') << std::right <<
316           std::setw(3) <<
317           time.bars << "|" <<
318           std::setw(2) <<
319           time.beats << "|" <<
320           std::setw(4) <<
321           time.ticks;
322         
323         return oss.str();
324 }
325
326 Glib::ustring
327 ExportTimespanSelector::smpte_str (nframes_t frames)
328 {
329         if (!session) {
330                 return "Error!";
331         }
332         
333         std::ostringstream oss;
334         SMPTE::Time time;
335         
336         session->smpte_time (frames, time);
337         
338         oss << std::setfill('0') << std::right <<
339           std::setw(2) <<
340           time.hours << ":" <<
341           std::setw(2) <<
342           time.minutes << ":" <<
343           std::setw(2) <<
344           time.seconds << ":" <<
345           std::setw(2) <<
346           time.frames;
347         
348         return oss.str();
349 }
350
351 Glib::ustring
352 ExportTimespanSelector::ms_str (nframes_t frames)
353 {
354         if (!session) {
355                 return "Error!";
356         }
357         
358         std::ostringstream oss;
359         nframes_t left;
360         int hrs;
361         int mins;
362         int secs;
363         int sec_promilles;
364         
365         left = frames;
366         hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
367         left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
368         mins = (int) floor (left / (session->frame_rate() * 60.0f));
369         left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
370         secs = (int) floor (left / (float) session->frame_rate());
371         left -= (nframes_t) floor (secs * session->frame_rate());
372         sec_promilles = (int) (left * 1000 / (float) session->frame_rate() + 0.5);
373         
374         oss << std::setfill('0') << std::right <<
375           std::setw(2) <<
376           hrs << ":" <<
377           std::setw(2) <<
378           mins << ":" <<
379           std::setw(2) <<
380           secs << "." <<
381           std::setw(3) <<
382           sec_promilles;
383         
384         return oss.str();
385 }
386
387 void
388 ExportTimespanSelector::update_range_name (Glib::ustring const & path, Glib::ustring const & new_text)
389 {
390         Gtk::TreeStore::iterator it = range_list->get_iter (path);
391         it->get_value (range_cols.location)->set_name (new_text);
392         
393         CriticalSelectionChanged();
394 }