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/session_region.h>
31 #include <gtkmm2ext/stop_signal.h>
35 #include "ardour_ui.h"
36 #include "gui_thread.h"
42 using namespace ARDOUR;
44 using namespace Editing;
45 using namespace ActionManager;
47 #define wave_cursor_width 43
48 #define wave_cursor_height 61
49 #define wave_cursor_x_hot 0
50 #define wave_cursor_y_hot 25
51 static const gchar wave_cursor_bits[] = {
52 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
54 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
56 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
58 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
60 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff,
62 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
64 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
66 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
68 0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x00, 0x04, 0x00,
70 0x02, 0x04, 0x00, 0x04, 0x00, 0x02, 0x02, 0x0c, 0x08, 0x0c, 0x00,
72 0x02, 0x1c, 0x08, 0x0c, 0x00, 0x02, 0x02, 0x1c, 0x08, 0x0c, 0x04,
74 0x02, 0x3c, 0x18, 0x0c, 0x04, 0x02, 0x02, 0x7c, 0x18, 0x1c, 0x0c,
76 0x82, 0xfc, 0x38, 0x1c, 0x0c, 0x02, 0xc2, 0xfc, 0x78, 0x3c, 0x1c,
78 0xe2, 0xfd, 0xf9, 0x7d, 0x1c, 0x02, 0xf2, 0xff, 0xfb, 0xff, 0x1c,
80 0xfa, 0xff, 0xfb, 0xff, 0x3f, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff,
82 0xfe, 0xff, 0xff, 0xff, 0xff, 0x03, 0xfa, 0xff, 0xff, 0xff, 0x3f,
84 0xf2, 0xff, 0xfb, 0xfd, 0x3c, 0x02, 0xe2, 0xfd, 0x7b, 0x7c, 0x1c,
86 0xc2, 0xfc, 0x39, 0x3c, 0x1c, 0x02, 0x82, 0xfc, 0x18, 0x1c, 0x1c,
88 0x02, 0xfc, 0x18, 0x1c, 0x0c, 0x02, 0x02, 0x7c, 0x18, 0x0c, 0x0c,
90 0x02, 0x3c, 0x08, 0x0c, 0x04, 0x02, 0x02, 0x1c, 0x08, 0x0c, 0x04,
92 0x02, 0x1c, 0x08, 0x0c, 0x00, 0x02, 0x02, 0x0c, 0x00, 0x04, 0x00,
94 0x02, 0x04, 0x00, 0x04, 0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00,
96 0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
98 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
100 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
102 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff,
104 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
106 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
108 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
110 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
114 #define wave_cursor_mask_width 43
115 #define wave_cursor_mask_height 61
116 #define wave_cursor_mask_x_hot 0
117 #define wave_cursor_mask_y_hot 25
118 static const gchar wave_cursor_mask_bits[] = {
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
137 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x08, 0x0c, 0x00,
139 0x00, 0x1c, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x08, 0x0c, 0x04,
141 0x00, 0x3c, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x7c, 0x18, 0x1c, 0x0c,
143 0x80, 0xfc, 0x38, 0x1c, 0x0c, 0x00, 0xc0, 0xfc, 0x78, 0x3c, 0x1c,
145 0xe0, 0xfd, 0xf9, 0x7d, 0x1c, 0x00, 0xf0, 0xff, 0xfb, 0xff, 0x1c,
147 0xf8, 0xff, 0xfb, 0xff, 0x3f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
149 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, 0xff, 0xff, 0xff, 0x3f,
151 0xf0, 0xff, 0xfb, 0xfd, 0x3c, 0x00, 0xe0, 0xfd, 0x7b, 0x7c, 0x1c,
153 0xc0, 0xfc, 0x39, 0x3c, 0x1c, 0x00, 0x80, 0xfc, 0x18, 0x1c, 0x1c,
155 0x00, 0xfc, 0x18, 0x1c, 0x0c, 0x00, 0x00, 0x7c, 0x18, 0x0c, 0x0c,
157 0x00, 0x3c, 0x08, 0x0c, 0x04, 0x00, 0x00, 0x1c, 0x08, 0x0c, 0x04,
159 0x00, 0x1c, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00,
161 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
163 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
181 GdkCursor *wave_cursor = 0;
184 Editor::handle_audio_region_removed (AudioRegion* ignored)
186 redisplay_regions ();
190 Editor::handle_new_audio_region (AudioRegion *region)
192 /* don't copy region - the one we are being notified
193 about belongs to the session, and so it will
196 add_audio_region_to_region_display (region);
200 Editor::region_hidden (Region* r)
202 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::region_hidden), r));
204 redisplay_regions ();
208 Editor::add_audio_region_to_region_display (AudioRegion *region)
213 if (!show_automatic_regions_in_region_list && region->automatic()) {
217 if (region->hidden()) {
219 TreeModel::iterator iter = region_list_model->get_iter (_("/Hidden"));
220 TreeModel::Row parent;
221 TreeModel::Row child;
223 if (iter == region_list_model->children().end()) {
225 parent = *(region_list_model->append());
227 parent[region_list_columns.name] = _("Hidden");
228 parent[region_list_columns.region] = 0;
233 row = *(region_list_model->append (parent.children()));
235 } else if (region->whole_file()) {
237 TreeModel::Row row = *(region_list_model->append());
239 if (region->source().name()[0] == '/') { // external file
241 if (region->whole_file()) {
243 str += PBD::basename_nosuffix (region->source().name());
245 str = region->name();
250 str = region->name();
254 row[region_list_columns.name] = str;
255 row[region_list_columns.region] = region;
261 /* find parent node, add as new child */
263 TreeModel::iterator i;
264 TreeModel::Children rows = region_list_model->children();
266 for (i = rows.begin(); i != rows.end(); ++i) {
268 Region* rr = (*i)[region_list_columns.region];
269 AudioRegion* r = dynamic_cast<AudioRegion*>(rr);
271 if (r && r->whole_file()) {
273 if (region->source_equivalent (*r)) {
274 row = *(region_list_model->append ((*i).children()));
280 if (i == rows.end()) {
281 TreeModel::Row row = *(region_list_model->append());
287 row[region_list_columns.region] = region;
289 if (region->n_channels() > 1) {
290 row[region_list_columns.name] = string_compose("%1 [%2]", region->name(), region->n_channels());
292 row[region_list_columns.name] = region->name();
297 Editor::region_list_selection_changed()
301 if (region_list_display.get_selection()->count_selected_rows() > 0) {
307 for (vector<Glib::RefPtr<Gtk::Action> >::iterator i = region_list_selection_sensitive_actions.begin(); i != region_list_selection_sensitive_actions.end(); ++i) {
308 (*i)->set_sensitive (sensitive);
312 // set_selected_regionview_from_region_list (*region, false);
317 Editor::insert_into_tmp_audio_regionlist(AudioRegion* region)
319 /* keep all whole files at the beginning */
321 if (region->whole_file()) {
322 tmp_audio_region_list.push_front (region);
324 tmp_audio_region_list.push_back (region);
329 Editor::redisplay_regions ()
333 region_list_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
334 region_list_model.clear ();
336 /* now add everything we have, via a temporary list used to help with
340 tmp_audio_region_list.clear();
341 session->foreach_audio_region (this, &Editor::insert_into_tmp_audio_regionlist);
343 for (list<AudioRegion*>::iterator r = tmp_audio_region_list.begin(); r != tmp_audio_region_list.end(); ++r) {
344 add_audio_region_to_region_display (*r);
347 region_list_display.set_model (region_list_sort_model);
352 Editor::region_list_clear ()
354 region_list_model->clear();
358 Editor::build_region_list_menu ()
360 region_list_menu = dynamic_cast<Menu*>(ui_manager->get_widget ("/RegionListMenu"));
362 /* now grab specific menu items that we need */
364 toggle_full_region_list_action = ui_manager->get_action ("<Actions>/RegionList/rlShowAll");
366 region_list_selection_sensitive_actions.push_back (ui_manager->get_action ("<Actions>/RegionList/rlHide"));
367 region_list_selection_sensitive_actions.push_back (ui_manager->get_action ("<Actions>/RegionList/rlAudition"));
368 region_list_selection_sensitive_actions.push_back (ui_manager->get_action ("<Actions>/RegionList/rlRemove"));
370 session_sensitive_actions.push_back (ui_manager->get_action ("<Actions>/RegionList/rlEmbedAudio"));
371 session_sensitive_actions.push_back (ui_manager->get_action ("<Actions>/RegionList/rlImportAudio"));
375 Editor::toggle_show_auto_regions ()
377 //show_automatic_regions_in_region_list = toggle_auto_regions_item->get_active();
378 show_automatic_regions_in_region_list = true;
379 redisplay_regions ();
383 Editor::toggle_full_region_list ()
385 if (toggle_full_region_list_item->get_active()) {
386 region_list_display.expand_all ();
388 region_list_display.collapse_all ();
393 Editor::region_list_display_key_press (GdkEventKey* ev)
399 Editor::region_list_display_key_release (GdkEventKey* ev)
401 switch (ev->keyval) {
403 remove_region_from_region_list ();
414 Editor::region_list_display_button_press (GdkEventButton *ev)
418 TreeModel::Path path;
419 TreeViewColumn* column;
423 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
424 if ((iter = region_list_model->get_iter (path))) {
425 region = (*iter)[region_list_columns.region];
433 if (Keyboard::is_delete_event (ev)) {
434 session->remove_region_from_region_list (*region);
438 if (Keyboard::is_context_menu_event (ev)) {
439 if (region_list_menu == 0) {
440 build_region_list_menu ();
442 region_list_menu->popup (ev->button, ev->time);
446 switch (ev->button) {
448 /* audition on double click */
449 if (ev->type == GDK_2BUTTON_PRESS) {
450 consider_auditioning (*region);
457 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
458 consider_auditioning (*region);
471 Editor::region_list_display_button_release (GdkEventButton *ev)
474 TreeModel::Path path;
475 TreeViewColumn* column;
480 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
481 if ((iter = region_list_model->get_iter (path))) {
482 region = (*iter)[region_list_columns.region];
486 if (Keyboard::is_delete_event (ev)) {
487 session->remove_region_from_region_list (*region);
491 switch (ev->button) {
508 Editor::consider_auditioning (Region& region)
510 AudioRegion* r = dynamic_cast<AudioRegion*> (®ion);
513 session->cancel_audition ();
517 if (session->is_auditioning()) {
518 session->cancel_audition ();
519 if (r == last_audition_region) {
524 session->audition_region (*r);
525 last_audition_region = r;
529 Editor::region_list_sorter (TreeModel::iterator a, TreeModel::iterator b)
533 Region* r1 = (*a)[region_list_columns.region];
534 Region* r2 = (*b)[region_list_columns.region];
536 AudioRegion* region1 = dynamic_cast<AudioRegion*> (r1);
537 AudioRegion* region2 = dynamic_cast<AudioRegion*> (r2);
539 if (region1 == 0 || region2 == 0) {
542 switch (region_list_sort_type) {
544 s1 = (*a)[region_list_columns.name];
545 s2 = (*b)[region_list_columns.name];
546 return (s1.compare (s2));
552 switch (region_list_sort_type) {
554 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
558 cmp = region1->length() - region2->length();
562 cmp = region1->position() - region2->position();
566 cmp = region1->source().timestamp() - region2->source().timestamp();
570 cmp = region1->start() - region2->start();
574 cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
577 case BySourceFileName:
578 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
581 case BySourceFileLength:
582 cmp = region1->source().length() - region2->source().length();
585 case BySourceFileCreationDate:
586 cmp = region1->source().timestamp() - region2->source().timestamp();
590 if (region1->source().name() == region2->source().name()) {
591 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
593 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
600 } else if (cmp > 0) {
608 Editor::reset_region_list_sort_type (RegionListSortType type)
610 if (type != region_list_sort_type) {
611 region_list_sort_type = type;
615 region_list_display.get_column (0)->set_title (_("Regions/name"));
619 region_list_display.get_column (0)->set_title (_("Regions/length"));
623 region_list_display.get_column (0)->set_title (_("Regions/position"));
627 region_list_display.get_column (0)->set_title (_("Regions/creation"));
631 region_list_display.get_column (0)->set_title (_("Regions/start"));
635 region_list_display.get_column (0)->set_title (_("Regions/end"));
638 case BySourceFileName:
639 region_list_display.get_column (0)->set_title (_("Regions/file name"));
642 case BySourceFileLength:
643 region_list_display.get_column (0)->set_title (_("Regions/file size"));
646 case BySourceFileCreationDate:
647 region_list_display.get_column (0)->set_title (_("Regions/file date"));
651 region_list_display.get_column (0)->set_title (_("Regions/file system"));
655 region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
660 Editor::reset_region_list_sort_direction (bool up)
663 //region_list_display.set_sort_type (up ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING);
664 /* reset to force resort */
665 region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
669 Editor::region_list_selection_mapover (slot<void,Region&> sl)
671 Glib::RefPtr<TreeSelection> selection = region_list_display.get_selection();
672 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
673 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
675 if (selection->count_selected_rows() == 0 || session == 0) {
679 for (; i != rows.end(); ++i) {
682 if ((iter = region_list_model->get_iter (*i))) {
683 sl (*((*iter)[region_list_columns.region]));
689 Editor::hide_a_region (Region& r)
695 Editor::remove_a_region (Region& r)
697 session->remove_region_from_region_list (r);
701 Editor::audition_region_from_region_list ()
703 region_list_selection_mapover (mem_fun (*this, &Editor::consider_auditioning));
707 Editor::hide_region_from_region_list ()
709 region_list_selection_mapover (mem_fun (*this, &Editor::hide_a_region));
713 Editor::remove_region_from_region_list ()
715 region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region));
719 Editor::region_list_display_drag_data_received (GdkDragContext *context,
722 GtkSelectionData *data,
726 vector<string> paths;
728 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
729 do_embed_sndfiles (paths, false);
732 gtk_drag_finish (context, TRUE, FALSE, time);