~ubuntu-branches/ubuntu/utopic/ardour3/utopic

« back to all changes in this revision

Viewing changes to gtk2_ardour/editor_regions.cc

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2013-09-21 19:05:02 UTC
  • Revision ID: package-import@ubuntu.com-20130921190502-8gsftrku6jnzhd7v
Tags: upstream-3.4~dfsg
ImportĀ upstreamĀ versionĀ 3.4~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    Copyright (C) 2000-2005 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
*/
 
19
 
 
20
#include <cstdlib>
 
21
#include <cmath>
 
22
#include <algorithm>
 
23
#include <string>
 
24
#include <sstream>
 
25
 
 
26
#include "pbd/basename.h"
 
27
#include "pbd/enumwriter.h"
 
28
 
 
29
#include "ardour/audioregion.h"
 
30
#include "ardour/audiofilesource.h"
 
31
#include "ardour/silentfilesource.h"
 
32
#include "ardour/region_factory.h"
 
33
#include "ardour/session.h"
 
34
#include "ardour/profile.h"
 
35
 
 
36
#include "gtkmm2ext/choice.h"
 
37
#include "gtkmm2ext/treeutils.h"
 
38
 
 
39
#include "audio_clock.h"
 
40
#include "editor.h"
 
41
#include "editing.h"
 
42
#include "keyboard.h"
 
43
#include "ardour_ui.h"
 
44
#include "gui_thread.h"
 
45
#include "actions.h"
 
46
#include "region_view.h"
 
47
#include "utils.h"
 
48
#include "editor_regions.h"
 
49
#include "editor_drag.h"
 
50
#include "main_clock.h"
 
51
 
 
52
#include "i18n.h"
 
53
 
 
54
using namespace std;
 
55
using namespace ARDOUR;
 
56
using namespace PBD;
 
57
using namespace Gtk;
 
58
using namespace Glib;
 
59
using namespace Editing;
 
60
using Gtkmm2ext::Keyboard;
 
61
 
 
62
struct ColumnInfo {
 
63
    int         index;
 
64
    const char* label;
 
65
    const char* tooltip;
 
66
};
 
67
 
 
68
EditorRegions::EditorRegions (Editor* e)
 
69
        : EditorComponent (e)
 
70
        , old_focus (0)
 
71
        , name_editable (0)
 
72
        , _menu (0)
 
73
        , _show_automatic_regions (true)
 
74
        , ignore_region_list_selection_change (false)
 
75
        , ignore_selected_region_change (false)
 
76
        , _no_redisplay (false)
 
77
        , _sort_type ((Editing::RegionListSortType) 0)
 
78
        , expanded (false)
 
79
{
 
80
        _display.set_size_request (100, -1);
 
81
        _display.set_rules_hint (true);
 
82
        _display.set_name ("EditGroupList");
 
83
 
 
84
        /* Try to prevent single mouse presses from initiating edits.
 
85
           This relies on a hack in gtktreeview.c:gtk_treeview_button_press()
 
86
        */
 
87
        _display.set_data ("mouse-edits-require-mod1", (gpointer) 0x1);
 
88
 
 
89
        _model = TreeStore::create (_columns);
 
90
        _model->set_sort_func (0, sigc::mem_fun (*this, &EditorRegions::sorter));
 
91
        _model->set_sort_column (0, SORT_ASCENDING);
 
92
 
 
93
        _display.set_model (_model);
 
94
 
 
95
        _display.append_column ("", _columns.name);
 
96
        _display.append_column ("", _columns.position);
 
97
        _display.append_column ("", _columns.end);
 
98
        _display.append_column ("", _columns.length);
 
99
        _display.append_column ("", _columns.sync);
 
100
        _display.append_column ("", _columns.fadein);
 
101
        _display.append_column ("", _columns.fadeout);
 
102
        _display.append_column ("", _columns.locked);
 
103
        _display.append_column ("", _columns.glued);
 
104
        _display.append_column ("", _columns.muted);
 
105
        _display.append_column ("", _columns.opaque);
 
106
 
 
107
        TreeViewColumn* col;
 
108
        Gtk::Label* l;
 
109
 
 
110
        ColumnInfo ci[] = {
 
111
                { 0, _("Region"), _("Region name, with number of channels in []'s") },
 
112
                { 1, _("Position"),  _("Position of start of region") },
 
113
                { 2, _("End"),  _("Position of end of region") },
 
114
                { 3, _("Length"),  _("Length of the region") },
 
115
                { 4, _("Sync"),  _("Position of region sync point, relative to start of the region") },
 
116
                { 5, _("Fade In"),  _("Length of region fade-in (units: secondary clock), () if disabled") },
 
117
                { 6, _("Fade Out"),  _("Length of region fade-out (units: secondary clock), () if dsisabled") },
 
118
                { 7, _("L"),  _("Region position locked?") },
 
119
                { 8, _("G"),  _("Region position glued to Bars|Beats time?") },
 
120
                { 9, _("M"),  _("Region muted?") },
 
121
                { 10, _("O"),  _("Region opaque (blocks regions below it from being heard)?") },
 
122
                { -1, 0, 0 }
 
123
        };
 
124
        
 
125
        for (int i = 0; ci[i].index >= 0; ++i) {
 
126
                col = _display.get_column (ci[i].index);
 
127
                l = manage (new Label (ci[i].label));
 
128
                ARDOUR_UI::instance()->set_tip (*l, ci[i].tooltip);
 
129
                col->set_widget (*l);
 
130
                l->show ();
 
131
 
 
132
                if (ci[i].index > 6) {
 
133
                        col->set_expand (false);
 
134
                        col->set_alignment (ALIGN_CENTER);
 
135
                } 
 
136
        }
 
137
 
 
138
        _display.set_headers_visible (true);
 
139
        _display.set_rules_hint ();
 
140
 
 
141
        /* show path as the row tooltip */
 
142
        _display.set_tooltip_column (14); /* path */
 
143
 
 
144
        CellRendererText* region_name_cell = dynamic_cast<CellRendererText*>(_display.get_column_cell_renderer (0));
 
145
        region_name_cell->property_editable() = true;
 
146
        region_name_cell->signal_edited().connect (sigc::mem_fun (*this, &EditorRegions::name_edit));
 
147
        region_name_cell->signal_editing_started().connect (sigc::mem_fun (*this, &EditorRegions::name_editing_started));
 
148
 
 
149
        _display.get_selection()->set_select_function (sigc::mem_fun (*this, &EditorRegions::selection_filter));
 
150
 
 
151
        TreeViewColumn* tv_col = _display.get_column(0);
 
152
        CellRendererText* renderer = dynamic_cast<CellRendererText*>(_display.get_column_cell_renderer (0));
 
153
        tv_col->add_attribute(renderer->property_text(), _columns.name);
 
154
        tv_col->add_attribute(renderer->property_foreground_gdk(), _columns.color_);
 
155
        tv_col->set_expand (true);
 
156
 
 
157
        CellRendererToggle* locked_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (7));
 
158
        locked_cell->property_activatable() = true;
 
159
        locked_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRegions::locked_changed));
 
160
 
 
161
        TreeViewColumn* locked_col = _display.get_column (7);
 
162
        locked_col->add_attribute (locked_cell->property_visible(), _columns.property_toggles_visible);
 
163
 
 
164
        CellRendererToggle* glued_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (8));
 
165
        glued_cell->property_activatable() = true;
 
166
        glued_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRegions::glued_changed));
 
167
 
 
168
        TreeViewColumn* glued_col = _display.get_column (8);
 
169
        glued_col->add_attribute (glued_cell->property_visible(), _columns.property_toggles_visible);
 
170
 
 
171
        CellRendererToggle* muted_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (9));
 
172
        muted_cell->property_activatable() = true;
 
173
        muted_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRegions::muted_changed));
 
174
 
 
175
        TreeViewColumn* muted_col = _display.get_column (9);
 
176
        muted_col->add_attribute (muted_cell->property_visible(), _columns.property_toggles_visible);
 
177
 
 
178
        CellRendererToggle* opaque_cell = dynamic_cast<CellRendererToggle*> (_display.get_column_cell_renderer (10));
 
179
        opaque_cell->property_activatable() = true;
 
