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