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 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 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
445 if ((iter = region_list_model->get_iter (path))) {
446 region = (*iter)[region_list_columns.region];
454 if (Keyboard::is_delete_event (ev)) {
455 session->remove_region_from_region_list (*region);
459 if (Keyboard::is_context_menu_event (ev)) {
460 show_region_list_display_context_menu (ev->button, ev->time);
464 switch (ev->button) {
466 /* audition on double click */
467 if (ev->type == GDK_2BUTTON_PRESS) {
468 consider_auditioning (*region);
475 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
476 consider_auditioning (*region);
489 Editor::region_list_display_button_release (GdkEventButton *ev)
492 TreeModel::Path path;
493 TreeViewColumn* column;
498 if (region_list_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
499 if ((iter = region_list_model->get_iter (path))) {
500 region = (*iter)[region_list_columns.region];
504 if (Keyboard::is_delete_event (ev)) {
505 session->remove_region_from_region_list (*region);
509 switch (ev->button) {
526 Editor::consider_auditioning (Region& region)
528 AudioRegion* r = dynamic_cast<AudioRegion*> (®ion);
531 session->cancel_audition ();
535 if (session->is_auditioning()) {
536 session->cancel_audition ();
537 if (r == last_audition_region) {
542 session->audition_region (*r);
543 last_audition_region = r;
547 Editor::region_list_sorter (TreeModel::iterator a, TreeModel::iterator b)
551 Region* r1 = (*a)[region_list_columns.region];
552 Region* r2 = (*b)[region_list_columns.region];
554 AudioRegion* region1 = dynamic_cast<AudioRegion*> (r1);
555 AudioRegion* region2 = dynamic_cast<AudioRegion*> (r2);
557 if (region1 == 0 || region2 == 0) {
560 switch (region_list_sort_type) {
562 s1 = (*a)[region_list_columns.name];
563 s2 = (*b)[region_list_columns.name];
564 return (s1.compare (s2));
570 switch (region_list_sort_type) {
572 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
576 cmp = region1->length() - region2->length();
580 cmp = region1->position() - region2->position();
584 cmp = region1->source().timestamp() - region2->source().timestamp();
588 cmp = region1->start() - region2->start();
592 cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
595 case BySourceFileName:
596 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
599 case BySourceFileLength:
600 cmp = region1->source().length() - region2->source().length();
603 case BySourceFileCreationDate:
604 cmp = region1->source().timestamp() - region2->source().timestamp();
608 if (region1->source().name() == region2->source().name()) {
609 cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
611 cmp = strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
618 } else if (cmp > 0) {
626 Editor::reset_region_list_sort_type (RegionListSortType type)
628 if (type != region_list_sort_type) {
629 region_list_sort_type = type;
630 region_list_model->set_sort_func (0, (mem_fun (*this, &Editor::region_list_sorter)));
635 Editor::reset_region_list_sort_direction (bool up)
637 region_list_model->set_sort_column (0, up ? SORT_ASCENDING : SORT_DESCENDING);
641 Editor::region_list_selection_mapover (slot<void,Region&> sl)
643 Glib::RefPtr<TreeSelection> selection = region_list_display.get_selection();
644 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
645 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
647 if (selection->count_selected_rows() == 0 || session == 0) {
651 for (; i != rows.end(); ++i) {
654 if ((iter = region_list_model->get_iter (*i))) {
655 sl (*((*iter)[region_list_columns.region]));
661 Editor::hide_a_region (Region& r)
667 Editor::remove_a_region (Region& r)
669 session->remove_region_from_region_list (r);
673 Editor::audition_region_from_region_list ()
675 region_list_selection_mapover (mem_fun (*this, &Editor::consider_auditioning));
679 Editor::hide_region_from_region_list ()
681 region_list_selection_mapover (mem_fun (*this, &Editor::hide_a_region));
685 Editor::remove_region_from_region_list ()
687 region_list_selection_mapover (mem_fun (*this, &Editor::remove_a_region));
691 Editor::region_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& context,
693 const SelectionData& data,
694 guint info, guint time)
696 vector<string> paths;
698 if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
699 do_embed_sndfiles (paths, false);
700 context->drag_finish (true, false, time);