180
        opaque_cell->signal_toggled().connect (sigc::mem_fun (*this, &EditorRegions::opaque_changed));
 
181
 
 
182
        TreeViewColumn* opaque_col = _display.get_column (10);
 
183
        opaque_col->add_attribute (opaque_cell->property_visible(), _columns.property_toggles_visible);
 
184
 
 
185
        _display.get_selection()->set_mode (SELECTION_MULTIPLE);
 
186
        _display.add_object_drag (_columns.region.index(), "regions");
 
187
 
 
188
        /* setup DnD handling */
 
189
 
 
190
        list<TargetEntry> region_list_target_table;
 
191
 
 
192
        region_list_target_table.push_back (TargetEntry ("text/plain"));
 
193
        region_list_target_table.push_back (TargetEntry ("text/uri-list"));
 
194
        region_list_target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
 
195
 
 
196
        _display.add_drop_targets (region_list_target_table);
 
197
        _display.signal_drag_data_received().connect (sigc::mem_fun(*this, &EditorRegions::drag_data_received));
 
198
 
 
199
        _scroller.add (_display);
 
200
        _scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC);
 
201
 
 
202
        _display.signal_button_press_event().connect (sigc::mem_fun(*this, &EditorRegions::button_press), false);
 
203
        _change_connection = _display.get_selection()->signal_changed().connect (sigc::mem_fun(*this, &EditorRegions::selection_changed));
 
204
 
 
205
        _scroller.signal_key_press_event().connect (sigc::mem_fun(*this, &EditorRegions::key_press), false);
 
206
        _scroller.signal_focus_in_event().connect (sigc::mem_fun (*this, &EditorRegions::focus_in), false);
 
207
        _scroller.signal_focus_out_event().connect (sigc::mem_fun (*this, &EditorRegions::focus_out));
 
208
 
 
209
        _display.signal_enter_notify_event().connect (sigc::mem_fun (*this, &EditorRegions::enter_notify), false);
 
210
        _display.signal_leave_notify_event().connect (sigc::mem_fun (*this, &EditorRegions::leave_notify), false);
 
211
 
 
212
        // _display.signal_popup_menu().connect (sigc::bind (sigc::mem_fun (*this, &Editor::show__display_context_menu), 1, 0));
 
213
 
 
214
        //ARDOUR_UI::instance()->secondary_clock.mode_changed.connect (sigc::mem_fun(*this, &Editor::redisplay_regions));
 
215
        ARDOUR_UI::instance()->secondary_clock->mode_changed.connect (sigc::mem_fun(*this, &EditorRegions::update_all_rows));
 
216
        ARDOUR::Region::RegionPropertyChanged.connect (region_property_connection, MISSING_INVALIDATOR, boost::bind (&EditorRegions::region_changed, this, _1, _2), gui_context());
 
217
        ARDOUR::RegionFactory::CheckNewRegion.connect (check_new_region_connection, MISSING_INVALIDATOR, boost::bind (&EditorRegions::add_region, this, _1), gui_context());
 
218
 
 
219
        e->EditorFreeze.connect (editor_freeze_connection, MISSING_INVALIDATOR, boost::bind (&EditorRegions::freeze_tree_model, this), gui_context());
 
220
        e->EditorThaw.connect (editor_thaw_connection, MISSING_INVALIDATOR, boost::bind (&EditorRegions::thaw_tree_model, this), gui_context());
 
221
}
 
222
 
 
223
bool
 
224
EditorRegions::focus_in (GdkEventFocus*)
 
225
{
 
226
        Window* win = dynamic_cast<Window*> (_scroller.get_toplevel ());
 
227
 
 
228
        if (win) {
 
229
                old_focus = win->get_focus ();
 
230
        } else {
 
231
                old_focus = 0;
 
232
        }
 
233
 
 
234
        name_editable = 0;
 
235
 
 
236
        /* try to do nothing on focus in (doesn't work, hence selection_count nonsense) */
 
237
        return true;
 
238
}
 
239
 
 
240
bool
 
241
EditorRegions::focus_out (GdkEventFocus*)
 
242
{
 
243
        if (old_focus) {
 
244
                old_focus->grab_focus ();
 
245
                old_focus = 0;
 
246
        }
 
247
 
 
248
        name_editable = 0;
 
249
 
 
250
        return false;
 
251
}
 
252
 
 
253
bool
 
254
EditorRegions::enter_notify (GdkEventCrossing*)
 
255
{
 
256
        if (name_editable) {
 
257
                return true;
 
258
        }
 
259
 
 
260
        /* arm counter so that ::selection_filter() will deny selecting anything for the
 
261
           next two attempts to change selection status.
 
262
        */
 
263
        _scroller.grab_focus ();
 
264
        Keyboard::magic_widget_grab_focus ();
 
265
        return false;
 
266
}
 
267
 
 
268
bool
 
269
EditorRegions::leave_notify (GdkEventCrossing*)
 
270
{
 
271
        if (old_focus) {
 
272
                old_focus->grab_focus ();
 
273
                old_focus = 0;
 
274
        }
 
275
 
 
276
        Keyboard::magic_widget_drop_focus ();
 
277
        return false;
 
278
}
 
279
 
 
280
void
 
281
EditorRegions::set_session (ARDOUR::Session* s)
 
282
{
 
283
        SessionHandlePtr::set_session (s);
 
284
        redisplay ();
 
285
}
 
286
 
 
287
void
 
288
EditorRegions::add_region (boost::shared_ptr<Region> region)
 
