2 Copyright (C) 2000-2005 Paul Davis
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.
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.
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.
26 #include "pbd/basename.h"
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"
34 #include <gtkmm2ext/stop_signal.h>
39 #include "ardour_ui.h"
40 #include "gui_thread.h"
42 #include "region_view.h"
44 #include "editor_regions.h"
50 using namespace ARDOUR;
54 using namespace Editing;
56 EditorRegions::EditorRegions (Editor* e)
57 : EditorComponent (e),
59 _show_automatic_regions (true),
60 _sort_type ((Editing::RegionListSortType) 0),
63 _display.set_size_request (100, -1);
64 _display.set_name ("RegionListDisplay");
65 /* Try to prevent single mouse presses from initiating edits.
66 This relies on a hack in gtktreeview.c:gtk_treeview_button_press()
68 _display.set_data ("mouse-edits-require-mod1", (gpointer) 0x1);
70 _model = TreeStore::create (_columns);
71 _model->set_sort_func (0, mem_fun (*this, &EditorRegions::sorter));
72 _model->set_sort_column (0, SORT_ASCENDING);
74 _display.set_model (_model);
75 _display.append_column (_("Regions"), _columns.name);
76 _display.append_column (_("Start"), _columns.start);
77 _display.append_column (_("End"), _columns.end);
78 _display.append_column (_("Length"), _columns.length);
79 _display.append_column (_("Sync"), _columns.sync);
80 _display.append_column (_("Fade In"), _columns.fadein);
81 _display.append_column (_("Fade Out"), _columns.fadeout);
82 _display.append_column (_("L"), _columns.locked);
83 _display.append_column (_("G"), _columns.glued);
84 _display.append_column (_("M"), _columns.muted);
85 _display.append_column (_("O"), _columns.opaque);
86 _display.append_column (_("Used"), _columns.used);
87 _display.append_column (_("Path"), _columns.path);
88 _display.set_headers_visible (true);
89 //_display.set_grid_lines (TREE_VIEW_GRID_LINES_BOTH);
91 CellRendererText* region_name_cell = dynamic_cast<CellRendererText*>(_display.get_column_cell_renderer (0));
92 region_name_cell->property_editable() = true;
93 region_name_cell->signal_edited().connect (mem_fun (*this, &EditorRegions::name_edit));
95 _display.get_selection()->set_select_function (mem_fun (*this, &EditorRegions::selection_filter));
97 TreeViewColumn* tv_col = _display.get_column(0);
98 CellRendererText* renderer = dynamic_cast<CellRendererText*>(_display.get_column_cell_renderer (0));
99 tv_col->add_attribute(renderer->property_text(), _columns.name);
100 tv_col->add_attribute(renderer->property_foreground_gdk(), _columns.color_);
102 _display.get_selection()->set_mode (SELECTION_MULTIPLE);
103 _display.add_object_drag (_columns.region.index(), "regions");
105 /* setup DnD handling */
107 list<TargetEntry> region_list_target_table;
109 region_list_target_table.push_back (TargetEntry ("text/plain"));
110 region_list_target_table.push_back (TargetEntry ("text/uri-list"));
111 region_list_target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
113 _display.add_drop_targets (region_list_target_table);
114 _display.signal_drag_data_received().connect (mem_fun(*this, &EditorRegions::drag_data_received));
116 _scroller.add (_display);
117 _scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC);
119 _display.signal_key_press_event().connect (mem_fun(*this, &EditorRegions::key_press));
120 _display.signal_key_release_event().connect (mem_fun(*this, &EditorRegions::key_release));
121 _display.signal_button_press_event().connect (mem_fun(*this, &EditorRegions::button_press), false);
122 _display.signal_button_release_event().connect (mem_fun(*this, &EditorRegions::button_release));
123 _change_connection = _display.get_selection()->signal_changed().connect (mem_fun(*this, &EditorRegions::selection_changed));
124 // _display.signal_popup_menu().connect (bind (mem_fun (*this, &Editor::show__display_context_menu), 1, 0));
126 //ARDOUR_UI::instance()->secondary_clock.mode_changed.connect (mem_fun(*this, &Editor::redisplay_regions));
127 ARDOUR_UI::instance()->secondary_clock.mode_changed.connect (mem_fun(*this, &EditorRegions::update_all_rows));
128 ARDOUR::Region::RegionPropertyChanged.connect (mem_fun(*this, &EditorRegions::update_row));
133 EditorRegions::connect_to_session (ARDOUR::Session* s)
135 EditorComponent::connect_to_session (s);
137 _session_connections.push_back (_session->RegionsAdded.connect (mem_fun(*this, &EditorRegions::handle_new_regions)));
138 _session_connections.push_back (_session->RegionRemoved.connect (mem_fun(*this, &EditorRegions::handle_region_removed)));
139 _session_connections.push_back (_session->RegionHiddenChange.connect (mem_fun(*this, &EditorRegions::region_hidden)));
145 EditorRegions::handle_region_removed (boost::weak_ptr<Region> wregion)
147 ENSURE_GUI_THREAD (bind (mem_fun (*this, &EditorRegions::handle_region_removed), wregion));
153 EditorRegions::handle_new_regions (vector<boost::weak_ptr<Region> >& v)
155 ENSURE_GUI_THREAD (bind (mem_fun (*this, &EditorRegions::handle_new_regions), v));
160 EditorRegions::region_hidden (boost::shared_ptr<Region> r)
162 ENSURE_GUI_THREAD(bind (mem_fun(*this, &EditorRegions::region_hidden), r));
167 EditorRegions::add_regions (vector<boost::weak_ptr<Region> >& regions)
169 for (vector<boost::weak_ptr<Region> >::iterator x = regions.begin(); x != regions.end(); ++x) {
170 boost::shared_ptr<Region> region ((*x).lock());
178 EditorRegions::add_region (boost::shared_ptr<Region> region)
180 if (!region || !_session) {
187 bool missing_source = boost::dynamic_pointer_cast<SilentFileSource>(region->source());
189 if (!_show_automatic_regions && region->automatic()) {
193 if (region->hidden()) {
194 TreeModel::iterator iter = _model->get_iter ("0");
195 TreeModel::Row parent;
196 TreeModel::Row child;
199 parent = *(_model->append());
200 parent[_columns.name] = _("Hidden");
201 boost::shared_ptr<Region> proxy = parent[_columns.region];
204 if ((*iter)[_columns.name] != _("Hidden")) {
205 parent = *(_model->insert(iter));
206 parent[_columns.name] = _("Hidden");
207 boost::shared_ptr<Region> proxy = parent[_columns.region];
214 row = *(_model->append (parent.children()));
216 } else if (region->whole_file()) {
218 TreeModel::iterator i;
219 TreeModel::Children rows = _model->children();
221 for (i = rows.begin(); i != rows.end(); ++i) {
222 boost::shared_ptr<Region> rr = (*i)[_columns.region];
224 if (rr && region->region_list_equivalent (rr)) {
229 row = *(_model->append());
231 if (missing_source) {
232 c.set_rgb(65535,0,0); // FIXME: error color from style
234 } else if (region->automatic()){
235 c.set_rgb(0,65535,0); // FIXME: error color from style
238 set_color(c, rgba_from_style ("RegionListWholeFile", 0xff, 0, 0, 0, "fg", Gtk::STATE_NORMAL, false ));
242 row[_columns.color_] = c;
244 if (region->source()->name()[0] == '/') { // external file
246 if (region->whole_file()) {
248 boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(region->source());
252 str = region_name_from_path (afs->path(), region->n_channels() > 1);
254 str += region->source()->name();
258 str = region->name();
262 str = region->name();
265 if (region->n_channels() > 1) {
266 std::stringstream foo;
267 foo << region->n_channels ();
273 row[_columns.name] = str;
274 row[_columns.region] = region;
276 if (missing_source) {
277 row[_columns.path] = _("(MISSING) ") + region->source()->name();
280 row[_columns.path] = region->source()->name();
284 if (region->automatic()) {
290 /* find parent node, add as new child */
292 TreeModel::iterator i;
293 TreeModel::Children rows = _model->children();
294 bool found_parent = false;
296 for (i = rows.begin(); i != rows.end(); ++i) {
297 boost::shared_ptr<Region> rr = (*i)[_columns.region];
298 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion>(rr);
300 if (r && r->whole_file()) {
302 if (region->source_equivalent (r)) {
303 row = *(_model->append ((*i).children()));
309 TreeModel::iterator ii;
310 TreeModel::Children subrows = (*i).children();
312 for (ii = subrows.begin(); ii != subrows.end(); ++ii) {
313 boost::shared_ptr<Region> rrr = (*ii)[_columns.region];
315 if (region->region_list_equivalent (rrr)) {
323 row = *(_model->append());
327 row[_columns.region] = region;
329 populate_row(region, (*row));
334 EditorRegions::region_changed (Change what_changed, boost::weak_ptr<Region> region)
336 ENSURE_GUI_THREAD (bind (mem_fun (*this, &EditorRegions::region_changed), what_changed, region));
338 boost::shared_ptr<Region> r = region.lock ();
344 if (what_changed & ARDOUR::NameChanged) {
345 /* find the region in our model and change its name */
346 TreeModel::Children rows = _model->children ();
347 TreeModel::iterator i = rows.begin ();
348 while (i != rows.end ()) {
349 TreeModel::Children children = (*i)->children ();
350 TreeModel::iterator j = children.begin ();
351 while (j != children.end()) {
352 boost::shared_ptr<Region> c = (*j)[_columns.region];
359 if (j != children.end()) {
360 (*j)[_columns.name] = r->name ();
371 EditorRegions::selection_changed ()
373 if (_display.get_selection()->count_selected_rows() > 0) {
376 TreeView::Selection::ListHandle_Path rows = _display.get_selection()->get_selected_rows ();
378 _editor->deselect_all ();
380 for (TreeView::Selection::ListHandle_Path::iterator i = rows.begin(); i != rows.end(); ++i) {
382 if (iter = _model->get_iter (*i)) { // they could have clicked on a row that is just a placeholder, like "Hidden"
383 boost::shared_ptr<Region> region = (*iter)[_columns.region];
387 if (region->automatic()) {
388 _display.get_selection()->unselect(*i);
391 _change_connection.block (true);
392 //editor_regions_selection_changed_connection.block(true);
394 _editor->set_selected_regionview_from_region_list (region, Selection::Add);
396 _change_connection.block (false);
397 //editor_regions_selection_changed_connection.block(false);
403 _editor->deselect_all ();
408 EditorRegions::set_selected (RegionSelection& regions)
410 for (RegionSelection::iterator iter = regions.begin(); iter != regions.end(); ++iter) {
412 TreeModel::iterator i;
413 TreeModel::Children rows = _model->children();
414 boost::shared_ptr<Region> r ((*iter)->region());
416 for (i = rows.begin(); i != rows.end(); ++i) {
418 boost::shared_ptr<Region> compared_region = (*i)[_columns.region];
420 if (r == compared_region) {
421 _display.get_selection()->select(*i);
425 if (!(*i).children().empty()) {
426 if (set_selected_in_subrow(r, (*i), 2)) {
435 EditorRegions::set_selected_in_subrow (boost::shared_ptr<Region> region, TreeModel::Row const &parent_row, int level)
437 TreeModel::iterator i;
438 TreeModel::Children subrows = (*parent_row).children();
440 for (i = subrows.begin(); i != subrows.end(); ++i) {
442 boost::shared_ptr<Region> compared_region = (*i)[_columns.region];
444 if (region == compared_region) {
445 _display.get_selection()->select(*i);
449 if (!(*i).children().empty()) {
450 if (set_selected_in_subrow (region, (*i), level + 1)) {
459 EditorRegions::insert_into_tmp_regionlist(boost::shared_ptr<Region> region)
461 /* keep all whole files at the beginning */
463 if (region->whole_file()) {
464 tmp_region_list.push_front (region);
466 tmp_region_list.push_back (region);
471 EditorRegions::redisplay ()
473 if (_no_redisplay || !_session) {
477 bool tree_expanded = false;
479 if (_toggle_full_action && _toggle_full_action->get_active()) { //If the list was expanded prior to rebuilding,
480 tree_expanded = true; //expand it again afterwards
483 _display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
486 /* now add everything we have, via a temporary list used to help with
490 tmp_region_list.clear();
491 _session->foreach_region (this, &EditorRegions::insert_into_tmp_regionlist);
493 for (list<boost::shared_ptr<Region> >::iterator r = tmp_region_list.begin(); r != tmp_region_list.end(); ++r) {
496 tmp_region_list.clear();
498 _display.set_model (_model);
501 _display.expand_all();
506 EditorRegions::update_row (boost::shared_ptr<Region> region)
508 if (!region || !_session) {
512 TreeModel::iterator i;
513 TreeModel::Children rows = _model->children();
515 for (i = rows.begin(); i != rows.end(); ++i) {
517 // cerr << "Level 1: Compare " << region->name() << " with parent " << (*i)[_columns.name] << "\n";
519 boost::shared_ptr<Region> compared_region = (*i)[_columns.region];
521 if (region == compared_region) {
522 // cerr << "Matched\n";
523 populate_row(region, (*i));
527 if (!(*i).children().empty()) {
528 if (update_subrows(region, (*i), 2)) {
533 // cerr << "Returning - No match\n";
537 EditorRegions::update_subrows (boost::shared_ptr<Region> region, TreeModel::Row const &parent_row, int level)
539 TreeModel::iterator i;
540 TreeModel::Children subrows = (*parent_row).children();
542 for (i = subrows.begin(); i != subrows.end(); ++i) {
544 // cerr << "Level " << level << ": Compare " << region->name() << " with child " << (*i)[_columns.name] << "\n";
546 boost::shared_ptr<Region> compared_region = (*i)[_columns.region];
548 if (region == compared_region) {
549 populate_row(region, (*i));
550 // cerr << "Matched\n";
554 if (!(*i).children().empty()) {
555 if (update_subrows (region, (*i), level + 1)) {
564 EditorRegions::update_all_rows ()
570 TreeModel::iterator i;
571 TreeModel::Children rows = _model->children();
573 for (i = rows.begin(); i != rows.end(); ++i) {
575 boost::shared_ptr<Region> region = (*i)[_columns.region];
577 if (!region->automatic()) {
578 cerr << "level 1 : Updating " << region->name() << "\n";
579 populate_row(region, (*i));
582 if (!(*i).children().empty()) {
583 update_all_subrows ((*i), 2);
589 EditorRegions::update_all_subrows (TreeModel::Row const &parent_row, int level)
591 TreeModel::iterator i;
592 TreeModel::Children subrows = (*parent_row).children();
594 for (i = subrows.begin(); i != subrows.end(); ++i) {
596 boost::shared_ptr<Region> region = (*i)[_columns.region];
598 if (!region->automatic()) {
599 cerr << "level " << level << " : Updating " << region->name() << "\n";
600 populate_row(region, (*i));
603 if (!(*i).children().empty()) {
604 update_all_subrows ((*i), level + 1);
610 EditorRegions::populate_row (boost::shared_ptr<Region> region, TreeModel::Row const &row)
617 char fadeout_str[16];
623 bool missing_source = boost::dynamic_pointer_cast<SilentFileSource>(region->source());
625 boost::shared_ptr<AudioRegion> audioRegion = boost::dynamic_pointer_cast<AudioRegion>(region);
627 bool fades_in_seconds = false;
631 length_str[0] = '\0';
633 fadein_str[0] = '\0';
634 fadeout_str[0] = '\0';
637 used = _editor->get_regionview_count_from_region_list (region);
638 sprintf (used_str, "%4d" , used);
640 switch (ARDOUR_UI::instance()->secondary_clock.mode ()) {
641 case AudioClock::SMPTE:
642 case AudioClock::Off: /* If the secondary clock is off, default to SMPTE */
643 _session->smpte_time (region->position(), smpte);
644 sprintf (start_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
645 _session->smpte_time (region->position() + region->length() - 1, smpte);
646 sprintf (end_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
647 _session->smpte_time (region->length(), smpte);
648 sprintf (length_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
649 _session->smpte_time (region->sync_position() + region->position(), smpte);
650 sprintf (sync_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
652 if (audioRegion && !fades_in_seconds) {
653 _session->smpte_time (audioRegion->fade_in()->back()->when, smpte);
654 sprintf (fadein_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
655 _session->smpte_time (audioRegion->fade_out()->back()->when, smpte);
656 sprintf (fadeout_str, "%02d:%02d:%02d:%02d", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
661 case AudioClock::BBT:
662 _session->tempo_map().bbt_time (region->position(), bbt);
663 sprintf (start_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
664 _session->tempo_map().bbt_time (region->position() + region->length() - 1, bbt);
665 sprintf (end_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
666 _session->tempo_map().bbt_time (region->length(), bbt);
667 sprintf (length_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
668 _session->tempo_map().bbt_time (region->sync_position() + region->position(), bbt);
669 sprintf (sync_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
671 if (audioRegion && !fades_in_seconds) {
672 _session->tempo_map().bbt_time (audioRegion->fade_in()->back()->when, bbt);
673 sprintf (fadein_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
674 _session->tempo_map().bbt_time (audioRegion->fade_out()->back()->when, bbt);
675 sprintf (fadeout_str, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
679 case AudioClock::MinSec:
685 left = region->position();
686 hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
687 left -= (nframes_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
688 mins = (int) floor (left / (_session->frame_rate() * 60.0f));
689 left -= (nframes_t) floor (mins * _session->frame_rate() * 60.0f);
690 secs = left / (float) _session->frame_rate();
691 sprintf (start_str, "%02d:%02d:%06.3f", hrs, mins, secs);
693 left = region->position() + region->length() - 1;
694 hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
695 left -= (nframes_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
696 mins = (int) floor (left / (_session->frame_rate() * 60.0f));
697 left -= (nframes_t) floor (mins * _session->frame_rate() * 60.0f);
698 secs = left / (float) _session->frame_rate();
699 sprintf (end_str, "%02d:%02d:%06.3f", hrs, mins, secs);
701 left = region->length();
702 hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
703 left -= (nframes_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
704 mins = (int) floor (left / (_session->frame_rate() * 60.0f));
705 left -= (nframes_t) floor (mins * _session->frame_rate() * 60.0f);
706 secs = left / (float) _session->frame_rate();
707 sprintf (length_str, "%02d:%02d:%06.3f", hrs, mins, secs);
709 left = region->sync_position() + region->position();
710 hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
711 left -= (nframes_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
712 mins = (int) floor (left / (_session->frame_rate() * 60.0f));
713 left -= (nframes_t) floor (mins * _session->frame_rate() * 60.0f);
714 secs = left / (float) _session->frame_rate();
715 sprintf (sync_str, "%02d:%02d:%06.3f", hrs, mins, secs);
717 if (audioRegion && !fades_in_seconds) {
718 left = audioRegion->fade_in()->back()->when;
719 hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
720 left -= (nframes_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
721 mins = (int) floor (left / (_session->frame_rate() * 60.0f));
722 left -= (nframes_t) floor (mins * _session->frame_rate() * 60.0f);
723 secs = left / (float) _session->frame_rate();
724 sprintf (fadein_str, "%02d:%02d:%06.3f", hrs, mins, secs);
726 left = audioRegion->fade_out()->back()->when;
727 hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
728 left -= (nframes_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
729 mins = (int) floor (left / (_session->frame_rate() * 60.0f));
730 left -= (nframes_t) floor (mins * _session->frame_rate() * 60.0f);
731 secs = left / (float) _session->frame_rate();
732 sprintf (fadeout_str, "%02d:%02d:%06.3f", hrs, mins, secs);
737 case AudioClock::Frames:
738 snprintf (start_str, sizeof (start_str), "%u", region->position());
739 snprintf (end_str, sizeof (end_str), "%u", (region->position() + region->length() - 1));
740 snprintf (length_str, sizeof (length_str), "%u", region->length());
741 snprintf (sync_str, sizeof (sync_str), "%u", region->sync_position() + region->position());
743 if (audioRegion && !fades_in_seconds) {
744 snprintf (fadein_str, sizeof (fadein_str), "%u", uint (audioRegion->fade_in()->back()->when));
745 snprintf (fadeout_str, sizeof (fadeout_str), "%u", uint (audioRegion->fade_out()->back()->when));
754 if (audioRegion && fades_in_seconds) {
760 left = audioRegion->fade_in()->back()->when;
761 mins = (int) floor (left / (_session->frame_rate() * 60.0f));
762 left -= (nframes_t) floor (mins * _session->frame_rate() * 60.0f);
763 millisecs = (int) floor ((left * 1000.0f) / _session->frame_rate());
765 if (audioRegion->fade_in()->back()->when >= _session->frame_rate()) {
766 sprintf (fadein_str, "%01dM %01dmS", mins, millisecs);
768 sprintf (fadein_str, "%01dmS", millisecs);
771 left = audioRegion->fade_out()->back()->when;
772 mins = (int) floor (left / (_session->frame_rate() * 60.0f));
773 left -= (nframes_t) floor (mins * _session->frame_rate() * 60.0f);
774 millisecs = (int) floor ((left * 1000.0f) / _session->frame_rate());
776 if (audioRegion->fade_out()->back()->when >= _session->frame_rate()) {
777 sprintf (fadeout_str, "%01dM %01dmS", mins, millisecs);
779 sprintf (fadeout_str, "%01dmS", millisecs);
784 row[_columns.start] = _("Multiple");
785 row[_columns.end] = _("Multiple");
786 row[_columns.sync] = _("Multiple");
787 row[_columns.fadein] = _("Multiple");
788 row[_columns.fadeout] = _("Multiple");
789 row[_columns.locked] = false;
790 row[_columns.glued] = false;
791 row[_columns.muted] = false;
792 row[_columns.opaque] = false;
794 row[_columns.start] = start_str;
795 row[_columns.end] = end_str;
797 if (region->sync_position() == 0) {
798 row[_columns.sync] = _("Start");
799 } else if (region->sync_position() == region->length() - 1) {
800 row[_columns.sync] = _("End");
802 row[_columns.sync] = sync_str;
806 if (audioRegion->fade_in_active()) {
807 row[_columns.fadein] = string_compose("%1%2%3", " ", fadein_str, " ");
809 row[_columns.fadein] = string_compose("%1%2%3", "(", fadein_str, ")");
812 row[_columns.fadein] = "";
816 if (audioRegion->fade_out_active()) {
817 row[_columns.fadeout] = string_compose("%1%2%3", " ", fadeout_str, " ");
819 row[_columns.fadeout] = string_compose("%1%2%3", "(", fadeout_str, ")");
822 row[_columns.fadeout] = "";
825 row[_columns.locked] = region->locked();
827 if (region->positional_lock_style() == Region::MusicTime) {
828 row[_columns.glued] = true;
830 row[_columns.glued] = false;
833 row[_columns.muted] = region->muted();
834 row[_columns.opaque] = region->opaque();
837 row[_columns.length] = length_str;
838 row[_columns.used] = used_str;
840 if (missing_source) {
841 row[_columns.path] = _("MISSING ") + region->source()->name();
843 row[_columns.path] = region->source()->name();
846 if (region->n_channels() > 1) {
847 row[_columns.name] = string_compose("%1 [%2]", region->name(), region->n_channels());
849 row[_columns.name] = region->name();
854 EditorRegions::build_menu ()
856 _menu = dynamic_cast<Menu*>(ActionManager::get_widget ("/RegionListMenu"));
858 /* now grab specific menu items that we need */
860 Glib::RefPtr<Action> act;
862 act = ActionManager::get_action (X_("RegionList"), X_("rlShowAll"));
864 _toggle_full_action = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
867 act = ActionManager::get_action (X_("RegionList"), X_("rlShowAuto"));
869 _toggle_show_auto_regions_action = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
874 EditorRegions::toggle_show_auto_regions ()
876 _show_automatic_regions = _toggle_show_auto_regions_action->get_active();
881 EditorRegions::toggle_full ()
883 if (_toggle_full_action->get_active()) {
884 _display.expand_all ();
886 _display.collapse_all ();
891 EditorRegions::show_context_menu (int button, int time)
897 if (_display.get_selection()->count_selected_rows() > 0) {
898 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, true);
900 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, false);
903 _menu->popup (button, time);
907 EditorRegions::key_press (GdkEventKey* /*ev*/)
913 EditorRegions::key_release (GdkEventKey* ev)
915 switch (ev->keyval) {
928 EditorRegions::button_press (GdkEventButton *ev)
930 boost::shared_ptr<Region> region;
932 TreeModel::Path path;
933 TreeViewColumn* column;
937 if (_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
938 if ((iter = _model->get_iter (path))) {
939 region = (*iter)[_columns.region];
943 if (Keyboard::is_context_menu_event (ev)) {
944 show_context_menu (ev->button, ev->time);
948 if (region != 0 && Keyboard::is_button2_event (ev)) {
949 // start/stop audition
950 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
951 _editor->consider_auditioning (region);
960 EditorRegions::button_release (GdkEventButton *ev)
963 TreeModel::Path path;
964 TreeViewColumn* column;
967 boost::shared_ptr<Region> region;
969 if (_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
970 if ((iter = _model->get_iter (path))) {
971 region = (*iter)[_columns.region];
975 if (region && Keyboard::is_delete_event (ev)) {
976 _session->remove_region_from_region_list (region);
984 EditorRegions::sorter (TreeModel::iterator a, TreeModel::iterator b)
988 boost::shared_ptr<Region> r1 = (*a)[_columns.region];
989 boost::shared_ptr<Region> r2 = (*b)[_columns.region];
991 /* handle rows without regions, like "Hidden" */
1001 boost::shared_ptr<AudioRegion> region1 = boost::dynamic_pointer_cast<AudioRegion> (r1);
1002 boost::shared_ptr<AudioRegion> region2 = boost::dynamic_pointer_cast<AudioRegion> (r2);
1004 if (region1 == 0 || region2 == 0) {
1007 switch (_sort_type) {
1009 s1 = (*a)[_columns.name];
1010 s2 = (*b)[_columns.name];
1011 return (s1.compare (s2));
1017 switch (_sort_type) {
1019 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
1023 cmp = region1->length() - region2->length();
1027 cmp = region1->position() - region2->position();
1031 cmp = region1->source()->timestamp() - region2->source()->timestamp();
1035 cmp = region1->start() - region2->start();
1039 cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
1042 case BySourceFileName:
1043 cmp = strcasecmp (region1->source()->name().c_str(), region2->source()->name().c_str());
1046 case BySourceFileLength:
1047 cmp = region1->source_length(0) - region2->source_length(0);
1050 case BySourceFileCreationDate:
1051 cmp = region1->source()->timestamp() - region2->source()->timestamp();
1054 case BySourceFileFS:
1055 if (region1->source()->name() == region2->source()->name()) {
1056 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
1058 cmp = strcasecmp (region1->source()->name().c_str(), region2->source()->name().c_str());
1065 } else if (cmp > 0) {
1073 EditorRegions::reset_sort_type (RegionListSortType type, bool force)
1075 if (type != _sort_type || force) {
1077 _model->set_sort_func (0, (mem_fun (*this, &EditorRegions::sorter)));
1082 EditorRegions::reset_sort_direction (bool up)
1084 _model->set_sort_column (0, up ? SORT_ASCENDING : SORT_DESCENDING);
1088 EditorRegions::selection_mapover (slot<void,boost::shared_ptr<Region> > sl)
1090 Glib::RefPtr<TreeSelection> selection = _display.get_selection();
1091 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1092 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1094 if (selection->count_selected_rows() == 0 || _session == 0) {
1098 for (; i != rows.end(); ++i) {
1101 if ((iter = _model->get_iter (*i))) {
1103 /* some rows don't have a region associated with them, but can still be
1104 selected (XXX maybe prevent them from being selected)
1107 boost::shared_ptr<Region> r = (*iter)[_columns.region];
1118 EditorRegions::remove_region ()
1120 selection_mapover (mem_fun (*_editor, &Editor::remove_a_region));
1124 EditorRegions::drag_data_received (const RefPtr<Gdk::DragContext>& context,
1126 const SelectionData& data,
1127 guint info, guint time)
1129 vector<ustring> paths;
1131 if (data.get_target() == "GTK_TREE_MODEL_ROW") {
1132 _display.on_drag_data_received (context, x, y, data, info, time);
1136 if (_editor->convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
1137 nframes64_t pos = 0;
1138 if (Profile->get_sae() || Config->get_only_copy_imported_files()) {
1139 _editor->do_import (paths, Editing::ImportDistinctFiles, Editing::ImportAsRegion, SrcBest, pos);
1141 _editor->do_embed (paths, Editing::ImportDistinctFiles, ImportAsRegion, pos);
1143 context->drag_finish (true, false, time);
1148 EditorRegions::selection_filter (const RefPtr<TreeModel>& model, const TreeModel::Path& path, bool /*yn*/)
1150 /* not possible to select rows that do not represent regions, like "Hidden" */
1152 TreeModel::iterator iter = model->get_iter (path);
1155 boost::shared_ptr<Region> r =(*iter)[_columns.region];
1165 EditorRegions::name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1167 boost::shared_ptr<Region> region;
1170 if ((iter = _model->get_iter (path))) {
1171 region = (*iter)[_columns.region];
1172 (*iter)[_columns.name] = new_text;
1175 /* now mapover everything */
1178 vector<RegionView*> equivalents;
1179 _editor->get_regions_corresponding_to (region, equivalents);
1181 for (vector<RegionView*>::iterator i = equivalents.begin(); i != equivalents.end(); ++i) {
1182 if (new_text != (*i)->region()->name()) {
1183 (*i)->region()->set_name (new_text);
1190 boost::shared_ptr<Region>
1191 EditorRegions::get_dragged_region ()
1193 list<boost::shared_ptr<Region> > regions;
1195 _display.get_object_drag_data (regions, &source);
1196 assert (regions.size() == 1);
1197 return regions.front ();
1201 EditorRegions::clear ()
1203 _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
1205 _display.set_model (_model);
1208 boost::shared_ptr<Region>
1209 EditorRegions::get_single_selection ()
1211 Glib::RefPtr<TreeSelection> selected = _display.get_selection();
1213 if (selected->count_selected_rows() != 1) {
1214 return boost::shared_ptr<Region> ();
1217 TreeView::Selection::ListHandle_Path rows = selected->get_selected_rows ();
1219 /* only one row selected, so rows.begin() is it */
1221 TreeIter iter = _model->get_iter (*rows.begin());
1224 return boost::shared_ptr<Region> ();
1227 return (*iter)[_columns.region];