Make smf_track_get_next_event gracefully handle empty tracks.
[ardour.git] / gtk2_ardour / editor_region_list.cc
1 /*
2     Copyright (C) 2000-2005 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cstdlib>
21 #include <cmath>
22 #include <algorithm>
23 #include <string>
24 #include <sstream>
25
26 #include <pbd/basename.h>
27
28 #include <ardour/audioregion.h>
29 #include <ardour/audiofilesource.h>
30 #include <ardour/silentfilesource.h>
31 #include <ardour/session_region.h>
32 #include <ardour/profile.h>
33
34
35 #include <gtkmm2ext/stop_signal.h>
36
37 #include "editor.h"
38 #include "editing.h"
39 #include "keyboard.h"
40 #include "ardour_ui.h"
41 #include "gui_thread.h"
42 #include "actions.h"
43 #include "region_view.h"
44 #include "utils.h"
45
46
47 #include "i18n.h"
48
49 using namespace sigc;
50 using namespace ARDOUR;
51 using namespace PBD;
52 using namespace Gtk;
53 using namespace Glib;
54 using namespace Editing;
55
56 void
57 Editor::handle_region_removed (boost::weak_ptr<Region> wregion)
58 {
59         ENSURE_GUI_THREAD (mem_fun (*this, &Editor::redisplay_regions));
60         redisplay_regions ();
61 }
62
63 void
64 Editor::handle_new_regions (vector<boost::weak_ptr<Region> >& v)
65 {
66         ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::handle_new_regions), v));
67         add_regions_to_region_display (v);
68 }
69
70 void
71 Editor::region_hidden (boost::shared_ptr<Region> r)
72 {
73         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::region_hidden), r));    
74         redisplay_regions ();
75 }
76
77 void
78 Editor::add_regions_to_region_display (vector<boost::weak_ptr<Region> >& regions)
79 {
80         for (vector<boost::weak_ptr<Region> >::iterator x = regions.begin(); x != regions.end(); ++x) {
81                 boost::shared_ptr<Region> region ((*x).lock());
82                 if (region) {
83                         add_region_to_region_display (region);
84                 }
85         }
86 }
87
88 void
89 Editor::add_region_to_region_display (boost::shared_ptr<Region> region)
90 {
91         if (!region || !session) {
92                 return;
93         }
94         
95         string str;
96         TreeModel::Row row;
97         Gdk::Color c;
98         bool missing_source = boost::dynamic_pointer_cast<SilentFileSource>(region->source());
99
100         if (!show_automatic_regions_in_region_list && region->automatic()) {
101                 return;
102         }
103
104         if (region->hidden()) {
105                 TreeModel::iterator iter = region_list_model->get_iter ("0");
106                 TreeModel::Row parent;
107                 TreeModel::Row child;
108
109                 if (!iter) {
110                         parent = *(region_list_model->append());
111                         parent[region_list_columns.name] = _("Hidden");
112                         boost::shared_ptr<Region> proxy = parent[region_list_columns.region];
113                         proxy.reset ();
114                 } else {
115                         if ((*iter)[region_list_columns.name] != _("Hidden")) {
116                                 parent = *(region_list_model->insert(iter));
117                                 parent[region_list_columns.name] = _("Hidden");
118                                 boost::shared_ptr<Region> proxy = parent[region_list_columns.region];
119                                 proxy.reset ();
120                         } else {
121                                 parent = *iter;
122                         }
123                 }
124
125                 row = *(region_list_model->append (parent.children()));
126
127         } else if (region->whole_file()) {
128
129                 TreeModel::iterator i;
130                 TreeModel::Children rows = region_list_model->children();
131
132                 for (i = rows.begin(); i != rows.end(); ++i) {
133                         boost::shared_ptr<Region> rr = (*i)[region_list_columns.region];
134                         
135                         if (rr && region->region_list_equivalent (rr)) {
136                                 return;
137                         }
138                 }
139
140                 row = *(region_list_model->append());
141                 
142                 if (missing_source) {
143                         c.set_rgb(65535,0,0);     // FIXME: error color from style
144                 
145                 } else if (region->automatic()){
146                         c.set_rgb(0,65535,0);     // FIXME: error color from style
147                 
148                 } else {
149                         set_color(c, rgba_from_style ("RegionListWholeFile", 0xff, 0, 0, 0, "fg", Gtk::STATE_NORMAL, false ));
150                 
151                 }
152                 
153                 row[region_list_columns.color_] = c;
154
155                 if (region->source()->name()[0] == '/') { // external file
156                         
157                         if (region->whole_file()) {
158                                 
159                                 boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(region->source());
160                                 str = ".../";
161
162                                 if (afs) {
163                                         str = region_name_from_path (afs->path(), region->n_channels() > 1);
164                                 } else {
165                                         str += region->source()->name();
166                                 }
167
168                         } else {
169                                 str = region->name();
170                         }
171
172                 } else {
173                         str = region->name();
174                 }
175
176                 if (region->n_channels() > 1) {
177                         std::stringstream foo;
178                         foo << region->n_channels ();
179                         str += " [";
180                         str += foo.str();
181                         str += ']';
182                 }
183
184                 row[region_list_columns.name] = str;
185                 row[region_list_columns.region] = region;
186                 
187                 if (missing_source) {
188                         row[region_list_columns.path] = _("(MISSING) ") + region->source()->name();
189                 
190                 } else {
191                         row[region_list_columns.path] = region->source()->name();
192                 
193                 } 
194                 
195                 if (region->automatic()) {
196                         return;
197                 }
198                         
199         } else {
200
201                 /* find parent node, add as new child */
202                 
203                 TreeModel::iterator i;
204                 TreeModel::Children rows = region_list_model->children();
205                 bool found_parent = false;
206
207                 for (i = rows.begin(); i != rows.end(); ++i) {
208                         boost::shared_ptr<Region> rr = (*i)[region_list_columns.region];
209                         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion>(rr);
210                         
211                         if (r && r->whole_file()) {
212                                 
213                                 if (region->source_equivalent (r)) {
214                                         row = *(region_list_model->append ((*i).children()));
215                                         found_parent = true;
216                                         break;
217                                 }
218                         }
219                         
220                         TreeModel::iterator ii;
221                         TreeModel::Children subrows = (*i).children();
222
223                         for (ii = subrows.begin(); ii != subrows.end(); ++ii) {
224                                 boost::shared_ptr<Region> rrr = (*ii)[region_list_columns.region];
225
226                                 if (region->region_list_equivalent (rrr)) {
227                                         return;
228                                 
229                                 }
230                         }
231                 }
232                 
233                 if (!found_parent) {
234                         row = *(region_list_model->append());
235                 }       
236         }
237         
238         row[region_list_columns.region] = region;
239         
240         populate_row(region, row);
241 }
242
243
244 void
245 Editor::region_list_region_changed (Change what_changed, boost::weak_ptr<Region> region)
246 {
247         ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::region_list_region_changed), what_changed, region));
248         
249         boost::shared_ptr<Region> r = region.lock ();
250         
251         if (!r) {
252                 return;
253         }
254         
255         if (what_changed & ARDOUR::NameChanged) {
256                 /* find the region in our model and change its name */
257                 TreeModel::Children rows = region_list_model->children ();
258                 TreeModel::iterator i = rows.begin ();
259                 while (i != rows.end ()) {
260                         TreeModel::Children children = (*i)->children ();
261                         TreeModel::iterator j = children.begin ();
262                         while (j != children.end()) {
263                                 boost::shared_ptr<Region> c = (*j)[region_list_columns.region];
264                                 if (c == r) {
265                                         break;
266                                 }
267                                 ++j;
268                         }
269
270                         if (j != children.end()) {
271                                 (*j)[region_list_columns.name] = r->name ();
272                                 break;
273                         }
274                         
275                         ++i;
276                 }
277
278         }
279 }
280
281 void
282 Editor::region_list_selection_changed() 
283 {
284         if (region_list_display.get_selection()->count_selected_rows() > 0) {
285                 
286                 TreeIter iter;
287                 TreeView::Selection::ListHandle_Path rows = region_list_display.get_selection()->get_selected_rows ();
288                 
289                 deselect_all();
290                 
291                 for (TreeView::Selection::ListHandle_Path::iterator i = rows.begin(); i != rows.end(); ++i) {
292                         
293                         if (iter = region_list_model->get_iter (*i)) {                                                                  // they could have clicked on a row that is just a placeholder, like "Hidden"
294                                 boost::shared_ptr<Region> region = (*iter)[region_list_columns.region];
295                                 
296                                 if (region) {
297                                         
298                                         if (region->automatic()) {
299                                                 region_list_display.get_selection()->unselect(*i);
300                                                 
301                                         } else {
302                                                 region_list_change_connection.block(true);
303                                                 //editor_regions_selection_changed_connection.block(true);
304
305                                                 set_selected_regionview_from_region_list (region, Selection::Add);
306
307                                                 region_list_change_connection.block(false);
308                                                 //editor_regions_selection_changed_connection.block(false);
309                                         }
310                                 }
311                         }
312                 }
313         } else {
314                 deselect_all();
315         }
316 }
317
318 void
319 Editor::set_selected_in_region_list(RegionSelection& regions)
320 {
321         for (RegionSelection::iterator iter = regions.begin(); iter != regions.end(); ++iter) {
322         
323                 TreeModel::iterator i;
324                 TreeModel::Children rows = region_list_model->children();
325                 boost::shared_ptr<Region> r ((*iter)->region());
326                 
327                 for (i = rows.begin(); i != rows.end(); ++i) {
328                         
329                         boost::shared_ptr<Region> compared_region = (*i)[region_list_columns.region];
330
331                         if (r == compared_region) {
332                                 region_list_display.get_selection()->select(*i);;
333                                 break;
334                         }
335                         
336                         if (!(*i).children().empty()) {
337                                 if (set_selected_in_region_list_subrow(r, (*i), 2)) {
338                                         break;
339                                 }
340                         }
341                 }
342         }
343 }
344
345 bool
346 Editor::set_selected_in_region_list_subrow (boost::shared_ptr<Region> region, TreeModel::Row const &parent_row, int level)
347 {
348         TreeModel::iterator i;
349         TreeModel::Children subrows = (*parent_row).children();
350         
351         for (i = subrows.begin(); i != subrows.end(); ++i) {
352                 
353                 boost::shared_ptr<Region> compared_region = (*i)[region_list_columns.region];
354                 
355                 if (region == compared_region) {
356                         region_list_display.get_selection()->select(*i);;
357                         return true;
358                 }
359                 
360                 if (!(*i).children().empty()) {
361                         if (update_region_subrows(region, (*i), level + 1)) {
362                                 return true;
363                         }
364                 }
365         }
366         return false;
367 }
368
369 void
370 Editor::insert_into_tmp_regionlist(boost::shared_ptr<Region> region)
371 {
372         /* keep all whole files at the beginning */
373         
374         if (region->whole_file()) {
375                 tmp_region_list.push_front (region);
376         } else {
377                 tmp_region_list.push_back (region);
378         }
379 }
380
381 void
382 Editor::redisplay_regions ()
383 {       
384         if (no_region_list_redisplay || !session) {
385                 return;
386         }
387                 
388         bool tree_expanded = false;
389         
390         if (toggle_full_region_list_action && toggle_full_region_list_action->get_active()) {   //If the list was expanded prior to rebuilding, 
391                 tree_expanded = true;                                                                                                                           //expand it again afterwards
392         }
393         
394         region_list_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
395         region_list_model->clear ();
396
397         /* now add everything we have, via a temporary list used to help with
398                 sorting.
399         */
400         
401         tmp_region_list.clear();
402         session->foreach_region (this, &Editor::insert_into_tmp_regionlist);
403
404         for (list<boost::shared_ptr<Region> >::iterator r = tmp_region_list.begin(); r != tmp_region_list.end(); ++r) {
405                 add_region_to_region_display (*r);
406         }
407         tmp_region_list.clear();
408         
409         region_list_display.set_model (region_list_model);
410         
411         if (tree_expanded) {
412                 region_list_display.expand_all();
413         }
414 }
415
416 void
417 Editor::update_region_row (boost::shared_ptr<Region> region)
418 {       
419         if (!region || !session) {
420                 return;
421         }
422         
423         TreeModel::iterator i;
424         TreeModel::Children rows = region_list_model->children();
425         
426         for (i = rows.begin(); i != rows.end(); ++i) {
427                 
428 //              cerr << "Level 1: Compare " << region->name() << " with parent " << (*i)[region_list_columns.name] << "\n";
429                 
430                 boost::shared_ptr<Region> compared_region = (*i)[region_list_columns.region];
431                 
432                 if (region == compared_region) {
433 //                      cerr << "Matched\n";
434                         populate_row(region, (*i));
435                         return;
436                 }
437                 
438                 if (!(*i).children().empty()) {
439                         if (update_region_subrows(region, (*i), 2)) {
440                                 return;
441                         }
442                 }
443         }
444 //      cerr << "Returning - No match\n";
445 }
446
447 bool
448 Editor::update_region_subrows (boost::shared_ptr<Region> region, TreeModel::Row const &parent_row, int level)
449 {
450         TreeModel::iterator i;
451         TreeModel::Children subrows = (*parent_row).children();
452         
453         for (i = subrows.begin(); i != subrows.end(); ++i) {
454                 
455 //              cerr << "Level " << level << ": Compare " << region->name() << " with child " << (*i)[region_list_columns.name] << "\n";
456                 
457                 boost::shared_ptr<Region> compared_region = (*i)[region_list_columns.region];
458                 
459                 if (region == compared_region) {
460                         populate_row(region, (*i));
461 //                      cerr << "Matched\n";
462                         return true;
463                 }
464                 
465                 if (!(*i).children().empty()) {
466                         if (update_region_subrows(region, (*i), level + 1)) {
467                                 return true;
468                         }
469                 }
470         }
471         return false;
472 }
473
474 void
475 Editor::update_all_region_rows ()
476 {
477         if (!session) {
478                 return;
479         }
480
481         TreeModel::iterator i;
482         TreeModel::Children rows = region_list_model->children();
483         
484         for (i = rows.begin(); i != rows.end(); ++i) {
485                 
486                 boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
487         
488                 if (!region->automatic()) {
489                         cerr << "level 1 : Updating " << region->name() << "\n";
490                         populate_row(region, (*i));
491                 }
492                 
493                 if (!(*i).children().empty()) {
494                         update_all_region_subrows((*i), 2);
495                 }
496         }
497 }
498
499 void
500 Editor::update_all_region_subrows (TreeModel::Row const &parent_row, int level)
501 {
502         TreeModel::iterator i;
503         TreeModel::Children subrows = (*parent_row).children();
504         
505         for (i = subrows.begin(); i != subrows.end(); ++i) {
506                 
507                 boost::shared_ptr<Region> region = (*i)[region_list_columns.region];
508                 
509                 if (!region->automatic()) {
510                         cerr << "level " << level << " : Updating " << region->name() << "\n";
511                         populate_row(region, (*i));
512                 }
513                         
514                 if (!(*i).children().empty()) {
515                         update_all_region_subrows((*i), level + 1);
516                 }
517         }
518 }
519
520 void
521 Editor::populate_row (boost::shared_ptr<Region> region, TreeModel::Row const &row)
522 {
523         char start_str[16];
524         char end_str[16];
525         char length_str[16];
526         char sync_str[16];
527         char fadein_str[16];
528         char fadeout_str[16];
529         char used_str[8];
530         int used;
531         BBT_Time bbt;                           // FIXME Why do these have to be declared here ?
532         SMPTE::Time smpte;                      // FIXME I would like them declared in the case statment where they are used.
533         
534         bool missing_source = boost::dynamic_pointer_cast<SilentFileSource>(region->source());
535         
536         boost::shared_ptr<AudioRegion> audioRegion = boost::dynamic_pointer_cast<AudioRegion>(region);
537         
538         bool fades_in_seconds = false;
539
540         start_str[0] = '\0';
541         end_str[0] = '\0';
542         length_str[0] = '\0';
543         sync_str[0] = '\0';
544         fadein_str[0] = '\0';
545         fadeout_str[0] = '\0';
546         used_str[0] = '\0';
547
548         used = get_regionview_count_from_region_list(region);
549         sprintf (used_str, "%4d" , used);
550         
551         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
552         case AudioClock::SMPTE:
553         case AudioClock::Off:                                                                                           /* If the secondary clock is off, default to SMPTE */
554                 session->smpte_time (region->position(), smpte);
555                 sprintf (start_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
556                 session->smpte_time (region->position() + region->length() - 1, smpte);
557                 sprintf (end_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
558                 session->smpte_time (region->length(), smpte);
559                 sprintf (length_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
560                 session->smpte_time (region->sync_position() + region->position(), smpte);
561                 sprintf (sync_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
562                 
563                 if (audioRegion && !fades_in_seconds) { 
564                         session->smpte_time (audioRegion->fade_in()->back()->when, smpte);
565                         sprintf (fadein_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
566                         session->smpte_time (audioRegion->fade_out()->back()->when, smpte);
567                         sprintf (fadeout_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
568                 }
569                 
570                 break;
571                 
572         case AudioClock::BBT:
573                 session->tempo_map().bbt_time (region->position(), bbt);
574                 sprintf (start_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
575                 session->tempo_map().bbt_time (region->position() + region->length() - 1, bbt);
576                 sprintf (end_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
577                 session->tempo_map().bbt_time (region->length(), bbt);
578                 sprintf (length_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
579                 session->tempo_map().bbt_time (region->sync_position() + region->position(), bbt);
580                 sprintf (sync_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
581                 
582                 if (audioRegion && !fades_in_seconds) {
583                         session->tempo_map().bbt_time (audioRegion->fade_in()->back()->when, bbt);
584                         sprintf (fadein_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
585                         session->tempo_map().bbt_time (audioRegion->fade_out()->back()->when, bbt);
586                         sprintf (fadeout_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
587                 } 
588                 break;
589                 
590         case AudioClock::MinSec:
591                 nframes_t left;
592                 int hrs;
593                 int mins;
594                 float secs;
595         
596                 left = region->position();
597                 hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
598                 left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
599                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
600                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
601                 secs = left / (float) session->frame_rate();
602                 sprintf (start_str, "%02d:%02d:%06.3f", hrs, mins, secs);
603                 
604                 left = region->position() + region->length() - 1;
605                 hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
606                 left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
607                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
608                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
609                 secs = left / (float) session->frame_rate();
610                 sprintf (end_str, "%02d:%02d:%06.3f", hrs, mins, secs);
611                 
612                 left = region->length();
613                 hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
614                 left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
615                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
616                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
617                 secs = left / (float) session->frame_rate();
618                 sprintf (length_str, "%02d:%02d:%06.3f", hrs, mins, secs);
619                 
620                 left = region->sync_position() + region->position();
621                 hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
622                 left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
623                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
624                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
625                 secs = left / (float) session->frame_rate();
626                 sprintf (sync_str, "%02d:%02d:%06.3f", hrs, mins, secs);
627                 
628                 if (audioRegion && !fades_in_seconds) {
629                         left = audioRegion->fade_in()->back()->when;
630                         hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
631                         left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
632                         mins = (int) floor (left / (session->frame_rate() * 60.0f));
633                         left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
634                         secs = left / (float) session->frame_rate();
635                         sprintf (fadein_str, "%02d:%02d:%06.3f", hrs, mins, secs);
636                         
637                         left = audioRegion->fade_out()->back()->when;
638                         hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
639                         left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
640                         mins = (int) floor (left / (session->frame_rate() * 60.0f));
641                         left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
642                         secs = left / (float) session->frame_rate();
643                         sprintf (fadeout_str, "%02d:%02d:%06.3f", hrs, mins, secs);
644                 }
645                 
646                 break;
647                 
648         case AudioClock::Frames:
649                 snprintf (start_str, sizeof (start_str), "%u", region->position());
650                 snprintf (end_str, sizeof (end_str), "%u", (region->position() + region->length() - 1));
651                 snprintf (length_str, sizeof (length_str), "%u", region->length());
652                 snprintf (sync_str, sizeof (sync_str), "%u", region->sync_position() + region->position());
653                 
654                 if (audioRegion && !fades_in_seconds) {
655                         snprintf (fadein_str, sizeof (fadein_str), "%u", uint (audioRegion->fade_in()->back()->when));
656                         snprintf (fadeout_str, sizeof (fadeout_str), "%u", uint (audioRegion->fade_out()->back()->when));
657                 }
658                 
659                 break;
660         
661         default:
662                 break;
663         }
664         
665         if (audioRegion && fades_in_seconds) {
666                         
667                 nframes_t left;
668                 int mins;
669                 int millisecs;
670                 
671                 left = audioRegion->fade_in()->back()->when;
672                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
673                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
674                 millisecs = (int) floor ((left * 1000.0f) / session->frame_rate());
675                 
676                 if (audioRegion->fade_in()->back()->when >= session->frame_rate()) {
677                         sprintf (fadein_str, "%01dM %01dmS", mins, millisecs);
678                 } else {
679                         sprintf (fadein_str, "%01dmS", millisecs);
680                 }
681                 
682                 left = audioRegion->fade_out()->back()->when;
683                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
684                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
685                 millisecs = (int) floor ((left * 1000.0f) / session->frame_rate());
686                 
687                 if (audioRegion->fade_out()->back()->when >= session->frame_rate()) {
688                         sprintf (fadeout_str, "%01dM %01dmS", mins, millisecs);
689                 } else {
690                         sprintf (fadeout_str, "%01dmS", millisecs);
691                 }
692         }
693         
694         if (used > 1) {
695                 row[region_list_columns.start] = _("Multiple");
696                 row[region_list_columns.end] = _("Multiple");
697                 row[region_list_columns.sync] = _("Multiple");
698                 row[region_list_columns.fadein] = _("Multiple");
699                 row[region_list_columns.fadeout] = _("Multiple");
700                 row[region_list_columns.locked] = false;
701                 row[region_list_columns.glued] = false;
702                 row[region_list_columns.muted] = false;
703                 row[region_list_columns.opaque] = false;
704         } else {
705                 row[region_list_columns.start] = start_str;
706                 row[region_list_columns.end] = end_str;
707                 
708                 if (region->sync_position() == region->position()) {
709                         row[region_list_columns.sync] = _("Start");
710                 } else if (region->sync_position() == (region->position() + region->length() - 1)) {
711                         row[region_list_columns.sync] = _("End");
712                 } else {
713                         row[region_list_columns.sync] = sync_str;
714                 }
715         
716                 if (audioRegion) {
717                         if (audioRegion->fade_in_active()) {
718                                 row[region_list_columns.fadein] = string_compose("%1%2%3", " ", fadein_str, " ");
719                         } else {
720                                 row[region_list_columns.fadein] = string_compose("%1%2%3", "(", fadein_str, ")");
721                         }
722                 } else {
723                         row[region_list_columns.fadein] = "";
724                 }
725
726                 if (audioRegion) {
727                         if (audioRegion->fade_out_active()) {
728                                 row[region_list_columns.fadeout] = string_compose("%1%2%3", " ", fadeout_str, " ");
729                         } else {
730                                 row[region_list_columns.fadeout] = string_compose("%1%2%3", "(", fadeout_str, ")");
731                         }
732                 } else {
733                         row[region_list_columns.fadeout] = "";
734                 }
735                 
736                 row[region_list_columns.locked] = region->locked();
737         
738                 if (region->positional_lock_style() == Region::MusicTime) {
739                         row[region_list_columns.glued] = true;
740                 } else {
741                         row[region_list_columns.glued] = false;
742                 }
743                 
744                 row[region_list_columns.muted] = region->muted();
745                 row[region_list_columns.opaque] = region->opaque();
746         }
747         
748         row[region_list_columns.length] = length_str;
749         row[region_list_columns.used] = used_str;
750         
751         if (missing_source) {
752                 row[region_list_columns.path] = _("MISSING ") + region->source()->name();
753         } else {
754                 row[region_list_columns.path] = region->source()->name();
755         }
756         
757         if (region->n_channels() > 1) {
758                 row[region_list_columns.name] = string_compose("%1  [%2]", region->name(), region->n_channels());
759         } else {
760                 row[region_list_columns.name] = region->name();
761         }
762 }
763
764 void
765 Editor::build_region_list_menu ()
766 {
767         region_list_menu = dynamic_cast<Menu*>(ActionManager::get_widget ("/RegionListMenu"));
768                                                
769         /* now grab specific menu items that we need */
770
771         Glib::RefPtr<Action> act;
772
773         act = ActionManager::get_action (X_("RegionList"), X_("rlShowAll"));
774         if (act) {
775                 toggle_full_region_list_action = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
776         }
777
778         act = ActionManager::get_action (X_("RegionList"), X_("rlShowAuto"));
779         if (act) {
780                 toggle_show_auto_regions_action = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
781         }
782 }
783
784 void
785 Editor::toggle_show_auto_regions ()
786 {
787         show_automatic_regions_in_region_list = toggle_show_auto_regions_action->get_active();
788         redisplay_regions ();
789 }
790
791 void
792 Editor::toggle_full_region_list ()
793 {
794         if (toggle_full_region_list_action->get_active()) {
795                 region_list_display.expand_all ();
796         } else {
797                 region_list_display.collapse_all ();
798         }
799 }
800
801 void
802 Editor::show_region_list_display_context_menu (int button, int time)
803 {
804         if (region_list_menu == 0) {
805                 build_region_list_menu ();
806         }
807
808         if (region_list_display.get_selection()->count_selected_rows() > 0) {
809                 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, true);
810         } else {
811                 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, false);
812         }
813
814         region_list_menu->popup (button, time);
815 }
816
817 bool
818 Editor::region_list_display_key_press (GdkEventKey* ev)
819 {
820         return false;
821 }
822
823 bool
824 Editor::region_list_display_key_release (GdkEventKey* ev)
825 {
826         switch (ev->keyval) {
827         case GDK_Delete:
828                 remove_region_from_region_list ();
829                 return true;
830                 break;
831         default:
832                 break;
833         }
834
835         return false;
836 }
837
838 bool
839 Editor::region_list_display_button_press (GdkEventButton *ev)
840 {
841         boost::shared_ptr<Region> region;
842         TreeIter iter;
843         TreeModel::Path path;
844         TreeViewColumn* column;
845         int cellx;
846         int celly;
847
848         if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
849                 if ((iter = region_list_model->get_iter (path))) {
850                         region = (*iter)[region_list_columns.region];
851                 }
852         }
853
854         if (Keyboard::is_context_menu_event (ev)) {
855                 show_region_list_display_context_menu (ev->button, ev->time);
856                 return true;
857         }
858
859         if (region == 0) {
860                 region_list_display.get_selection()->unselect_all();
861                 deselect_all();
862                 return false;
863         }
864
865         switch (ev->button) {
866         case 1:
867                 break;
868
869         case 2:
870                 // audition on middle click (stop audition too)
871                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
872                         consider_auditioning (region);
873                 }
874                 return true;
875                 break;
876
877         default:
878                 break; 
879         }
880
881         return false;
882 }       
883
884 bool
885 Editor::region_list_display_button_release (GdkEventButton *ev)
886 {
887         TreeIter iter;
888         TreeModel::Path path;
889         TreeViewColumn* column;
890         int cellx;
891         int celly;
892         boost::shared_ptr<Region> region;
893
894         if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
895                 if ((iter = region_list_model->get_iter (path))) {
896                         region = (*iter)[region_list_columns.region];
897                 }
898         }
899
900         if (region && Keyboard::is_delete_event (ev)) {
901                 session->remove_region_from_region_list (region);
902                 return true;
903         }
904
905         return false;
906 }
907
908 void
909 Editor::consider_auditioning (boost::shared_ptr<Region> region)
910 {
911         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
912
913         if (r == 0) {
914                 session->cancel_audition ();
915                 return;
916         }
917
918         if (session->is_auditioning()) {
919                 session->cancel_audition ();
920                 if (r == last_audition_region) {
921                         return;
922                 }
923         }
924
925         session->audition_region (r);
926         last_audition_region = r;
927 }
928
929 int
930 Editor::region_list_sorter (TreeModel::iterator a, TreeModel::iterator b)
931 {
932         int cmp = 0;
933
934         boost::shared_ptr<Region> r1 = (*a)[region_list_columns.region];
935         boost::shared_ptr<Region> r2 = (*b)[region_list_columns.region];
936
937         /* handle rows without regions, like "Hidden" */
938
939         if (r1 == 0) {
940                 return -1;
941         }
942
943         if (r2 == 0) {
944                 return 1;
945         }
946
947         boost::shared_ptr<AudioRegion> region1 = boost::dynamic_pointer_cast<AudioRegion> (r1);
948         boost::shared_ptr<AudioRegion> region2 = boost::dynamic_pointer_cast<AudioRegion> (r2);
949
950         if (region1 == 0 || region2 == 0) {
951                 Glib::ustring s1;
952                 Glib::ustring s2;
953                 switch (region_list_sort_type) {
954                 case ByName:
955                         s1 = (*a)[region_list_columns.name];
956                         s2 = (*b)[region_list_columns.name];
957                         return (s1.compare (s2));
958                 default:
959                         return 0;
960                 }
961         }
962
963         switch (region_list_sort_type) {
964         case ByName:
965                 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
966                 break;
967
968         case ByLength:
969                 cmp = region1->length() - region2->length();
970                 break;
971                 
972         case ByPosition:
973                 cmp = region1->position() - region2->position();
974                 break;
975                 
976         case ByTimestamp:
977                 cmp = region1->source()->timestamp() - region2->source()->timestamp();
978                 break;
979         
980         case ByStartInFile:
981                 cmp = region1->start() - region2->start();
982                 break;
983                 
984         case ByEndInFile:
985                 cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
986                 break;
987                 
988         case BySourceFileName:
989                 cmp = strcasecmp (region1->source()->name().c_str(), region2->source()->name().c_str());
990                 break;
991
992         case BySourceFileLength:
993                 cmp = region1->source()->length() - region2->source()->length();
994                 break;
995                 
996         case BySourceFileCreationDate:
997                 cmp = region1->source()->timestamp() - region2->source()->timestamp();
998                 break;
999
1000         case BySourceFileFS:
1001                 if (region1->source()->name() == region2->source()->name()) {
1002                         cmp = strcasecmp (region1->name().c_str(),  region2->name().c_str());
1003                 } else {
1004                         cmp = strcasecmp (region1->source()->name().c_str(),  region2->source()->name().c_str());
1005                 }
1006                 break;
1007         }
1008
1009         if (cmp < 0) {
1010                 return -1;
1011         } else if (cmp > 0) {
1012                 return 1;
1013         } else {
1014                 return 0;
1015         }
1016 }
1017
1018 void
1019 Editor::reset_region_list_sort_type (RegionListSortType type)
1020 {
1021         if (type != region_list_sort_type) {
1022                 region_list_sort_type = type;
1023                 region_list_model->set_sort_func (0, (mem_fun (*this, &Editor::region_list_sorter)));
1024         }
1025 }
1026
1027 void
1028 Editor::reset_region_list_sort_direction (bool up)
1029 {
1030         region_list_model->set_sort_column (0, up ? SORT_ASCENDING : SORT_DESCENDING);
1031 }
1032
1033 void
1034 Editor::region_list_selection_mapover (slot<void,boost::shared_ptr<Region> > sl)
1035 {
1036         Glib::RefPtr<TreeSelection> selection = region_list_display.get_selection();
1037         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1038         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1039
1040         if (selection->count_selected_rows() == 0 || session == 0) {
1041                 return;
1042         }
1043
1044         for (; i != rows.end(); ++i) {
1045                 TreeIter iter;
1046
1047                 if ((iter = region_list_model->get_iter (*i))) {
1048
1049                         /* some rows don't have a region associated with them, but can still be
1050                            selected (XXX maybe prevent them from being selected)
1051                         */
1052
1053                         boost::shared_ptr<Region> r = (*iter)[region_list_columns.region];
1054
1055                         if (r) {
1056                                 sl (r);
1057                         }
1058                 }
1059         }
1060 }
1061
1062 void
1063 Editor::hide_a_region (boost::shared_ptr<Region> r)
1064 {
1065         r->set_hidden (true);
1066 }
1067
1068 void
1069 Editor::remove_a_region (boost::shared_ptr<Region> r)
1070 {
1071         session->remove_region_from_region_list (r);
1072 }
1073
1074 void
1075 Editor::audition_region_from_region_list ()
1076 {
1077         region_list_selection_mapover (mem_fun (*this, &Editor::consider_auditioning));
1078 }
1079
1080 void
1081 Editor::hide_region_from_region_list ()
1082 {
1083         region_list_selection_mapover (mem_fun (*this, &Editor::hide_a_region));
1084 }
1085
1086 void
1087 Editor::remove_region_from_region_list ()
1088 {
1089         region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region));
1090 }
1091
1092 void  
1093 Editor::region_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
1094                                                                                                 int x, int y, 
1095                                                                                                 const SelectionData& data,
1096                                                                                                 guint info, guint time)
1097 {
1098         vector<ustring> paths;
1099
1100         if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1101                 region_list_display.on_drag_data_received (context, x, y, data, info, time);
1102                 return;
1103         }
1104
1105         if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
1106                 nframes64_t pos = 0;
1107                 if (Profile->get_sae() || Config->get_only_copy_imported_files()) {
1108                         do_import (paths, Editing::ImportDistinctFiles, Editing::ImportAsRegion, SrcBest, pos); 
1109                 } else {
1110                         do_embed (paths, Editing::ImportDistinctFiles, ImportAsRegion, pos);
1111                 }
1112                 context->drag_finish (true, false, time);
1113         }
1114 }
1115
1116 bool
1117 Editor::region_list_selection_filter (const RefPtr<TreeModel>& model, const TreeModel::Path& path, bool yn)
1118 {
1119         /* not possible to select rows that do not represent regions, like "Hidden" */
1120         
1121         TreeModel::iterator iter = model->get_iter (path);
1122
1123         if (iter) {
1124                 boost::shared_ptr<Region> r =(*iter)[region_list_columns.region];
1125                 if (!r) {
1126                         return false;
1127                 }
1128         } 
1129
1130         return true;
1131 }
1132
1133 void
1134 Editor::region_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1135 {
1136         boost::shared_ptr<Region> region;
1137         TreeIter iter;
1138         
1139         if ((iter = region_list_model->get_iter (path))) {
1140                 region = (*iter)[region_list_columns.region];
1141                 (*iter)[region_list_columns.name] = new_text;
1142         }
1143         
1144         /* now mapover everything */
1145
1146         if (region) {
1147                 vector<RegionView*> equivalents;
1148                 get_regions_corresponding_to (region, equivalents);
1149
1150                 for (vector<RegionView*>::iterator i = equivalents.begin(); i != equivalents.end(); ++i) {
1151                         if (new_text != (*i)->region()->name()) {
1152                                 (*i)->region()->set_name (new_text);
1153                         }
1154                 }
1155         }
1156
1157 }
1158