289
{
 
290
        if (!region || !_session) {
 
291
                return;
 
292
        }
 
293
 
 
294
        string str;
 
295
        TreeModel::Row row;
 
296
        Gdk::Color c;
 
297
        bool missing_source = boost::dynamic_pointer_cast<SilentFileSource>(region->source());
 
298
 
 
299
        if (!_show_automatic_regions && region->automatic()) {
 
300
                return;
 
301
        }
 
302
 
 
303
        if (region->hidden()) {
 
304
 
 
305
                TreeModel::iterator iter = _model->get_iter ("0");
 
306
                TreeModel::Row parent;
 
307
 
 
308
                if (!iter) {
 
309
                        parent = *(_model->append());
 
310
                        parent[_columns.name] = _("Hidden");
 
311
                        boost::shared_ptr<Region> proxy = parent[_columns.region];
 
312
                        proxy.reset ();
 
313
                } else {
 
314
                        string s = (*iter)[_columns.name];
 
315
                        if (s != _("Hidden")) {
 
316
                                parent = *(_model->insert(iter));
 
317
                                parent[_columns.name] = _("Hidden");
 
318
                                boost::shared_ptr<Region> proxy = parent[_columns.region];
 
319
                                proxy.reset ();
 
320
                        } else {
 
321
                                parent = *iter;
 
322
                        }
 
323
                }
 
324
 
 
325
                row = *(_model->append (parent.children()));
 
326
 
 
327
        } else if (region->whole_file()) {
 
328
 
 
329
                TreeModel::iterator i;
 
330
                TreeModel::Children rows = _model->children();
 
331
 
 
332
                for (i = rows.begin(); i != rows.end(); ++i) {
 
333
                        boost::shared_ptr<Region> rr = (*i)[_columns.region];
 
334
 
 
335
                        if (rr && region->region_list_equivalent (rr)) {
 
336
                                return;
 
337
                        }
 
338
                }
 
339
 
 
340
                row = *(_model->append());
 
341
 
 
342
                if (missing_source) {
 
343
                        c.set_rgb(65535,0,0);     // FIXME: error color from style
 
344
 
 
345
                } else if (region->automatic()){
 
346
                        c.set_rgb(0,65535,0);     // FIXME: error color from style
 
347
 
 
348
                } else {
 
349
                        set_color(c, rgba_from_style ("RegionListWholeFile", 0xff, 0, 0, 0, "fg", Gtk::STATE_NORMAL, false ));
 
350
 
 
351
                }
 
352
 
 
353
                row[_columns.color_] = c;
 
354
 
 
355
                if (region->source()->name()[0] == '/') { // external file
 
356
 
 
357
                        if (region->whole_file()) {
 
358
 
 
359
                                boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(region->source());
 
360
                                str = ".../";
 
361
 
 
362
                                if (afs) {
 
363
                                        str = region_name_from_path (afs->path(), region->n_channels() > 1);
 
364
                                } else {
 
365
                                        str += region->source()->name();
 
366
                                }
 
367
 
 
368
                        } else {
 
369
                                str = region->name();
 
370
                        }
 
371
 
 
372
                } else {
 
373
                        str = region->name();
 
374
                }
 
375
 
 
376
                if (region->n_channels() > 1) {
 
377
                        std::stringstream foo;
 
378
                        foo << region->n_channels ();
 
379
                        str += " [";
 
380
                        str += foo.str();
 
381
                        str += "]";
 
382
                }
 
383
 
 
384
                row[_columns.name] = str;
 
385
                row[_columns.region] = region;
 
386
                row[_columns.property_toggles_visible] = false;
 
387
 
 
388
                if (missing_source) {
 
389
                        row[_columns.path] = _("(MISSING) ") + region->source()->name();
 
390
 
 
391
                } else {
 
392
                        boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource>(region->source());
 
393
                        if (fs) {
 
394
                                row[_columns.path] = fs->path();
 
395
                        } else {
 
396
                                row[_columns.path] = region->source()->name();
 
397
                        }
 
398
                }
 
399
 
 
400
                region_row_map.insert(pair<boost::shared_ptr<ARDOUR::Region>, Gtk::TreeModel::RowReference>(region, TreeRowReference(_model, TreePath (row))) );
 
401
                parent_regions_sources_map.insert(pair<string, Gtk::TreeModel::RowReference>(region->source_string(), TreeRowReference(_model, TreePath (row))) );
 
402
 
 
403
                return;
 
404
 
 
405
        } else {
 
406
                // find parent node, add as new child
 
407
                TreeModel::iterator i;
 
408
 
 
409
                boost::unordered_map<string, Gtk::TreeModel::RowReference>::iterator it;
 
410
 
 
411
                it = parent_regions_sources_map.find (region->source_string());
 
412
 
 
413
                if (it != parent_regions_sources_map.end()){
 
414
 
 
415
                        TreeModel::iterator j = _model->get_iter ((*it).second.get_path());
 
416
 
 
417
                        TreeModel::iterator ii;
 
418
                        TreeModel::Children subrows = (*j).children();
 
419
 
 
420
                        /* XXXX: should we be accounting for all regions? */
 
421
                        /*
 
422
                        for (ii = subrows.begin(); ii != subrows.end(); ++ii) {
 
423
                                boost::shared_ptr<Region> rr = (*ii)[_columns.region];
 
424
 
 
425
                                if (region->region_list_equivalent (rr)) {
 
426
                                        return;
 
427
                                }
 
428
                        }
 
429
                        */
 
430
 
 
431
                        row = *(_model->insert (subrows.end()));
 
432
                        
 
433
                } else {
 
434
                        row = *(_model->append());
 
435
                }
 
436
 
 
437
                row[_columns.property_toggles_visible] = true;
 
438
        }
 
439
 
 
440
        row[_columns.region] = region;
 
441
 
 
442
        region_row_map.insert(pair<boost::shared_ptr<ARDOUR::Region>, Gtk::TreeModel::RowReference>(region, TreeRowReference(_model, TreePath (row))) );
 
443
 
 
444
        populate_row(region, (*row));
 
445
}
 
446
 
 
447
void
 
448
EditorRegions::remove_unused_regions ()
 
449
{
 
450
        vector<string> choices;
 
451
        string prompt;
 
452
 
 
453
        if (!_session) {
 
454
                return;
 
455
        }
 
456
 
 
457
        prompt  = _("Do you really want to remove unused regions?"
 
458
                    "\n(This is destructive and cannot be undone)");
 
459
 
 
460
        choices.push_back (_("No, do nothing."));
 
461
        choices.push_back (_("Yes, remove."));
 
462
 
 
463
        Gtkmm2ext::Choice prompter (_("Remove unused regions"), prompt, choices);
 
464
 
 
465
        if (prompter.run () == 1) {
 
466
                _no_redisplay = true;
 
467
                _session->cleanup_regions ();
 
468
                _no_redisplay = false;
 
469
                redisplay ();
 
470
        }
 
471
}
 
472
 
 
473
void
 
474
EditorRegions::region_changed (boost::shared_ptr<Region> r, const PropertyChange& what_changed)
 
475
{
 
476
        PropertyChange our_interests;
 
477
 
 
478
        our_interests.add (ARDOUR::Properties::name);
 
479
        our_interests.add (ARDOUR::Properties::position);
 
480
        our_interests.add (ARDOUR::Properties::length);
 
481
        our_interests.add (ARDOUR::Properties::start);
 
482
        our_interests.add (ARDOUR::Properties::locked);
 
483
        our_interests.add (ARDOUR::Properties::position_lock_style);
 
484
        our_interests.add (ARDOUR::Properties::muted);
 
485
        our_interests.add (ARDOUR::Properties::opaque);
 
486
        our_interests.add (ARDOUR::Properties::fade_in);
 
487
        our_interests.add (ARDOUR::Properties::fade_out);
 
488
        our_interests.add (ARDOUR::Properties::fade_in_active);
 
489
        our_interests.add (ARDOUR::Properties::fade_out_active);
 
490
 
 
491
        if (what_changed.contains (our_interests)) {
 
492
 
 
493
                if (last_row != 0) {
 
494
 
 
495
                        TreeModel::iterator j = _model->get_iter (last_row.get_path());
 
496
                        boost::shared_ptr<Region> c = (*j)[_columns.region];
 
497
 
 
498
                        if (c == r) {
 
499
                                populate_row (r, (*j));
 
500
 
 
501
                                if (what_changed.contains (ARDOUR::Properties::hidden)) {
 
502
                                        redisplay ();
 
503
                                }
 
504
 
 
505
                                return;
 
506
                        }
 
507
                }
 
508
 
 
509
                RegionRowMap::iterator it;
 
510
 
 
511
                it = region_row_map.find (r);
 
512
 
 
513
                if (it != region_row_map.end()){
 
514
 
 
515
                        TreeModel::iterator j = _model->get_iter ((*it).second.get_path());
 
516
                        boost::shared_ptr<Region> c = (*j)[_columns.region];
 
517
 
 
518
                        if (c == r) {
 
519
                                populate_row (r, (*j));
 
520
 
 
521
                                if (what_changed.contains (ARDOUR::Properties::hidden)) {
 
522
                                        redisplay ();
 
523
                                }
 
524
 
 
525
                                return;
 
526
                        }
 
527
                }
 
528
        }
 
529
 
 
530
        if (what_changed.contains (ARDOUR::Properties::hidden)) {
 
531
                redisplay ();
 
532
        }
 
533
}
 
534
 
 
535
void
 
536
EditorRegions::selection_changed ()
 
537
{
 
538
        if (ignore_region_list_selection_change) {
 
539
                return;
 
540
        }
 
541
 
 
542
        _editor->_region_selection_change_updates_region_list = false;
 
543
 
 
544
        if (_display.get_selection()->count_selected_rows() > 0) {
 
545
 
 
546
                TreeIter iter;
 
547
                TreeView::Selection::ListHandle_Path rows = _display.get_selection()->get_selected_rows ();
 
548
 
 
549
                _editor->get_selection().clear_regions ();
 
550
 
 
551
                for (TreeView::Selection::ListHandle_Path::iterator i = rows.begin(); i != rows.end(); ++i) {
 
552
 
 
553
                        if ((iter = _model->get_iter (*i))) {
 
554
 
 
555
                                boost::shared_ptr<Region> region = (*iter)[_columns.region];
 
556
 
 
557
                                // they could have clicked on a row that is just a placeholder, like "Hidden"
 
558
                                // although that is not allowed by our selection filter. check it anyway
 
559
                                // since we need a region ptr.
 
560
 
 
561
                                if (region) {
 
562
 
 
563
                                        _change_connection.block (true);
 
564
                                        _editor->set_selected_regionview_from_region_list (region, Selection::Add);
 
565
                                        _change_connection.block (false);
 
566
                                }
 
567
                        }
 
568
                        
 
569
                }
 
570
        } else {
 
571
                _editor->get_selection().clear_regions ();
 
572
        }
 
573
 
 
574
        _editor->_region_selection_change_updates_region_list = true;
 
575
}
 
576
 
 
577
void
 
