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"
43 using namespace ARDOUR;
46 using namespace Editing;
48 #define wave_cursor_width 43
49 #define wave_cursor_height 61
50 #define wave_cursor_x_hot 0
51 #define wave_cursor_y_hot 25
52 static const gchar wave_cursor_bits[] = {
53 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
57 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
59 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
61 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff,
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, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
69 0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x00, 0x04, 0x00,
71 0x02, 0x04, 0x00, 0x04, 0x00, 0x02, 0x02, 0x0c, 0x08, 0x0c, 0x00,
73 0x02, 0x1c, 0x08, 0x0c, 0x00, 0x02, 0x02, 0x1c, 0x08, 0x0c, 0x04,
75 0x02, 0x3c, 0x18, 0x0c, 0x04, 0x02, 0x02, 0x7c, 0x18, 0x1c, 0x0c,
77 0x82, 0xfc, 0x38, 0x1c, 0x0c, 0x02, 0xc2, 0xfc, 0x78, 0x3c, 0x1c,
79 0xe2, 0xfd, 0xf9, 0x7d, 0x1c, 0x02, 0xf2, 0xff, 0xfb, 0xff, 0x1c,
81 0xfa, 0xff, 0xfb, 0xff, 0x3f, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff,
83 0xfe, 0xff, 0xff, 0xff, 0xff, 0x03, 0xfa, 0xff, 0xff, 0xff, 0x3f,
85 0xf2, 0xff, 0xfb, 0xfd, 0x3c, 0x02, 0xe2, 0xfd, 0x7b, 0x7c, 0x1c,
87 0xc2, 0xfc, 0x39, 0x3c, 0x1c, 0x02, 0x82, 0xfc, 0x18, 0x1c, 0x1c,
89 0x02, 0xfc, 0x18, 0x1c, 0x0c, 0x02, 0x02, 0x7c, 0x18, 0x0c, 0x0c,
91 0x02, 0x3c, 0x08, 0x0c, 0x04, 0x02, 0x02, 0x1c, 0x08, 0x0c, 0x04,
93 0x02, 0x1c, 0x08, 0x0c, 0x00, 0x02, 0x02, 0x0c, 0x00, 0x04, 0x00,
95 0x02, 0x04, 0x00, 0x04, 0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00,
97 0x02, 0x04, 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, 0x02, 0x00, 0x00, 0x00, 0x00,
103 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff,
105 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
107 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
109 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
111 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
115 #define wave_cursor_mask_width 43
116 #define wave_cursor_mask_height 61
117 #define wave_cursor_mask_x_hot 0
118 #define wave_cursor_mask_y_hot 25
119 static const gchar wave_cursor_mask_bits[] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
138 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x08, 0x0c, 0x00,
140 0x00, 0x1c, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x08, 0x0c, 0x04,
142 0x00, 0x3c, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x7c, 0x18, 0x1c, 0x0c,
144 0x80, 0xfc, 0x38, 0x1c, 0x0c, 0x00, 0xc0, 0xfc, 0x78, 0x3c, 0x1c,
146 0xe0, 0xfd, 0xf9, 0x7d, 0x1c, 0x00, 0xf0, 0xff, 0xfb, 0xff, 0x1c,
148 0xf8, 0xff, 0xfb, 0xff, 0x3f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
150 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, 0xff, 0xff, 0xff, 0x3f,
152 0xf0, 0xff, 0xfb, 0xfd, 0x3c, 0x00, 0xe0, 0xfd, 0x7b, 0x7c, 0x1c,
154 0xc0, 0xfc, 0x39, 0x3c, 0x1c, 0x00, 0x80, 0xfc, 0x18, 0x1c, 0x1c,
156 0x00, 0xfc, 0x18, 0x1c, 0x0c, 0x00, 0x00, 0x7c, 0x18, 0x0c, 0x0c,
158 0x00, 0x3c, 0x08, 0x0c, 0x04, 0x00, 0x00, 0x1c, 0x08, 0x0c, 0x04,
160 0x00, 0x1c, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00,
162 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
164 0x00, 0x04, 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, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
182 GdkCursor *wave_cursor = 0;
185 Editor::handle_audio_region_removed (AudioRegion* ignored)
187 redisplay_regions ();
191 Editor::handle_new_audio_region (AudioRegion *region)
193 /* don't copy region - the one we are being notified
194 about belongs to the session, and so it will
197 add_audio_region_to_region_display (region);
201 Editor::region_hidden (Region* r)
203 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::region_hidden), r));
205 redisplay_regions ();
209 Editor::add_audio_region_to_region_display (AudioRegion *region)
215 if (!show_automatic_regions_in_region_list && region->automatic()) {
219 if (region->hidden()) {
221 TreeModel::iterator iter = region_list_model->get_iter (_("/Hidden"));
222 TreeModel::Row parent;
223 TreeModel::Row child;
225 if (iter == region_list_model->children().end()) {
227 parent = *(region_list_model->append());
229 parent[region_list_columns.name] = _("Hidden");
230 parent[region_list_columns.region] = 0;
235 row = *(region_list_model->append (parent.children()));
237 } else if (region->whole_file()) {
239 TreeModel::Row row = *(region_list_model->append());
241 if (region->source().name()[0] == '/') { // external file
243 if (region->whole_file()) {
245 str += PBD::basename_nosuffix (region->source().name());
248 str = region->name();
253 str = region->name();
257 row[region_list_columns.name] = str;
258 row[region_list_columns.region] = region;
264 /* find parent node, add as new child */
266 TreeModel::iterator i;
267 TreeModel::Children rows = region_list_model->children();
268 bool found_parent = false;
270 for (i = rows.begin(); i != rows.end(); ++i) {
272 Region* rr = (*i)[region_list_columns.region];
273 AudioRegion* r = dynamic_cast<AudioRegion*>(rr);
275 if (r && r->whole_file()) {
276 if (region->source_equivalent (*r)) {
277 row = *(region_list_model->append ((*i).children()));
278 set_color(c, 65535, 0, 0);
279 row[region_list_columns.color_] = c;
287 row = *(region_list_model->append());
293 row[region_list_columns.region] = region;
295 if (region->n_channels() > 1) {
296 row[region_list_columns.name] = string_compose("%1 [%2]", region->name(), region->n_channels());
298 row[region_list_columns.name] = region->name();
303 Editor::region_list_selection_changed()
307 if (region_list_display.get_selection()->count_selected_rows() > 0) {
314 TreeView::Selection::ListHandle_Path rows = region_list_display.get_selection()->get_selected_rows ();
315 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
318 /* just set the first selected region (in fact, the selection model might be SINGLE, which
319 means there can only be one.
322 if ((iter = region_list_model->get_iter (*i))) {
323 set_selected_regionview_from_region_list (*((*iter)[region_list_columns.region]), false);
329 Editor::insert_into_tmp_audio_regionlist(AudioRegion* region)
331 /* keep all whole files at the beginning */
333 if (region->whole_file()) {
334 tmp_audio_region_list.push_front (region);
336 tmp_audio_region_list.push_back (region);
341 Editor::redisplay_regions ()
345 region_list_display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
346 region_list_model->clear ();
348 /* now add everything we have, via a temporary list used to help with
352 tmp_audio_region_list.clear();
353 session->foreach_audio_region (this, &Editor::insert_into_tmp_audio_regionlist);
355 for (list<AudioRegion*>::iterator r = tmp_audio_region_list.begin(); r != tmp_audio_region_list.end(); ++r) {
356 add_audio_region_to_region_display (*r);
359 region_list_display.set_model (region_list_model);
364 Editor::region_list_clear ()
366 region_list_model->clear();
370 Editor::build_region_list_menu ()
372 region_list_menu = dynamic_cast<Menu*>(ActionManager::get_widget ("/RegionListMenu"));
374 /* now grab specific menu items that we need */
376 toggle_full_region_list_action = ActionManager::get_action (X_("RegionList"), X_("rlShowAll"));
380 Editor::toggle_show_auto_regions ()
382 //show_automatic_regions_in_region_list = toggle_auto_regions_item->get_active();
383 show_automatic_regions_in_region_list = true;
384 redisplay_regions ();
388 Editor::toggle_full_region_list ()
390 if (toggle_full_region_list_item->get_active()) {
391 region_list_display.expand_all ();
393 region_list_display.collapse_all ();
398 Editor::show_region_list_display_context_menu (int button, int time)
400 if (region_list_menu == 0) {
401 build_region_list_menu ();
404 if (region_list_display.get_selection()->count_selected_rows() > 0) {
405 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, true);
407 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, false);
410 region_list_menu->popup (button, time);
414 Editor::region_list_display_key_press (GdkEventKey* ev)
420 Editor::region_list_display_key_release (GdkEventKey* ev)
422 switch (ev->keyval) {
424 remove_region_from_region_list ();
435 Editor::region_list_display_button_press (GdkEventButton *ev)
439 TreeModel::Path path;
440 TreeViewColumn* column;
444 cerr << "RL button press\n";
446 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
447 if ((iter = region_list_model->get_iter (path))) {
448 region = (*iter)[region_list_columns.region];
456 if (Keyboard::is_delete_event (ev)) {
457 session->remove_region_from_region_list (*region);
461 if (Keyboard::is_context_menu_event (ev)) {
462 show_region_list_display_context_menu (ev->button, ev->time);
466 switch (ev->button) {
468 /* audition on double click */
469 if (ev->type == GDK_2BUTTON_PRESS) {
470 consider_auditioning (*region);
477 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
478 consider_auditioning (*region);
491 Editor::region_list_display_button_release (GdkEventButton *ev)
494 TreeModel::Path path;
495 TreeViewColumn* column;
500 cerr << "RL button release\n";
502 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
503 if ((iter = region_list_model->get_iter (path))) {
504 region = (*iter)[region_list_columns.region];
508 if (Keyboard::is_delete_event (ev)) {
509 session->remove_region_from_region_list (*region);
513 switch (ev->button) {
530 Editor::consider_auditioning (Region& region)
532 AudioRegion* r = dynamic_cast<AudioRegion*> (®ion);
535 session->cancel_audition ();
539 if (session->is_auditioning()) {
540 session->cancel_audition ();
541 if (r == last_audition_region) {
546 session->audition_region (*r);
547 last_audition_region = r;
551 Editor::region_list_sorter (TreeModel::iterator a, TreeModel::iterator b)
555 Region* r1 = (*a)[region_list_columns.region];
556 Region* r2 = (*b)[region_list_columns.region];
558 AudioRegion* region1 = dynamic_cast<AudioRegion*> (r1);
559 AudioRegion* region2 = dynamic_cast<AudioRegion*> (r2);
561 if (region1 == 0 || region2 == 0) {
564 switch (region_list_sort_type) {
566 s1 = (*a)[region_list_columns.name];
567 s2 = (*b)[region_list_columns.name];
568 return (s1.compare (s2));
574 switch (region_list_sort_type) {
576 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
580 cmp = region1->length() - region2->length();
584 cmp = region1->position() - region2->position();
588 cmp = region1->source().timestamp() - region2->source().timestamp();
592 cmp = region1->start() - region2->start();
596 cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
599 case BySourceFileName:
600 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
603 case BySourceFileLength:
604 cmp = region1->source().length() - region2->source().length();
607 case BySourceFileCreationDate:
608 cmp = region1->source().timestamp() - region2->source().timestamp();
612 if (region1->source().name() == region2->source().name()) {
613 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
615 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
622 } else if (cmp > 0) {
630 Editor::reset_region_list_sort_type (RegionListSortType type)
632 if (type != region_list_sort_type) {
633 region_list_sort_type = type;
634 region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
639 Editor::reset_region_list_sort_direction (bool up)
641 // region_list_display.set_sort_type (up ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING);
645 Editor::region_list_selection_mapover (slot<void,Region&> sl)
647 Glib::RefPtr<TreeSelection> selection = region_list_display.get_selection();
648 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
649 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
651 if (selection->count_selected_rows() == 0 || session == 0) {
655 for (; i != rows.end(); ++i) {
658 if ((iter = region_list_model->get_iter (*i))) {
659 sl (*((*iter)[region_list_columns.region]));
665 Editor::hide_a_region (Region& r)
671 Editor::remove_a_region (Region& r)
673 session->remove_region_from_region_list (r);
677 Editor::audition_region_from_region_list ()
679 region_list_selection_mapover (mem_fun (*this, &Editor::consider_auditioning));
683 Editor::hide_region_from_region_list ()
685 region_list_selection_mapover (mem_fun (*this, &Editor::hide_a_region));
689 Editor::remove_region_from_region_list ()
691 region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region));
695 Editor::region_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
697 const SelectionData& data,
698 guint info, guint time)
700 vector<string> paths;
702 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
703 do_embed_sndfiles (paths, false);
704 context->drag_finish (true, false, time);