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