578
EditorRegions::set_selected (RegionSelection& regions)
 
579
{
 
580
        for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
 
581
 
 
582
                boost::shared_ptr<Region> r ((*i)->region());
 
583
 
 
584
                RegionRowMap::iterator it;
 
585
 
 
586
                it = region_row_map.find (r);
 
587
 
 
588
                if (it != region_row_map.end()){
 
589
                        TreeModel::iterator j = _model->get_iter ((*it).second.get_path());
 
590
                        _display.get_selection()->select(*j);
 
591
                }
 
592
        }
 
593
}
 
594
 
 
595
void
 
596
EditorRegions::redisplay ()
 
597
{
 
598
        if (_no_redisplay || !_session) {
 
599
                return;
 
600
        }
 
601
 
 
602
        bool tree_expanded = false;
 
603
 
 
604
        /* If the list was expanded prior to rebuilding, expand it again afterwards */
 
605
        if (toggle_full_action()->get_active()) {
 
606
                tree_expanded = true;
 
607
        }
 
608
 
 
609
        _display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
 
610
        _model->clear ();
 
611
        _model->set_sort_column (-2, SORT_ASCENDING); //Disable sorting to gain performance
 
612
 
 
613
 
 
614
        region_row_map.clear();
 
615
        parent_regions_sources_map.clear();
 
616
 
 
617
        /* now add everything we have, via a temporary list used to help with sorting */
 
618
 
 
619
        const RegionFactory::RegionMap& regions (RegionFactory::regions());
 
620
 
 
621
        for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end(); ++i) {
 
622
 
 
623
                if ( i->second->whole_file()) {
 
624
                        /* add automatic regions first so that children can find their parents as we add them */
 
625
                        add_region (i->second);
 
626
                        continue;
 
627
                }
 
628
 
 
629
                tmp_region_list.push_front (i->second);
 
630
        }
 
631
 
 
632
        for (list<boost::shared_ptr<Region> >::iterator r = tmp_region_list.begin(); r != tmp_region_list.end(); ++r) {
 
633
                add_region (*r);
 
634
        }
 
635
 
 
636
        _model->set_sort_column (0, SORT_ASCENDING); // renabale sorting
 
637
        _display.set_model (_model);
 
638
 
 
639
        tmp_region_list.clear();
 
640
 
 
641
        if (tree_expanded) {
 
642
                _display.expand_all();
 
643
        }
 
644
}
 
645
 
 
646
void
 
647
EditorRegions::update_row (boost::shared_ptr<Region> region)
 
648
{
 
649
        if (!region || !_session) {
 
650
                return;
 
651
        }
 
652
 
 
653
        RegionRowMap::iterator it;
 
654
 
 
655
        it = region_row_map.find (region);
 
656
 
 
657
        if (it != region_row_map.end()){
 
658
 
 
659
                TreeModel::iterator j = _model->get_iter ((*it).second.get_path());
 
660
                populate_row(region, (*j));
 
661
        }
 
662
}
 
663
 
 
664
void
 
665
EditorRegions::update_all_rows ()
 
666
{
 
667
        if (!_session) {
 
668
                return;
 
669
        }
 
670
 
 
671
        RegionRowMap::iterator i;
 
672
 
 
673
        for (i = region_row_map.begin(); i != region_row_map.end(); ++i) {
 
674
 
 
675
                TreeModel::iterator j = _model->get_iter ((*i).second.get_path());
 
676
 
 
677
                boost::shared_ptr<Region> region = (*j)[_columns.region];
 
678
 
 
679
                if (!region->automatic()) {
 
680
                        populate_row(region, (*j));
 
681
                }
 
682
        }
 
683
}
 
684
 
 
685
void
 
686
EditorRegions::format_position (framepos_t pos, char* buf, size_t bufsize, bool onoff)
 
