+ start = ranges.front().start;
+
+ for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
+
+ pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
+
+ if (i == ranges.begin()) {
+ ret = pl;
+ } else {
+
+ /* paste the next section into the nascent playlist,
+ offset to reflect the start of the first range we
+ chopped.
+ */
+
+ ret->paste (pl, (*i).start - start, 1.0f);
+ }
+ }
+
+ return ret;
+ }
+
+ boost::shared_ptr<Playlist>
+ Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
+ {
+ boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
+ return cut_copy (pmf, ranges, result_is_hidden);
+ }
+
+ boost::shared_ptr<Playlist>
+ Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
+ {
+ boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
+ return cut_copy (pmf, ranges, result_is_hidden);
+ }
+
+ boost::shared_ptr<Playlist>
+ Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
+ {
+ boost::shared_ptr<Playlist> the_copy;
+ RegionList thawlist;
+ char buf[32];
+
+ snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
+ string new_name = _name;
+ new_name += '.';
+ new_name += buf;
+
+ if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
+ return boost::shared_ptr<Playlist>();
+ }
+
+ partition_internal (start, start+cnt-1, true, thawlist);
+
+ for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
+ (*i)->resume_property_changes();
+ }
+
+ return the_copy;
+ }
+
+ boost::shared_ptr<Playlist>
+ Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
+ {
+ char buf[32];
+
+ snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
+ string new_name = _name;
+ new_name += '.';
+ new_name += buf;
+
+ cnt = min (_get_extent().second - start, cnt);
+ return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
+ }
+
+ int
+ Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
+ {
+ times = fabs (times);
+
+ {
+ RegionLock rl1 (this);
+ RegionLock rl2 (other.get());
+
+ int itimes = (int) floor (times);
+ framepos_t pos = position;
+ framecnt_t const shift = other->_get_extent().second;
+ layer_t top = top_layer ();
+
+ while (itimes--) {
+ for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
+ boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
+
+ /* put these new regions on top of all existing ones, but preserve
+ the ordering they had in the original playlist.
+ */
+
+ copy_of_region->set_pending_layer (copy_of_region->layer() + top);
+ add_region_internal (copy_of_region, (*i)->position() + pos);
+ }
+ pos += shift;
+ }
+ }
+
+ return 0;
+ }
+
+
+ void
+ Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
+ {
+ times = fabs (times);
+
+ RegionLock rl (this);
+ int itimes = (int) floor (times);
+ framepos_t pos = position + 1;
+
+ while (itimes--) {
+ boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
+ copy->set_pending_layer (DBL_MAX);
+ add_region_internal (copy, pos);
+ pos += region->length();
+ }
+
+ if (floor (times) != times) {
+ framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
+ string name;
+ RegionFactory::region_name (name, region->name(), false);
+
+ {
+ PropertyList plist;
+
+ plist.add (Properties::start, region->start());
+ plist.add (Properties::length, length);
+ plist.add (Properties::name, name);
+
+ boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
+ sub->set_pending_layer (DBL_MAX);
+ add_region_internal (sub, pos);
+ }
+ }
+ }
+
+ void
+ Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
+ {
+ RegionLock rlock (this);
+ RegionList copy (regions.rlist());
+ RegionList fixup;
+
+ for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
+
+ if ((*r)->last_frame() < at) {
+ /* too early */
+ continue;
+ }
+
+ if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
+ /* intersected region */
+ if (!move_intersected) {
+ continue;
+ }
+ }
+
+ /* do not move regions glued to music time - that
+ has to be done separately.
+ */
+
+ if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
+ fixup.push_back (*r);
+ continue;
+ }
+
+ (*r)->set_position ((*r)->position() + distance);
+ }
+
+ /* XXX: may not be necessary; Region::post_set should do this, I think */
+ for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
+ (*r)->recompute_position_from_lock_style ();
+ }
+ }
+
+ void
+ Playlist::split (framepos_t at)
+ {
+ RegionLock rlock (this);
+ RegionList copy (regions.rlist());
+
+ /* use a copy since this operation can modify the region list
+ */
+
+ for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
+ _split_region (*r, at);
+ }
+ }
+
+ void
+ Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
+ {
+ RegionLock rl (this);
+ _split_region (region, playlist_position);
+ }
+
+ void
+ Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
+ {
+ if (!region->covers (playlist_position)) {
+ return;
+ }
+
+ if (region->position() == playlist_position ||
+ region->last_frame() == playlist_position) {
+ return;
+ }
+
+ boost::shared_ptr<Region> left;
+ boost::shared_ptr<Region> right;
+ frameoffset_t before;
+ frameoffset_t after;
+ string before_name;
+ string after_name;
+
+ /* split doesn't change anything about length, so don't try to splice */
+
+ bool old_sp = _splicing;
+ _splicing = true;
+
+ before = playlist_position - region->position();
+ after = region->length() - before;
+
+ RegionFactory::region_name (before_name, region->name(), false);
+
+ {
+ PropertyList plist;
+
+ plist.add (Properties::position, region->position ());
+ plist.add (Properties::length, before);
+ plist.add (Properties::name, before_name);
+ plist.add (Properties::left_of_split, true);
+ plist.add (Properties::layering_index, region->layering_index ());
+ plist.add (Properties::layer, region->layer ());
+
+ /* note: we must use the version of ::create with an offset here,
+ since it supplies that offset to the Region constructor, which
+ is necessary to get audio region gain envelopes right.
+ */
+ left = RegionFactory::create (region, 0, plist);
+ }
+
+ RegionFactory::region_name (after_name, region->name(), false);
+
+ {
+ PropertyList plist;
+
+ plist.add (Properties::position, region->position() + before);
+ plist.add (Properties::length, after);
+ plist.add (Properties::name, after_name);
+ plist.add (Properties::right_of_split, true);
+ plist.add (Properties::layering_index, region->layering_index ());
+ plist.add (Properties::layer, region->layer ());
+
+ /* same note as above */
+ right = RegionFactory::create (region, before, plist);
+ }
+
+ add_region_internal (left, region->position());
+ add_region_internal (right, region->position() + before);
+
+ finalize_split_region (region, left, right);
+
+ remove_region_internal (region);
+
+ _splicing = old_sp;
+ }
+
+ void
+ Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
+ {
+ if (_splicing || in_set_state) {
+ /* don't respond to splicing moves or state setting */
+ return;
+ }
+
+ if (_edit_mode == Splice) {
+ splice_locked (at, distance, exclude);
+ }
+ }
+
+ void
+ Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
+ {
+ if (_splicing || in_set_state) {
+ /* don't respond to splicing moves or state setting */
+ return;
+ }
+
+ if (_edit_mode == Splice) {
+ splice_unlocked (at, distance, exclude);
+ }
+ }
+
+ void
+ Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
+ {
+ {
+ RegionLock rl (this);
+ core_splice (at, distance, exclude);
+ }
+ }
+
+ void
+ Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
+ {
+ core_splice (at, distance, exclude);
+ }
+
+ void
+ Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
+ {
+ _splicing = true;
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ if (exclude && (*i) == exclude) {
+ continue;
+ }
+
+ if ((*i)->position() >= at) {
+ framepos_t new_pos = (*i)->position() + distance;
+ if (new_pos < 0) {
+ new_pos = 0;
+ } else if (new_pos >= max_framepos - (*i)->length()) {
+ new_pos = max_framepos - (*i)->length();
+ }
+
+ (*i)->set_position (new_pos);
+ }
+ }
+
+ _splicing = false;
+
+ notify_contents_changed ();
+ }
+
+ void
+ Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
+ {
+ if (in_set_state || _splicing || _nudging || _shuffling) {
+ return;
+ }
+
+ if (what_changed.contains (Properties::position)) {
+
+ /* remove it from the list then add it back in
+ the right place again.
+ */
+
+ RegionSortByPosition cmp;
+
+ RegionList::iterator i = find (regions.begin(), regions.end(), region);
+
+ if (i == regions.end()) {
+ /* the region bounds are being modified but its not currently
+ in the region list. we will use its bounds correctly when/if
+ it is added
+ */
+ return;
+ }
+
+ regions.erase (i);
+ regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
+ }
+
+ if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
+
+ frameoffset_t delta = 0;
+
+ if (what_changed.contains (Properties::position)) {
+ delta = region->position() - region->last_position();
+ }
+
+ if (what_changed.contains (Properties::length)) {
+ delta += region->length() - region->last_length();
+ }
+
+ if (delta) {
+ possibly_splice (region->last_position() + region->last_length(), delta, region);
+ }
+
+ if (holding_state ()) {
+ pending_bounds.push_back (region);
+ } else {
+ notify_contents_changed ();
+ relayer ();
+ check_dependents (region, false);
+ }
+ }
+ }
+
+ void
+ Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
+ {
+ boost::shared_ptr<Region> region (weak_region.lock());
+
+ if (!region) {
+ return;
+ }
+
+ /* this makes a virtual call to the right kind of playlist ... */
+
+ region_changed (what_changed, region);
+ }
+
+ bool
+ Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
+ {
+ PropertyChange our_interests;
+ PropertyChange bounds;
+ PropertyChange pos_and_length;
+ bool save = false;
+
+ if (in_set_state || in_flush) {
+ return false;
+ }
+
+ our_interests.add (Properties::muted);
+ our_interests.add (Properties::layer);
+ our_interests.add (Properties::opaque);
+
+ bounds.add (Properties::start);
+ bounds.add (Properties::position);
+ bounds.add (Properties::length);
+
+ pos_and_length.add (Properties::position);
+ pos_and_length.add (Properties::length);
+
+ if (what_changed.contains (bounds)) {
+ region_bounds_changed (what_changed, region);
+ save = !(_splicing || _nudging);
+ }
+
+ if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
+ check_dependents (region, false);
+ }
+
+ if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
+ notify_region_moved (region);
+ } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
+ notify_region_end_trimmed (region);
+ } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
+ notify_region_start_trimmed (region);
+ }
+
+ /* don't notify about layer changes, since we are the only object that can initiate
+ them, and we notify in ::relayer()
+ */