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;
46 #define wave_cursor_width 43
47 #define wave_cursor_height 61
48 #define wave_cursor_x_hot 0
49 #define wave_cursor_y_hot 25
50 static const gchar wave_cursor_bits[] = {
51 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
55 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
57 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
59 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff,
61 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
63 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
65 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
67 0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x00, 0x04, 0x00,
69 0x02, 0x04, 0x00, 0x04, 0x00, 0x02, 0x02, 0x0c, 0x08, 0x0c, 0x00,
71 0x02, 0x1c, 0x08, 0x0c, 0x00, 0x02, 0x02, 0x1c, 0x08, 0x0c, 0x04,
73 0x02, 0x3c, 0x18, 0x0c, 0x04, 0x02, 0x02, 0x7c, 0x18, 0x1c, 0x0c,
75 0x82, 0xfc, 0x38, 0x1c, 0x0c, 0x02, 0xc2, 0xfc, 0x78, 0x3c, 0x1c,
77 0xe2, 0xfd, 0xf9, 0x7d, 0x1c, 0x02, 0xf2, 0xff, 0xfb, 0xff, 0x1c,
79 0xfa, 0xff, 0xfb, 0xff, 0x3f, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff,
81 0xfe, 0xff, 0xff, 0xff, 0xff, 0x03, 0xfa, 0xff, 0xff, 0xff, 0x3f,
83 0xf2, 0xff, 0xfb, 0xfd, 0x3c, 0x02, 0xe2, 0xfd, 0x7b, 0x7c, 0x1c,
85 0xc2, 0xfc, 0x39, 0x3c, 0x1c, 0x02, 0x82, 0xfc, 0x18, 0x1c, 0x1c,
87 0x02, 0xfc, 0x18, 0x1c, 0x0c, 0x02, 0x02, 0x7c, 0x18, 0x0c, 0x0c,
89 0x02, 0x3c, 0x08, 0x0c, 0x04, 0x02, 0x02, 0x1c, 0x08, 0x0c, 0x04,
91 0x02, 0x1c, 0x08, 0x0c, 0x00, 0x02, 0x02, 0x0c, 0x00, 0x04, 0x00,
93 0x02, 0x04, 0x00, 0x04, 0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00,
95 0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
97 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
99 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
101 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff,
103 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
105 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
107 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
109 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
113 #define wave_cursor_mask_width 43
114 #define wave_cursor_mask_height 61
115 #define wave_cursor_mask_x_hot 0
116 #define wave_cursor_mask_y_hot 25
117 static const gchar wave_cursor_mask_bits[] = {
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
136 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x08, 0x0c, 0x00,
138 0x00, 0x1c, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x08, 0x0c, 0x04,
140 0x00, 0x3c, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x7c, 0x18, 0x1c, 0x0c,
142 0x80, 0xfc, 0x38, 0x1c, 0x0c, 0x00, 0xc0, 0xfc, 0x78, 0x3c, 0x1c,
144 0xe0, 0xfd, 0xf9, 0x7d, 0x1c, 0x00, 0xf0, 0xff, 0xfb, 0xff, 0x1c,
146 0xf8, 0xff, 0xfb, 0xff, 0x3f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
148 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, 0xff, 0xff, 0xff, 0x3f,
150 0xf0, 0xff, 0xfb, 0xfd, 0x3c, 0x00, 0xe0, 0xfd, 0x7b, 0x7c, 0x1c,
152 0xc0, 0xfc, 0x39, 0x3c, 0x1c, 0x00, 0x80, 0xfc, 0x18, 0x1c, 0x1c,
154 0x00, 0xfc, 0x18, 0x1c, 0x0c, 0x00, 0x00, 0x7c, 0x18, 0x0c, 0x0c,
156 0x00, 0x3c, 0x08, 0x0c, 0x04, 0x00, 0x00, 0x1c, 0x08, 0x0c, 0x04,
158 0x00, 0x1c, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00,
160 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
162 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
180 GdkCursor *wave_cursor = 0;
183 Editor::handle_audio_region_removed (AudioRegion* ignored)
185 redisplay_regions ();
189 Editor::handle_new_audio_region (AudioRegion *region)
191 /* don't copy region - the one we are being notified
192 about belongs to the session, and so it will
195 add_audio_region_to_region_display (region);
199 Editor::region_hidden (Region* r)
201 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::region_hidden), r));
203 redisplay_regions ();
207 Editor::add_audio_region_to_region_display (AudioRegion *region)
212 if (!show_automatic_regions_in_region_list && region->automatic()) {
216 if (region->hidden()) {
218 TreeModel::iterator iter = region_list_model->get_iter (_("/Hidden"));
219 TreeModel::Row parent;
220 TreeModel::Row child;
222 if (iter == region_list_model->children().end()) {
224 parent = *(region_list_model->append());
226 parent[region_list_columns.name] = _("Hidden");
227 parent[region_list_columns.region] = 0;
232 row = *(region_list_model->append (parent.children()));
234 } else if (region->whole_file()) {
236 TreeModel::Row row = *(region_list_model->append());
238 if (region->source().name()[0] == '/') { // external file
240 if (region->whole_file()) {
242 str += PBD::basename_nosuffix (region->source().name());
244 str = region->name();
249 str = region->name();
253 row[region_list_columns.name] = str;
254 row[region_list_columns.region] = region;
260 /* find parent node, add as new child */
262 TreeModel::iterator i;
263 TreeModel::Children rows = region_list_model->children();
265 for (i = rows.begin(); i != rows.end(); ++i) {
267 Region* rr = (*i)[region_list_columns.region];
268 AudioRegion* r = dynamic_cast<AudioRegion*>(rr);
270 if (r && r->whole_file()) {
272 if (region->source_equivalent (*r)) {
273 row = *(region_list_model->append ((*i).children()));
279 if (i == rows.end()) {
280 TreeModel::Row row = *(region_list_model->append());
286 row[region_list_columns.region] = region;
288 if (region->n_channels() > 1) {
289 row[region_list_columns.name] = string_compose("%1 [%2]", region->name(), region->n_channels());
291 row[region_list_columns.name] = region->name();
296 Editor::region_list_selection_changed()
300 if (region_list_display.get_selection()->count_selected_rows() > 0) {
306 for (vector<Glib::RefPtr<Gtk::Action> >::iterator i = ActionManager::region_list_selection_sensitive_actions.begin(); i != ActionManager::region_list_selection_sensitive_actions.end(); ++i) {
307 (*i)->set_sensitive (sensitive);
311 // set_selected_regionview_from_region_list (*region, false);
316 Editor::insert_into_tmp_audio_regionlist(AudioRegion* region)
318 /* keep all whole files at the beginning */
320 if (region->whole_file()) {
321 tmp_audio_region_list.push_front (region);
323 tmp_audio_region_list.push_back (region);
328 Editor::redisplay_regions ()
332 region_list_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
333 region_list_model.clear ();
335 /* now add everything we have, via a temporary list used to help with
339 tmp_audio_region_list.clear();
340 session->foreach_audio_region (this, &Editor::insert_into_tmp_audio_regionlist);
342 for (list<AudioRegion*>::iterator r = tmp_audio_region_list.begin(); r != tmp_audio_region_list.end(); ++r) {
343 add_audio_region_to_region_display (*r);
346 region_list_display.set_model (region_list_sort_model);
351 Editor::region_list_clear ()
353 region_list_model->clear();
357 Editor::build_region_list_menu ()
359 region_list_menu = dynamic_cast<Menu*>(ActionManager::get_widget ("/RegionListMenu"));
361 /* now grab specific menu items that we need */
363 toggle_full_region_list_action = ActionManager::get_action ("<Actions>/RegionList/rlShowAll");
367 Editor::toggle_show_auto_regions ()
369 //show_automatic_regions_in_region_list = toggle_auto_regions_item->get_active();
370 show_automatic_regions_in_region_list = true;
371 redisplay_regions ();
375 Editor::toggle_full_region_list ()
377 if (toggle_full_region_list_item->get_active()) {
378 region_list_display.expand_all ();
380 region_list_display.collapse_all ();
385 Editor::region_list_display_key_press (GdkEventKey* ev)
391 Editor::region_list_display_key_release (GdkEventKey* ev)
393 switch (ev->keyval) {
395 remove_region_from_region_list ();
406 Editor::region_list_display_button_press (GdkEventButton *ev)
410 TreeModel::Path path;
411 TreeViewColumn* column;
415 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
416 if ((iter = region_list_model->get_iter (path))) {
417 region = (*iter)[region_list_columns.region];
425 if (Keyboard::is_delete_event (ev)) {
426 session->remove_region_from_region_list (*region);
430 if (Keyboard::is_context_menu_event (ev)) {
431 if (region_list_menu == 0) {
432 build_region_list_menu ();
434 region_list_menu->popup (ev->button, ev->time);
438 switch (ev->button) {
440 /* audition on double click */
441 if (ev->type == GDK_2BUTTON_PRESS) {
442 consider_auditioning (*region);
449 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
450 consider_auditioning (*region);
463 Editor::region_list_display_button_release (GdkEventButton *ev)
466 TreeModel::Path path;
467 TreeViewColumn* column;
472 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
473 if ((iter = region_list_model->get_iter (path))) {
474 region = (*iter)[region_list_columns.region];
478 if (Keyboard::is_delete_event (ev)) {
479 session->remove_region_from_region_list (*region);
483 switch (ev->button) {
500 Editor::consider_auditioning (Region& region)
502 AudioRegion* r = dynamic_cast<AudioRegion*> (®ion);
505 session->cancel_audition ();
509 if (session->is_auditioning()) {
510 session->cancel_audition ();
511 if (r == last_audition_region) {
516 session->audition_region (*r);
517 last_audition_region = r;
521 Editor::region_list_sorter (TreeModel::iterator a, TreeModel::iterator b)
525 Region* r1 = (*a)[region_list_columns.region];
526 Region* r2 = (*b)[region_list_columns.region];
528 AudioRegion* region1 = dynamic_cast<AudioRegion*> (r1);
529 AudioRegion* region2 = dynamic_cast<AudioRegion*> (r2);
531 if (region1 == 0 || region2 == 0) {
534 switch (region_list_sort_type) {
536 s1 = (*a)[region_list_columns.name];
537 s2 = (*b)[region_list_columns.name];
538 return (s1.compare (s2));
544 switch (region_list_sort_type) {
546 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
550 cmp = region1->length() - region2->length();
554 cmp = region1->position() - region2->position();
558 cmp = region1->source().timestamp() - region2->source().timestamp();
562 cmp = region1->start() - region2->start();
566 cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
569 case BySourceFileName:
570 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
573 case BySourceFileLength:
574 cmp = region1->source().length() - region2->source().length();
577 case BySourceFileCreationDate:
578 cmp = region1->source().timestamp() - region2->source().timestamp();
582 if (region1->source().name() == region2->source().name()) {
583 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
585 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
592 } else if (cmp > 0) {
600 Editor::reset_region_list_sort_type (RegionListSortType type)
602 if (type != region_list_sort_type) {
603 region_list_sort_type = type;
607 region_list_display.get_column (0)->set_title (_("Regions/name"));
611 region_list_display.get_column (0)->set_title (_("Regions/length"));
615 region_list_display.get_column (0)->set_title (_("Regions/position"));
619 region_list_display.get_column (0)->set_title (_("Regions/creation"));
623 region_list_display.get_column (0)->set_title (_("Regions/start"));
627 region_list_display.get_column (0)->set_title (_("Regions/end"));
630 case BySourceFileName:
631 region_list_display.get_column (0)->set_title (_("Regions/file name"));
634 case BySourceFileLength:
635 region_list_display.get_column (0)->set_title (_("Regions/file size"));
638 case BySourceFileCreationDate:
639 region_list_display.get_column (0)->set_title (_("Regions/file date"));
643 region_list_display.get_column (0)->set_title (_("Regions/file system"));
647 region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
652 Editor::reset_region_list_sort_direction (bool up)
655 //region_list_display.set_sort_type (up ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING);
656 /* reset to force resort */
657 region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
661 Editor::region_list_selection_mapover (slot<void,Region&> sl)
663 Glib::RefPtr<TreeSelection> selection = region_list_display.get_selection();
664 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
665 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
667 if (selection->count_selected_rows() == 0 || session == 0) {
671 for (; i != rows.end(); ++i) {
674 if ((iter = region_list_model->get_iter (*i))) {
675 sl (*((*iter)[region_list_columns.region]));
681 Editor::hide_a_region (Region& r)
687 Editor::remove_a_region (Region& r)
689 session->remove_region_from_region_list (r);
693 Editor::audition_region_from_region_list ()
695 region_list_selection_mapover (mem_fun (*this, &Editor::consider_auditioning));
699 Editor::hide_region_from_region_list ()
701 region_list_selection_mapover (mem_fun (*this, &Editor::hide_a_region));
705 Editor::remove_region_from_region_list ()
707 region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region));
711 Editor::region_list_display_drag_data_received (GdkDragContext *context,
714 GtkSelectionData *data,
718 vector<string> paths;
720 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
721 do_embed_sndfiles (paths, false);
724 gtk_drag_finish (context, TRUE, FALSE, time);