+
+
+bool
+Playlist::has_region_at (framepos_t const p) const
+{
+ RegionLock (const_cast<Playlist *> (this));
+
+ RegionList::const_iterator i = regions.begin ();
+ while (i != regions.end() && !(*i)->covers (p)) {
+ ++i;
+ }
+
+ return (i != regions.end());
+}
+
+/** Remove any region that uses a given source */
+void
+Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
+{
+ RegionLock rl (this);
+
+ RegionList::iterator i = regions.begin();
+ while (i != regions.end()) {
+ RegionList::iterator j = i;
+ ++j;
+
+ if ((*i)->uses_source (s)) {
+ remove_region_internal (*i);
+ }
+
+ i = j;
+ }
+}
+
+/** Look from a session frame time and find the start time of the next region
+ * which is on the top layer of this playlist.
+ * @param t Time to look from.
+ * @return Position of next top-layered region, or max_framepos if there isn't one.
+ */
+framepos_t
+Playlist::find_next_top_layer_position (framepos_t t) const
+{
+ RegionLock rlock (const_cast<Playlist *> (this));
+
+ layer_t const top = top_layer ();
+
+ RegionList copy = regions.rlist ();
+ copy.sort (RegionSortByPosition ());
+
+ for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
+ if ((*i)->position() >= t && (*i)->layer() == top) {
+ return (*i)->position();
+ }
+ }
+
+ return max_framepos;
+}
+
+boost::shared_ptr<Region>
+Playlist::combine (const RegionList& r)
+{
+ PropertyList plist;
+ uint32_t channels = 0;
+ uint32_t layer = 0;
+ framepos_t earliest_position = max_framepos;
+ vector<TwoRegions> old_and_new_regions;
+ vector<boost::shared_ptr<Region> > originals;
+ vector<boost::shared_ptr<Region> > copies;
+ string parent_name;
+ string child_name;
+ uint32_t max_level = 0;
+
+ /* find the maximum depth of all the regions we're combining */
+
+ for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
+ max_level = max (max_level, (*i)->max_source_level());
+ }
+
+ parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
+ child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
+
+ boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
+
+ for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
+ earliest_position = min (earliest_position, (*i)->position());
+ }
+
+ /* enable this so that we do not try to create xfades etc. as we add
+ * regions
+ */
+
+ pl->in_partition = true;
+
+ for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
+
+ /* copy the region */
+
+ boost::shared_ptr<Region> original_region = (*i);
+ boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
+
+ old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
+ originals.push_back (original_region);
+ copies.push_back (copied_region);
+
+ RegionFactory::add_compound_association (original_region, copied_region);
+
+ /* make position relative to zero */
+
+ pl->add_region (copied_region, original_region->position() - earliest_position);
+
+ /* use the maximum number of channels for any region */
+
+ channels = max (channels, original_region->n_channels());
+
+ /* it will go above the layer of the highest existing region */
+
+ layer = max (layer, original_region->layer());
+ }
+
+ pl->in_partition = false;
+
+ pre_combine (copies);
+
+ /* add any dependent regions to the new playlist */
+
+ copy_dependents (old_and_new_regions, pl.get());
+
+ /* now create a new PlaylistSource for each channel in the new playlist */
+
+ SourceList sources;
+ pair<framepos_t,framepos_t> extent = pl->get_extent();
+
+ for (uint32_t chn = 0; chn < channels; ++chn) {
+ sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
+
+ }
+
+ /* now a new whole-file region using the list of sources */
+
+ plist.add (Properties::start, 0);
+ plist.add (Properties::length, extent.second);
+ plist.add (Properties::name, parent_name);
+ plist.add (Properties::whole_file, true);
+
+ boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
+
+ /* now the non-whole-file region that we will actually use in the
+ * playlist
+ */
+
+ plist.clear ();
+ plist.add (Properties::start, 0);
+ plist.add (Properties::length, extent.second);
+ plist.add (Properties::name, child_name);
+ plist.add (Properties::layer, layer+1);
+
+ boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
+
+ /* remove all the selected regions from the current playlist
+ */
+
+ freeze ();
+
+ for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
+ remove_region (*i);
+ }
+
+ /* do type-specific stuff with the originals and the new compound
+ region
+ */
+
+ post_combine (originals, compound_region);
+
+ /* add the new region at the right location */
+
+ add_region (compound_region, earliest_position);
+
+ _combine_ops++;
+
+ thaw ();
+
+ return compound_region;
+}
+
+void
+Playlist::uncombine (boost::shared_ptr<Region> target)
+{
+ boost::shared_ptr<PlaylistSource> pls;
+ boost::shared_ptr<const Playlist> pl;
+ vector<boost::shared_ptr<Region> > originals;
+ vector<TwoRegions> old_and_new_regions;
+
+ // (1) check that its really a compound region
+
+ if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
+ return;
+ }
+
+ pl = pls->playlist();
+
+ framepos_t adjusted_start = 0; // gcc isn't smart enough
+ framepos_t adjusted_end = 0; // gcc isn't smart enough
+
+ /* the leftmost (earliest) edge of the compound region
+ starts at zero in its source, or larger if it
+ has been trimmed or content-scrolled.
+
+ the rightmost (latest) edge of the compound region
+ relative to its source is the starting point plus
+ the length of the region.
+ */
+
+ // (2) get all the original regions
+
+ const RegionList& rl (pl->region_list().rlist());
+ RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
+ frameoffset_t move_offset = 0;
+
+ /* there are two possibilities here:
+ 1) the playlist that the playlist source was based on
+ is us, so just add the originals (which belonged to
+ us anyway) back in the right place.
+
+ 2) the playlist that the playlist source was based on
+ is NOT us, so we need to make copies of each of
+ the original regions that we find, and add them
+ instead.
+ */
+ bool same_playlist = (pls->original() == id());
+
+ for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
+
+ boost::shared_ptr<Region> current (*i);
+
+ RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
+
+ if (ca == cassocs.end()) {
+ continue;
+ }
+
+ boost::shared_ptr<Region> original (ca->second);
+ bool modified_region;
+
+ if (i == rl.begin()) {
+ move_offset = (target->position() - original->position()) - target->start();
+ adjusted_start = original->position() + target->start();
+ adjusted_end = adjusted_start + target->length();
+ }
+
+ if (!same_playlist) {
+ framepos_t pos = original->position();
+ /* make a copy, but don't announce it */
+ original = RegionFactory::create (original, false);
+ /* the pure copy constructor resets position() to zero,
+ so fix that up.
+ */
+ original->set_position (pos);
+ }
+
+ /* check to see how the original region (in the
+ * playlist before compounding occured) overlaps
+ * with the new state of the compound region.
+ */
+
+ original->clear_changes ();
+ modified_region = false;
+
+ switch (original->coverage (adjusted_start, adjusted_end)) {
+ case OverlapNone:
+ /* original region does not cover any part
+ of the current state of the compound region
+ */
+ continue;
+
+ case OverlapInternal:
+ /* overlap is just a small piece inside the
+ * original so trim both ends
+ */
+ original->trim_to (adjusted_start, adjusted_end - adjusted_start);
+ modified_region = true;
+ break;
+
+ case OverlapExternal:
+ /* overlap fully covers original, so leave it
+ as is
+ */
+ break;
+
+ case OverlapEnd:
+ /* overlap starts within but covers end,
+ so trim the front of the region
+ */
+ original->trim_front (adjusted_start);
+ modified_region = true;
+ break;
+
+ case OverlapStart:
+ /* overlap covers start but ends within, so
+ * trim the end of the region.
+ */
+ original->trim_end (adjusted_end);
+ modified_region = true;
+ break;
+ }
+
+ if (move_offset) {
+ /* fix the position to match any movement of the compound region.
+ */
+ original->set_position (original->position() + move_offset);
+ modified_region = true;
+ }
+
+ if (modified_region) {
+ _session.add_command (new StatefulDiffCommand (original));
+ }
+
+ /* and add to the list of regions waiting to be
+ * re-inserted
+ */
+
+ originals.push_back (original);
+ old_and_new_regions.push_back (TwoRegions (*i, original));
+ }
+
+ pre_uncombine (originals, target);
+
+ in_partition = true;
+ freeze ();
+
+ // (3) remove the compound region
+
+ remove_region (target);
+
+ // (4) add the constituent regions
+
+ for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
+ add_region ((*i), (*i)->position());
+ }
+
+ /* now move dependent regions back from the compound to this playlist */
+
+ pl->copy_dependents (old_and_new_regions, this);
+
+ in_partition = false;
+ thaw ();
+}
+
+uint32_t
+Playlist::max_source_level () const
+{
+ RegionLock rlock (const_cast<Playlist *> (this));
+ uint32_t lvl = 0;
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ lvl = max (lvl, (*i)->max_source_level());
+ }
+
+ return lvl;
+}
+
+
+uint32_t
+Playlist::count_joined_regions () const
+{
+ RegionLock rlock (const_cast<Playlist *> (this));
+ uint32_t cnt = 0;
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->max_source_level() > 0) {
+ cnt++;
+ }
+ }
+
+ return cnt;
+}