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"
27 #include "pbd/enumwriter.h"
29 #include "ardour/audioregion.h"
30 #include "ardour/audiofilesource.h"
31 #include "ardour/region_factory.h"
32 #include "ardour/session.h"
33 #include "ardour/silentfilesource.h"
34 #include "ardour/profile.h"
39 #include "ardour_ui.h"
40 #include "gui_thread.h"
42 #include "region_view.h"
44 #include "editor_regions.h"
45 #include "editor_drag.h"
50 using namespace ARDOUR
;
54 using namespace Editing
;
55 using Gtkmm2ext::Keyboard
;
57 EditorRegions::EditorRegions (Editor
* e
)
60 , _show_automatic_regions (true)
61 , _sort_type ((Editing::RegionListSortType
) 0)
62 , _no_redisplay (false)
63 , ignore_region_list_selection_change (false)
64 , ignore_selected_region_change (false)
66 _display
.set_size_request (100, -1);
67 _display
.set_name ("RegionListDisplay");
68 /* Try to prevent single mouse presses from initiating edits.
69 This relies on a hack in gtktreeview.c:gtk_treeview_button_press()
71 _display
.set_data ("mouse-edits-require-mod1", (gpointer
) 0x1);
73 _model
= TreeStore::create (_columns
);
74 _model
->set_sort_func (0, sigc::mem_fun (*this, &EditorRegions::sorter
));
75 _model
->set_sort_column (0, SORT_ASCENDING
);
77 _display
.set_model (_model
);
78 _display
.append_column (_("Regions"), _columns
.name
);
79 _display
.append_column (_("Start"), _columns
.start
);
80 _display
.append_column (_("End"), _columns
.end
);
81 _display
.append_column (_("Length"), _columns
.length
);
82 _display
.append_column (_("Sync"), _columns
.sync
);
83 _display
.append_column (_("Fade In"), _columns
.fadein
);
84 _display
.append_column (_("Fade Out"), _columns
.fadeout
);
85 _display
.append_column (_("L"), _columns
.locked
);
86 _display
.append_column (_("G"), _columns
.glued
);
87 _display
.append_column (_("M"), _columns
.muted
);
88 _display
.append_column (_("O"), _columns
.opaque
);
89 _display
.append_column (_("Used"), _columns
.used
);
90 _display
.append_column (_("Path"), _columns
.path
);
91 _display
.set_headers_visible (true);
92 //_display.set_grid_lines (TREE_VIEW_GRID_LINES_BOTH);
94 CellRendererText
* region_name_cell
= dynamic_cast<CellRendererText
*>(_display
.get_column_cell_renderer (0));
95 region_name_cell
->property_editable() = true;
96 region_name_cell
->signal_edited().connect (sigc::mem_fun (*this, &EditorRegions::name_edit
));
98 _display
.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRegions::selection_filter
));
100 TreeViewColumn
* tv_col
= _display
.get_column(0);
101 CellRendererText
* renderer
= dynamic_cast<CellRendererText
*>(_display
.get_column_cell_renderer (0));
102 tv_col
->add_attribute(renderer
->property_text(), _columns
.name
);
103 tv_col
->add_attribute(renderer
->property_foreground_gdk(), _columns
.color_
);
105 CellRendererToggle
* locked_cell
= dynamic_cast<CellRendererToggle
*> (_display
.get_column_cell_renderer (7));
106 locked_cell
->property_activatable() = true;
107 locked_cell
->signal_toggled().connect (sigc::mem_fun (*this, &EditorRegions::locked_changed
));
108 TreeViewColumn
* locked_col
= _display
.get_column (7);
109 locked_col
->add_attribute (locked_cell
->property_visible(), _columns
.property_toggles_visible
);
111 CellRendererToggle
* glued_cell
= dynamic_cast<CellRendererToggle
*> (_display
.get_column_cell_renderer (8));
112 glued_cell
->property_activatable() = true;
113 glued_cell
->signal_toggled().connect (sigc::mem_fun (*this, &EditorRegions::glued_changed
));
114 TreeViewColumn
* glued_col
= _display
.get_column (8);
115 glued_col
->add_attribute (glued_cell
->property_visible(), _columns
.property_toggles_visible
);
117 CellRendererToggle
* muted_cell
= dynamic_cast<CellRendererToggle
*> (_display
.get_column_cell_renderer (9));
118 muted_cell
->property_activatable() = true;
119 muted_cell
->signal_toggled().connect (sigc::mem_fun (*this, &EditorRegions::muted_changed
));
120 TreeViewColumn
* muted_col
= _display
.get_column (9);
121 muted_col
->add_attribute (muted_cell
->property_visible(), _columns
.property_toggles_visible
);
123 CellRendererToggle
* opaque_cell
= dynamic_cast<CellRendererToggle
*> (_display
.get_column_cell_renderer (10));
124 opaque_cell
->property_activatable() = true;
125 opaque_cell
->signal_toggled().connect (sigc::mem_fun (*this, &EditorRegions::opaque_changed
));
126 TreeViewColumn
* opaque_col
= _display
.get_column (10);
127 opaque_col
->add_attribute (opaque_cell
->property_visible(), _columns
.property_toggles_visible
);
129 _display
.get_selection()->set_mode (SELECTION_MULTIPLE
);
130 _display
.add_object_drag (_columns
.region
.index(), "regions");
132 /* setup DnD handling */
134 list
<TargetEntry
> region_list_target_table
;
136 region_list_target_table
.push_back (TargetEntry ("text/plain"));
137 region_list_target_table
.push_back (TargetEntry ("text/uri-list"));
138 region_list_target_table
.push_back (TargetEntry ("application/x-rootwin-drop"));
140 _display
.add_drop_targets (region_list_target_table
);
141 _display
.signal_drag_data_received().connect (sigc::mem_fun(*this, &EditorRegions::drag_data_received
));
143 _scroller
.add (_display
);
144 _scroller
.set_policy (POLICY_AUTOMATIC
, POLICY_AUTOMATIC
);
146 _display
.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRegions::key_press
));
147 _display
.signal_button_press_event().connect (sigc::mem_fun(*this, &EditorRegions::button_press
), false);
148 _change_connection
= _display
.get_selection()->signal_changed().connect (sigc::mem_fun(*this, &EditorRegions::selection_changed
));
149 // _display.signal_popup_menu().connect (sigc::bind (sigc::mem_fun (*this, &Editor::show__display_context_menu), 1, 0));
151 //ARDOUR_UI::instance()->secondary_clock.mode_changed.connect (sigc::mem_fun(*this, &Editor::redisplay_regions));
152 ARDOUR_UI::instance()->secondary_clock
.mode_changed
.connect (sigc::mem_fun(*this, &EditorRegions::update_all_rows
));
153 ARDOUR::Region::RegionPropertyChanged
.connect (region_property_connection
, MISSING_INVALIDATOR
, ui_bind (&EditorRegions::region_changed
, this, _1
, _2
), gui_context());
154 ARDOUR::RegionFactory::CheckNewRegion
.connect (check_new_region_connection
, MISSING_INVALIDATOR
, ui_bind (&EditorRegions::add_region
, this, _1
), gui_context());
158 EditorRegions::set_session (ARDOUR::Session
* s
)
160 SessionHandlePtr::set_session (s
);
165 EditorRegions::add_regions (vector
<boost::shared_ptr
<Region
> >& regions
)
167 for (vector
<boost::shared_ptr
<Region
> >::iterator x
= regions
.begin(); x
!= regions
.end(); ++x
) {
173 EditorRegions::add_region (boost::shared_ptr
<Region
> region
)
175 if (!region
|| !_session
) {
182 bool missing_source
= boost::dynamic_pointer_cast
<SilentFileSource
>(region
->source());
184 if (!_show_automatic_regions
&& region
->automatic()) {
188 if (region
->hidden()) {
189 TreeModel::iterator iter
= _model
->get_iter ("0");
190 TreeModel::Row parent
;
191 TreeModel::Row child
;
194 parent
= *(_model
->append());
195 parent
[_columns
.name
] = _("Hidden");
196 boost::shared_ptr
<Region
> proxy
= parent
[_columns
.region
];
199 if ((*iter
)[_columns
.name
] != _("Hidden")) {
200 parent
= *(_model
->insert(iter
));
201 parent
[_columns
.name
] = _("Hidden");
202 boost::shared_ptr
<Region
> proxy
= parent
[_columns
.region
];
209 row
= *(_model
->append (parent
.children()));
211 } else if (region
->whole_file()) {
213 TreeModel::iterator i
;
214 TreeModel::Children rows
= _model
->children();
216 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
217 boost::shared_ptr
<Region
> rr
= (*i
)[_columns
.region
];
219 if (rr
&& region
->region_list_equivalent (rr
)) {
224 row
= *(_model
->append());
226 if (missing_source
) {
227 c
.set_rgb(65535,0,0); // FIXME: error color from style
229 } else if (region
->automatic()){
230 c
.set_rgb(0,65535,0); // FIXME: error color from style
233 set_color(c
, rgba_from_style ("RegionListWholeFile", 0xff, 0, 0, 0, "fg", Gtk::STATE_NORMAL
, false ));
237 row
[_columns
.color_
] = c
;
239 if (region
->source()->name()[0] == '/') { // external file
241 if (region
->whole_file()) {
243 boost::shared_ptr
<AudioFileSource
> afs
= boost::dynamic_pointer_cast
<AudioFileSource
>(region
->source());
247 str
= region_name_from_path (afs
->path(), region
->n_channels() > 1);
249 str
+= region
->source()->name();
253 str
= region
->name();
257 str
= region
->name();
260 if (region
->n_channels() > 1) {
261 std::stringstream foo
;
262 foo
<< region
->n_channels ();
268 row
[_columns
.name
] = str
;
269 row
[_columns
.region
] = region
;
270 row
[_columns
.property_toggles_visible
] = false;
272 if (missing_source
) {
273 row
[_columns
.path
] = _("(MISSING) ") + region
->source()->name();
276 row
[_columns
.path
] = region
->source()->name();
280 if (region
->automatic()) {
286 /* find parent node, add as new child */
288 TreeModel::iterator i
;
289 TreeModel::Children rows
= _model
->children();
290 bool found_parent
= false;
292 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
293 boost::shared_ptr
<Region
> rr
= (*i
)[_columns
.region
];
294 boost::shared_ptr
<AudioRegion
> r
= boost::dynamic_pointer_cast
<AudioRegion
>(rr
);
296 if (r
&& r
->whole_file()) {
298 if (region
->source_equivalent (r
)) {
299 row
= *(_model
->append ((*i
).children()));
305 TreeModel::iterator ii
;
306 TreeModel::Children subrows
= (*i
).children();
308 for (ii
= subrows
.begin(); ii
!= subrows
.end(); ++ii
) {
309 boost::shared_ptr
<Region
> rrr
= (*ii
)[_columns
.region
];
311 if (region
->region_list_equivalent (rrr
)) {
319 row
= *(_model
->append());
322 row
[_columns
.property_toggles_visible
] = true;
325 row
[_columns
.region
] = region
;
327 populate_row(region
, (*row
));
331 EditorRegions::region_changed (boost::shared_ptr
<Region
> r
, const PropertyChange
& what_changed
)
333 if (what_changed
.contains (ARDOUR::Properties::name
) ||
334 what_changed
.contains (ARDOUR::Properties::start
) ||
335 what_changed
.contains (ARDOUR::Properties::position
) ||
336 what_changed
.contains (ARDOUR::Properties::length
) ||
337 what_changed
.contains (ARDOUR::Properties::locked
) ||
338 what_changed
.contains (ARDOUR::Properties::position_lock_style
) ||
339 what_changed
.contains (ARDOUR::Properties::muted
) ||
340 what_changed
.contains (ARDOUR::Properties::opaque
)) {
342 /* find the region in our model and update its row */
343 TreeModel::Children rows
= _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
)[_columns
.region
];
356 if (j
!= children
.end()) {
357 populate_row (r
, *j
);
365 if (what_changed
.contains (ARDOUR::Properties::hidden
)) {
371 EditorRegions::selection_changed ()
373 if (ignore_region_list_selection_change
) {
377 if (_display
.get_selection()->count_selected_rows() > 0) {
380 TreeView::Selection::ListHandle_Path rows
= _display
.get_selection()->get_selected_rows ();
382 _editor
->get_selection().clear_regions ();
384 for (TreeView::Selection::ListHandle_Path::iterator i
= rows
.begin(); i
!= rows
.end(); ++i
) {
386 if (iter
= _model
->get_iter (*i
)) {
387 boost::shared_ptr
<Region
> region
= (*iter
)[_columns
.region
];
389 // they could have clicked on a row that is just a placeholder, like "Hidden"
393 if (region
->automatic()) {
395 _display
.get_selection()->unselect(*i
);
398 _change_connection
.block (true);
399 _editor
->set_selected_regionview_from_region_list (region
, Selection::Add
);
401 _change_connection
.block (false);
407 _editor
->get_selection().clear_regions ();
412 EditorRegions::set_selected (RegionSelection
& regions
)
414 for (RegionSelection::iterator iter
= regions
.begin(); iter
!= regions
.end(); ++iter
) {
416 TreeModel::iterator i
;
417 TreeModel::Children rows
= _model
->children();
418 boost::shared_ptr
<Region
> r ((*iter
)->region());
420 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
422 boost::shared_ptr
<Region
> compared_region
= (*i
)[_columns
.region
];
424 if (r
== compared_region
) {
425 _display
.get_selection()->select(*i
);
429 if (!(*i
).children().empty()) {
430 if (set_selected_in_subrow(r
, (*i
), 2)) {
439 EditorRegions::set_selected_in_subrow (boost::shared_ptr
<Region
> region
, TreeModel::Row
const &parent_row
, int level
)
441 TreeModel::iterator i
;
442 TreeModel::Children subrows
= (*parent_row
).children();
444 for (i
= subrows
.begin(); i
!= subrows
.end(); ++i
) {
446 boost::shared_ptr
<Region
> compared_region
= (*i
)[_columns
.region
];
448 if (region
== compared_region
) {
449 _display
.get_selection()->select(*i
);
453 if (!(*i
).children().empty()) {
454 if (set_selected_in_subrow (region
, (*i
), level
+ 1)) {
463 EditorRegions::insert_into_tmp_regionlist(boost::shared_ptr
<Region
> region
)
465 /* keep all whole files at the beginning */
467 if (region
->whole_file()) {
468 tmp_region_list
.push_front (region
);
470 tmp_region_list
.push_back (region
);
475 EditorRegions::redisplay ()
477 if (_no_redisplay
|| !_session
) {
481 bool tree_expanded
= false;
483 if (_toggle_full_action
&& _toggle_full_action
->get_active()) { //If the list was expanded prior to rebuilding,
484 tree_expanded
= true; //expand it again afterwards
487 _display
.set_model (Glib::RefPtr
<Gtk::TreeStore
>(0));
490 /* now add everything we have, via a temporary list used to help with
494 tmp_region_list
.clear();
496 const RegionFactory::RegionMap
& regions (RegionFactory::regions());
497 for (RegionFactory::RegionMap::const_iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
498 insert_into_tmp_regionlist (i
->second
);
501 for (list
<boost::shared_ptr
<Region
> >::iterator r
= tmp_region_list
.begin(); r
!= tmp_region_list
.end(); ++r
) {
504 tmp_region_list
.clear();
506 _display
.set_model (_model
);
509 _display
.expand_all();
514 EditorRegions::update_row (boost::shared_ptr
<Region
> region
)
516 if (!region
|| !_session
) {
520 TreeModel::iterator i
;
521 TreeModel::Children rows
= _model
->children();
525 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
527 // cerr << "Level 1: Compare " << region->name() << " with parent " << (*i)[_columns.name] << "\n";
529 boost::shared_ptr
<Region
> compared_region
= (*i
)[_columns
.region
];
531 if (region
== compared_region
) {
532 // cerr << "Matched\n";
533 populate_row(region
, (*i
));
537 if (!(*i
).children().empty()) {
538 if (update_subrows(region
, (*i
), 2)) {
544 // cerr << "Returning - No match\n";
548 EditorRegions::update_subrows (boost::shared_ptr
<Region
> region
, TreeModel::Row
const &parent_row
, int level
)
550 TreeModel::iterator i
;
551 TreeModel::Children subrows
= (*parent_row
).children();
553 for (i
= subrows
.begin(); i
!= subrows
.end(); ++i
) {
555 // cerr << "Level " << level << ": Compare " << region->name() << " with child " << (*i)[_columns.name] << "\n";
557 boost::shared_ptr
<Region
> compared_region
= (*i
)[_columns
.region
];
559 if (region
== compared_region
) {
560 populate_row(region
, (*i
));
561 // cerr << "Matched\n";
565 if (!(*i
).children().empty()) {
566 if (update_subrows (region
, (*i
), level
+ 1)) {
576 EditorRegions::update_all_rows ()
582 TreeModel::iterator i
;
583 TreeModel::Children rows
= _model
->children();
585 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
587 boost::shared_ptr
<Region
> region
= (*i
)[_columns
.region
];
589 if (!region
->automatic()) {
590 populate_row(region
, (*i
));
593 if (!(*i
).children().empty()) {
594 update_all_subrows ((*i
), 2);
600 EditorRegions::update_all_subrows (TreeModel::Row
const &parent_row
, int level
)
602 TreeModel::iterator i
;
603 TreeModel::Children subrows
= (*parent_row
).children();
605 for (i
= subrows
.begin(); i
!= subrows
.end(); ++i
) {
607 boost::shared_ptr
<Region
> region
= (*i
)[_columns
.region
];
609 if (!region
->automatic()) {
610 populate_row(region
, (*i
));
613 if (!(*i
).children().empty()) {
614 update_all_subrows ((*i
), level
+ 1);
620 EditorRegions::populate_row (boost::shared_ptr
<Region
> region
, TreeModel::Row
const &row
)
627 char fadeout_str
[16];
631 Timecode::Time timecode
;
633 bool missing_source
= boost::dynamic_pointer_cast
<SilentFileSource
>(region
->source());
635 boost::shared_ptr
<AudioRegion
> audioRegion
= boost::dynamic_pointer_cast
<AudioRegion
>(region
);
637 bool fades_in_seconds
= false;
641 length_str
[0] = '\0';
643 fadein_str
[0] = '\0';
644 fadeout_str
[0] = '\0';
647 used
= _editor
->get_regionview_count_from_region_list (region
);
648 sprintf (used_str
, "%4d" , used
);
650 switch (ARDOUR_UI::instance()->secondary_clock
.mode ()) {
651 case AudioClock::Timecode
:
652 case AudioClock::Off
: /* If the secondary clock is off, default to Timecode */
653 _session
->timecode_time (region
->position(), timecode
);
654 sprintf (start_str
, "%02d:%02d:%02d:%02d", timecode
.hours
, timecode
.minutes
, timecode
.seconds
, timecode
.frames
);
655 _session
->timecode_time (region
->position() + region
->length() - 1, timecode
);
656 sprintf (end_str
, "%02d:%02d:%02d:%02d", timecode
.hours
, timecode
.minutes
, timecode
.seconds
, timecode
.frames
);
657 _session
->timecode_time (region
->length(), timecode
);
658 sprintf (length_str
, "%02d:%02d:%02d:%02d", timecode
.hours
, timecode
.minutes
, timecode
.seconds
, timecode
.frames
);
659 _session
->timecode_time (region
->sync_position() + region
->position(), timecode
);
660 sprintf (sync_str
, "%02d:%02d:%02d:%02d", timecode
.hours
, timecode
.minutes
, timecode
.seconds
, timecode
.frames
);
662 if (audioRegion
&& !fades_in_seconds
) {
663 _session
->timecode_time (audioRegion
->fade_in()->back()->when
, timecode
);
664 sprintf (fadein_str
, "%02d:%02d:%02d:%02d", timecode
.hours
, timecode
.minutes
, timecode
.seconds
, timecode
.frames
);
665 _session
->timecode_time (audioRegion
->fade_out()->back()->when
, timecode
);
666 sprintf (fadeout_str
, "%02d:%02d:%02d:%02d", timecode
.hours
, timecode
.minutes
, timecode
.seconds
, timecode
.frames
);
671 case AudioClock::BBT
:
672 _session
->tempo_map().bbt_time (region
->position(), bbt
);
673 sprintf (start_str
, "%03d|%02d|%04d" , bbt
.bars
, bbt
.beats
, bbt
.ticks
);
674 _session
->tempo_map().bbt_time (region
->position() + region
->length() - 1, bbt
);
675 sprintf (end_str
, "%03d|%02d|%04d" , bbt
.bars
, bbt
.beats
, bbt
.ticks
);
676 _session
->tempo_map().bbt_time (region
->length(), bbt
);
677 sprintf (length_str
, "%03d|%02d|%04d" , bbt
.bars
, bbt
.beats
, bbt
.ticks
);
678 _session
->tempo_map().bbt_time (region
->sync_position() + region
->position(), bbt
);
679 sprintf (sync_str
, "%03d|%02d|%04d" , bbt
.bars
, bbt
.beats
, bbt
.ticks
);
681 if (audioRegion
&& !fades_in_seconds
) {
682 _session
->tempo_map().bbt_time (audioRegion
->fade_in()->back()->when
, bbt
);
683 sprintf (fadein_str
, "%03d|%02d|%04d" , bbt
.bars
, bbt
.beats
, bbt
.ticks
);
684 _session
->tempo_map().bbt_time (audioRegion
->fade_out()->back()->when
, bbt
);
685 sprintf (fadeout_str
, "%03d|%02d|%04d" , bbt
.bars
, bbt
.beats
, bbt
.ticks
);
689 case AudioClock::MinSec
:
695 left
= region
->position();
696 hrs
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
* 60.0f
));
697 left
-= (nframes_t
) floor (hrs
* _session
->frame_rate() * 60.0f
* 60.0f
);
698 mins
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
));
699 left
-= (nframes_t
) floor (mins
* _session
->frame_rate() * 60.0f
);
700 secs
= left
/ (float) _session
->frame_rate();
701 sprintf (start_str
, "%02d:%02d:%06.3f", hrs
, mins
, secs
);
703 left
= region
->position() + region
->length() - 1;
704 hrs
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
* 60.0f
));
705 left
-= (nframes_t
) floor (hrs
* _session
->frame_rate() * 60.0f
* 60.0f
);
706 mins
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
));
707 left
-= (nframes_t
) floor (mins
* _session
->frame_rate() * 60.0f
);
708 secs
= left
/ (float) _session
->frame_rate();
709 sprintf (end_str
, "%02d:%02d:%06.3f", hrs
, mins
, secs
);
711 left
= region
->length();
712 hrs
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
* 60.0f
));
713 left
-= (nframes_t
) floor (hrs
* _session
->frame_rate() * 60.0f
* 60.0f
);
714 mins
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
));
715 left
-= (nframes_t
) floor (mins
* _session
->frame_rate() * 60.0f
);
716 secs
= left
/ (float) _session
->frame_rate();
717 sprintf (length_str
, "%02d:%02d:%06.3f", hrs
, mins
, secs
);
719 left
= region
->sync_position() + region
->position();
720 hrs
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
* 60.0f
));
721 left
-= (nframes_t
) floor (hrs
* _session
->frame_rate() * 60.0f
* 60.0f
);
722 mins
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
));
723 left
-= (nframes_t
) floor (mins
* _session
->frame_rate() * 60.0f
);
724 secs
= left
/ (float) _session
->frame_rate();
725 sprintf (sync_str
, "%02d:%02d:%06.3f", hrs
, mins
, secs
);
727 if (audioRegion
&& !fades_in_seconds
) {
728 left
= audioRegion
->fade_in()->back()->when
;
729 hrs
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
* 60.0f
));
730 left
-= (nframes_t
) floor (hrs
* _session
->frame_rate() * 60.0f
* 60.0f
);
731 mins
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
));
732 left
-= (nframes_t
) floor (mins
* _session
->frame_rate() * 60.0f
);
733 secs
= left
/ (float) _session
->frame_rate();
734 sprintf (fadein_str
, "%02d:%02d:%06.3f", hrs
, mins
, secs
);
736 left
= audioRegion
->fade_out()->back()->when
;
737 hrs
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
* 60.0f
));
738 left
-= (nframes_t
) floor (hrs
* _session
->frame_rate() * 60.0f
* 60.0f
);
739 mins
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
));
740 left
-= (nframes_t
) floor (mins
* _session
->frame_rate() * 60.0f
);
741 secs
= left
/ (float) _session
->frame_rate();
742 sprintf (fadeout_str
, "%02d:%02d:%06.3f", hrs
, mins
, secs
);
747 case AudioClock::Frames
:
748 snprintf (start_str
, sizeof (start_str
), "%" PRId64
, region
->position());
749 snprintf (end_str
, sizeof (end_str
), "%" PRId64
, (region
->last_frame()));
750 snprintf (length_str
, sizeof (length_str
), "%" PRId64
, region
->length());
751 snprintf (sync_str
, sizeof (sync_str
), "%" PRId64
, region
->sync_position() + region
->position());
753 if (audioRegion
&& !fades_in_seconds
) {
754 snprintf (fadein_str
, sizeof (fadein_str
), "%u", uint (audioRegion
->fade_in()->back()->when
));
755 snprintf (fadeout_str
, sizeof (fadeout_str
), "%u", uint (audioRegion
->fade_out()->back()->when
));
764 if (audioRegion
&& fades_in_seconds
) {
770 left
= audioRegion
->fade_in()->back()->when
;
771 mins
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
));
772 left
-= (nframes_t
) floor (mins
* _session
->frame_rate() * 60.0f
);
773 millisecs
= (int) floor ((left
* 1000.0f
) / _session
->frame_rate());
775 if (audioRegion
->fade_in()->back()->when
>= _session
->frame_rate()) {
776 sprintf (fadein_str
, "%01dM %01dmS", mins
, millisecs
);
778 sprintf (fadein_str
, "%01dmS", millisecs
);
781 left
= audioRegion
->fade_out()->back()->when
;
782 mins
= (int) floor (left
/ (_session
->frame_rate() * 60.0f
));
783 left
-= (nframes_t
) floor (mins
* _session
->frame_rate() * 60.0f
);
784 millisecs
= (int) floor ((left
* 1000.0f
) / _session
->frame_rate());
786 if (audioRegion
->fade_out()->back()->when
>= _session
->frame_rate()) {
787 sprintf (fadeout_str
, "%01dM %01dmS", mins
, millisecs
);
789 sprintf (fadeout_str
, "%01dmS", millisecs
);
794 row
[_columns
.start
] = _("Multiple");
795 row
[_columns
.end
] = _("Multiple");
796 row
[_columns
.sync
] = _("Multiple");
797 row
[_columns
.fadein
] = _("Multiple");
798 row
[_columns
.fadeout
] = _("Multiple");
799 row
[_columns
.locked
] = false;
800 row
[_columns
.glued
] = false;
801 row
[_columns
.muted
] = false;
802 row
[_columns
.opaque
] = false;
804 row
[_columns
.start
] = start_str
;
805 row
[_columns
.end
] = end_str
;
807 if (region
->sync_position() == 0) {
808 row
[_columns
.sync
] = _("Start");
809 } else if (region
->sync_position() == region
->length() - 1) {
810 row
[_columns
.sync
] = _("End");
812 row
[_columns
.sync
] = sync_str
;
816 if (audioRegion
->fade_in_active()) {
817 row
[_columns
.fadein
] = string_compose("%1%2%3", " ", fadein_str
, " ");
819 row
[_columns
.fadein
] = string_compose("%1%2%3", "(", fadein_str
, ")");
822 row
[_columns
.fadein
] = "";
826 if (audioRegion
->fade_out_active()) {
827 row
[_columns
.fadeout
] = string_compose("%1%2%3", " ", fadeout_str
, " ");
829 row
[_columns
.fadeout
] = string_compose("%1%2%3", "(", fadeout_str
, ")");
832 row
[_columns
.fadeout
] = "";
835 row
[_columns
.locked
] = region
->locked();
837 if (region
->position_lock_style() == MusicTime
) {
838 row
[_columns
.glued
] = true;
840 row
[_columns
.glued
] = false;
843 row
[_columns
.muted
] = region
->muted();
844 row
[_columns
.opaque
] = region
->opaque();
847 row
[_columns
.length
] = length_str
;
848 row
[_columns
.used
] = used_str
;
850 if (missing_source
) {
851 row
[_columns
.path
] = _("MISSING ") + region
->source()->name();
853 row
[_columns
.path
] = region
->source()->name();
856 if (region
->n_channels() > 1) {
857 row
[_columns
.name
] = string_compose("%1 [%2]", region
->name(), region
->n_channels());
859 row
[_columns
.name
] = region
->name();
864 EditorRegions::build_menu ()
866 _menu
= dynamic_cast<Menu
*>(ActionManager::get_widget ("/RegionListMenu"));
868 /* now grab specific menu items that we need */
870 Glib::RefPtr
<Action
> act
;
872 _hide_action
= ActionManager::get_action (X_("RegionList"), X_("rlHide"));
873 _show_action
= ActionManager::get_action (X_("RegionList"), X_("rlShow"));
875 act
= ActionManager::get_action (X_("RegionList"), X_("rlShowAll"));
877 _toggle_full_action
= Glib::RefPtr
<ToggleAction
>::cast_dynamic (act
);
880 act
= ActionManager::get_action (X_("RegionList"), X_("rlShowAuto"));
882 _toggle_show_auto_regions_action
= Glib::RefPtr
<ToggleAction
>::cast_dynamic (act
);
887 EditorRegions::toggle_show_auto_regions ()
889 _show_automatic_regions
= _toggle_show_auto_regions_action
->get_active();
894 EditorRegions::toggle_full ()
896 if (_toggle_full_action
->get_active()) {
897 _display
.expand_all ();
899 _display
.collapse_all ();
904 EditorRegions::show_context_menu (int button
, int time
)
910 if (_display
.get_selection()->count_selected_rows() > 0) {
911 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions
, true);
913 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions
, false);
916 /* Enable the "Show" option if any selected regions are hidden, and vice versa for "Hide" */
918 bool have_shown
= false;
919 bool have_hidden
= false;
921 TreeView::Selection::ListHandle_Path rows
= _display
.get_selection()->get_selected_rows ();
922 for (TreeView::Selection::ListHandle_Path::iterator i
= rows
.begin(); i
!= rows
.end(); ++i
) {
923 TreeIter t
= _model
->get_iter (*i
);
924 boost::shared_ptr
<Region
> r
= (*t
)[_columns
.region
];
934 _hide_action
->set_sensitive (have_shown
);
935 _show_action
->set_sensitive (have_hidden
);
937 _menu
->popup (button
, time
);
941 EditorRegions::key_press (GdkEventKey
* /*ev*/)
948 EditorRegions::button_press (GdkEventButton
*ev
)
950 boost::shared_ptr
<Region
> region
;
952 TreeModel::Path path
;
953 TreeViewColumn
* column
;
957 if (_display
.get_path_at_pos ((int)ev
->x
, (int)ev
->y
, path
, column
, cellx
, celly
)) {
958 if ((iter
= _model
->get_iter (path
))) {
959 region
= (*iter
)[_columns
.region
];
963 if (Keyboard::is_context_menu_event (ev
)) {
964 show_context_menu (ev
->button
, ev
->time
);
968 if (region
!= 0 && Keyboard::is_button2_event (ev
)) {
969 // start/stop audition
970 if (!Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
971 _editor
->consider_auditioning (region
);
980 EditorRegions::sorter (TreeModel::iterator a
, TreeModel::iterator b
)
984 boost::shared_ptr
<Region
> r1
= (*a
)[_columns
.region
];
985 boost::shared_ptr
<Region
> r2
= (*b
)[_columns
.region
];
987 /* handle rows without regions, like "Hidden" */
997 boost::shared_ptr
<AudioRegion
> region1
= boost::dynamic_pointer_cast
<AudioRegion
> (r1
);
998 boost::shared_ptr
<AudioRegion
> region2
= boost::dynamic_pointer_cast
<AudioRegion
> (r2
);
1000 if (region1
== 0 || region2
== 0) {
1003 switch (_sort_type
) {
1005 s1
= (*a
)[_columns
.name
];
1006 s2
= (*b
)[_columns
.name
];
1007 return (s1
.compare (s2
));
1013 switch (_sort_type
) {
1015 cmp
= strcasecmp (region1
->name().c_str(), region2
->name().c_str());
1019 cmp
= region1
->length() - region2
->length();
1023 cmp
= region1
->position() - region2
->position();
1027 cmp
= region1
->source()->timestamp() - region2
->source()->timestamp();
1031 cmp
= region1
->start() - region2
->start();
1035 // cerr << "Compare " << (region1->start() + region1->length()) << " and " << (region2->start() + region2->length()) << endl;
1036 cmp
= (region1
->start() + region1
->length()) - (region2
->start() + region2
->length());
1039 case BySourceFileName
:
1040 cmp
= strcasecmp (region1
->source()->name().c_str(), region2
->source()->name().c_str());
1043 case BySourceFileLength
:
1044 cmp
= region1
->source_length(0) - region2
->source_length(0);
1047 case BySourceFileCreationDate
:
1048 cmp
= region1
->source()->timestamp() - region2
->source()->timestamp();
1051 case BySourceFileFS
:
1052 if (region1
->source()->name() == region2
->source()->name()) {
1053 cmp
= strcasecmp (region1
->name().c_str(), region2
->name().c_str());
1055 cmp
= strcasecmp (region1
->source()->name().c_str(), region2
->source()->name().c_str());
1060 // cerr << "Comparison on " << enum_2_string (_sort_type) << " gives " << cmp << endl;
1064 } else if (cmp
> 0) {
1072 EditorRegions::reset_sort_type (RegionListSortType type
, bool force
)
1074 if (type
!= _sort_type
|| force
) {
1076 _model
->set_sort_func (0, (sigc::mem_fun (*this, &EditorRegions::sorter
)));
1081 EditorRegions::reset_sort_direction (bool up
)
1083 _model
->set_sort_column (0, up
? SORT_ASCENDING
: SORT_DESCENDING
);
1087 EditorRegions::selection_mapover (sigc::slot
<void,boost::shared_ptr
<Region
> > sl
)
1089 Glib::RefPtr
<TreeSelection
> selection
= _display
.get_selection();
1090 TreeView::Selection::ListHandle_Path rows
= selection
->get_selected_rows ();
1091 TreeView::Selection::ListHandle_Path::iterator i
= rows
.begin();
1093 if (selection
->count_selected_rows() == 0 || _session
== 0) {
1097 for (; i
!= rows
.end(); ++i
) {
1100 if ((iter
= _model
->get_iter (*i
))) {
1102 /* some rows don't have a region associated with them, but can still be
1103 selected (XXX maybe prevent them from being selected)
1106 boost::shared_ptr
<Region
> r
= (*iter
)[_columns
.region
];
1117 EditorRegions::drag_data_received (const RefPtr
<Gdk::DragContext
>& context
,
1119 const SelectionData
& data
,
1120 guint info
, guint time
)
1122 vector
<ustring
> paths
;
1124 if (data
.get_target() == "GTK_TREE_MODEL_ROW") {
1125 /* something is being dragged over the region list */
1126 _editor
->_drags
->abort ();
1127 _display
.on_drag_data_received (context
, x
, y
, data
, info
, time
);
1131 if (_editor
->convert_drop_to_paths (paths
, context
, x
, y
, data
, info
, time
) == 0) {
1132 nframes64_t pos
= 0;
1133 if (Profile
->get_sae() || Config
->get_only_copy_imported_files()) {
1134 _editor
->do_import (paths
, Editing::ImportDistinctFiles
, Editing::ImportAsRegion
, SrcBest
, pos
);
1136 _editor
->do_embed (paths
, Editing::ImportDistinctFiles
, ImportAsRegion
, pos
);
1138 context
->drag_finish (true, false, time
);
1143 EditorRegions::selection_filter (const RefPtr
<TreeModel
>& model
, const TreeModel::Path
& path
, bool /*yn*/)
1145 /* not possible to select rows that do not represent regions, like "Hidden" */
1147 TreeModel::iterator iter
= model
->get_iter (path
);
1150 boost::shared_ptr
<Region
> r
=(*iter
)[_columns
.region
];
1160 EditorRegions::name_edit (const Glib::ustring
& path
, const Glib::ustring
& new_text
)
1162 boost::shared_ptr
<Region
> region
;
1165 if ((iter
= _model
->get_iter (path
))) {
1166 region
= (*iter
)[_columns
.region
];
1167 (*iter
)[_columns
.name
] = new_text
;
1170 /* now mapover everything */
1173 vector
<RegionView
*> equivalents
;
1174 _editor
->get_regions_corresponding_to (region
, equivalents
);
1176 for (vector
<RegionView
*>::iterator i
= equivalents
.begin(); i
!= equivalents
.end(); ++i
) {
1177 if (new_text
!= (*i
)->region()->name()) {
1178 (*i
)->region()->set_name (new_text
);
1185 /** @return Region that has been dragged out of the list, or 0 */
1186 boost::shared_ptr
<Region
>
1187 EditorRegions::get_dragged_region ()
1189 list
<boost::shared_ptr
<Region
> > regions
;
1191 _display
.get_object_drag_data (regions
, &source
);
1193 if (regions
.empty()) {
1194 return boost::shared_ptr
<Region
> ();
1197 assert (regions
.size() == 1);
1198 return regions
.front ();
1202 EditorRegions::clear ()
1204 _display
.set_model (Glib::RefPtr
<Gtk::TreeStore
> (0));
1206 _display
.set_model (_model
);
1209 boost::shared_ptr
<Region
>
1210 EditorRegions::get_single_selection ()
1212 Glib::RefPtr
<TreeSelection
> selected
= _display
.get_selection();
1214 if (selected
->count_selected_rows() != 1) {
1215 return boost::shared_ptr
<Region
> ();
1218 TreeView::Selection::ListHandle_Path rows
= selected
->get_selected_rows ();
1220 /* only one row selected, so rows.begin() is it */
1222 TreeIter iter
= _model
->get_iter (*rows
.begin());
1225 return boost::shared_ptr
<Region
> ();
1228 return (*iter
)[_columns
.region
];
1232 EditorRegions::locked_changed (Glib::ustring
const & path
)
1234 TreeIter i
= _model
->get_iter (path
);
1236 boost::shared_ptr
<ARDOUR::Region
> region
= (*i
)[_columns
.region
];
1238 region
->set_locked (!(*i
)[_columns
.locked
]);
1244 EditorRegions::glued_changed (Glib::ustring
const & path
)
1246 TreeIter i
= _model
->get_iter (path
);
1248 boost::shared_ptr
<ARDOUR::Region
> region
= (*i
)[_columns
.region
];
1250 /* `glued' means MusicTime, and we're toggling here */
1251 region
->set_position_lock_style ((*i
)[_columns
.glued
] ? AudioTime
: MusicTime
);
1258 EditorRegions::muted_changed (Glib::ustring
const & path
)
1260 TreeIter i
= _model
->get_iter (path
);
1262 boost::shared_ptr
<ARDOUR::Region
> region
= (*i
)[_columns
.region
];
1264 region
->set_muted (!(*i
)[_columns
.muted
]);
1271 EditorRegions::opaque_changed (Glib::ustring
const & path
)
1273 TreeIter i
= _model
->get_iter (path
);
1275 boost::shared_ptr
<ARDOUR::Region
> region
= (*i
)[_columns
.region
];
1277 region
->set_opaque (!(*i
)[_columns
.opaque
]);