687
{
 
688
        Timecode::BBT_Time bbt;
 
689
        Timecode::Time timecode;
 
690
 
 
691
        switch (ARDOUR_UI::instance()->secondary_clock->mode ()) {
 
692
        case AudioClock::BBT:
 
693
                _session->tempo_map().bbt_time (pos, bbt);
 
694
                if (onoff) {
 
695
                        snprintf (buf, bufsize, "%03d|%02d|%04d" , bbt.bars, bbt.beats, bbt.ticks);
 
696
                } else {
 
697
                        snprintf (buf, bufsize, "(%03d|%02d|%04d)" , bbt.bars, bbt.beats, bbt.ticks);
 
698
                }
 
699
                break;
 
700
 
 
701
        case AudioClock::MinSec:
 
702
                framepos_t left;
 
703
                int hrs;
 
704
                int mins;
 
705
                float secs;
 
706
 
 
707
                left = pos;
 
708
                hrs = (int) floor (left / (_session->frame_rate() * 60.0f * 60.0f));
 
709
                left -= (framecnt_t) floor (hrs * _session->frame_rate() * 60.0f * 60.0f);
 
710
                mins = (int) floor (left / (_session->frame_rate() * 60.0f));
 
711
                left -= (framecnt_t) floor (mins * _session->frame_rate() * 60.0f);
 
712
                secs = left / (float) _session->frame_rate();
 
713
                if (onoff) {
 
714
                        snprintf (buf, bufsize, "%02d:%02d:%06.3f", hrs, mins, secs);
 
715
                } else {
 
716
                        snprintf (buf, bufsize, "(%02d:%02d:%06.3f)", hrs, mins, secs);
 
717
                }
 
718
                break;
 
719
 
 
720
        case AudioClock::Frames:
 
721
                if (onoff) {
 
722
                        snprintf (buf, bufsize, "%" PRId64, pos);
 
723
                } else {
 
724
                        snprintf (buf, bufsize, "(%" PRId64 ")", pos);
 
725
                }
 
726
                break;
 
727
 
 
728
        case AudioClock::Timecode:
 
729
        default:
 
730
                _session->timecode_time (pos, timecode);
 
731
                if (onoff) {
 
732
                        snprintf (buf, bufsize, "%02d:%02d:%02d:%02d", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
 
733
                } else {
 
734
                        snprintf (buf, bufsize, "(%02d:%02d:%02d:%02d)", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
 
735
                }
 
736
                break;
 
737
        }
 
738
}
 
739
 
 
740
void
 
741
EditorRegions::populate_row (boost::shared_ptr<Region> region, TreeModel::Row const &row)
 
742
{
 
743
        boost::shared_ptr<AudioRegion> audioregion = boost::dynamic_pointer_cast<AudioRegion>(region);
 
744
        //uint32_t used = _session->playlists->region_use_count (region);
 
745
        /* Presently a region is only used once so let's save on the sequential scan to determine use count */
 
746
        uint32_t used = 1;
 
747
 
 
748
        populate_row_position (region, row, used);
 
749
        populate_row_end (region, row, used);
 
750
        populate_row_sync (region, row, used);
 
751
        populate_row_fade_in (region, row, used, audioregion);
 
752
        populate_row_fade_out (region, row, used, audioregion);
 
753
        populate_row_locked (region, row, used);
 
754
        populate_row_glued (region, row, used);
 
755
        populate_row_muted (region, row, used);
 
756
        populate_row_opaque (region, row, used);
 
757
        populate_row_length (region, row);
 
758
        populate_row_source (region, row);
 
759
        populate_row_name (region, row);
 
760
        populate_row_used (region, row, used);
 
761
}
 
762
 
 
763
#if 0
 
764
        if (audioRegion && fades_in_seconds) {
 
765
 
 
766
                framepos_t left;
 
767
                int mins;
 
768
                int millisecs;
 
769
 
 
770
                left = audioRegion->fade_in()->back()->when;
 
771
                mins = (int) floor (left / (_session->frame_rate() * 60.0f));
 
772
                left -= (framepos_t) floor (mins * _session->frame_rate() * 60.0f);
 
773
                millisecs = (int) floor ((left * 1000.0f) / _session->frame_rate());
 
774
 
 
775
                if (audioRegion->fade_in()->back()->when >= _session->frame_rate()) {
 
776
                        sprintf (fadein_str, "%01dM %01dmS", mins, millisecs);
 
777
                } else {
 
778
                        sprintf (fadein_str, "%01dmS", millisecs);
 
779
                }
 
780
 
 
781
                left = audioRegion->fade_out()->back()->when;
 
782
                mins = (int) floor (left / (_session->frame_rate() * 60.0f));
 
783
                left -= (framepos_t) floor (mins * _session->frame_rate() * 60.0f);
 
784
                millisecs = (int) floor ((left * 1000.0f) / _session->frame_rate());
 
785
 
 
786
                if (audioRegion->fade_out()->back()->when >= _session->frame_rate()) {
 
787
                        sprintf (fadeout_str, "%01dM %01dmS", mins, millisecs);
 
788
                } else {
 
789
                        sprintf (fadeout_str, "%01dmS", millisecs);
 
790
                }
 
791
        }
 
792
#endif
 
793
 
 
794
void
 
795
EditorRegions::populate_row_used (boost::shared_ptr<Region>, TreeModel::Row const& row, uint32_t used)
 
796
{
 
797
        char buf[8];
 
798
        snprintf (buf, sizeof (buf), "%4d" , used);
 
799
        row[_columns.used] = buf;
 
800
}
 
801
 
 
802
void
 
803
EditorRegions::populate_row_length (boost::shared_ptr<Region> region, TreeModel::Row const &row)
 
804
{
 
805
        char buf[16];
 
806
        format_position (region->length(), buf, sizeof (buf));
 
807
        row[_columns.length] = buf;
 
808
}
 
809
 
 
810
void
 
811
EditorRegions::populate_row_end (boost::shared_ptr<Region> region, TreeModel::Row const &row, uint32_t used)
 
812
{
 
813
        if (region->whole_file()) {
 
814
                row[_columns.end] = "";
 
815
        } else if (used > 1) {
 
816
                row[_columns.end] = _("Mult.");
 
817
        } else {
 
818
                char buf[16];
 
819
                format_position (region->last_frame(), buf, sizeof (buf));
 
820
                row[_columns.end] = buf;
 
821
        }
 
822
}
 
823
 
 
824
void
 
825
EditorRegions::populate_row_position (boost::shared_ptr<Region> region, TreeModel::Row const &row, uint32_t used)
 
826
{
 
827
        if (region->whole_file()) {
 
828
                row[_columns.position] = "";
 
829
        } else if (used > 1) {
 
830
                row[_columns.position] = _("Mult.");
 
831
        } else {
 
832
                char buf[16];
 
833
                format_position (region->position(), buf, sizeof (buf));
 
834
                row[_columns.position] = buf;
 
835
        }
 
836
}
 
837
 
 
838
void
 
839
EditorRegions::populate_row_sync (boost::shared_ptr<Region> region, TreeModel::Row const &row, uint32_t used)
 
840
{
 
841
        if (region->whole_file()) {
 
842
                row[_columns.sync] = "";
 
843
        } else if (used > 1) {
 
844
                row[_columns.sync] = _("Mult."); /* translators: a short phrase for "multiple" as in "many" */
 
845
        } else {
 
846
                if (region->sync_position() == region->position()) {
 
847
                        row[_columns.sync] = _("Start");
 
848
                } else if (region->sync_position() == (region->last_frame())) {
 
849
                        row[_columns.sync] = _("End");
 
850
                } else {
 
851
                        char buf[16];
 
852
                        format_position (region->sync_position(), buf, sizeof (buf));
 
853
                        row[_columns.sync] = buf;
 
854
                }
 
855
        }
 
856
}
 
857
 
 
858
void
 
859
EditorRegions::populate_row_fade_in (boost::shared_ptr<Region> region, TreeModel::Row const &row, uint32_t used, boost::shared_ptr<AudioRegion> audioregion)
 
860
{
 
861
        if (!audioregion || region->whole_file()) {
 
862
                row[_columns.fadein] = "";
 
863
        } else {
 
864
                if (used > 1) {
 
865
                        row[_columns.fadein] = _("Multiple");
 
866
                } else {
 
867
                        char buf[32];
 
868
                        format_position (audioregion->fade_in()->back()->when, buf, sizeof (buf), audioregion->fade_in_active());
 
869
                        row[_columns.fadein] = buf;
 
870
                }
 
871
        }
 
872
}
 
873
 
 
874
void
 
875
EditorRegions::populate_row_fade_out (boost::shared_ptr<Region> region, TreeModel::Row const &row, uint32_t used, boost::shared_ptr<AudioRegion> audioregion)
 
876
{
 
877
        if (!audioregion || region->whole_file()) {
 
878
                row[_columns.fadeout] = "";
 
879
        } else {
 
880
                if (used > 1) {
 
881
                        row[_columns.fadeout] = _("Multiple");
 
882
                } else {
 
883
                        char buf[32];
 
884
                        format_position (audioregion->fade_out()->back()->when, buf, sizeof (buf), audioregion->fade_out_active());
 
885
                        row[_columns.fadeout] = buf;
 
886
                }
 
887
        }
 
888
}
 
889
 
 
890
void
 
891
EditorRegions::populate_row_locked (boost::shared_ptr<Region> region, TreeModel::Row const &row, uint32_t used)
 
892
{
 
893
        if (region->whole_file()) {
 
894
                row[_columns.locked] = false;
 
895
        } else if (used > 1) {
 
896
                row[_columns.locked] = false;
 
897
        } else {
 
898
                row[_columns.locked] = region->locked();
 
899
        }
 
900
}
 
901
 
 
902
void
 
903
EditorRegions::populate_row_glued (boost::shared_ptr<Region> region, TreeModel::Row const &row, uint32_t used)
 
904
{
 
905
        if (region->whole_file() || used > 1) {
 
906
                row[_columns.glued] = false;
 
907
        } else {
 
908
                if (region->position_lock_style() == MusicTime) {
 
909
                        row[_columns.glued] = true;
 
910
                } else {
 
911
                        row[_columns.glued] = false;
 
912
                }
 
913
        }
 
914
}
 
915
 
 
916
void
 
917
EditorRegions::populate_row_muted (boost::shared_ptr<Region> region, TreeModel::Row const &row, uint32_t used)
 
918
{
 
919
        if (region->whole_file() || used > 1) {
 
920
                row[_columns.muted] = false;
 
921
        } else {
 
922
                row[_columns.muted] = region->muted();
 
923
        }
 
924
}
 
925
 
 
926
void
 
927
EditorRegions::populate_row_opaque (boost::shared_ptr<Region> region, TreeModel::Row const &row, uint32_t used)
 
928
{
 
929
        if (region->whole_file() || used > 1) {
 
930
                row[_columns.opaque] = false;
 
931
        } else {
 
932
                row[_columns.opaque] = region->opaque();
 
933
        }
 
934
}
 
935
 
 
936
void
 
937
EditorRegions::populate_row_name (boost::shared_ptr<Region> region, TreeModel::Row const &row)
 
938
{
 
939
        if (region->n_channels() > 1) {
 
940
                row[_columns.name] = string_compose("%1  [%2]", region->name(), region->n_channels());
 
941
        } else {
 
942
                row[_columns.name] = region->name();
 
943
        }
 
944
}
 
945
 
 
946
void
 
947
EditorRegions::populate_row_source (boost::shared_ptr<Region> region, TreeModel::Row const &row)
 
948
{
 
949
        if (boost::dynamic_pointer_cast<SilentFileSource>(region->source())) {
 
950
                row[_columns.path] = _("MISSING ") + region->source()->name();
 
951
        } else {
 
952
                row[_columns.path] = region->source()->name();
 
953
        }
 
954
}
 
955
 
 
956
void
 
957
EditorRegions::toggle_show_auto_regions ()
 
958
{
 
959
        _show_automatic_regions = toggle_show_auto_regions_action()->get_active();
 
960
        redisplay ();
 
961
}
 
962
 
 
963
void
 
964
EditorRegions::toggle_full ()
 
965
{
 
966
        set_full (toggle_full_action()->get_active ());
 
967
}
 
968
 
 
969
void
 
970
EditorRegions::set_full (bool f)
 
971
{
 
972
        if (f) {
 
973
                _display.expand_all ();
 
974
                expanded = true;
 
975
        } else {
 
976
                _display.collapse_all ();
 
977
                expanded = false;
 
978
        }
 
979
}
 
980
 
 
981
void
 
982
EditorRegions::show_context_menu (int button, int time)
 
983
{
 
984
        if (_menu == 0) {
 
985
                _menu = dynamic_cast<Menu*> (ActionManager::get_widget ("/RegionListMenu"));
 
986
        }
 
987
 
 
988
        if (_display.get_selection()->count_selected_rows() > 0) {
 
989
                ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, true);
 
990
        } else {
 
991
                ActionManager::set_sensitive (ActionManager::region_list_selection_sensitive_actions, false);
 
992
        }
 
993
 
 
994
        /* Enable the "Show" option if any selected regions are hidden, and vice versa for "Hide" */
 
995
 
 
996
        bool have_shown = false;
 
997
        bool have_hidden = false;
 
998
 
 
999
        TreeView::Selection::ListHandle_Path rows = _display.get_selection()->get_selected_rows ();
 
1000
        for (TreeView::Selection::ListHandle_Path::iterator i = rows.begin(); i != rows.end(); ++i) {
 
1001
                TreeIter t = _model->get_iter (*i);
 
1002
                boost::shared_ptr<Region> r = (*t)[_columns.region];
 
1003
                if (r) {
 
1004
                        if (r->hidden ()) {
 
1005
                                have_hidden = true;
 
1006
                        } else {
 
1007
                                have_shown = true;
 
1008
                        }
 
1009
                }
 
1010
        }
 
1011
 
 
1012
        hide_action()->set_sensitive (have_shown);
 
1013
        show_action()->set_sensitive (have_hidden);
 
1014
 
 
1015
        _menu->popup (button, time);
 
1016
}
 
1017
 
 
1018
bool
 
1019
EditorRegions::key_press (GdkEventKey* ev)
 
1020
{
 
1021
        TreeViewColumn *col;
 
1022
 
 
1023
        switch (ev->keyval) {
 
1024
        case GDK_Tab:
 
1025
        case GDK_ISO_Left_Tab:
 
1026
 
 
1027
                if (name_editable) {
 
1028
                        name_editable->editing_done ();
 
1029
                        name_editable = 0;
 
1030
                }
 
1031
 
 
1032
                col = _display.get_column (0); // select&focus on name column
 
1033
 
 
1034
                if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
 
1035
                        treeview_select_previous (_display, _model, col);
 
1036
                } else {
 
1037
                        treeview_select_next (_display, _model, col);
 
1038
                }
 
1039
 
 
1040
                return true;
 
1041
                break;
 
1042
 
 
1043
        default:
 
1044
                break;
 
1045
        }
 
1046
 
 
1047
        return false;
 
1048
}
 
