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 row = *(region_list_model->append());
240 set_color(c, 65535, 0, 0);
241 row[region_list_columns.color_] = c;
243 if (region->source().name()[0] == '/') { // external file
245 if (region->whole_file()) {
247 str += PBD::basename_nosuffix (region->source().name());
250 str = region->name();
255 str = region->name();
259 row[region_list_columns.name] = str;
260 row[region_list_columns.region] = region;
266 /* find parent node, add as new child */
268 TreeModel::iterator i;
269 TreeModel::Children rows = region_list_model->children();
270 bool found_parent = false;
272 for (i = rows.begin(); i != rows.end(); ++i) {
274 Region* rr = (*i)[region_list_columns.region];
275 AudioRegion* r = dynamic_cast<AudioRegion*>(rr);
277 if (r && r->whole_file()) {
278 if (region->source_equivalent (*r)) {
279 row = *(region_list_model->append ((*i).children()));
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 Glib::RefPtr<Action> act;
378 act = ActionManager::get_action (X_("RegionList"), X_("rlShowAll"));
380 toggle_full_region_list_action = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
383 act = ActionManager::get_action (X_("RegionList"), X_("rlShowAuto"));
385 toggle_show_auto_regions_action = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
390 Editor::toggle_show_auto_regions ()
392 show_automatic_regions_in_region_list = toggle_show_auto_regions_action->get_active();
393 redisplay_regions ();
397 Editor::toggle_full_region_list ()
399 if (toggle_full_region_list_action->get_active()) {
400 region_list_display.expand_all ();
402 region_list_display.collapse_all ();
407 Editor::show_region_list_display_context_menu (int button, int time)
409 if (region_list_menu == 0) {
410 build_region_list_menu ();
413 if (region_list_display.get_selection()->count_selected_rows() > 0) {
414 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, true);
416 ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, false);
419 region_list_menu->popup (button, time);
423 Editor::region_list_display_key_press (GdkEventKey* ev)
429 Editor::region_list_display_key_release (GdkEventKey* ev)
431 switch (ev->keyval) {
433 remove_region_from_region_list ();
444 Editor::region_list_display_button_press (GdkEventButton *ev)
448 TreeModel::Path path;
449 TreeViewColumn* column;
453 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
454 if ((iter = region_list_model->get_iter (path))) {
455 region = (*iter)[region_list_columns.region];
463 if (Keyboard::is_delete_event (ev)) {
464 session->remove_region_from_region_list (*region);
468 if (Keyboard::is_context_menu_event (ev)) {
469 show_region_list_display_context_menu (ev->button, ev->time);
473 switch (ev->button) {
475 /* audition on double click */
476 if (ev->type == GDK_2BUTTON_PRESS) {
477 consider_auditioning (*region);
484 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
485 consider_auditioning (*region);
498 Editor::region_list_display_button_release (GdkEventButton *ev)
501 TreeModel::Path path;
502 TreeViewColumn* column;
507 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
508 if ((iter = region_list_model->get_iter (path))) {
509 region = (*iter)[region_list_columns.region];
513 if (region && Keyboard::is_delete_event (ev)) {
514 session->remove_region_from_region_list (*region);
522 Editor::consider_auditioning (Region& region)
524 AudioRegion* r = dynamic_cast<AudioRegion*> (®ion);
527 session->cancel_audition ();
531 if (session->is_auditioning()) {
532 session->cancel_audition ();
533 if (r == last_audition_region) {
538 session->audition_region (*r);
539 last_audition_region = r;
543 Editor::region_list_sorter (TreeModel::iterator a, TreeModel::iterator b)
547 Region* r1 = (*a)[region_list_columns.region];
548 Region* r2 = (*b)[region_list_columns.region];
550 AudioRegion* region1 = dynamic_cast<AudioRegion*> (r1);
551 AudioRegion* region2 = dynamic_cast<AudioRegion*> (r2);
553 if (region1 == 0 || region2 == 0) {
556 switch (region_list_sort_type) {
558 s1 = (*a)[region_list_columns.name];
559 s2 = (*b)[region_list_columns.name];
560 return (s1.compare (s2));
566 switch (region_list_sort_type) {
568 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
572 cmp = region1->length() - region2->length();
576 cmp = region1->position() - region2->position();
580 cmp = region1->source().timestamp() - region2->source().timestamp();
584 cmp = region1->start() - region2->start();
588 cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
591 case BySourceFileName:
592 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
595 case BySourceFileLength:
596 cmp = region1->source().length() - region2->source().length();
599 case BySourceFileCreationDate:
600 cmp = region1->source().timestamp() - region2->source().timestamp();
604 if (region1->source().name() == region2->source().name()) {
605 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
607 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
614 } else if (cmp > 0) {
622 Editor::reset_region_list_sort_type (RegionListSortType type)
624 if (type != region_list_sort_type) {
625 region_list_sort_type = type;
626 region_list_model->set_sort_func (0, (mem_fun (*this, &Editor::region_list_sorter)));
631 Editor::reset_region_list_sort_direction (bool up)
633 region_list_model->set_sort_column (0, up ? SORT_ASCENDING : SORT_DESCENDING);
637 Editor::region_list_selection_mapover (slot<void,Region&> sl)
639 Glib::RefPtr<TreeSelection> selection = region_list_display.get_selection();
640 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
641 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
643 if (selection->count_selected_rows() == 0 || session == 0) {
647 for (; i != rows.end(); ++i) {
650 if ((iter = region_list_model->get_iter (*i))) {
651 sl (*((*iter)[region_list_columns.region]));
657 Editor::hide_a_region (Region& r)
663 Editor::remove_a_region (Region& r)
665 session->remove_region_from_region_list (r);
669 Editor::audition_region_from_region_list ()
671 region_list_selection_mapover (mem_fun (*this, &Editor::consider_auditioning));
675 Editor::hide_region_from_region_list ()
677 region_list_selection_mapover (mem_fun (*this, &Editor::hide_a_region));
681 Editor::remove_region_from_region_list ()
683 region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region));
687 Editor::region_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
689 const SelectionData& data,
690 guint info, guint time)
692 vector<string> paths;
694 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
695 do_embed_sndfiles (paths, false);
696 context->drag_finish (true, false, time);