fix compose mess, and a number of 64 bit printf specs
[ardour.git] / gtk2_ardour / editor_region_list.cc
1 /*
2     Copyright (C) 2000 Paul Davis 
3
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.
8
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.
13
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.
17
18     $Id$
19 */
20
21 #include <cstdlib>
22 #include <cmath>
23 #include <algorithm>
24 #include <string>
25
26 #include <pbd/basename.h>
27
28 #include <ardour/audioregion.h>
29 #include <ardour/session_region.h>
30
31 #include <gtkmm2ext/stop_signal.h>
32
33 #include "editor.h"
34 #include "editing.h"
35 #include "ardour_ui.h"
36 #include "gui_thread.h"
37
38 #include "i18n.h"
39
40 using namespace sigc;
41 using namespace ARDOUR;
42 using namespace Gtk;
43 using namespace Editing;
44
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,
51 0x00,
52    0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
53 0x00,
54    0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
55 0x00,
56    0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
57 0x00,
58    0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff,
59 0x03,
60    0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
61 0x02,
62    0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
63 0x02,
64    0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
65 0x02,
66    0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x00, 0x04, 0x00,
67 0x02,
68    0x02, 0x04, 0x00, 0x04, 0x00, 0x02, 0x02, 0x0c, 0x08, 0x0c, 0x00,
69 0x02,
70    0x02, 0x1c, 0x08, 0x0c, 0x00, 0x02, 0x02, 0x1c, 0x08, 0x0c, 0x04,
71 0x02,
72    0x02, 0x3c, 0x18, 0x0c, 0x04, 0x02, 0x02, 0x7c, 0x18, 0x1c, 0x0c,
73 0x02,
74    0x82, 0xfc, 0x38, 0x1c, 0x0c, 0x02, 0xc2, 0xfc, 0x78, 0x3c, 0x1c,
75 0x02,
76    0xe2, 0xfd, 0xf9, 0x7d, 0x1c, 0x02, 0xf2, 0xff, 0xfb, 0xff, 0x1c,
77 0x02,
78    0xfa, 0xff, 0xfb, 0xff, 0x3f, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff,
79 0x03,
80    0xfe, 0xff, 0xff, 0xff, 0xff, 0x03, 0xfa, 0xff, 0xff, 0xff, 0x3f,
81 0x02,
82    0xf2, 0xff, 0xfb, 0xfd, 0x3c, 0x02, 0xe2, 0xfd, 0x7b, 0x7c, 0x1c,
83 0x02,
84    0xc2, 0xfc, 0x39, 0x3c, 0x1c, 0x02, 0x82, 0xfc, 0x18, 0x1c, 0x1c,
85 0x02,
86    0x02, 0xfc, 0x18, 0x1c, 0x0c, 0x02, 0x02, 0x7c, 0x18, 0x0c, 0x0c,
87 0x02,
88    0x02, 0x3c, 0x08, 0x0c, 0x04, 0x02, 0x02, 0x1c, 0x08, 0x0c, 0x04,
89 0x02,
90    0x02, 0x1c, 0x08, 0x0c, 0x00, 0x02, 0x02, 0x0c, 0x00, 0x04, 0x00,
91 0x02,
92    0x02, 0x04, 0x00, 0x04, 0x00, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00,
93 0x02,
94    0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
95 0x02,
96    0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
97 0x02,
98    0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
99 0x02,
100    0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfe, 0xff, 0xff, 0xff, 0xff,
101 0x03,
102    0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
103 0x00,
104    0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
105 0x00,
106    0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
107 0x00,
108    0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 0x00,
110    0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
111
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,
118 0x00,
119    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120 0x00,
121    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 0x00,
123    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00,
125    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00,
127    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 0x00,
129    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x00,
131    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 0x00,
133    0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
134 0x00,
135    0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x08, 0x0c, 0x00,
136 0x00,
137    0x00, 0x1c, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x08, 0x0c, 0x04,
138 0x00,
139    0x00, 0x3c, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x7c, 0x18, 0x1c, 0x0c,
140 0x00,
141    0x80, 0xfc, 0x38, 0x1c, 0x0c, 0x00, 0xc0, 0xfc, 0x78, 0x3c, 0x1c,
142 0x00,
143    0xe0, 0xfd, 0xf9, 0x7d, 0x1c, 0x00, 0xf0, 0xff, 0xfb, 0xff, 0x1c,
144 0x00,
145    0xf8, 0xff, 0xfb, 0xff, 0x3f, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
146 0x07,
147    0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, 0xff, 0xff, 0xff, 0x3f,
148 0x00,
149    0xf0, 0xff, 0xfb, 0xfd, 0x3c, 0x00, 0xe0, 0xfd, 0x7b, 0x7c, 0x1c,
150 0x00,
151    0xc0, 0xfc, 0x39, 0x3c, 0x1c, 0x00, 0x80, 0xfc, 0x18, 0x1c, 0x1c,
152 0x00,
153    0x00, 0xfc, 0x18, 0x1c, 0x0c, 0x00, 0x00, 0x7c, 0x18, 0x0c, 0x0c,
154 0x00,
155    0x00, 0x3c, 0x08, 0x0c, 0x04, 0x00, 0x00, 0x1c, 0x08, 0x0c, 0x04,
156 0x00,
157    0x00, 0x1c, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00,
158 0x00,
159    0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
160 0x00,
161    0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00,
163    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 0x00,
165    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166 0x00,
167    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00,
169    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170 0x00,
171    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172 0x00,
173    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00,
175    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 0x00,
177    0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
178
179 GdkCursor *wave_cursor = 0;
180
181 void
182 Editor::handle_audio_region_removed (AudioRegion* ignored)
183 {
184         redisplay_regions ();
185 }
186
187 void
188 Editor::handle_new_audio_region (AudioRegion *region)
189 {
190         /* don't copy region - the one we are being notified
191            about belongs to the session, and so it will
192            never be edited.
193         */
194         add_audio_region_to_region_display (region);
195 }
196
197 void
198 Editor::region_hidden (Region* r)
199 {
200         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::region_hidden), r));    
201
202         redisplay_regions ();
203 }
204
205 void
206 Editor::add_audio_region_to_region_display (AudioRegion *region)
207 {
208         using namespace Gtk::CTree_Helpers;
209
210         vector<const char*> item;
211         RowList::iterator i;
212         RowList::iterator tmpi;
213         string str;
214
215         if (!show_automatic_regions_in_region_list && region->automatic()) {
216                 return;
217         }
218
219         if (region->hidden()) {
220
221                 if (region_list_hidden_node == region_list_display.rows().end()) {
222                         item.clear ();
223                         item.push_back (_("hidden"));
224                         region_list_hidden_node = region_list_display.rows().insert (region_list_display.rows().end(),
225                                                                          Element (item));
226                         (*region_list_hidden_node).set_data (0);
227                         (*region_list_hidden_node).set_leaf (false);
228                 }
229
230                 item.clear ();
231                 if (region->n_channels() > 1) {
232                         str = string_compose("%1  [%2]", region->name(), region->n_channels());
233                         item.push_back (str.c_str());
234                 } else {
235                         item.push_back (region->name().c_str());
236                 }
237
238                 tmpi = region_list_hidden_node->subtree().insert (region_list_hidden_node->subtree().end(), 
239                                                                   Element (item));
240                 (*tmpi).set_data (region);
241                 return;
242
243         } else if (region->whole_file()) {
244
245                 item.clear ();
246
247                 if (region->source().name()[0] == '/') { // external file
248
249                         if (region->whole_file()) {
250                                 str = ".../";
251                                 str += PBD::basename_nosuffix (region->source().name());
252                         } else {
253                                 str = region->name();
254                         }
255
256                 } else {
257
258                         str = region->name();
259
260                 }
261
262                 item.push_back (str.c_str());
263
264                 tmpi = region_list_display.rows().insert (region_list_display.rows().end(), 
265                                                           Element (item));
266
267                 (*tmpi).set_data (region);
268                 (*tmpi).set_leaf (false);
269
270                 return;
271                 
272         } else {
273
274                 /* find parent node, add as new child */
275                 
276                 for (i = region_list_display.rows().begin(); i != region_list_display.rows().end(); ++i) {
277
278                         AudioRegion* r = static_cast<AudioRegion*> ((*i).get_data());
279
280                         if (r && r->whole_file()) {
281                                 
282                                 if (region->source_equivalent (*r)) {
283
284                                         item.clear ();
285                                         
286                                         if (region->n_channels() > 1) {
287                                                 str = string_compose("%1  [%2]", region->name(), region->n_channels());
288                                                 item.push_back (str.c_str());
289                                         } else {
290                                                 item.push_back (region->name().c_str());
291                                         }
292
293                                         
294                                         tmpi = i->subtree().insert (i->subtree().end(), Element (item));
295                                         (*tmpi).set_data (region);
296                                         
297                                         return;
298                                 }
299                         }
300                 }
301         }
302         
303         item.clear ();
304         
305         if (region->n_channels() > 1) {
306                 str = string_compose("%1  [%2]", region->name(), region->n_channels());
307                 item.push_back (str.c_str());
308         } else {
309                 item.push_back (region->name().c_str());
310         }
311         
312         tmpi = region_list_display.rows().insert (region_list_display.rows().end(), Element (item));
313         (*tmpi).set_data (region);
314         (*tmpi).set_leaf (true);
315 }
316
317 void
318 Editor::insert_into_tmp_audio_regionlist(AudioRegion* region)
319 {
320         /* keep all whole files at the beginning */
321         
322         if (region->whole_file()) {
323                 tmp_audio_region_list.push_front (region);
324         } else {
325                 tmp_audio_region_list.push_back (region);
326         }
327 }
328
329 void
330 Editor::redisplay_regions ()
331 {
332         if (session) {
333                 region_list_display.freeze ();
334                 region_list_clear ();
335                 region_list_hidden_node = region_list_display.rows().end();
336
337                 /* now add everything we have, via a temporary list used to help with
338                    sorting.
339                 */
340                 
341                 tmp_audio_region_list.clear();
342                 session->foreach_audio_region (this, &Editor::insert_into_tmp_audio_regionlist);
343
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);
346                 }
347                 
348                 region_list_display.sort ();
349                 region_list_display.thaw ();
350         }
351 }
352
353 void
354 Editor::region_list_clear ()
355 {
356         /* ---------------------------------------- */
357         /* XXX MAKE ME A FUNCTION (no CTree::clear() in gtkmm 1.2) */
358
359         gtk_ctree_remove_node (region_list_display.gobj(), NULL);
360
361         /* ---------------------------------------- */
362 }
363
364 void
365 Editor::region_list_column_click (gint col)
366 {
367         bool sensitive;
368
369         if (region_list_menu == 0) {
370                 build_region_list_menu ();
371         }
372
373         if (region_list_display.selection().size() != 0) {
374                 sensitive = true;
375         } else {
376                 sensitive = false;
377         }
378
379         for (vector<MenuItem*>::iterator i = rl_context_menu_region_items.begin(); i != rl_context_menu_region_items.end(); ++i) {
380                 (*i)->set_sensitive (sensitive);
381         }
382
383         region_list_menu->popup (0, 0);
384 }
385
386 void
387 Editor::build_region_list_menu ()
388 {
389         using namespace Gtk::Menu_Helpers;
390
391         region_list_menu = new Menu;
392         
393         MenuList& items = region_list_menu->items();
394         region_list_menu->set_name ("ArdourContextMenu");
395
396         items.push_back (MenuElem (_("Audition"), mem_fun(*this, &Editor::audition_region_from_region_list)));
397         rl_context_menu_region_items.push_back (items.back());
398         items.push_back (MenuElem (_("Hide"), mem_fun(*this, &Editor::hide_region_from_region_list)));
399         rl_context_menu_region_items.push_back (items.back());
400         items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_region_from_region_list)));
401         rl_context_menu_region_items.push_back (items.back());
402
403
404         items.push_back (SeparatorElem());
405         
406
407         // items.push_back (MenuElem (_("Find")));
408         items.push_back (CheckMenuElem (_("Show all"), mem_fun(*this, &Editor::toggle_full_region_list)));
409         toggle_full_region_list_item = static_cast<CheckMenuItem*> (items.back());
410         
411         Gtk::Menu *sort_menu = manage (new Menu);
412         MenuList& sort_items = sort_menu->items();
413         sort_menu->set_name ("ArdourContextMenu");
414         RadioMenuItem::Group sort_order_group;
415         RadioMenuItem::Group sort_type_group;
416
417         sort_items.push_back (RadioMenuElem (sort_order_group, _("Ascending"),
418                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_direction), true)));
419         sort_items.push_back (RadioMenuElem (sort_order_group, _("Descending"), 
420                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_direction), false)));
421         sort_items.push_back (SeparatorElem());
422
423         sort_items.push_back (RadioMenuElem (sort_type_group, _("By Region Name"),
424                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_type), ByName)));
425         sort_items.push_back (RadioMenuElem (sort_type_group, _("By Region Length"),
426                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_type), ByLength)));
427         sort_items.push_back (RadioMenuElem (sort_type_group, _("By Region Position"),
428                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_type), ByPosition)));
429         sort_items.push_back (RadioMenuElem (sort_type_group, _("By Region Timestamp"),
430                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_type), ByTimestamp)));
431         sort_items.push_back (RadioMenuElem (sort_type_group, _("By Region Start in File"),
432                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_type), ByStartInFile)));
433         sort_items.push_back (RadioMenuElem (sort_type_group, _("By Region End in File"),
434                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_type), ByEndInFile)));
435         sort_items.push_back (RadioMenuElem (sort_type_group, _("By Source File Name"),
436                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_type), BySourceFileName)));
437         sort_items.push_back (RadioMenuElem (sort_type_group, _("By Source File Length"),
438                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_type), BySourceFileLength)));
439         sort_items.push_back (RadioMenuElem (sort_type_group, _("By Source File Creation Date"),
440                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_type), BySourceFileCreationDate)));
441         sort_items.push_back (RadioMenuElem (sort_type_group, _("By Source Filesystem"),
442                                              bind (mem_fun(*this, &Editor::reset_region_list_sort_type), BySourceFileFS)));
443         
444         items.push_back (MenuElem (_("Sorting"), *sort_menu));
445         items.push_back (SeparatorElem());
446
447 //      items.push_back (CheckMenuElem (_("Display Automatic Regions"), mem_fun(*this, &Editor::toggle_show_auto_regions)));
448 //      toggle_auto_regions_item = static_cast<CheckMenuItem*> (items.back());
449 //      toggle_auto_regions_item->set_active (show_automatic_regions_in_region_list);
450 //      items.push_back (SeparatorElem());
451
452         items.push_back (MenuElem (_("Import audio (copy)"), bind (mem_fun(*this, &Editor::import_audio), false)));
453         import_audio_item = items.back();
454         if (!session) {
455                 import_audio_item->set_sensitive (false);
456         }
457         items.push_back (MenuElem (_("Embed audio (link)"), mem_fun(*this, &Editor::embed_audio)));
458         embed_audio_item = items.back();
459         if (!session) {
460                 embed_audio_item->set_sensitive (false);
461         }
462 }
463
464 void
465 Editor::toggle_show_auto_regions ()
466 {
467         //show_automatic_regions_in_region_list = toggle_auto_regions_item->get_active();
468         show_automatic_regions_in_region_list = true;
469         redisplay_regions ();
470 }
471
472 void
473 Editor::toggle_full_region_list ()
474 {
475         region_list_display.freeze ();
476         if (toggle_full_region_list_item->get_active()) {
477                 for (CTree_Helpers::RowIterator r = region_list_display.rows().begin(); r != region_list_display.rows().end(); ++r) {
478                         r->expand_recursive ();
479                 }
480         } else {
481                 for (CTree_Helpers::RowIterator r = region_list_display.rows().begin(); r != region_list_display.rows().end(); ++r) {
482                         r->collapse ();
483                 }
484         }
485         region_list_display.thaw ();
486 }
487
488 gint
489 Editor::region_list_display_key_press (GdkEventKey* ev)
490 {
491         return FALSE;
492 }
493
494 gint
495 Editor::region_list_display_key_release (GdkEventKey* ev)
496 {
497         switch (ev->keyval) {
498         case GDK_Delete:
499                 remove_selected_regions_from_region_list ();
500                 return TRUE;
501                 break;
502         default:
503                 break;
504         }
505
506         return FALSE;
507
508 }
509
510 gint
511 Editor::region_list_display_button_press (GdkEventButton *ev)
512 {
513         int row, col;
514         AudioRegion *region;
515
516         if (Keyboard::is_delete_event (ev)) {
517                 if (region_list_display.get_selection_info ((int)ev->x, (int)ev->y, &row, &col) != 0) {
518                         if ((region = (AudioRegion *) region_list_display.row(row).get_data()) != 0) {
519                                 delete region;
520                         }
521                 }
522                 return TRUE;
523         }
524
525         if (Keyboard::is_context_menu_event (ev)) {
526                 region_list_column_click (-1);
527                 return TRUE;
528         }
529
530         switch (ev->button) {
531         case 1:
532                 if (region_list_display.get_selection_info ((int)ev->x, (int)ev->y, &row, &col) != 0) {
533                         if ((region = (AudioRegion *) region_list_display.row(row).get_data()) != 0) {
534
535                                 if (wave_cursor == 0) {
536                                         GdkPixmap *source, *mask;
537                                         GdkColor fg = { 0, 65535, 0, 0 }; /* Red. */
538                                         GdkColor bg = { 0, 0, 0, 65535 }; /* Blue. */
539                                         
540                                         source = gdk_bitmap_create_from_data (NULL, wave_cursor_bits,
541                                                                               wave_cursor_width, wave_cursor_height);
542                                         mask = gdk_bitmap_create_from_data (NULL, wave_cursor_mask_bits,
543                                                                             wave_cursor_mask_width, wave_cursor_mask_height);
544
545                                         wave_cursor = gdk_cursor_new_from_pixmap (source, 
546                                                                                   mask,
547                                                                                   &fg, 
548                                                                                   &bg,
549                                                                                   wave_cursor_x_hot,
550                                                                                   wave_cursor_y_hot);
551                                         gdk_pixmap_unref (source);
552                                         gdk_pixmap_unref (mask);
553                                 }
554                                 region_list_display_drag_region = region;
555                                 need_wave_cursor = 1;
556
557                                 /* audition on double click */
558                                 if (ev->type == GDK_2BUTTON_PRESS) {
559                                         consider_auditioning (region);
560                                 }
561
562                                 return TRUE;
563                         }
564                         
565                 }
566                 break;
567
568         case 2:
569                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
570                         if (region_list_display.get_selection_info ((int)ev->x, (int)ev->y, &row, &col) != 0) {
571                                 if ((region = (AudioRegion *) region_list_display.get_row_data (row)) != 0) {
572                                         if (consider_auditioning (region)) {
573                                                 region_list_display.row(row).select();
574                                         }
575                                         else {
576                                                 region_list_display.row(row).unselect();
577                                         }
578                                         return TRUE;
579                                 }
580                         }
581                 } 
582
583                 /* to prevent regular selection -- i dont think this is needed JLC */
584                 return stop_signal (region_list_display, "button_press_event");
585                 break;
586
587         case 3:
588                 break;
589         default:
590                 break; 
591         }
592
593         return FALSE;
594 }
595
596 gint
597 Editor::region_list_display_button_release (GdkEventButton *ev)
598 {
599         int row, col;
600
601         if (region_list_display.get_selection_info ((int)ev->x, (int)ev->y, &row, &col) != 0) {
602                 region_list_button_region = (AudioRegion *) region_list_display.get_row_data (row);
603         } else {
604                 region_list_button_region = 0;
605         }
606
607         if (Keyboard::is_delete_event (ev)) {
608                 remove_region_from_region_list ();
609                 return TRUE;
610         }
611
612         switch (ev->button) {
613         case 1:
614                 if (region_list_display_drag_region) {
615                         insert_region_list_drag (*region_list_display_drag_region);
616                 }
617
618                 track_canvas_scroller.get_window().set_cursor (current_canvas_cursor);
619                 region_list_display.get_window().set_cursor (0);
620
621                 region_list_display_drag_region = 0;
622                 need_wave_cursor = 0;
623
624                 return TRUE;
625                 break;
626
627         case 3:
628                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
629
630                         if (region_list_menu == 0) {
631                                 build_region_list_menu ();
632                         }
633                         
634                         bool sensitive;
635                         
636                         if (region_list_display.selection().size() != 0) {
637                                 sensitive = true;
638                         } else {
639                                 sensitive = false;
640                         }
641
642                         for (vector<MenuItem*>::iterator i = rl_context_menu_region_items.begin(); i != rl_context_menu_region_items.end(); ++i) {
643                                 (*i)->set_sensitive (sensitive);
644                         }
645
646                         region_list_menu->popup (0, 0);
647                 }
648
649                 return TRUE;
650                 break;
651         default:
652                 break;
653         }
654         return FALSE;
655 }
656
657 gint
658 Editor::region_list_display_motion (GdkEventMotion *ev)
659 {
660         if (need_wave_cursor == 1) {
661                 track_canvas_scroller.get_window().set_cursor (wave_cursor);
662                 region_list_display.get_window().set_cursor (wave_cursor);
663                 gdk_flush ();
664                 need_wave_cursor = 2;
665         }
666         return FALSE;
667 }
668
669 void
670 Editor::region_list_display_selected (gint row, gint col, GdkEvent *ev)
671 {
672         AudioRegion* region = static_cast<AudioRegion *>(region_list_display.get_row_data (row));
673
674         if (session == 0 || region == 0) {
675                 return;
676         }
677
678         set_selected_regionview_from_region_list (*region, false);
679 }
680
681 void
682 Editor::region_list_display_unselected (gint row, gint col, GdkEvent *ev)
683 {
684 }
685
686 bool
687 Editor::consider_auditioning (AudioRegion *r)
688 {
689         if (r == 0) {
690                 session->cancel_audition ();
691                 return false;
692         }
693
694         if (session->is_auditioning()) {
695                 session->cancel_audition ();
696                 if (r == last_audition_region) {
697                         return false;
698                 }
699         }
700
701         session->audition_region (*r);
702         last_audition_region = r;
703
704         return true;
705 }
706
707 gint
708 Editor::region_list_display_enter_notify (GdkEventCrossing *ev)
709 {
710         ARDOUR_UI::instance()->allow_focus (true);
711         region_list_display.grab_focus ();
712         return FALSE;
713 }
714
715 gint
716 Editor::region_list_display_leave_notify (GdkEventCrossing *ev)
717 {
718         ARDOUR_UI::instance()->allow_focus (false);
719         return FALSE;
720 }
721
722 gint
723 Editor::_region_list_sorter (GtkCList* clist, gconstpointer a, gconstpointer b)
724 {
725         Editor* editor = static_cast<Editor*> (gtk_object_get_data (GTK_OBJECT(clist), "editor"));
726         return editor->region_list_sorter (a, b);
727 }
728
729 gint
730 Editor::region_list_sorter (gconstpointer a, gconstpointer b)
731 {
732         GtkCListRow* row1 = (GtkCListRow *) a;
733         GtkCListRow* row2 = (GtkCListRow *) b;
734
735         AudioRegion* region1 = static_cast<AudioRegion*> (row1->data);
736         AudioRegion* region2 = static_cast<AudioRegion*> (row2->data);
737
738         if (region1 == 0 || region2 == 0) {
739                 switch (region_list_sort_type) {
740                 case ByName:
741                         return true; /* XXX compare text in rows */
742                 default:
743                         return true;
744                 }
745         }
746
747         switch (region_list_sort_type) {
748         case ByName:
749                 return strcasecmp (region1->name().c_str(), region2->name().c_str());
750                 break;
751
752         case ByLength:
753                 return region1->length() - region2->length();
754                 break;
755                 
756         case ByPosition:
757                 return region1->position() - region2->position();
758                 break;
759                 
760         case ByTimestamp:
761                 return region1->source().timestamp() - region2->source().timestamp();
762                 break;
763         
764         case ByStartInFile:
765                 return region1->start() - region2->start();
766                 break;
767                 
768         case ByEndInFile:
769                 return (region1->start() + region1->length()) - (region2->start() + region2->length());
770                 break;
771                 
772         case BySourceFileName:
773                 return strcasecmp (region1->source().name().c_str(), region2->source().name().c_str());
774                 break;
775
776         case BySourceFileLength:
777                 return region1->source().length() - region2->source().length();
778                 break;
779                 
780         case BySourceFileCreationDate:
781                 return region1->source().timestamp() - region2->source().timestamp();
782                 break;
783
784         case BySourceFileFS:
785                 if (region1->source().name() == region2->source().name()) {
786                         return strcasecmp (region1->name().c_str(),  region2->name().c_str());
787                 } else {
788                         return strcasecmp (region1->source().name().c_str(),  region2->source().name().c_str());
789                 }
790                 break;
791         }
792
793         return FALSE;
794 }
795
796 void
797 Editor::reset_region_list_sort_type (RegionListSortType type)
798 {
799         if (type != region_list_sort_type) {
800                 region_list_sort_type = type;
801
802                 switch (type) {
803                 case ByName:
804                         region_list_display.set_column_title(0, _("Regions/name"));
805                         break;
806                         
807                 case ByLength:
808                         region_list_display.set_column_title (0, _("Regions/length"));
809                         break;
810                         
811                 case ByPosition:
812                         region_list_display.set_column_title (0, _("Regions/position"));
813                         break;
814                         
815                 case ByTimestamp:
816                         region_list_display.set_column_title (0, _("Regions/creation"));
817                         break;
818                         
819                 case ByStartInFile:
820                         region_list_display.set_column_title (0, _("Regions/start"));
821                         break;
822                         
823                 case ByEndInFile:
824                         region_list_display.set_column_title (0, _("Regions/end"));
825                         break;
826                         
827                 case BySourceFileName:
828                         region_list_display.set_column_title (0, _("Regions/file name"));
829                         break;
830                         
831                 case BySourceFileLength:
832                         region_list_display.set_column_title (0, _("Regions/file size"));
833                         break;
834                         
835                 case BySourceFileCreationDate:
836                         region_list_display.set_column_title (0, _("Regions/file date"));
837                         break;
838                         
839                 case BySourceFileFS:
840                         region_list_display.set_column_title (0, _("Regions/file system"));
841                         break;
842                 }
843                         
844                 region_list_display.sort ();
845         }
846 }
847
848 void
849 Editor::reset_region_list_sort_direction (bool up)
850 {
851         region_list_display.set_sort_type (up ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING);
852         region_list_display.sort ();
853 }
854
855 void
856 Editor::audition_region_from_region_list ()
857 {
858         if (region_list_button_region) {
859                 consider_auditioning (dynamic_cast<AudioRegion*> (region_list_button_region));
860         }
861 }
862
863 void
864 Editor::hide_region_from_region_list ()
865 {
866         if (session == 0 || region_list_button_region == 0) {
867                 return;
868         }
869
870         region_list_button_region->set_hidden (true);
871 }
872
873 void
874 Editor::remove_region_from_region_list ()
875 {
876         if (session == 0 || region_list_button_region == 0) {
877                 return;
878         }
879
880         session->remove_region_from_region_list (*region_list_button_region);
881 }
882
883 void
884 Editor::remove_selected_regions_from_region_list ()
885 {
886         using namespace Gtk::CTree_Helpers;
887         SelectionList& selected = region_list_display.selection();
888
889         /* called from idle context to avoid snafus with the list
890            state.
891         */
892         
893         if (selected.empty() || session == 0) {
894                 return;
895         }
896         
897         vector<Region*> to_be_deleted;
898
899         for (SelectionList::iterator i = selected.begin(); i != selected.end(); ++i) {
900                 to_be_deleted.push_back (static_cast<Region*> ((*i).get_data()));
901         }
902
903         for (vector<Region*>::iterator i = to_be_deleted.begin(); i != to_be_deleted.end(); ++i) {
904                 session->remove_region_from_region_list (**i);
905         }
906
907         return;
908 }
909
910 void  
911 Editor::region_list_display_drag_data_received  (GdkDragContext     *context,
912                                                  gint                x,
913                                                  gint                y,
914                                                  GtkSelectionData   *data,
915                                                  guint               info,
916                                                  guint               time)
917 {
918         vector<string> paths;
919
920         if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
921                 do_embed_sndfiles (paths, false);
922         }
923
924         gtk_drag_finish (context, TRUE, FALSE, time);
925 }