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"
41 using namespace ARDOUR;
43 using namespace Editing;
45 #define wave_cursor_width 43
46 #define wave_cursor_height 61
47 #define wave_cursor_x_hot 0
48 #define wave_cursor_y_hot 25
49 static const gchar wave_cursor_bits[] = {
50 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
52 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
54 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
56 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
58 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff,
60 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
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, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x00, 0x04, 0x00,
68 0x02, 0x04, 0x00, 0x04, 0x00, 0x02, 0x02, 0x0c, 0x08, 0x0c, 0x00,
70 0x02, 0x1c, 0x08, 0x0c, 0x00, 0x02, 0x02, 0x1c, 0x08, 0x0c, 0x04,
72 0x02, 0x3c, 0x18, 0x0c, 0x04, 0x02, 0x02, 0x7c, 0x18, 0x1c, 0x0c,
74 0x82, 0xfc, 0x38, 0x1c, 0x0c, 0x02, 0xc2, 0xfc, 0x78, 0x3c, 0x1c,
76 0xe2, 0xfd, 0xf9, 0x7d, 0x1c, 0x02, 0xf2, 0xff, 0xfb, 0xff, 0x1c,
78 0xfa, 0xff, 0xfb, 0xff, 0x3f, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff,
80 0xfe, 0xff, 0xff, 0xff, 0xff, 0x03, 0xfa, 0xff, 0xff, 0xff, 0x3f,
82 0xf2, 0xff, 0xfb, 0xfd, 0x3c, 0x02, 0xe2, 0xfd, 0x7b, 0x7c, 0x1c,
84 0xc2, 0xfc, 0x39, 0x3c, 0x1c, 0x02, 0x82, 0xfc, 0x18, 0x1c, 0x1c,
86 0x02, 0xfc, 0x18, 0x1c, 0x0c, 0x02, 0x02, 0x7c, 0x18, 0x0c, 0x0c,
88 0x02, 0x3c, 0x08, 0x0c, 0x04, 0x02, 0x02, 0x1c, 0x08, 0x0c, 0x04,
90 0x02, 0x1c, 0x08, 0x0c, 0x00, 0x02, 0x02, 0x0c, 0x00, 0x04, 0x00,
92 0x02, 0x04, 0x00, 0x04, 0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00,
94 0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
96 0x02, 0x00, 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, 0xfe, 0xff, 0xff, 0xff, 0xff,
102 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
104 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
106 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
108 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
112 #define wave_cursor_mask_width 43
113 #define wave_cursor_mask_height 61
114 #define wave_cursor_mask_x_hot 0
115 #define wave_cursor_mask_y_hot 25
116 static const gchar wave_cursor_mask_bits[] = {
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
135 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x08, 0x0c, 0x00,
137 0x00, 0x1c, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x08, 0x0c, 0x04,
139 0x00, 0x3c, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x7c, 0x18, 0x1c, 0x0c,
141 0x80, 0xfc, 0x38, 0x1c, 0x0c, 0x00, 0xc0, 0xfc, 0x78, 0x3c, 0x1c,
143 0xe0, 0xfd, 0xf9, 0x7d, 0x1c, 0x00, 0xf0, 0xff, 0xfb, 0xff, 0x1c,
145 0xf8, 0xff, 0xfb, 0xff, 0x3f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
147 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, 0xff, 0xff, 0xff, 0x3f,
149 0xf0, 0xff, 0xfb, 0xfd, 0x3c, 0x00, 0xe0, 0xfd, 0x7b, 0x7c, 0x1c,
151 0xc0, 0xfc, 0x39, 0x3c, 0x1c, 0x00, 0x80, 0xfc, 0x18, 0x1c, 0x1c,
153 0x00, 0xfc, 0x18, 0x1c, 0x0c, 0x00, 0x00, 0x7c, 0x18, 0x0c, 0x0c,
155 0x00, 0x3c, 0x08, 0x0c, 0x04, 0x00, 0x00, 0x1c, 0x08, 0x0c, 0x04,
157 0x00, 0x1c, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00,
159 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
161 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x00, 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 };
179 GdkCursor *wave_cursor = 0;
182 Editor::handle_audio_region_removed (AudioRegion* ignored)
184 redisplay_regions ();
188 Editor::handle_new_audio_region (AudioRegion *region)
190 /* don't copy region - the one we are being notified
191 about belongs to the session, and so it will
194 add_audio_region_to_region_display (region);
198 Editor::region_hidden (Region* r)
200 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::region_hidden), r));
202 redisplay_regions ();
206 Editor::add_audio_region_to_region_display (AudioRegion *region)
211 if (!show_automatic_regions_in_region_list && region->automatic()) {
215 if (region->hidden()) {
217 TreeModel::iterator iter = region_list_model->get_iter (_("/Hidden"));
218 TreeModel::Row parent;
219 TreeModel::Row child;
221 if (iter == region_list_model->children().end()) {
223 parent = *(region_list_model->append());
225 parent[region_list_columns.name] = _("Hidden");
226 parent[region_list_columns.region] = 0;
231 row = *(region_list_model->append (parent.children()));
233 } else if (region->whole_file()) {
235 TreeModel::Row row = *(region_list_model->append());
237 if (region->source().name()[0] == '/') { // external file
239 if (region->whole_file()) {
241 str += PBD::basename_nosuffix (region->source().name());
243 str = region->name();
248 str = region->name();
252 row[region_list_columns.name] = str;
253 row[region_list_columns.region] = region;
259 /* find parent node, add as new child */
261 TreeModel::iterator i;
262 TreeModel::Children rows = region_list_model->children();
264 for (i = rows.begin(); i != rows.end(); ++i) {
266 Region* rr = (*i)[region_list_columns.region];
267 AudioRegion* r = dynamic_cast<AudioRegion*>(rr);
269 if (r && r->whole_file()) {
271 if (region->source_equivalent (*r)) {
272 row = *(region_list_model->append ((*i).children()));
278 if (i == rows.end()) {
279 TreeModel::Row row = *(region_list_model->append());
285 row[region_list_columns.region] = region;
287 if (region->n_channels() > 1) {
288 row[region_list_columns.name] = string_compose("%1 [%2]", region->name(), region->n_channels());
290 row[region_list_columns.name] = region->name();
295 Editor::region_list_selection_changed()
299 if (region_list_display.get_selection()->count_selected_rows() > 0) {
305 for (vector<Glib::RefPtr<Gtk::Action> >::iterator i = region_list_selection_requiring_actions.begin(); i != region_list_selection_requiring_actions.end(); ++i) {
306 (*i)->set_sensitive (sensitive);
310 // set_selected_regionview_from_region_list (*region, false);
315 Editor::insert_into_tmp_audio_regionlist(AudioRegion* region)
317 /* keep all whole files at the beginning */
319 if (region->whole_file()) {
320 tmp_audio_region_list.push_front (region);
322 tmp_audio_region_list.push_back (region);
327 Editor::redisplay_regions ()
331 region_list_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
332 region_list_model.clear ();
334 /* now add everything we have, via a temporary list used to help with
338 tmp_audio_region_list.clear();
339 session->foreach_audio_region (this, &Editor::insert_into_tmp_audio_regionlist);
341 for (list<AudioRegion*>::iterator r = tmp_audio_region_list.begin(); r != tmp_audio_region_list.end(); ++r) {
342 add_audio_region_to_region_display (*r);
345 region_list_display.set_model (region_list_sort_model);
350 Editor::region_list_clear ()
352 region_list_model->clear();
356 Editor::build_region_list_menu ()
358 region_list_menu = dynamic_cast<Menu*>(ui_manager->get_widget ("/RegionListMenu"));
360 /* now grab specific menu items that we need */
362 toggle_full_region_list_action = ui_manager->get_action ("<Actions>/RegionList/rlShowAll");
364 region_list_selection_requiring_actions.push_back (ui_manager->get_action ("<Actions>/RegionList/rlHide"));
365 region_list_selection_requiring_actions.push_back (ui_manager->get_action ("<Actions>/RegionList/rlAudition"));
366 region_list_selection_requiring_actions.push_back (ui_manager->get_action ("<Actions>/RegionList/rlRemove"));
368 session_requiring_actions.push_back (ui_manager->get_action ("<Actions>/RegionList/rlEmbedAudio"));
369 session_requiring_actions.push_back (ui_manager->get_action ("<Actions>/RegionList/rlImportAudio"));
373 Editor::toggle_show_auto_regions ()
375 //show_automatic_regions_in_region_list = toggle_auto_regions_item->get_active();
376 show_automatic_regions_in_region_list = true;
377 redisplay_regions ();
381 Editor::toggle_full_region_list ()
383 if (toggle_full_region_list_item->get_active()) {
384 region_list_display.expand_all ();
386 region_list_display.collapse_all ();
391 Editor::region_list_display_key_press (GdkEventKey* ev)
397 Editor::region_list_display_key_release (GdkEventKey* ev)
399 switch (ev->keyval) {
401 remove_selected_regions_from_region_list ();
412 Editor::region_list_display_button_press (GdkEventButton *ev)
416 TreeModel::Path path;
417 TreeViewColumn* column;
421 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
422 if ((iter = region_list_model->get_iter (path))) {
423 region = (*iter)[region_list_columns.region];
431 if (Keyboard::is_delete_event (ev)) {
432 session->remove_region_from_region_list (*region);
436 if (Keyboard::is_context_menu_event (ev)) {
437 if (region_list_menu == 0) {
438 build_region_list_menu ();
440 region_list_menu->popup (ev->button, ev->time);
444 switch (ev->button) {
446 /* audition on double click */
447 if (ev->type == GDK_2BUTTON_PRESS) {
448 consider_auditioning (*region);
455 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
456 consider_auditioning (*region);
469 Editor::region_list_display_button_release (GdkEventButton *ev)
472 TreeModel::Path path;
473 TreeViewColumn* column;
478 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
479 if ((iter = region_list_model->get_iter (path))) {
480 region = (*iter)[region_list_columns.region];
484 if (Keyboard::is_delete_event (ev)) {
485 session->remove_region_from_region_list (*region);
489 switch (ev->button) {
506 Editor::consider_auditioning (Region& region)
508 AudioRegion* r = dynamic_cast<AudioRegion*> (®ion);
511 session->cancel_audition ();
515 if (session->is_auditioning()) {
516 session->cancel_audition ();
517 if (r == last_audition_region) {
522 session->audition_region (*r);
523 last_audition_region = r;
527 Editor::region_list_sorter (TreeModel::iterator a, TreeModel::iterator b)
531 Region* r1 = (*a)[region_list_columns.region];
532 Region* r2 = (*b)[region_list_columns.region];
534 AudioRegion* region1 = dynamic_cast<AudioRegion*> (r1);
535 AudioRegion* region2 = dynamic_cast<AudioRegion*> (r2);
537 if (region1 == 0 || region2 == 0) {
540 switch (region_list_sort_type) {
542 s1 = (*a)[region_list_columns.name];
543 s2 = (*b)[region_list_columns.name];
544 return (s1.compare (s2));
550 switch (region_list_sort_type) {
552 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
556 cmp = region1->length() - region2->length();
560 cmp = region1->position() - region2->position();
564 cmp = region1->source().timestamp() - region2->source().timestamp();
568 cmp = region1->start() - region2->start();
572 cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
575 case BySourceFileName:
576 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
579 case BySourceFileLength:
580 cmp = region1->source().length() - region2->source().length();
583 case BySourceFileCreationDate:
584 cmp = region1->source().timestamp() - region2->source().timestamp();
588 if (region1->source().name() == region2->source().name()) {
589 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
591 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
598 } else if (cmp > 0) {
606 Editor::reset_region_list_sort_type (RegionListSortType type)
608 if (type != region_list_sort_type) {
609 region_list_sort_type = type;
613 region_list_display.get_column (0)->set_title (_("Regions/name"));
617 region_list_display.get_column (0)->set_title (_("Regions/length"));
621 region_list_display.get_column (0)->set_title (_("Regions/position"));
625 region_list_display.get_column (0)->set_title (_("Regions/creation"));
629 region_list_display.get_column (0)->set_title (_("Regions/start"));
633 region_list_display.get_column (0)->set_title (_("Regions/end"));
636 case BySourceFileName:
637 region_list_display.get_column (0)->set_title (_("Regions/file name"));
640 case BySourceFileLength:
641 region_list_display.get_column (0)->set_title (_("Regions/file size"));
644 case BySourceFileCreationDate:
645 region_list_display.get_column (0)->set_title (_("Regions/file date"));
649 region_list_display.get_column (0)->set_title (_("Regions/file system"));
653 region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
658 Editor::reset_region_list_sort_direction (bool up)
661 //region_list_display.set_sort_type (up ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING);
662 /* reset to force resort */
663 region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
667 Editor::region_list_selection_mapover (slot<void,Region&> sl)
669 Glib::RefPtr<TreeSelection> selection = region_list_display.get_selection();
670 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
671 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
673 if (selection->count_selected_rows() == 0 || session == 0) {
677 for (; i != rows.end(); ++i) {
680 if ((iter = region_list_model->get_iter (*i))) {
681 sl (*((*iter)[region_list_columns.region]));
687 Editor::hide_a_region (Region& r)
693 Editor::remove_a_region (Region& r)
695 session->remove_region_from_region_list (r);
699 Editor::audition_region_from_region_list ()
701 region_list_selection_mapover (mem_fun (*this, &Editor::consider_auditioning));
705 Editor::hide_region_from_region_list ()
707 region_list_selection_mapover (mem_fun (*this, &Editor::hide_a_region));
711 Editor::remove_selected_regions_from_region_list ()
713 region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region));
717 Editor::region_list_display_drag_data_received (GdkDragContext *context,
720 GtkSelectionData *data,
724 vector<string> paths;
726 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
727 do_embed_sndfiles (paths, false);
730 gtk_drag_finish (context, TRUE, FALSE, time);