1
Fork 0

TrackEventRef is dead.

This commit is contained in:
Leonard Ritter 2010-02-09 21:41:00 +01:00
parent 4b89959a42
commit 98546885fa
7 changed files with 259 additions and 268 deletions

232
jsong.cpp
View File

@ -7,6 +7,110 @@
#include <cassert>
namespace Jacker {
enum {
JSongVersion = 2,
};
class JSongWriter {
public:
typedef std::map<Pattern *, int> Pattern2IdMap;
Pattern2IdMap pattern2id;
void collect(Json::Value &root, PatternEvent &event) {
root["frame"] = event.frame;
root["channel"] = event.channel;
root["param"] = event.param;
root["value"] = event.value;
}
void collect(Json::Value &root, Pattern &pattern) {
root["name"] = pattern.name;
root["length"] = pattern.get_length();
root["channel_count"] = pattern.get_channel_count();
Json::Value events;
for (Pattern::iterator iter = pattern.begin();
iter != pattern.end(); ++iter) {
Json::Value event;
collect(event, iter->second);
if (!event.empty())
events.append(event);
}
if (!events.empty()) {
root["events"] = events;
}
}
void collect(Json::Value &root, Song::Event &event) {
Pattern2IdMap::iterator iter = pattern2id.find(event.pattern);
if (iter == pattern2id.end())
return;
root["frame"] = event.frame;
root["track"] = event.track;
root["pattern"] = iter->second;
}
void collect(Json::Value &root, Song &song) {
Json::Value events;
for (Song::iterator iter = song.begin();
iter != song.end(); ++iter) {
Json::Value event;
collect(event, iter->second);
if (!event.empty())
events.append(event);
}
if (!events.empty()) {
root["events"] = events;
}
}
void collect(Json::Value &root, Model &model) {
root["format"] = "jacker-song";
root["version"] = JSongVersion;
root["end_cue"] = model.end_cue;
root["frames_per_beat"] = model.frames_per_beat;
root["beats_per_bar"] = model.beats_per_bar;
root["beats_per_minute"] = model.beats_per_minute;
Json::Value patterns;
int index = 0;
for (PatternList::iterator iter = model.patterns.begin();
iter != model.patterns.end(); ++iter) {
Json::Value pattern;
collect(pattern, *(*iter));
if (!pattern.empty()) {
patterns.append(pattern);
pattern2id.insert(Pattern2IdMap::value_type(*iter,index));
index++;
}
}
if (!patterns.empty()) {
root["patterns"] = patterns;
}
Json::Value song;
collect(song, model.song);
if (!song.empty()) {
root["song"] = song;
}
}
void write(Json::Value &root, const std::string &filepath) {
assert(!root.empty());
std::ofstream out(filepath.c_str());
assert(out);
out << root;
out.close();
}
};
class JSongReader {
public:
@ -53,8 +157,9 @@ public:
patterns.push_back(&pattern);
}
bool build(const Json::Value &root, Track::Event &event) {
bool build(const Json::Value &root, Song::Event &event) {
extract(root["frame"], event.frame);
extract(root["track"], event.track);
int pattern_index = -1;
if (!extract(root["pattern"], pattern_index))
return false;
@ -64,15 +169,12 @@ public:
return true;
}
void build(const Json::Value &root, Track &track) {
extract(root["name"], track.name);
extract(root["order"], track.order);
void build(const Json::Value &root, Song &song) {
const Json::Value events = root["events"];
for (size_t i = 0; i < events.size(); ++i) {
Track::Event event;
Song::Event event;
if (build(events[i], event))
track.add_event(event);
song.add_event(event);
}
}
@ -89,11 +191,8 @@ public:
build(patterns[i], pattern);
}
const Json::Value tracks = root["tracks"];
for (size_t i = 0; i < tracks.size(); ++i) {
Track &track = model.new_track();
build(tracks[i], track);
}
const Json::Value song = root["song"];
build(song, model.song);
}
bool read(Json::Value &root, const std::string &filepath) {
@ -109,115 +208,6 @@ public:
return result;
}
};
class JSongWriter {
public:
typedef std::map<Pattern *, int> Pattern2IdMap;
Pattern2IdMap pattern2id;
void collect(Json::Value &root, PatternEvent &event) {
root["frame"] = event.frame;
root["channel"] = event.channel;
root["param"] = event.param;
root["value"] = event.value;
}
void collect(Json::Value &root, Pattern &pattern) {
root["name"] = pattern.name;
root["length"] = pattern.get_length();
root["channel_count"] = pattern.get_channel_count();
Json::Value events;
for (Pattern::iterator iter = pattern.begin();
iter != pattern.end(); ++iter) {
Json::Value event;
collect(event, iter->second);
if (!event.empty())
events.append(event);
}
if (!events.empty()) {
root["events"] = events;
}
}
void collect(Json::Value &root, TrackEvent &event) {
Pattern2IdMap::iterator iter = pattern2id.find(event.pattern);
if (iter == pattern2id.end())
return;
root["frame"] = event.frame;
root["pattern"] = iter->second;
}
void collect(Json::Value &root, Track &track) {
root["name"] = track.name;
root["order"] = track.order;
Json::Value events;
for (Track::iterator iter = track.begin();
iter != track.end(); ++iter) {
Json::Value event;
collect(event, iter->second);
if (!event.empty())
events.append(event);
}
if (!events.empty()) {
root["events"] = events;
}
}
void collect(Json::Value &root, Model &model) {
root["format"] = "jacker-song";
root["end_cue"] = model.end_cue;
root["frames_per_beat"] = model.frames_per_beat;
root["beats_per_bar"] = model.beats_per_bar;
root["beats_per_minute"] = model.beats_per_minute;
Json::Value patterns;
int index = 0;
for (PatternList::iterator iter = model.patterns.begin();
iter != model.patterns.end(); ++iter) {
Json::Value pattern;
collect(pattern, *(*iter));
if (!pattern.empty()) {
patterns.append(pattern);
pattern2id.insert(Pattern2IdMap::value_type(*iter,index));
index++;
}
}
if (!patterns.empty()) {
root["patterns"] = patterns;
}
Json::Value tracks;
for (TrackArray::iterator iter = model.tracks.begin();
iter != model.tracks.end(); ++iter) {
Json::Value track;
collect(track, *(*iter));
if (!track.empty()) {
tracks.append(track);
}
}
if (!tracks.empty()) {
root["tracks"] = tracks;
}
}
void write(Json::Value &root, const std::string &filepath) {
assert(!root.empty());
std::ofstream out(filepath.c_str());
assert(out);
out << root;
out.close();
}
};
bool read_jsong(Model &model, const std::string &filepath) {
JSongReader reader;

View File

@ -360,12 +360,12 @@ public:
bool found = false;
Pattern *active_pattern = pattern_view->get_pattern();
if (active_pattern) {
TrackEventRefList refs;
model.find_events(frame, refs);
if (!refs.empty()) {
TrackEventRefList::iterator iter;
for (iter = refs.begin(); iter != refs.end(); ++iter) {
TrackEvent &evt = iter->iter->second;
Song::IterList events;
model.song.find_events(frame, events);
if (!events.empty()) {
Song::IterList::iterator iter;
for (iter = events.begin(); iter != events.end(); ++iter) {
Song::Event &evt = (*iter)->second;
if (evt.pattern == active_pattern) {
found = true;
pattern_view->set_play_position(frame - evt.frame);

View File

@ -192,9 +192,6 @@ Pattern::iterator Pattern::get_event(int frame, int channel, int param) {
}
void Pattern::update_keys() {
typedef std::list<Event> EventList;
typedef std::list<Pattern::iterator> IterList;
IterList dead_iters;
EventList events;
@ -243,22 +240,21 @@ int SongEvent::get_last_frame() const {
//=============================================================================
Song::Song() {
order = -1;
}
Song::iterator Song::add_event(const Event &event) {
return BaseClass::add_event(event);
}
Song::iterator Song::add_event(int frame, Pattern &pattern) {
return add_event(Event(frame, pattern));
Song::iterator Song::add_event(int frame, int track, Pattern &pattern) {
return add_event(Event(frame, track, pattern));
}
Song::iterator Song::get_event(int frame) {
return BaseClass::find(frame);
}
void Song::find_events(int frame, EventList &events) {
void Song::find_events(int frame, IterList &events) {
events.clear();
Song::iterator iter;
for (iter = begin(); iter != end(); ++iter) {
@ -270,6 +266,30 @@ void Song::find_events(int frame, EventList &events) {
}
}
void Song::update_keys() {
IterList dead_iters;
EventList events;
for (iterator iter = begin(); iter != end(); ++iter) {
if (iter->first != iter->second.frame) {
events.push_back(iter->second);
dead_iters.push_back(iter);
}
}
for (IterList::iterator iter = dead_iters.begin();
iter != dead_iters.end(); ++iter) {
erase(*iter);
}
for (EventList::iterator iter = events.begin();
iter != events.end(); ++iter) {
if (iter->frame == -1) // skip
continue;
add_event(*iter);
}
}
//=============================================================================
Measure::Measure() {
@ -305,7 +325,7 @@ void Model::reset() {
frames_per_beat = 4;
beats_per_bar = 4;
patterns.clear();
tracks.clear();
song.clear();
}
Pattern &Model::new_pattern() {
@ -315,22 +335,6 @@ Pattern &Model::new_pattern() {
return *pattern;
}
Track &Model::new_track() {
Track *track = new Track();
tracks.push_back(track);
renumber_tracks();
return *track;
}
void Model::renumber_tracks() {
TrackArray::iterator iter;
int index = 0;
for (iter = tracks.begin(); iter != tracks.end(); ++iter) {
(*iter)->order = index;
index++;
}
}
int Model::get_track_count() const {
return 8;
}

View File

@ -39,6 +39,8 @@ public:
typedef typename map::key_type Key;
typedef typename map::mapped_type Event;
typedef EventCollection<Map_T> BaseClass;
typedef std::list<Event> EventList;
typedef std::list<typename Map_T::iterator> IterList;
typename map::iterator add_event(const Event &event) {
return extract_iterator<typename map::key_type, Event>(
@ -175,19 +177,14 @@ struct SongEvent {
class Song : public EventCollection< std::multimap<int,SongEvent> > {
friend class Model;
public:
typedef std::list<iterator> EventList;
// name of track (non-unique)
std::string name;
// order of track
int order;
iterator add_event(const Event &event);
iterator add_event(int frame, Pattern &pattern);
iterator add_event(int frame, int track, Pattern &pattern);
iterator get_event(int frame);
void find_events(int frame, EventList &events);
void find_events(int frame, IterList &events);
void update_keys();
protected:
Song();
};

View File

@ -183,28 +183,32 @@ void Player::init_message(Message &msg) {
void Player::mix_frame() {
assert(model);
Song::EventList events;
model.song.find_events(position, events);
Song::IterList events;
model->song.find_events(position, events);
if (events.empty())
return;
Track::Event &event = iter->second;
Pattern &pattern = *event.pattern;
Pattern::iterator row_iter = pattern.begin();
Pattern::Row row;
pattern.collect_events(position - event.frame, row_iter, row);
// first run: process all cc events
for (int channel = 0; channel < pattern.get_channel_count(); ++channel) {
on_cc(row.get_value(channel, ParamCCIndex),
row.get_value(channel, ParamCCValue));
Song::IterList::iterator iter;
for (iter = events.begin(); iter != events.end(); ++iter) {
Song::Event &event = (*iter)->second;
Pattern &pattern = *event.pattern;
Pattern::iterator row_iter = pattern.begin();
Pattern::Row row;
pattern.collect_events(position - event.frame, row_iter, row);
// first run: process all cc events
for (int channel = 0; channel < pattern.get_channel_count(); ++channel) {
on_cc(row.get_value(channel, ParamCCIndex),
row.get_value(channel, ParamCCValue));
}
// second run: process volume and notes
for (int channel = 0; channel < pattern.get_channel_count(); ++channel) {
on_volume(channel, row.get_value(channel, ParamVolume));
on_note(channel, row.get_value(channel, ParamNote));
}
}
// second run: process volume and notes
for (int channel = 0; channel < pattern.get_channel_count(); ++channel) {
on_volume(channel, row.get_value(channel, ParamVolume));
on_note(channel, row.get_value(channel, ParamNote));
}
}
void Player::handle_message(Message msg) {

View File

@ -207,22 +207,27 @@ void TrackView::get_event_location(int x, int y, int &frame, int &track) const {
track = (y - origin_y)/TrackHeight;
}
bool TrackView::find_event(const TrackCursor &cur, TrackEventRef &ref) {
bool TrackView::find_event(const TrackCursor &cur, Song::iterator &event) {
if (cur.get_track() >= model->get_track_count())
return false;
Track *track = &model->get_track(cur.get_track());
Track::iterator iter = track->find_event(cur.get_frame());
if (iter == track->end())
Song::IterList events;
model->song.find_events(cur.get_frame(), events);
if (events.empty())
return false;
ref.track = track;
ref.iter = iter;
return true;
for (Song::IterList::reverse_iterator iter = events.rbegin();
iter != events.rend(); ++iter) {
if ((*iter)->second.track == cur.get_track()) {
event = *iter;
return true;
}
}
return false;
}
void TrackView::render_event(const TrackEventRef &ref) {
bool selected = is_event_selected(ref);
void TrackView::render_event(Song::iterator event) {
bool selected = is_event_selected(event);
int x,y,w,h;
get_event_rect(ref, x, y, w, h);
get_event_rect(event, x, y, w, h);
// main border
gc->set_foreground(colors[ColorBlack]);
window->draw_rectangle(gc, false, x, y+1, w, h-3);
@ -231,7 +236,7 @@ void TrackView::render_event(const TrackEventRef &ref) {
// right shadow
window->draw_rectangle(gc, true, x+w+1, y+2, 1, h-2);
pango_layout->set_width((w-4)*Pango::SCALE);
pango_layout->set_text(ref.iter->second.pattern->name.c_str());
pango_layout->set_text(event->second.pattern->name.c_str());
if (selected) {
// fill
window->draw_rectangle(gc, true, x+2, y+3, w-3, h-6);
@ -250,13 +255,13 @@ void TrackView::render_event(const TrackEventRef &ref) {
}
}
void TrackView::render_track(Track &track) {
void TrackView::render_track(int track) {
int width = 0;
int height = 0;
window->get_size(width, height);
int x,y;
get_event_pos(0, track.order, x, y);
get_event_pos(0, track, x, y);
gc->set_foreground(colors[ColorTrack]);
window->draw_rectangle(gc, true, 0, y, width, TrackHeight);
@ -272,20 +277,15 @@ bool TrackView::on_expose_event(GdkEventExpose* event) {
window->draw_rectangle(gc, true, 0, 0, width, height);
// first pass: render tracks
TrackArray::iterator track_iter;
for (track_iter = model->tracks.begin();
track_iter != model->tracks.end(); ++track_iter) {
Track &track = *(*track_iter);
for (int track = 0; track < model->get_track_count(); ++track) {
render_track(track);
}
// second pass: render events
for (track_iter = model->tracks.begin();
track_iter != model->tracks.end(); ++track_iter) {
Track &track = *(*track_iter);
for (Track::iterator iter = track.begin(); iter != track.end(); ++iter) {
render_event(TrackEventRef(track,iter));
}
for (Song::iterator iter = model->song.begin();
iter != model->song.end(); ++iter) {
render_event(iter);
}
// draw play cursor
@ -310,8 +310,9 @@ void TrackView::invalidate_play_position() {
window->invalidate_rect(rect, true);
}
bool TrackView::is_event_selected(const TrackEventRef &ref) {
return (selection.find(ref) != selection.end());
bool TrackView::is_event_selected(Song::iterator event) {
return (std::find(selection.begin(), selection.end(),
event) != selection.end());
}
void TrackView::clear_selection() {
@ -319,35 +320,38 @@ void TrackView::clear_selection() {
selection.clear();
}
void TrackView::select_event(const TrackEventRef &ref) {
selection.insert(ref);
void TrackView::select_event(Song::iterator event) {
if (is_event_selected(event))
return;
selection.push_back(event);
invalidate_selection();
}
void TrackView::deselect_event(const TrackEventRef &ref) {
void TrackView::deselect_event(Song::iterator event) {
if (!is_event_selected(event))
return;
invalidate_selection();
selection.erase(ref);
selection.remove(event);
}
void TrackView::add_track() {
model->new_track();
// TODO
//model->new_track();
invalidate();
}
void TrackView::new_pattern(const TrackCursor &cur) {
if (cur.get_track() < model->get_track_count()) {
Track &track = model->get_track(cur.get_track());
Pattern &pattern = model->new_pattern();
TrackEventRef ref(track,
track.add_event(cur.get_frame(), pattern));
Song::iterator event = model->song.add_event(cur.get_frame(), cur.get_track(), pattern);
clear_selection();
select_event(ref);
select_event(event);
}
}
void TrackView::edit_pattern(const TrackEventRef &ref) {
Pattern *pattern = ref.iter->second.pattern;
void TrackView::edit_pattern(Song::iterator iter) {
Pattern *pattern = iter->second.pattern;
_pattern_edit_request(pattern);
}
@ -382,19 +386,19 @@ bool TrackView::on_button_press_event(GdkEventButton* event) {
if (event->button == 1) {
TrackCursor cur(*this);
cur.set_pos(event->x, event->y);
TrackEventRef ref;
Song::iterator evt;
if (!ctrl_down)
if (!ctrl_down && (selection.size() == 1))
clear_selection();
if (find_event(cur, ref)) {
if (ctrl_down && is_event_selected(ref)) {
deselect_event(ref);
if (find_event(cur, evt)) {
if (ctrl_down && is_event_selected(evt)) {
deselect_event(evt);
} else {
select_event(ref);
select_event(evt);
if (double_click) {
interact_mode = InteractNone;
edit_pattern(ref);
edit_pattern(evt);
} else {
interact_mode = InteractDrag;
drag.start(event->x, event->y);
@ -404,6 +408,7 @@ bool TrackView::on_button_press_event(GdkEventButton* event) {
cur.set_frame(quantize_frame(cur.get_frame()));
new_pattern(cur);
} else {
clear_selection();
interact_mode = InteractSelect;
drag.start(event->x, event->y);
}
@ -413,9 +418,9 @@ bool TrackView::on_button_press_event(GdkEventButton* event) {
return true;
}
bool TrackView::can_resize_event(const TrackEventRef &ref, int x) {
bool TrackView::can_resize_event(Song::iterator event, int x) {
int ex, ey, ew, eh;
get_event_rect(ref,ex,ey,ew,eh);
get_event_rect(event,ex,ey,ew,eh);
return std::abs(x - (ex+ew)) < ResizeThreshold;
}
@ -449,9 +454,9 @@ bool TrackView::on_motion_notify_event(GdkEventMotion *event) {
if (interact_mode == InteractNone) {
TrackCursor cur(*this);
cur.set_pos(event->x, event->y);
TrackEventRef ref;
if (find_event(cur, ref)) {
if (can_resize_event(ref, event->x))
Song::iterator evt;
if (find_event(cur, evt)) {
if (can_resize_event(evt, event->x))
window->set_cursor(Gdk::Cursor(Gdk::SB_H_DOUBLE_ARROW));
else
window->set_cursor(Gdk::Cursor(Gdk::HAND1));
@ -465,8 +470,8 @@ bool TrackView::on_motion_notify_event(GdkEventMotion *event) {
invalidate_selection();
TrackCursor cur(*this);
cur.set_pos(drag.start_x, drag.start_y);
TrackEventRef ref;
if (find_event(cur, ref) && can_resize_event(ref,drag.start_x)) {
Song::iterator evt;
if (find_event(cur, evt) && can_resize_event(evt,drag.start_x)) {
interact_mode = InteractResize;
} else {
interact_mode = InteractMove;
@ -494,12 +499,11 @@ void TrackView::apply_move() {
bool can_move = true;
// verify that we can move
for (EventSet::iterator iter = selection.begin();
for (Song::IterList::iterator iter = selection.begin();
iter != selection.end(); ++iter) {
const TrackEventRef &ref = *iter;
Track::Event &event = ref.iter->second;
Song::Event &event = (*iter)->second;
int frame = event.frame + ofs_frame;
int track = ref.track->order + ofs_track;
int track = event.track + ofs_track;
if ((frame < 0)||(track < 0)||(track >= model->get_track_count())) {
can_move = false;
break;
@ -507,27 +511,22 @@ void TrackView::apply_move() {
}
if (can_move) {
EventSet new_selection;
Song::IterList new_selection;
// verify that we can move
for (EventSet::iterator iter = selection.begin();
// do the actual move
for (Song::IterList::iterator iter = selection.begin();
iter != selection.end(); ++iter) {
const TrackEventRef &ref = *iter;
Track::Event event = ref.iter->second;
Song::Event event = (*iter)->second;
event.frame += ofs_frame;
int track_index = ref.track->order + ofs_track;
event.track += ofs_track;
model->delete_event(ref);
Track &track = model->get_track(track_index);
TrackEventRef new_ref(track,
track.add_event(event));
new_selection.insert(new_ref);
model->song.erase(*iter);
Song::iterator new_event = model->song.add_event(event);
new_selection.push_back(new_event);
}
selection.clear();
//selection = new_selection;
selection = new_selection;
}
}
@ -537,10 +536,9 @@ void TrackView::apply_resize() {
get_drag_offset(ofs_frame, ofs_track);
// verify that we can move
for (EventSet::iterator iter = selection.begin();
for (Song::IterList::iterator iter = selection.begin();
iter != selection.end(); ++iter) {
const TrackEventRef &ref = *iter;
Track::Event &event = ref.iter->second;
Song::Event &event = (*iter)->second;
int length = std::max(event.pattern->get_length() + ofs_frame,
get_step_size());
@ -609,17 +607,17 @@ int TrackView::quantize_frame(int frame) {
return frame - (frame%get_step_size());
}
void TrackView::get_event_rect(const TrackEventRef &ref, int &x, int &y, int &w, int &h) {
Track::Event &event = ref.iter->second;
void TrackView::get_event_rect(Song::iterator iter, int &x, int &y, int &w, int &h) {
Song::Event &event = iter->second;
int frame = event.frame;
int track = ref.track->order;
int track = event.track;
int length = event.pattern->get_length();
if (moving() && is_event_selected(ref)) {
if (moving() && is_event_selected(iter)) {
int ofs_frame,ofs_track;
get_drag_offset(ofs_frame, ofs_track);
frame += ofs_frame;
track += ofs_track;
} else if (resizing() && is_event_selected(ref)) {
} else if (resizing() && is_event_selected(iter)) {
int ofs_frame,ofs_track;
get_drag_offset(ofs_frame, ofs_track);
length = std::max(length + ofs_frame, get_step_size());
@ -635,7 +633,7 @@ void TrackView::invalidate() {
}
void TrackView::invalidate_selection() {
EventSet::iterator iter;
Song::IterList::iterator iter;
for (iter = selection.begin(); iter != selection.end(); ++iter) {
int x,y,w,h;
get_event_rect(*iter, x, y, w, h);
@ -647,9 +645,9 @@ void TrackView::invalidate_selection() {
void TrackView::erase_events() {
invalidate_selection();
EventSet::iterator iter;
Song::IterList::iterator iter;
for (iter = selection.begin(); iter != selection.end(); ++iter) {
iter->track->erase(iter->iter);
model->song.erase(*iter);
}
selection.clear();

View File

@ -89,8 +89,6 @@ public:
typedef sigc::signal<void, Pattern *> type_pattern_edit_request;
typedef sigc::signal<void, TrackView *, GdkEventButton*> type_context_menu;
typedef std::set<TrackEventRef> EventSet;
TrackView(BaseObjectType* cobject,
const Glib::RefPtr<Gtk::Builder>& builder);
@ -123,19 +121,19 @@ public:
void get_event_length(int w, int h, int &length, int &track) const;
void get_event_location(int x, int y, int &frame, int &track) const;
void get_event_rect(const TrackEventRef &ref, int &x, int &y, int &w, int &h);
bool find_event(const TrackCursor &cursor, TrackEventRef &ref);
void get_event_rect(Song::iterator event, int &x, int &y, int &w, int &h);
bool find_event(const TrackCursor &cursor, Song::iterator &event);
void clear_selection();
void select_event(const TrackEventRef &ref);
void deselect_event(const TrackEventRef &ref);
bool is_event_selected(const TrackEventRef &ref);
void select_event(Song::iterator event);
void deselect_event(Song::iterator event);
bool is_event_selected(Song::iterator event);
void set_play_position(int pos);
void invalidate();
void add_track();
void new_pattern(const TrackCursor &cursor);
void edit_pattern(const TrackEventRef &ref);
void edit_pattern(Song::iterator event);
void erase_events();
type_pattern_edit_request signal_pattern_edit_request();
@ -143,13 +141,13 @@ public:
protected:
void invalidate_selection();
void invalidate_play_position();
void render_event(const TrackEventRef &ref);
void render_track(Track &track);
void render_event(Song::iterator event);
void render_track(int track);
void render_select_box();
void invalidate_select_box();
void select_from_box();
bool can_resize_event(const TrackEventRef &ref, int x);
bool can_resize_event(Song::iterator event, int x);
void get_drag_offset(int &frame, int &track);
int get_step_size();
int quantize_frame(int frame);
@ -179,7 +177,7 @@ protected:
Drag drag;
Model *model;
EventSet selection;
Song::IterList selection;
type_pattern_edit_request _pattern_edit_request;
type_context_menu _signal_context_menu;