region list patch #2 from chris g, slightly reworked by me; sv_se po changes, possibl...
[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         TreeModel::iterator i;
364         TreeModel::Children rows = region_list_model->children();
365         
366         for (i = rows.begin(); i != rows.end(); ++i) {
367                 
368                 cerr << "Level 1: Compare " << region->name() << " with parent " << (*i)[region_list_columns.name] << "\n";
369                 
370                 boost::shared_ptr<Region> compared_region = (*i)[region_list_columns.region];
371                 
372                 if (region == compared_region) {
373                         cerr << "Matched\n";
374                         populate_row(region, (*i));
375                         return;
376                 }
377                 
378                 if (!(*i).children().empty()) {
379                         if (update_region_subrows(region, (*i), 2)) {
380                                 return;
381                         }
382                 }
383         }
384         cerr << "Returning - No match\n\n";
385 }
386
387 bool
388 Editor::update_region_subrows (boost::shared_ptr<Region> region, TreeModel::Row const &parent_row, int level)
389 {
390         TreeModel::iterator i;
391         TreeModel::Children subrows = (*parent_row).children();
392         
393         for (i = subrows.begin(); i != subrows.end(); ++i) {
394                 
395                 cerr << "Level " << level << ": Compare " << region->name() << " with child " << (*i)[region_list_columns.name] << "\n";
396                 
397                 boost::shared_ptr<Region> compared_region = (*i)[region_list_columns.region];
398                 
399                 if (region == compared_region) {
400                         populate_row(region, (*i));
401                         cerr << "Matched\n";
402                         return true;
403                 }
404                 
405                 if (!(*i).children().empty()) {
406                         
407                         if (update_region_subrows(region, (*i), level + 1)) {
408                                 return true;
409                         }
410                 
411                 }
412         }
413         return false;
414 }
415
416
417 void
418 Editor::populate_row (boost::shared_ptr<Region> region, TreeModel::Row const &row)
419 {
420         char start_str[16];
421         char end_str[16];
422         char length_str[16];
423         char sync_str[16];
424         char fadein_str[16];
425         char fadeout_str[16];
426         char used_str[8];
427         int used;
428         BBT_Time bbt;                           // FIXME Why do these have to be declared here ?
429         SMPTE::Time smpte;                      // FIXME I would like them declared in the case statment where they are used.
430         
431         bool missing_source = boost::dynamic_pointer_cast<SilentFileSource>(region->source());
432         
433         boost::shared_ptr<AudioRegion> audioRegion = boost::dynamic_pointer_cast<AudioRegion>(region);
434         
435         bool fades_in_seconds = false;
436
437         start_str[0] = '\0';
438         end_str[0] = '\0';
439         length_str[0] = '\0';
440         sync_str[0] = '\0';
441         fadein_str[0] = '\0';
442         fadeout_str[0] = '\0';
443         used_str[0] = '\0';
444
445         used = get_regionview_count_from_region_list(region);
446         sprintf (used_str, "%4d" , used);
447         
448         switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
449         case AudioClock::SMPTE:
450         case AudioClock::Off:                                                                                           /* If the secondary clock is off, default to SMPTE */
451                 session->smpte_time (region->position(), smpte);
452                 sprintf (start_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
453                 session->smpte_time (region->position() + region->length() - 1, smpte);
454                 sprintf (end_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
455                 session->smpte_time (region->length(), smpte);
456                 sprintf (length_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
457                 session->smpte_time (region->sync_position() + region->position(), smpte);
458                 sprintf (sync_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
459                 
460                 if (audioRegion && !fades_in_seconds) { 
461                         session->smpte_time (audioRegion->fade_in()->back()->when, smpte);
462                         sprintf (fadein_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
463                         session->smpte_time (audioRegion->fade_out()->back()->when, smpte);
464                         sprintf (fadeout_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
465                 }
466                 
467                 break;
468                 
469         case AudioClock::BBT:
470                 session->tempo_map().bbt_time (region->position(), bbt);
471                 sprintf (start_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
472                 session->tempo_map().bbt_time (region->position() + region->length() - 1, bbt);
473                 sprintf (end_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
474                 session->tempo_map().bbt_time (region->length(), bbt);
475                 sprintf (length_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
476                 session->tempo_map().bbt_time (region->sync_position() + region->position(), bbt);
477                 sprintf (sync_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
478                 
479                 if (audioRegion && !fades_in_seconds) {
480                         session->tempo_map().bbt_time (audioRegion->fade_in()->back()->when, bbt);
481                         sprintf (fadein_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
482                         session->tempo_map().bbt_time (audioRegion->fade_out()->back()->when, bbt);
483                         sprintf (fadeout_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
484                 } 
485                 break;
486                 
487         case AudioClock::MinSec:
488                 nframes_t left;
489                 int hrs;
490                 int mins;
491                 float secs;
492         
493                 left = region->position();
494                 hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
495                 left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
496                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
497                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
498                 secs = left / (float) session->frame_rate();
499                 sprintf (start_str, "%02d:%02d:%06.3f", hrs, mins, secs);
500                 
501                 left = region->position() + region->length() - 1;
502                 hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
503                 left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
504                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
505                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
506                 secs = left / (float) session->frame_rate();
507                 sprintf (end_str, "%02d:%02d:%06.3f", hrs, mins, secs);
508                 
509                 left = region->length();
510                 hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
511                 left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
512                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
513                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
514                 secs = left / (float) session->frame_rate();
515                 sprintf (length_str, "%02d:%02d:%06.3f", hrs, mins, secs);
516                 
517                 left = region->sync_position() + region->position();
518                 hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
519                 left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
520                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
521                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
522                 secs = left / (float) session->frame_rate();
523                 sprintf (sync_str, "%02d:%02d:%06.3f", hrs, mins, secs);
524                 
525                 if (audioRegion && !fades_in_seconds) {
526                         left = audioRegion->fade_in()->back()->when;
527                         hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
528                         left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
529                         mins = (int) floor (left / (session->frame_rate() * 60.0f));
530                         left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
531                         secs = left / (float) session->frame_rate();
532                         sprintf (fadein_str, "%02d:%02d:%06.3f", hrs, mins, secs);
533                         
534                         left = audioRegion->fade_out()->back()->when;
535                         hrs = (int) floor (left / (session->frame_rate() * 60.0f * 60.0f));
536                         left -= (nframes_t) floor (hrs * session->frame_rate() * 60.0f * 60.0f);
537                         mins = (int) floor (left / (session->frame_rate() * 60.0f));
538                         left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
539                         secs = left / (float) session->frame_rate();
540                         sprintf (fadeout_str, "%02d:%02d:%06.3f", hrs, mins, secs);
541                 }
542                 
543                 break;
544                 
545         case AudioClock::Frames:
546                 snprintf (start_str, sizeof (start_str), "%u", region->position());
547                 snprintf (end_str, sizeof (end_str), "%u", (region->position() + region->length() - 1));
548                 snprintf (length_str, sizeof (length_str), "%u", region->length());
549                 snprintf (sync_str, sizeof (sync_str), "%u", region->sync_position() + region->position());
550                 
551                 if (audioRegion && !fades_in_seconds) {
552                         snprintf (fadein_str, sizeof (fadein_str), "%u", uint (audioRegion->fade_in()->back()->when));
553                         snprintf (fadeout_str, sizeof (fadeout_str), "%u", uint (audioRegion->fade_out()->back()->when));
554                 }
555                 
556                 break;
557         
558         default:
559                 break;
560         }
561         
562         if (audioRegion && fades_in_seconds) {
563                         
564                 nframes_t left;
565                 int mins;
566                 int millisecs;
567                 
568                 left = audioRegion->fade_in()->back()->when;
569                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
570                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
571                 millisecs = (int) floor ((left * 1000.0f) / session->frame_rate());
572                 
573                 if (audioRegion->fade_in()->back()->when >= session->frame_rate()) {
574                         sprintf (fadein_str, "%01dM %01dmS", mins, millisecs);
575                 } else {
576                         sprintf (fadein_str, "%01dmS", millisecs);
577                 }
578                 
579                 left = audioRegion->fade_out()->back()->when;
580                 mins = (int) floor (left / (session->frame_rate() * 60.0f));
581                 left -= (nframes_t) floor (mins * session->frame_rate() * 60.0f);
582                 millisecs = (int) floor ((left * 1000.0f) / session->frame_rate());
583                 
584                 if (audioRegion->fade_out()->back()->when >= session->frame_rate()) {
585                         sprintf (fadeout_str, "%01dM %01dmS", mins, millisecs);
586                 } else {
587                         sprintf (fadeout_str, "%01dmS", millisecs);
588                 }
589         }
590         
591         if (used > 1) {
592                 row[region_list_columns.start] = _("Multiple");
593                 row[region_list_columns.end] = _("Multiple");
594                 row[region_list_columns.sync] = _("Multiple");
595                 row[region_list_columns.fadein] = _("Multiple");
596                 row[region_list_columns.fadeout] = _("Multiple");
597                 row[region_list_columns.locked] = _(" ");
598                 row[region_list_columns.glued] = _(" ");
599                 row[region_list_columns.muted] = _(" ");
600                 row[region_list_columns.opaque] = _(" ");
601         } else {
602                 row[region_list_columns.start] = start_str;
603                 row[region_list_columns.end] = end_str;
604                 
605                 if (region->sync_position() == region->position()) {
606                         row[region_list_columns.sync] = _("Start");
607                 } else if (region->sync_position() == (region->position() + region->length() - 1)) {
608                         row[region_list_columns.sync] = _("End");
609                 } else {
610                         row[region_list_columns.sync] = sync_str;
611                 }
612         
613                 if (audioRegion) {
614                         if (audioRegion->fade_in_active()) {
615                                 row[region_list_columns.fadein] = string_compose("%1%2%3", " ", fadein_str, " ");
616                         } else {
617                                 row[region_list_columns.fadein] = string_compose("%1%2%3", "(", fadein_str, ")");
618                         }
619                 } else {
620                         row[region_list_columns.fadein] = "";
621                 }
622
623                 if (audioRegion) {
624                         if (audioRegion->fade_out_active()) {
625                                 row[region_list_columns.fadeout] = string_compose("%1%2%3", " ", fadeout_str, " ");
626                         } else {
627                                 row[region_list_columns.fadeout] = string_compose("%1%2%3", "(", fadeout_str, ")");
628                         }
629                 } else {
630                         row[region_list_columns.fadeout] = "";
631                 }
632                 
633                 row[region_list_columns.locked] = region->locked();
634         
635                 if (region->positional_lock_style() == Region::MusicTime) {
636                         row[region_list_columns.glued] = true;
637                 } else {
638                         row[region_list_columns.glued] = false;
639                 }
640                 
641                 row[region_list_columns.muted] = region->muted();
642                 row[region_list_columns.opaque] = region->opaque();
643         }
644         
645         row[region_list_columns.length] = length_str;
646         row[region_list_columns.used] = used_str;
647         
648         if (missing_source) {
649                 row[region_list_columns.path] = _("MISSING ") + region->source()->name();
650         } else {
651                 row[region_list_columns.path] = region->source()->name();
652         }
653         
654         if (region->n_channels() > 1) {
655                 row[region_list_columns.name] = string_compose("%1  [%2]", region->name(), region->n_channels());
656         } else {
657                 row[region_list_columns.name] = region->name();
658         }
659 }
660
661 void
662 Editor::build_region_list_menu ()
663 {
664         region_list_menu = dynamic_cast<Menu*>(ActionManager::get_widget ("/RegionListMenu"));
665                                                
666         /* now grab specific menu items that we need */
667
668         Glib::RefPtr<Action> act;
669
670         act = ActionManager::get_action (X_("RegionList"), X_("rlShowAll"));
671         if (act) {
672                 toggle_full_region_list_action = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
673         }
674
675         act = ActionManager::get_action (X_("RegionList"), X_("rlShowAuto"));
676         if (act) {
677                 toggle_show_auto_regions_action = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
678         }
679 }
680
681 void
682 Editor::toggle_show_auto_regions ()
683 {
684         show_automatic_regions_in_region_list = toggle_show_auto_regions_action->get_active();
685         redisplay_regions ();
686 }
687
688 void
689 Editor::toggle_full_region_list ()
690 {
691         if (toggle_full_region_list_action->get_active()) {
692                 region_list_display.expand_all ();
693         } else {
694                 region_list_display.collapse_all ();
695         }
696 }
697
698 void
699 Editor::show_region_list_display_context_menu (int button, int time)
700 {
701         if (region_list_menu == 0) {
702                 build_region_list_menu ();
703         }
704
705         if (region_list_display.get_selection()->count_selected_rows() > 0) {
706                 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, true);
707         } else {
708                 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, false);
709         }
710
711         region_list_menu->popup (button, time);
712 }
713
714 bool
715 Editor::region_list_display_key_press (GdkEventKey* ev)
716 {
717         return false;
718 }
719
720 bool
721 Editor::region_list_display_key_release (GdkEventKey* ev)
722 {
723         switch (ev->keyval) {
724         case GDK_Delete:
725                 remove_region_from_region_list ();
726                 return true;
727                 break;
728         default:
729                 break;
730         }
731
732         return false;
733 }
734
735 bool
736 Editor::region_list_display_button_press (GdkEventButton *ev)
737 {
738         boost::shared_ptr<Region> region;
739         TreeIter iter;
740         TreeModel::Path path;
741         TreeViewColumn* column;
742         int cellx;
743         int celly;
744
745         // cerr << "Button press release, button = " << ev->button << endl;
746
747         if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
748                 if ((iter = region_list_model->get_iter (path))) {
749                         region = (*iter)[region_list_columns.region];
750                 }
751         }
752
753         if (Keyboard::is_context_menu_event (ev)) {
754                 show_region_list_display_context_menu (ev->button, ev->time);
755                 cerr << "\tcontext menu event, event handled\n";
756                 return true;
757         }
758
759         if (region == 0) {
760                 cerr << "\tno region, event not handled\n";
761                 return false;
762         }
763
764         switch (ev->button) {
765         case 1:
766                 break;
767
768         case 2:
769                 // audition on middle click (stop audition too)
770                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
771                         consider_auditioning (region);
772                 }
773                 cerr << "\taudition, event handled\n";
774                 return true;
775                 break;
776
777         default:
778                 break; 
779         }
780
781         cerr << "\tnot handled\n";
782         return false;
783 }       
784
785 bool
786 Editor::region_list_display_button_release (GdkEventButton *ev)
787 {
788         TreeIter iter;
789         TreeModel::Path path;
790         TreeViewColumn* column;
791         int cellx;
792         int celly;
793         boost::shared_ptr<Region> region;
794
795         if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
796                 if ((iter = region_list_model->get_iter (path))) {
797                         region = (*iter)[region_list_columns.region];
798                 }
799         }
800
801         if (region && Keyboard::is_delete_event (ev)) {
802                 session->remove_region_from_region_list (region);
803                 return true;
804         }
805
806         return false;
807 }
808
809 void
810 Editor::consider_auditioning (boost::shared_ptr<Region> region)
811 {
812         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
813
814         if (r == 0) {
815                 session->cancel_audition ();
816                 return;
817         }
818
819         if (session->is_auditioning()) {
820                 session->cancel_audition ();
821                 if (r == last_audition_region) {
822                         return;
823                 }
824         }
825
826         session->audition_region (r);
827         last_audition_region = r;
828 }
829
830 int
831 Editor::region_list_sorter (TreeModel::iterator a, TreeModel::iterator b)
832 {
833         int cmp = 0;
834
835         boost::shared_ptr<Region> r1 = (*a)[region_list_columns.region];
836         boost::shared_ptr<Region> r2 = (*b)[region_list_columns.region];
837
838         /* handle rows without regions, like "Hidden" */
839
840         if (r1 == 0) {
841                 return -1;
842         }
843
844         if (r2 == 0) {
845                 return 1;
846         }
847
848         boost::shared_ptr<AudioRegion> region1 = boost::dynamic_pointer_cast<AudioRegion> (r1);
849         boost::shared_ptr<AudioRegion> region2 = boost::dynamic_pointer_cast<AudioRegion> (r2);
850
851         if (region1 == 0 || region2 == 0) {
852                 Glib::ustring s1;
853                 Glib::ustring s2;
854                 switch (region_list_sort_type) {
855                 case ByName:
856                         s1 = (*a)[region_list_columns.name];
857                         s2 = (*b)[region_list_columns.name];
858                         return (s1.compare (s2));
859                 default:
860                         return 0;
861                 }
862         }
863
864         switch (region_list_sort_type) {
865         case ByName:
866                 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
867                 break;
868
869         case ByLength:
870                 cmp = region1->length() - region2->length();
871                 break;
872                 
873         case ByPosition:
874                 cmp = region1->position() - region2->position();
875                 break;
876                 
877         case ByTimestamp:
878                 cmp = region1->source()->timestamp() - region2->source()->timestamp();
879                 break;
880         
881         case ByStartInFile:
882                 cmp = region1->start() - region2->start();
883                 break;
884                 
885         case ByEndInFile:
886                 cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
887                 break;
888                 
889         case BySourceFileName:
890                 cmp = strcasecmp (region1->source()->name().c_str(), region2->source()->name().c_str());
891                 break;
892
893         case BySourceFileLength:
894                 cmp = region1->source()->length() - region2->source()->length();
895                 break;
896                 
897         case BySourceFileCreationDate:
898                 cmp = region1->source()->timestamp() - region2->source()->timestamp();
899                 break;
900
901         case BySourceFileFS:
902                 if (region1->source()->name() == region2->source()->name()) {
903                         cmp = strcasecmp (region1->name().c_str(),  region2->name().c_str());
904                 } else {
905                         cmp = strcasecmp (region1->source()->name().c_str(),  region2->source()->name().c_str());
906                 }
907                 break;
908         }
909
910         if (cmp < 0) {
911                 return -1;
912         } else if (cmp > 0) {
913                 return 1;
914         } else {
915                 return 0;
916         }
917 }
918
919 void
920 Editor::reset_region_list_sort_type (RegionListSortType type)
921 {
922         if (type != region_list_sort_type) {
923                 region_list_sort_type = type;
924                 region_list_model->set_sort_func (0, (mem_fun (*this, &Editor::region_list_sorter)));
925         }
926 }
927
928 void
929 Editor::reset_region_list_sort_direction (bool up)
930 {
931         region_list_model->set_sort_column (0, up ? SORT_ASCENDING : SORT_DESCENDING);
932 }
933
934 void
935 Editor::region_list_selection_mapover (slot<void,boost::shared_ptr<Region> > sl)
936 {
937         Glib::RefPtr<TreeSelection> selection = region_list_display.get_selection();
938         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
939         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
940
941         if (selection->count_selected_rows() == 0 || session == 0) {
942                 return;
943         }
944
945         for (; i != rows.end(); ++i) {
946                 TreeIter iter;
947
948                 if ((iter = region_list_model->get_iter (*i))) {
949
950                         /* some rows don't have a region associated with them, but can still be
951                            selected (XXX maybe prevent them from being selected)
952                         */
953
954                         boost::shared_ptr<Region> r = (*iter)[region_list_columns.region];
955
956                         if (r) {
957                                 sl (r);
958                         }
959                 }
960         }
961 }
962
963 void
964 Editor::hide_a_region (boost::shared_ptr<Region> r)
965 {
966         r->set_hidden (true);
967 }
968
969 void
970 Editor::remove_a_region (boost::shared_ptr<Region> r)
971 {
972         session->remove_region_from_region_list (r);
973 }
974
975 void
976 Editor::audition_region_from_region_list ()
977 {
978         region_list_selection_mapover (mem_fun (*this, &Editor::consider_auditioning));
979 }
980
981 void
982 Editor::hide_region_from_region_list ()
983 {
984         region_list_selection_mapover (mem_fun (*this, &Editor::hide_a_region));
985 }
986
987 void
988 Editor::remove_region_from_region_list ()
989 {
990         region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region));
991 }
992
993 void  
994 Editor::region_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
995                                                 int x, int y, 
996                                                 const SelectionData& data,
997                                                 guint info, guint time)
998 {
999         vector<ustring> paths;
1000
1001         if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1002                 cerr << "Delete drag data drop to treeview\n";
1003                 region_list_display.on_drag_data_received (context, x, y, data, info, time);
1004                 return;
1005         }
1006
1007         if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
1008                 nframes64_t pos = 0;
1009                 do_embed (paths, Editing::ImportDistinctFiles, ImportAsRegion, pos);
1010                 context->drag_finish (true, false, time);
1011         }
1012 }
1013
1014 bool
1015 Editor::region_list_selection_filter (const RefPtr<TreeModel>& model, const TreeModel::Path& path, bool yn)
1016 {
1017         /* not possible to select rows that do not represent regions, like "Hidden" */
1018         
1019         TreeModel::iterator iter = model->get_iter (path);
1020
1021         if (iter) {
1022                 boost::shared_ptr<Region> r =(*iter)[region_list_columns.region];
1023                 if (!r) {
1024                         return false;
1025                 }
1026         } 
1027
1028         return true;
1029 }
1030
1031 void
1032 Editor::region_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1033 {
1034         boost::shared_ptr<Region> region;
1035         TreeIter iter;
1036         
1037         if ((iter = region_list_model->get_iter (path))) {
1038                 region = (*iter)[region_list_columns.region];
1039                 (*iter)[region_list_columns.name] = new_text;
1040         }
1041         
1042         /* now mapover everything */
1043
1044         if (region) {
1045                 vector<RegionView*> equivalents;
1046                 get_regions_corresponding_to (region, equivalents);
1047
1048                 for (vector<RegionView*>::iterator i = equivalents.begin(); i != equivalents.end(); ++i) {
1049                         if (new_text != (*i)->region()->name()) {
1050                                 (*i)->region()->set_name (new_text);
1051                         }
1052                 }
1053         }
1054
1055 }
1056