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