1049
 
 
1050
bool
 
1051
EditorRegions::button_press (GdkEventButton *ev)
 
1052
{
 
1053
        boost::shared_ptr<Region> region;
 
1054
        TreeIter iter;
 
1055
        TreeModel::Path path;
 
1056
        TreeViewColumn* column;
 
1057
        int cellx;
 
1058
        int celly;
 
1059
 
 
1060
        if (_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
 
1061
                if ((iter = _model->get_iter (path))) {
 
1062
                        region = (*iter)[_columns.region];
 
1063
                }
 
1064
        }
 
1065
 
 
1066
        if (Keyboard::is_context_menu_event (ev)) {
 
1067
                show_context_menu (ev->button, ev->time);
 
1068
                return false;
 
1069
        }
 
1070
 
 
1071
        if (region != 0 && Keyboard::is_button2_event (ev)) {
 
1072
                // start/stop audition
 
1073
                if (!Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
 
1074
                        _editor->consider_auditioning (region);
 
1075
                }
 
1076
                return true;
 
1077
        }
 
1078
 
 
1079
        return false;
 
1080
}
 
1081
 
 
1082
int
 
1083
EditorRegions::sorter (TreeModel::iterator a, TreeModel::iterator b)
 
1084
{
 
1085
        int cmp = 0;
 
1086
 
 
1087
        boost::shared_ptr<Region> r1 = (*a)[_columns.region];
 
1088
        boost::shared_ptr<Region> r2 = (*b)[_columns.region];
 
1089
 
 
1090
        /* handle rows without regions, like "Hidden" */
 
1091
 
 
1092
        if (r1 == 0) {
 
1093
                return -1;
 
1094
        }
 
1095
 
 
1096
        if (r2 == 0) {
 
1097
                return 1;
 
1098
        }
 
1099
 
 
1100
        boost::shared_ptr<AudioRegion> region1 = boost::dynamic_pointer_cast<AudioRegion> (r1);
 
1101
        boost::shared_ptr<AudioRegion> region2 = boost::dynamic_pointer_cast<AudioRegion> (r2);
 
1102
 
 
1103
        if (region1 == 0 || region2 == 0) {
 
1104
                std::string s1;
 
1105
                std::string s2;
 
1106
                switch (_sort_type) {
 
1107
                case ByName:
 
1108
                        s1 = (*a)[_columns.name];
 
1109
                        s2 = (*b)[_columns.name];
 
1110
                        return (s1.compare (s2));
 
1111
                default:
 
1112
                        return 0;
 
1113
                }
 
1114
        }
 
1115
 
 
1116
        switch (_sort_type) {
 
1117
        case ByName:
 
1118
                cmp = strcasecmp (region1->name().c_str(), region2->name().c_str());
 
1119
                break;
 
1120
 
 
1121
        case ByLength:
 
1122
                cmp = region1->length() - region2->length();
 
1123
                break;
 
1124
 
 
1125
        case ByPosition:
 
1126
                cmp = region1->position() - region2->position();
 
1127
                break;
 
1128
 
 
1129
        case ByTimestamp:
 
1130
                cmp = region1->source()->timestamp() - region2->source()->timestamp();
 
1131
                break;
 
1132
 
 
1133
        case ByStartInFile:
 
1134
                cmp = region1->start() - region2->start();
 
1135
                break;
 
1136
 
 
1137
        case ByEndInFile:
 
1138
                // cerr << "Compare " << (region1->start() + region1->length()) << " and " << (region2->start() + region2->length()) << endl;
 
1139
                cmp = (region1->start() + region1->length()) - (region2->start() + region2->length());
 
1140
                break;
 
1141
 
 
1142
        case BySourceFileName:
 
1143
                cmp = strcasecmp (region1->source()->name().c_str(), region2->source()->name().c_str());
 
1144
                break;
 
1145
 
 
1146
        case BySourceFileLength:
 
1147
                cmp = region1->source_length(0) - region2->source_length(0);
 
1148
                break;
 
1149
 
 
1150
        case BySourceFileCreationDate:
 
1151
                cmp = region1->source()->timestamp() - region2->source()->timestamp();
 
1152
                break;
 
1153
 
 
1154
        case BySourceFileFS:
 
1155
                if (region1->source()->name() == region2->source()->name()) {
 
1156
                        cmp = strcasecmp (region1->name().c_str(),  region2->name().c_str());
 
1157
                } else {
 
1158
                        cmp = strcasecmp (region1->source()->name().c_str(),  region2->source()->name().c_str());
 
1159
                }
 
1160
                break;
 
1161
        }
 
1162
 
 
1163
        // cerr << "Comparison on " << enum_2_string (_sort_type) << " gives " << cmp << endl;
 
1164
 
 
1165
        if (cmp < 0) {
 
1166
                return -1;
 
1167
        } else if (cmp > 0) {
 
1168
                return 1;
 
1169
        } else {
 
1170
                return 0;
 
1171
        }
 
1172
}
 
1173
 
 
1174
void
 
1175
EditorRegions::reset_sort_type (RegionListSortType type, bool force)
 
1176
{
 
1177
        if (type != _sort_type || force) {
 
1178
                _sort_type = type;
 
1179
                _model->set_sort_func (0, (sigc::mem_fun (*this, &EditorRegions::sorter)));
 
1180
        }
 
1181
}
 
1182
 
 
1183
void
 
1184
EditorRegions::reset_sort_direction (bool up)
 
1185
{
 
1186
        _model->set_sort_column (0, up ? SORT_ASCENDING : SORT_DESCENDING);
 
1187
}
 
1188
 
 
1189
void
 
1190
EditorRegions::selection_mapover (sigc::slot<void,boost::shared_ptr<Region> > sl)
 
