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();
264 bool found_parent = false;
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()));
282 row = *(region_list_model->append());
288 row[region_list_columns.region] = region;
290 if (region->n_channels() > 1) {
291 row[region_list_columns.name] = string_compose("%1 [%2]", region->name(), region->n_channels());
293 row[region_list_columns.name] = region->name();
298 Editor::region_list_selection_changed()
302 if (region_list_display.get_selection()->count_selected_rows() > 0) {
308 for (vector<Glib::RefPtr<Gtk::Action> >::iterator i = ActionManager::region_list_selection_sensitive_actions.begin(); i != ActionManager::region_list_selection_sensitive_actions.end(); ++i) {
309 (*i)->set_sensitive (sensitive);
313 // set_selected_regionview_from_region_list (*region, false);
318 Editor::insert_into_tmp_audio_regionlist(AudioRegion* region)
320 /* keep all whole files at the beginning */
322 if (region->whole_file()) {
323 tmp_audio_region_list.push_front (region);
325 tmp_audio_region_list.push_back (region);
330 Editor::redisplay_regions ()
334 region_list_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
335 region_list_model->clear ();
337 /* now add everything we have, via a temporary list used to help with
341 tmp_audio_region_list.clear();
342 session->foreach_audio_region (this, &Editor::insert_into_tmp_audio_regionlist);
344 for (list<AudioRegion*>::iterator r = tmp_audio_region_list.begin(); r != tmp_audio_region_list.end(); ++r) {
345 add_audio_region_to_region_display (*r);
348 region_list_display.set_model (region_list_sort_model);
353 Editor::region_list_clear ()
355 region_list_model->clear();
359 Editor::build_region_list_menu ()
361 region_list_menu = dynamic_cast<Menu*>(ActionManager::get_widget ("/RegionListMenu"));
363 /* now grab specific menu items that we need */
365 toggle_full_region_list_action = ActionManager::get_action ("<Actions>/RegionList/rlShowAll");
369 Editor::toggle_show_auto_regions ()
371 //show_automatic_regions_in_region_list = toggle_auto_regions_item->get_active();
372 show_automatic_regions_in_region_list = true;
373 redisplay_regions ();
377 Editor::toggle_full_region_list ()
379 if (toggle_full_region_list_item->get_active()) {
380 region_list_display.expand_all ();
382 region_list_display.collapse_all ();
387 Editor::region_list_display_key_press (GdkEventKey* ev)
393 Editor::region_list_display_key_release (GdkEventKey* ev)
395 switch (ev->keyval) {
397 remove_region_from_region_list ();
408 Editor::region_list_display_button_press (GdkEventButton *ev)
412 TreeModel::Path path;
413 TreeViewColumn* column;
417 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
418 if ((iter = region_list_model->get_iter (path))) {
419 region = (*iter)[region_list_columns.region];
427 if (Keyboard::is_delete_event (ev)) {
428 session->remove_region_from_region_list (*region);
432 if (Keyboard::is_context_menu_event (ev)) {
433 if (region_list_menu == 0) {
434 build_region_list_menu ();
436 region_list_menu->popup (ev->button, ev->time);
440 switch (ev->button) {
442 /* audition on double click */
443 if (ev->type == GDK_2BUTTON_PRESS) {
444 consider_auditioning (*region);
451 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
452 consider_auditioning (*region);
465 Editor::region_list_display_button_release (GdkEventButton *ev)
468 TreeModel::Path path;
469 TreeViewColumn* column;
474 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
475 if ((iter = region_list_model->get_iter (path))) {
476 region = (*iter)[region_list_columns.region];
480 if (Keyboard::is_delete_event (ev)) {
481 session->remove_region_from_region_list (*region);
485 switch (ev->button) {
502 Editor::consider_auditioning (Region& region)
504 AudioRegion* r = dynamic_cast<AudioRegion*> (®ion);
507 session->cancel_audition ();
511 if (session->is_auditioning()) {
512 session->cancel_audition ();
513 if (r == last_audition_region) {
518 session->audition_region (*r);
519 last_audition_region = r;
523 Editor::region_list_sorter (TreeModel::iterator a, TreeModel::iterator b)
527 Region* r1 = (*a)[region_list_columns.region];
528 Region* r2 = (*b)[region_list_columns.region];
530 AudioRegion* region1 = dynamic_cast<AudioRegion*> (r1);
531 AudioRegion* region2 = dynamic_cast<AudioRegion*> (r2);
533 if (region1 == 0 || region2 == 0) {
536 switch (region_list_sort_type) {
538 s1 = (*a)[region_list_columns.name];
539 s2 = (*b)[region_list_columns.name];
540 return (s1.compare (s2));
546 switch (region_list_sort_type) {
548 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
552 cmp = region1->length() - region2->length();
556 cmp = region1->position() - region2->position();
560 cmp = region1->source().timestamp() - region2->source().timestamp();
564 cmp = region1->start() - region2->start();
568 cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
571 case BySourceFileName:
572 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
575 case BySourceFileLength:
576 cmp = region1->source().length() - region2->source().length();
579 case BySourceFileCreationDate:
580 cmp = region1->source().timestamp() - region2->source().timestamp();
584 if (region1->source().name() == region2->source().name()) {
585 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
587 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
594 } else if (cmp > 0) {
602 Editor::reset_region_list_sort_type (RegionListSortType type)
604 if (type != region_list_sort_type) {
605 region_list_sort_type = type;
609 region_list_display.get_column (0)->set_title (_("Regions/name"));
613 region_list_display.get_column (0)->set_title (_("Regions/length"));
617 region_list_display.get_column (0)->set_title (_("Regions/position"));
621 region_list_display.get_column (0)->set_title (_("Regions/creation"));
625 region_list_display.get_column (0)->set_title (_("Regions/start"));
629 region_list_display.get_column (0)->set_title (_("Regions/end"));
632 case BySourceFileName:
633 region_list_display.get_column (0)->set_title (_("Regions/file name"));
636 case BySourceFileLength:
637 region_list_display.get_column (0)->set_title (_("Regions/file size"));
640 case BySourceFileCreationDate:
641 region_list_display.get_column (0)->set_title (_("Regions/file date"));
645 region_list_display.get_column (0)->set_title (_("Regions/file system"));
649 region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
654 Editor::reset_region_list_sort_direction (bool up)
657 //region_list_display.set_sort_type (up ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING);
658 /* reset to force resort */
659 region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
663 Editor::region_list_selection_mapover (slot<void,Region&> sl)
665 Glib::RefPtr<TreeSelection> selection = region_list_display.get_selection();
666 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
667 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
669 if (selection->count_selected_rows() == 0 || session == 0) {
673 for (; i != rows.end(); ++i) {
676 if ((iter = region_list_model->get_iter (*i))) {
677 sl (*((*iter)[region_list_columns.region]));
683 Editor::hide_a_region (Region& r)
689 Editor::remove_a_region (Region& r)
691 session->remove_region_from_region_list (r);
695 Editor::audition_region_from_region_list ()
697 region_list_selection_mapover (mem_fun (*this, &Editor::consider_auditioning));
701 Editor::hide_region_from_region_list ()
703 region_list_selection_mapover (mem_fun (*this, &Editor::hide_a_region));
707 Editor::remove_region_from_region_list ()
709 region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region));
713 Editor::region_list_display_drag_data_received (GdkDragContext *context,
716 GtkSelectionData *data,
720 vector<string> paths;
722 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
723 do_embed_sndfiles (paths, false);
726 gtk_drag_finish (context, TRUE, FALSE, time);