1 #include <msp/strings/format.h>
2 #include <msp/strings/utils.h>
5 #include "trackchain.h"
12 TrackChain::TrackChain(Layout &l):
15 layout.signal_object_removed.connect(sigc::mem_fun(this, &TrackChain::object_removed));
18 void TrackChain::set_name(const string &n)
21 signal_name_changed.emit(name);
24 void TrackChain::add_track(Track &track)
26 if(tracks.count(&track))
29 Validity valid = check_validity(track);
31 throw_bad_chain(valid);
33 tracks.insert(&track);
35 on_track_added(track);
36 signal_track_added.emit(track);
39 void TrackChain::add_tracks(const TrackSet &trks)
42 for(TrackSet::const_iterator i=trks.begin(); i!=trks.end(); ++i)
46 while(!pending.empty())
48 Validity valid = UNLINKED;
49 for(TrackSet::iterator i=pending.begin(); i!=pending.end(); ++i)
50 if((valid=check_validity(**i))==VALID)
57 signal_track_added.emit(*t);
62 throw_bad_chain(valid);
66 TrackChain::Validity TrackChain::check_validity(Track &track) const
75 return ends[0]->get_link_slot(track)>=0 ? VALID : UNLINKED;
78 Validity result = UNLINKED;
79 for(unsigned i=0; i<2; ++i)
81 int j = ends[i]->get_link_slot(track);
84 const TrackType::Endpoint &ep1 = ends[i].endpoint();
85 const TrackType::Endpoint &ep2 = ends[i]->get_type().get_endpoint(j);
86 if(!ep1.has_common_paths(ep2))
96 void TrackChain::throw_bad_chain(Validity v)
99 throw bad_chain("unlinked");
101 throw bad_chain("bad path");
102 else if(v==INCOMPATIBLE)
103 throw bad_chain("incompatible");
106 void TrackChain::update_ends(Track &track)
109 // We don't really know the proper endpoint yet; just pick something
110 ends[0] = TrackIter(&track, 0);
113 // We're adding the second track; determine endpoints for both
114 ends[0] = TrackIter(&*ends[0], ends[0]->get_link_slot(track));
115 ends[1] = TrackIter(&track, track.get_link_slot(*ends[0]));
120 for(unsigned i=0; i<2; ++i)
121 if(ends[i]->get_link_slot(track)>=0)
126 // Closed loop; clear the ends
127 ends[0] = TrackIter();
128 ends[1] = TrackIter();
131 ends[linked-1] = TrackIter(&track, track.get_link_slot(*ends[linked-1]));
135 bool TrackChain::has_track(Track &t) const
137 return tracks.count(&t);
140 TrackIter TrackChain::iter_for(Track &t, Direction d) const
142 if(!tracks.count(&t))
144 else if(d==UNSPECIFIED)
145 return TrackIter(&t, 0);
150 TrackIter TrackChain::get_end(unsigned i) const
153 throw out_of_range("TrackChain::get_end");
157 else if(i==1 && !ends[1])
158 return ends[0].reverse();
163 bool TrackChain::is_loop() const
165 return !tracks.empty() && !ends[0] && !ends[1];
168 void TrackChain::object_removed(Object &obj)
170 if(Track *track = dynamic_cast<Track *>(&obj))
173 /* TODO If the track was in the middle of the chain, keep only the
175 signal_track_removed.emit(*track);
180 void operator<<(LexicalConverter &conv, TrackChain::Direction dir)
184 case TrackChain::UNSPECIFIED: conv.result("UNSPECIFIED"); return;
185 case TrackChain::UP: conv.result("UP"); return;
186 case TrackChain::DOWN: conv.result("DOWN"); return;
187 default: throw lexical_error(format("conversion of Direction(%d) to string", static_cast<int>(dir)));
191 void operator>>(const LexicalConverter &conv, TrackChain::Direction &dir)
193 const string &str = conv.get();
194 if(str=="UNSPECIFIED")
195 dir = TrackChain::UNSPECIFIED;
197 dir = TrackChain::UP;
199 dir = TrackChain::DOWN;
201 throw lexical_error(format("conversion of '%s' to Direction", str));