1191
{
 
1192
        Glib::RefPtr<TreeSelection> selection = _display.get_selection();
 
1193
        TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
 
1194
        TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
 
1195
 
 
1196
        if (selection->count_selected_rows() == 0 || _session == 0) {
 
1197
                return;
 
1198
        }
 
1199
 
 
1200
        for (; i != rows.end(); ++i) {
 
1201
                TreeIter iter;
 
1202
 
 
1203
                if ((iter = _model->get_iter (*i))) {
 
1204
 
 
1205
                        /* some rows don't have a region associated with them, but can still be
 
1206
                           selected (XXX maybe prevent them from being selected)
 
1207
                        */
 
1208
 
 
1209
                        boost::shared_ptr<Region> r = (*iter)[_columns.region];
 
1210
 
 
1211
                        if (r) {
 
1212
                                sl (r);
 
1213
                        }
 
1214
                }
 
1215
        }
 
1216
}
 
1217
 
 
1218
 
 
1219
void
 
1220
EditorRegions::drag_data_received (const RefPtr<Gdk::DragContext>& context,
 
1221
                                   int x, int y,
 
1222
                                   const SelectionData& data,
 
1223
                                   guint info, guint time)
 
1224
{
 
1225
        vector<string> paths;
 
1226
 
 
1227
        if (data.get_target() == "GTK_TREE_MODEL_ROW") {
 
1228
                /* something is being dragged over the region list */
 
1229
                _editor->_drags->abort ();
 
1230
                _display.on_drag_data_received (context, x, y, data, info, time);
 
1231
                return;
 
1232
        }
 
1233
 
 
1234
        if (_editor->convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
 
1235
                framepos_t pos = 0;
 
1236
                bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
 
1237
 
 
1238
                if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
 
1239
                        _editor->do_import (paths, Editing::ImportDistinctFiles, Editing::ImportAsRegion, SrcBest, pos);
 
1240
                } else {
 
1241
                        _editor->do_embed (paths, Editing::ImportDistinctFiles, ImportAsRegion, pos);
 
1242
                }
 
1243
                context->drag_finish (true, false, time);
 
1244
        }
 
1245
}
 
1246
 
 
1247
bool
 
1248
EditorRegions::selection_filter (const RefPtr<TreeModel>& model, const TreeModel::Path& path, bool already_selected)
 
1249
{
 
1250
        /* not possible to select rows that do not represent regions, like "Hidden" */
 
1251
 
 
1252
        if (already_selected) {
 
1253
                /* deselecting anything is OK with us */
 
1254
                return true;
 
1255
        }
 
1256
 
 
1257
        TreeModel::iterator iter = model->get_iter (path);
 
1258
 
 
1259
        if (iter) {
 
1260
                boost::shared_ptr<Region> r =(*iter)[_columns.region];
 
1261
                if (!r) {
 
1262
                        return false;
 
1263
                }
 
1264
        }
 
1265
 
 
1266
        return true;
 
1267
}
 
1268
 
 
1269
void
 
1270
EditorRegions::name_editing_started (CellEditable* ce, const Glib::ustring&)
 
1271
{
 
1272
        name_editable = ce;
 
1273
 
 
1274
        /* give it a special name */
 
1275
 
 
1276
        Gtk::Entry *e = dynamic_cast<Gtk::Entry*> (ce);
 
1277
 
 
1278
        if (e) {
 
1279
                e->set_name (X_("RegionNameEditorEntry"));
 
1280
        }
 
1281
}
 
1282
 
 
1283
void
 
1284
EditorRegions::name_edit (const std::string& path, const std::string& new_text)
 
1285
{
 
1286
        name_editable = 0;
 
1287
 
 
1288
        boost::shared_ptr<Region> region;
 
1289
        TreeIter iter;
 
1290
 
 
1291
        if ((iter = _model->get_iter (path))) {
 
1292
                region = (*iter)[_columns.region];
 
1293
                (*iter)[_columns.name] = new_text;
 
1294
        }
 
1295
 
 
1296
        /* now mapover everything */
 
1297
 
 
1298
        if (region) {
 
1299
                vector<RegionView*> equivalents;
 
1300
                _editor->get_regions_corresponding_to (region, equivalents, false);
 
1301
 
 
1302
                for (vector<RegionView*>::iterator i = equivalents.begin(); i != equivalents.end(); ++i) {
 
1303
                        if (new_text != (*i)->region()->name()) {
 
1304
                                (*i)->region()->set_name (new_text);
 
1305
                        }
 
1306
                }
 
1307
        }
 
1308
 
 
1309
}
 
1310
 
 
1311
/** @return Region that has been dragged out of the list, or 0 */
 
1312
boost::shared_ptr<Region>
 
1313
EditorRegions::get_dragged_region ()
 
1314
{
 
1315
        list<boost::shared_ptr<Region> > regions;
 
1316
        TreeView* source;
 
1317
        _display.get_object_drag_data (regions, &source);
 
1318
 
 
1319
        if (regions.empty()) {
 
1320
                return boost::shared_ptr<Region> ();
 
1321
        }
 
1322
 
 
1323
        assert (regions.size() == 1);
 
1324
        return regions.front ();
 
1325
}
 
1326
 
 
1327
void
 
1328
EditorRegions::clear ()
 
1329
{
 
1330
        _display.set_model (Glib::RefPtr<Gtk::TreeStore> (0));
 
1331
        _model->clear ();
 
1332
        _display.set_model (_model);
 
1333
 
 
1334
        /* Clean up the maps */
 
1335
        region_row_map.clear();
 
1336
        parent_regions_sources_map.clear();
 
1337
}
 
1338
 
 
1339
boost::shared_ptr<Region>
 
1340
EditorRegions::get_single_selection ()
 
1341
{
 
1342
        Glib::RefPtr<TreeSelection> selected = _display.get_selection();
 
1343
 
 
1344
        if (selected->count_selected_rows() != 1) {
 
1345
                return boost::shared_ptr<Region> ();
 
1346
        }
 
1347
 
 
1348
        TreeView::Selection::ListHandle_Path rows = selected->get_selected_rows ();
 
1349
 
 
1350
        /* only one row selected, so rows.begin() is it */
 
1351
 
 
1352
        TreeIter iter = _model->get_iter (*rows.begin());
 
1353
 
 
1354
        if (!iter) {
 
1355
                return boost::shared_ptr<Region> ();
 
1356
        }
 
1357
 
 
1358
        return (*iter)[_columns.region];
 
1359
}
 
1360
 
 
1361
void
 
1362
EditorRegions::freeze_tree_model (){
 
1363
 
 
1364
        _display.set_model (Glib::RefPtr<Gtk::TreeStore>(0));
 
1365
        _model->set_sort_column (-2, SORT_ASCENDING); //Disable sorting to gain performance
 
1366
 
 
1367
}
 
1368
 
 
1369
void
 
1370
EditorRegions::thaw_tree_model (){
 
1371
 
 
1372
        _model->set_sort_column (0, SORT_ASCENDING); // renabale sorting
 
1373
        _display.set_model (_model);
 
1374
 
 
1375
        if (toggle_full_action()->get_active()) {
 
1376
                _display.expand_all();
 
1377
        }
 
1378
}
 
1379
 
 
1380
void
 
1381
EditorRegions::locked_changed (std::string const & path)
 
1382
{
 
1383
        TreeIter i = _model->get_iter (path);
 
1384
        if (i) {
 
1385
                boost::shared_ptr<ARDOUR::Region> region = (*i)[_columns.region];
 
1386
                if (region) {
 
1387
                        region->set_locked (!(*i)[_columns.locked]);
 
1388
                }
 
1389
        }
 
1390
}
 
1391
 
 
1392
void
 
1393
EditorRegions::glued_changed (std::string const & path)
 
1394
{
 
1395
        TreeIter i = _model->get_iter (path);
 
1396
        if (i) {
 
1397
                boost::shared_ptr<ARDOUR::Region> region = (*i)[_columns.region];
 
1398
                if (region) {
 
1399
                        /* `glued' means MusicTime, and we're toggling here */
 
1400
                        region->set_position_lock_style ((*i)[_columns.glued] ? AudioTime : MusicTime);
 
1401
                }
 
1402
        }
 
1403
 
 
1404
}
 
1405
 
 
1406
void
 
1407
EditorRegions::muted_changed (std::string const & path)
 
