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"
48 using namespace ARDOUR
;
52 using namespace Editing
;
55 Editor::handle_audio_region_removed (boost::weak_ptr
<AudioRegion
> wregion
)
57 ENSURE_GUI_THREAD (mem_fun (*this, &Editor::redisplay_regions
));
62 Editor::handle_new_audio_regions (vector
<boost::weak_ptr
<AudioRegion
> >& v
)
64 ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::handle_new_audio_regions
), v
));
65 add_audio_regions_to_region_display (v
);
69 Editor::region_hidden (boost::shared_ptr
<Region
> r
)
71 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::region_hidden
), r
));
77 Editor::add_audio_regions_to_region_display (vector
<boost::weak_ptr
<AudioRegion
> >& regions
)
79 region_list_display
.set_model (Glib::RefPtr
<Gtk::TreeStore
>(0));
80 for (vector
<boost::weak_ptr
<AudioRegion
> >::iterator x
= regions
.begin(); x
!= regions
.end(); ++x
) {
81 boost::shared_ptr
<AudioRegion
> region ((*x
).lock());
83 add_audio_region_to_region_display (region
);
86 region_list_display
.set_model (region_list_model
);
90 Editor::add_audio_region_to_region_display (boost::shared_ptr
<AudioRegion
> region
)
97 missing_source
= boost::dynamic_pointer_cast
<SilentFileSource
>(region
->source());
99 if (!show_automatic_regions_in_region_list
&& region
->automatic()) {
103 if (region
->hidden()) {
105 TreeModel::iterator iter
= region_list_model
->get_iter ("0");
106 TreeModel::Row parent
;
107 TreeModel::Row child
;
111 parent
= *(region_list_model
->append());
113 parent
[region_list_columns
.name
] = _("Hidden");
117 if ((*iter
)[region_list_columns
.name
] != _("Hidden")) {
119 parent
= *(region_list_model
->insert(iter
));
120 parent
[region_list_columns
.name
] = _("Hidden");
128 row
= *(region_list_model
->append (parent
.children()));
130 } else if (region
->whole_file()) {
132 TreeModel::iterator i
;
133 TreeModel::Children rows
= region_list_model
->children();
135 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
137 boost::shared_ptr
<Region
> rr
= (*i
)[region_list_columns
.region
];
139 if (rr
&& region
->region_list_equivalent (rr
)) {
144 row
= *(region_list_model
->append());
145 if (missing_source
) {
146 c
.set_rgb(65535,0,0); // FIXME: error color from style
148 set_color(c
, rgba_from_style ("RegionListWholeFile", 0xff, 0, 0, 0, "fg", Gtk::STATE_NORMAL
, false ));
150 row
[region_list_columns
.color_
] = c
;
152 if (region
->source()->name()[0] == '/') { // external file
154 /* XXX there was old code here to try to show an abbreviated version
155 of the path name for whole file regions.
158 str
= region
->name();
162 str
= region
->name();
166 if (region
->n_channels() > 1) {
167 std::stringstream foo
;
168 foo
<< region
->n_channels ();
174 if (missing_source
) {
175 str
+= _(" (MISSING)");
178 row
[region_list_columns
.name
] = str
;
179 row
[region_list_columns
.region
] = region
;
185 /* find parent node, add as new child */
187 TreeModel::iterator i
;
188 TreeModel::Children rows
= region_list_model
->children();
189 bool found_parent
= false;
191 for (i
= rows
.begin(); i
!= rows
.end(); ++i
) {
193 boost::shared_ptr
<Region
> rr
= (*i
)[region_list_columns
.region
];
194 boost::shared_ptr
<AudioRegion
> r
= boost::dynamic_pointer_cast
<AudioRegion
>(rr
);
196 if (r
&& r
->whole_file()) {
197 if (region
->source_equivalent (r
)) {
198 row
= *(region_list_model
->append ((*i
).children()));
204 TreeModel::iterator ii
;
205 TreeModel::Children subrows
= (*i
).children();
207 for (ii
= subrows
.begin(); ii
!= subrows
.end(); ++ii
) {
209 boost::shared_ptr
<Region
> rrr
= (*ii
)[region_list_columns
.region
];
211 if (region
->region_list_equivalent (rrr
)) {
218 row
= *(region_list_model
->append());
224 row
[region_list_columns
.region
] = region
;
226 if (region
->n_channels() > 1) {
227 row
[region_list_columns
.name
] = string_compose("%1 [%2]", region
->name(), region
->n_channels());
229 row
[region_list_columns
.name
] = region
->name();
234 Editor::region_list_selection_changed()
238 if (region_list_display
.get_selection()->count_selected_rows() > 0) {
245 TreeView::Selection::ListHandle_Path rows
= region_list_display
.get_selection()->get_selected_rows ();
246 TreeView::Selection::ListHandle_Path::iterator i
= rows
.begin();
249 if ((iter
= region_list_model
->get_iter (*i
))) {
250 boost::shared_ptr
<Region
> r
= (*iter
)[region_list_columns
.region
];
252 /* they could have clicked on a row that is just a placeholder, like "Hidden" */
256 /* just set the first selected region (in fact, the selection model might be SINGLE, which
257 means there can only be one.
260 set_selected_regionview_from_region_list (r
, Selection::Set
);
267 Editor::insert_into_tmp_audio_regionlist(boost::shared_ptr
<AudioRegion
> region
)
269 /* keep all whole files at the beginning */
271 if (region
->whole_file()) {
272 tmp_audio_region_list
.push_front (region
);
274 tmp_audio_region_list
.push_back (region
);
279 Editor::redisplay_regions ()
281 if (no_region_list_redisplay
) {
287 region_list_display
.set_model (Glib::RefPtr
<Gtk::TreeStore
>(0));
288 region_list_model
->clear ();
290 /* now add everything we have, via a temporary list used to help with
294 tmp_audio_region_list
.clear();
295 session
->foreach_audio_region (this, &Editor::insert_into_tmp_audio_regionlist
);
297 for (list
<boost::shared_ptr
<AudioRegion
> >::iterator r
= tmp_audio_region_list
.begin(); r
!= tmp_audio_region_list
.end(); ++r
) {
298 add_audio_region_to_region_display (*r
);
300 tmp_audio_region_list
.clear();
302 region_list_display
.set_model (region_list_model
);
307 Editor::build_region_list_menu ()
309 region_list_menu
= dynamic_cast<Menu
*>(ActionManager::get_widget ("/RegionListMenu"));
311 /* now grab specific menu items that we need */
313 Glib::RefPtr
<Action
> act
;
315 act
= ActionManager::get_action (X_("RegionList"), X_("rlShowAll"));
317 toggle_full_region_list_action
= Glib::RefPtr
<ToggleAction
>::cast_dynamic (act
);
320 act
= ActionManager::get_action (X_("RegionList"), X_("rlShowAuto"));
322 toggle_show_auto_regions_action
= Glib::RefPtr
<ToggleAction
>::cast_dynamic (act
);
327 Editor::toggle_show_auto_regions ()
329 show_automatic_regions_in_region_list
= toggle_show_auto_regions_action
->get_active();
330 redisplay_regions ();
334 Editor::toggle_full_region_list ()
336 if (toggle_full_region_list_action
->get_active()) {
337 region_list_display
.expand_all ();
339 region_list_display
.collapse_all ();
344 Editor::show_region_list_display_context_menu (int button
, int time
)
346 if (region_list_menu
== 0) {
347 build_region_list_menu ();
350 if (region_list_display
.get_selection()->count_selected_rows() > 0) {
351 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions
, true);
353 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions
, false);
356 region_list_menu
->popup (button
, time
);
360 Editor::region_list_display_key_press (GdkEventKey
* ev
)
366 Editor::region_list_display_key_release (GdkEventKey
* ev
)
368 switch (ev
->keyval
) {
370 remove_region_from_region_list ();
381 Editor::region_list_display_button_press (GdkEventButton
*ev
)
383 boost::shared_ptr
<Region
> region
;
385 TreeModel::Path path
;
386 TreeViewColumn
* column
;
390 if (region_list_display
.get_path_at_pos ((int)ev
->x
, (int)ev
->y
, path
, column
, cellx
, celly
)) {
391 if ((iter
= region_list_model
->get_iter (path
))) {
392 region
= (*iter
)[region_list_columns
.region
];
396 if (Keyboard::is_context_menu_event (ev
)) {
397 show_region_list_display_context_menu (ev
->button
, ev
->time
);
401 if (region
!= 0 && Keyboard::is_button2_event (ev
)) {
402 // start/stop audition
403 if (!Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
404 consider_auditioning (region
);
413 Editor::region_list_display_button_release (GdkEventButton
*ev
)
416 TreeModel::Path path
;
417 TreeViewColumn
* column
;
420 boost::shared_ptr
<Region
> region
;
422 if (region_list_display
.get_path_at_pos ((int)ev
->x
, (int)ev
->y
, path
, column
, cellx
, celly
)) {
423 if ((iter
= region_list_model
->get_iter (path
))) {
424 region
= (*iter
)[region_list_columns
.region
];
428 if (region
&& Keyboard::is_delete_event (ev
)) {
429 session
->remove_region_from_region_list (region
);
437 Editor::consider_auditioning (boost::shared_ptr
<Region
> region
)
439 boost::shared_ptr
<AudioRegion
> r
= boost::dynamic_pointer_cast
<AudioRegion
> (region
);
442 session
->cancel_audition ();
446 if (session
->is_auditioning()) {
447 session
->cancel_audition ();
448 if (r
== last_audition_region
) {
453 session
->audition_region (r
);
454 last_audition_region
= r
;
458 Editor::region_list_sorter (TreeModel::iterator a
, TreeModel::iterator b
)
462 boost::shared_ptr
<Region
> r1
= (*a
)[region_list_columns
.region
];
463 boost::shared_ptr
<Region
> r2
= (*b
)[region_list_columns
.region
];
465 /* handle rows without regions, like "Hidden" */
475 boost::shared_ptr
<AudioRegion
> region1
= boost::dynamic_pointer_cast
<AudioRegion
> (r1
);
476 boost::shared_ptr
<AudioRegion
> region2
= boost::dynamic_pointer_cast
<AudioRegion
> (r2
);
478 if (region1
== 0 || region2
== 0) {
481 switch (region_list_sort_type
) {
483 s1
= (*a
)[region_list_columns
.name
];
484 s2
= (*b
)[region_list_columns
.name
];
485 return (s1
.compare (s2
));
491 switch (region_list_sort_type
) {
493 cmp
= strcasecmp (region1
->name().c_str(), region2
->name().c_str());
497 cmp
= region1
->length() - region2
->length();
501 cmp
= region1
->position() - region2
->position();
505 cmp
= region1
->source()->timestamp() - region2
->source()->timestamp();
509 cmp
= region1
->start() - region2
->start();
513 cmp
= (region1
->start() + region1
->length()) - (region2
->start() + region2
->length());
516 case BySourceFileName
:
517 cmp
= strcasecmp (region1
->source()->name().c_str(), region2
->source()->name().c_str());
520 case BySourceFileLength
:
521 cmp
= region1
->source()->length() - region2
->source()->length();
524 case BySourceFileCreationDate
:
525 cmp
= region1
->source()->timestamp() - region2
->source()->timestamp();
529 if (region1
->source()->name() == region2
->source()->name()) {
530 cmp
= strcasecmp (region1
->name().c_str(), region2
->name().c_str());
532 cmp
= strcasecmp (region1
->source()->name().c_str(), region2
->source()->name().c_str());
539 } else if (cmp
> 0) {
547 Editor::reset_region_list_sort_type (RegionListSortType type
)
549 if (type
!= region_list_sort_type
) {
550 region_list_sort_type
= type
;
551 region_list_model
->set_sort_func (0, (mem_fun (*this, &Editor::region_list_sorter
)));
556 Editor::reset_region_list_sort_direction (bool up
)
558 region_list_model
->set_sort_column (0, up
? SORT_ASCENDING
: SORT_DESCENDING
);
562 Editor::region_list_selection_mapover (slot
<void,boost::shared_ptr
<Region
> > sl
)
564 Glib::RefPtr
<TreeSelection
> selection
= region_list_display
.get_selection();
565 TreeView::Selection::ListHandle_Path rows
= selection
->get_selected_rows ();
566 TreeView::Selection::ListHandle_Path::iterator i
= rows
.begin();
568 if (selection
->count_selected_rows() == 0 || session
== 0) {
572 for (; i
!= rows
.end(); ++i
) {
575 if ((iter
= region_list_model
->get_iter (*i
))) {
577 /* some rows don't have a region associated with them, but can still be
578 selected (XXX maybe prevent them from being selected)
581 boost::shared_ptr
<Region
> r
= (*iter
)[region_list_columns
.region
];
591 Editor::hide_a_region (boost::shared_ptr
<Region
> r
)
593 r
->set_hidden (true);
597 Editor::remove_a_region (boost::shared_ptr
<Region
> r
)
599 session
->remove_region_from_region_list (r
);
603 Editor::audition_region_from_region_list ()
605 region_list_selection_mapover (mem_fun (*this, &Editor::consider_auditioning
));
609 Editor::hide_region_from_region_list ()
611 region_list_selection_mapover (mem_fun (*this, &Editor::hide_a_region
));
615 Editor::remove_region_from_region_list ()
617 region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region
));
621 Editor::region_list_display_drag_data_received (const RefPtr
<Gdk::DragContext
>& context
,
623 const SelectionData
& data
,
624 guint info
, guint time
)
626 vector
<ustring
> paths
;
628 if (data
.get_target() == "GTK_TREE_MODEL_ROW") {
629 region_list_display
.on_drag_data_received (context
, x
, y
, data
, info
, time
);
633 if (convert_drop_to_paths (paths
, context
, x
, y
, data
, info
, time
) == 0) {
635 if (Profile
->get_sae() || Config
->get_only_copy_imported_files()) {
636 do_import (paths
, Editing::ImportDistinctFiles
, Editing::ImportAsRegion
, SrcBest
, pos
);
638 do_embed (paths
, Editing::ImportDistinctFiles
, ImportAsRegion
, pos
);
640 context
->drag_finish (true, false, time
);
645 Editor::region_list_selection_filter (const RefPtr
<TreeModel
>& model
, const TreeModel::Path
& path
, bool yn
)
647 /* not possible to select rows that do not represent regions, like "Hidden" */
649 TreeModel::iterator iter
= model
->get_iter (path
);
652 boost::shared_ptr
<Region
> r
=(*iter
)[region_list_columns
.region
];
662 Editor::region_name_edit (const Glib::ustring
& path
, const Glib::ustring
& new_text
)
664 boost::shared_ptr
<Region
> region
;
667 if ((iter
= region_list_model
->get_iter (path
))) {
668 region
= (*iter
)[region_list_columns
.region
];
669 (*iter
)[region_list_columns
.name
] = new_text
;
672 /* now mapover everything */
675 vector
<RegionView
*> equivalents
;
676 get_regions_corresponding_to (region
, equivalents
);
678 for (vector
<RegionView
*>::iterator i
= equivalents
.begin(); i
!= equivalents
.end(); ++i
) {
679 if (new_text
!= (*i
)->region()->name()) {
680 (*i
)->region()->set_name (new_text
);