1408
{
 
1409
        TreeIter i = _model->get_iter (path);
 
1410
        if (i) {
 
1411
                boost::shared_ptr<ARDOUR::Region> region = (*i)[_columns.region];
 
1412
                if (region) {
 
1413
                        region->set_muted (!(*i)[_columns.muted]);
 
1414
                }
 
1415
        }
 
1416
 
 
1417
}
 
1418
 
 
1419
void
 
1420
EditorRegions::opaque_changed (std::string const & path)
 
1421
{
 
1422
        TreeIter i = _model->get_iter (path);
 
1423
        if (i) {
 
1424
                boost::shared_ptr<ARDOUR::Region> region = (*i)[_columns.region];
 
1425
                if (region) {
 
1426
                        region->set_opaque (!(*i)[_columns.opaque]);
 
1427
                }
 
1428
        }
 
1429
 
 
1430
}
 
1431
 
 
1432
XMLNode &
 
1433
EditorRegions::get_state () const
 
1434
{
 
1435
        XMLNode* node = new XMLNode (X_("RegionList"));
 
1436
 
 
1437
        node->add_property (X_("sort-type"), enum_2_string (_sort_type));
 
1438
 
 
1439
        RefPtr<Action> act = ActionManager::get_action (X_("RegionList"), X_("SortAscending"));
 
1440
        bool const ascending = RefPtr<RadioAction>::cast_dynamic(act)->get_active ();
 
1441
        node->add_property (X_("sort-ascending"), ascending ? "yes" : "no");
 
1442
        node->add_property (X_("show-all"), toggle_full_action()->get_active() ? "yes" : "no");
 
1443
        node->add_property (X_("show-automatic-regions"), _show_automatic_regions ? "yes" : "no");
 
1444
 
 
1445
        return *node;
 
1446
}
 
1447
 
 
1448
void
 
1449
EditorRegions::set_state (const XMLNode & node)
 
1450
{
 
1451
        bool changed = false;
 
1452
 
 
1453
        if (node.name() != X_("RegionList")) {
 
1454
                return;
 
1455
        }
 
1456
 
 
1457
        XMLProperty const * p = node.property (X_("sort-type"));
 
1458
 
 
1459
        if (p) {
 
1460
                Editing::RegionListSortType const t = static_cast<Editing::RegionListSortType> (string_2_enum (p->value(), _sort_type));
 
1461
 
 
1462
                if (_sort_type != t) {
 
1463
                        changed = true;
 
1464
                }
 
1465
 
 
1466
                reset_sort_type (t, true);
 
1467
                RefPtr<RadioAction> ract = sort_type_action (t);
 
1468
                ract->set_active ();
 
1469
        }
 
1470
 
 
1471
        p = node.property (X_("sort-ascending"));
 
1472
 
 
1473
        if (p) {
 
1474
                bool const yn = string_is_affirmative (p->value ());
 
1475
                SortType old_sort_type;
 
1476
                int old_sort_column;
 
1477
 
 
1478
                _model->get_sort_column_id (old_sort_column, old_sort_type);
 
1479
 
 
1480
                if (old_sort_type != (yn ? SORT_ASCENDING : SORT_DESCENDING)) {
 
1481
                        changed = true;
 
1482
                }
 
1483
 
 
1484
                reset_sort_direction (yn);
 
1485
                RefPtr<Action> act;
 
1486
 
 
1487
                if (yn) {
 
1488
                        act = ActionManager::get_action (X_("RegionList"), X_("SortAscending"));
 
1489
                } else {
 
1490
                        act = ActionManager::get_action (X_("RegionList"), X_("SortDescending"));
 
1491
                }
 
1492
 
 
1493
                RefPtr<RadioAction>::cast_dynamic(act)->set_active ();
 
1494
        }
 
1495
 
 
1496
        p = node.property (X_("show-all"));
 
1497
        if (p) {
 
1498
                bool const yn = string_is_affirmative (p->value ());
 
1499
 
 
1500
                if (expanded != yn) {
 
1501
                        changed = true;
 
1502
                }
 
1503
 
 
1504
                set_full (yn);
 
1505
                toggle_full_action()->set_active (yn);
 
1506
        }
 
1507
 
 
1508
        p = node.property (X_("show-automatic-regions"));
 
1509
        if (p) {
 
1510
                bool const yn = string_is_affirmative (p->value ());
 
1511
 
 
1512
                if (yn != _show_automatic_regions) {
 
1513
                        _show_automatic_regions = yn;
 
1514
                        toggle_show_auto_regions_action()->set_active (yn);
 
1515
                        changed = true;
 
1516
                }
 
1517
        }
 
1518
 
 
1519
        if (changed) {
 
1520
                redisplay ();
 
1521
        }
 
1522
}
 
1523
 
 
1524
RefPtr<RadioAction>
 
1525
EditorRegions::sort_type_action (Editing::RegionListSortType t) const
 
1526
{
 
1527
        const char* action = 0;
 
1528
 
 
1529
        switch (t) {
 
1530
        case Editing::ByName:
 
1531
                action = X_("SortByRegionName");
 
1532
                break;
 
1533
        case Editing::ByLength:
 
1534
                action = X_("SortByRegionLength");
 
1535
                break;
 
1536
        case Editing::ByPosition:
 
1537
                action = X_("SortByRegionPosition");
 
1538
                break;
 
1539
        case Editing::ByTimestamp:
 
1540
                action = X_("SortByRegionTimestamp");
 
1541
                break;
 
1542
        case Editing::ByStartInFile:
 
1543
                action = X_("SortByRegionStartinFile");
 
1544
                break;
 
1545
        case Editing::ByEndInFile:
 
1546
                action = X_("SortByRegionEndinFile");
 
1547
                break;
 
1548
        case Editing::BySourceFileName:
 
1549
                action = X_("SortBySourceFileName");
 
1550
                break;
 
1551
        case Editing::BySourceFileLength:
 
1552
                action = X_("SortBySourceFileLength");
 
1553
                break;
 
1554
        case Editing::BySourceFileCreationDate:
 
1555
                action = X_("SortBySourceFileCreationDate");
 
1556
                break;
 
1557
        case Editing::BySourceFileFS:
 
1558
                action = X_("SortBySourceFilesystem");
 
1559
                break;
 
1560
        default:
 
1561
                fatal << string_compose (_("programming error: %1: %2"), "EditorRegions: impossible sort type", (int) t) << endmsg;
 
1562
                /*NOTREACHED*/
 
1563
        }
 
1564
 
 
1565
        RefPtr<Action> act = ActionManager::get_action (X_("RegionList"), action);
 
1566
        assert (act);
 
1567
 
 
1568
        return RefPtr<RadioAction>::cast_dynamic (act);
 
1569
}
 
1570
 
 
1571
RefPtr<Action>
 
1572
EditorRegions::hide_action () const
 
1573
{
 
1574
        return ActionManager::get_action (X_("RegionList"), X_("rlHide"));
 
1575
 
 
1576
}
 
1577
 
 
1578
RefPtr<Action>
 
1579
EditorRegions::show_action () const
 
1580
{
 
1581
        return ActionManager::get_action (X_("RegionList"), X_("rlShow"));
 
1582
}
 
1583
 
 
1584
RefPtr<Action>
 
1585
EditorRegions::remove_unused_regions_action () const
 
1586
{
 
1587
        return ActionManager::get_action (X_("RegionList"), X_("removeUnusedRegions"));
 
1588
}
 
1589
 
 
1590
RefPtr<ToggleAction>
 
1591
EditorRegions::toggle_full_action () const
 
1592
{
 
1593
        Glib::RefPtr<Action> act = ActionManager::get_action (X_("RegionList"), X_("rlShowAll"));
 
1594
        assert (act);
 
1595
        return Glib::RefPtr<ToggleAction>::cast_dynamic (act);
 
1596
}
 
1597
 
 
1598
RefPtr<ToggleAction>
 
1599
EditorRegions::toggle_show_auto_regions_action () const
 
1600
{
 
1601
        Glib::RefPtr<Action> act = ActionManager::get_action (X_("RegionList"), X_("rlShowAuto"));
 
1602
        assert (act);
 
1603
        return Glib::RefPtr<ToggleAction>::cast_dynamic (act);
 
1604
}