From e52f3b6d67fd980f60a8239a371fa26e20d956d1 Mon Sep 17 00:00:00 2001 From: Leonard Ritter Date: Wed, 10 Feb 2010 20:50:06 +0100 Subject: [PATCH] save/load loop --- jacker.glade | 4 +- jsong.cpp | 33 +++++++++++ measure.cpp | 158 +++++++++++++++++++++++++++++++++++++++++++-------- measure.hpp | 19 +++++++ model.cpp | 8 +-- 5 files changed, 191 insertions(+), 31 deletions(-) diff --git a/jacker.glade b/jacker.glade index 1ee213c..a66ef54 100644 --- a/jacker.glade +++ b/jacker.glade @@ -1,7 +1,7 @@ - + Jacker center @@ -402,7 +402,7 @@ - 22 + 28 True True GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK diff --git a/jsong.cpp b/jsong.cpp index b6c6754..17da1e5 100644 --- a/jsong.cpp +++ b/jsong.cpp @@ -68,6 +68,11 @@ public: root["events"] = events; } } + + void collect(Json::Value &root, Loop &loop) { + root["begin"] = loop.get_begin(); + root["end"] = loop.get_end(); + } void collect(Json::Value &root, Model &model) { root["format"] = "jacker-song"; @@ -76,6 +81,13 @@ public: root["frames_per_beat"] = model.frames_per_beat; root["beats_per_bar"] = model.beats_per_bar; root["beats_per_minute"] = model.beats_per_minute; + root["enable_loop"] = model.enable_loop; + + Json::Value loop; + collect(loop, model.loop); + if (!loop.empty()) { + root["loop"] = loop; + } Json::Value patterns; @@ -130,6 +142,13 @@ public: target = value.asInt(); return true; } + + bool extract(const Json::Value &value, bool &target) { + if (!value.isBool()) + return false; + target = value.asBool(); + return true; + } void build(const Json::Value &root, Pattern::Event &event) { extract(root["frame"], event.frame); @@ -177,6 +196,14 @@ public: song.add_event(event); } } + + void build(const Json::Value &root, Loop &loop) { + int begin = 0; + int end = 0; + extract(root["begin"], begin); + extract(root["end"], end); + loop.set(begin, end); + } void build(const Json::Value &root, Model &model) { model.reset(); @@ -184,6 +211,12 @@ public: extract(root["frames_per_beat"], model.frames_per_beat); extract(root["beats_per_bar"], model.beats_per_bar); extract(root["beats_per_minute"], model.beats_per_minute); + extract(root["enable_loop"], model.enable_loop); + + const Json::Value loop = root["loop"]; + if (!loop.empty()) { + build(loop, model.loop); + } const Json::Value patterns = root["patterns"]; for (size_t i = 0; i < patterns.size(); ++i) { diff --git a/measure.cpp b/measure.cpp index a792d00..34ef0d8 100644 --- a/measure.cpp +++ b/measure.cpp @@ -1,6 +1,7 @@ #include "measure.hpp" #include "model.hpp" +#include namespace Jacker { @@ -17,6 +18,7 @@ MeasureView::MeasureView(BaseObjectType* cobject, : Gtk::Widget(cobject) { adjustment = NULL; model = NULL; + interact_mode = InteractNone; orientation = OrientationHorizontal; colors.resize(ColorCount); colors[ColorBlack].set("#000000"); @@ -119,26 +121,14 @@ bool MeasureView::on_expose_event(GdkEventExpose* event) { } // loop - if ((orientation == OrientationHorizontal) && model->enable_loop) { + if (/*(orientation == OrientationHorizontal) &&*/ model->enable_loop) { int lc1 = int(((model->loop.get_begin()-value)*scale)+0.5); int lc2 = int(((model->loop.get_end()-value)*scale)+0.5); - std::vector points; - x = lc1; y = height-1; flip(x,y); - points.push_back(Gdk::Point(x,y)); - x = lc1+8; y = height-1; flip(x,y); - points.push_back(Gdk::Point(x,y)); - x = lc1; y = height-9; flip(x,y); - points.push_back(Gdk::Point(x,y)); - window->draw_polygon(gc, true, points); - points.clear(); - x = lc2; y = height-1; flip(x,y); - points.push_back(Gdk::Point(x,y)); - x = lc2-8; y = height-1; flip(x,y); - points.push_back(Gdk::Point(x,y)); - x = lc2; y = height-9; flip(x,y); - points.push_back(Gdk::Point(x,y)); - window->draw_polygon(gc, true, points); + x = lc1; y = height-1; + render_arrow(x,y, true); + x = lc2; y = height-1; + render_arrow(x,y, false); } // bottom bar @@ -148,7 +138,60 @@ bool MeasureView::on_expose_event(GdkEventExpose* event) { return true; } +Gdk::Point MeasureView::flip(const Gdk::Point &pt) { + int x = pt.get_x(); + int y = pt.get_y(); + flip(x,y); + return Gdk::Point(x,y); +} + +void MeasureView::render_arrow(int x, int y, bool begin) { + std::vector points; + y -= 1; + const int R = 5; + points.push_back(flip(Gdk::Point(x,y))); + points.push_back(flip(Gdk::Point(x+R,y-R))); + points.push_back(flip(Gdk::Point(x+R,y-R*3))); + points.push_back(flip(Gdk::Point(x-R,y-R*3))); + points.push_back(flip(Gdk::Point(x-R,y-R))); + + gc->set_foreground(colors[ColorWhite]); + window->draw_polygon(gc, true, points); + gc->set_foreground(colors[ColorBlack]); + window->draw_polygon(gc, false, points); + + points.clear(); + if (begin) { + points.push_back(flip(Gdk::Point(x-R+2,y-R-2))); + points.push_back(flip(Gdk::Point(x+R-2,y-R-2))); + points.push_back(flip(Gdk::Point(x-R+2,y-R*3+2))); + window->draw_polygon(gc, false, points); + } else { + points.push_back(flip(Gdk::Point(x+R-1,y-R-1))); + points.push_back(flip(Gdk::Point(x-R+1,y-R-1))); + points.push_back(flip(Gdk::Point(x+R-1,y-R*3+1))); + window->draw_polygon(gc, true, points); + } +} + +int MeasureView::get_pixel(int frame) { + assert(adjustment); + if (!window) + return 0; + int width = 0; + int height = 0; + window->get_size(width, height); + flip(width, height); + double value = adjustment->get_value(); + double page_size = adjustment->get_page_size(); + double scale = (double)width / page_size; + return int((frame - value)*scale+0.5); +} + int MeasureView::get_frame(int x, int y) { + assert(adjustment); + if (!window) + return 0; flip(x,y); int width = 0; int height = 0; @@ -157,35 +200,100 @@ int MeasureView::get_frame(int x, int y) { double value = adjustment->get_value(); double page_size = adjustment->get_page_size(); double scale = (double)width / page_size; - int frame = (x / scale) + value; - frame -= frame % model->get_frames_per_bar(); + return ((x / scale) + value); +} + +int MeasureView::quantize_frame(int frame) { + assert(model); + int step = model->get_frames_per_bar(); + frame += step / 2; + frame -= frame % step; return frame; } -bool MeasureView::on_motion_notify_event(GdkEventMotion *event) { - - return false; +bool MeasureView::hit_loop_begin(int x) { + if (!model->enable_loop) + return false; + return (std::abs(get_pixel(model->loop.get_begin()) - x) <= 4); +} + +bool MeasureView::hit_loop_end(int x) { + if (!model->enable_loop) + return false; + return (std::abs(get_pixel(model->loop.get_end()) - x) <= 4); } bool MeasureView::on_button_press_event(GdkEventButton* event) { bool ctrl_down = event->state & Gdk::CONTROL_MASK; bool alt_down = event->state & Gdk::MOD1_MASK; int frame = get_frame(event->x,event->y); + int x = event->x; + int y = event->y; + flip(x,y); if (ctrl_down) { - model->loop.set_begin(frame); + model->loop.set_begin(quantize_frame(frame)); invalidate(); _loop_changed(); } else if (alt_down) { - model->loop.set_end(frame); + model->loop.set_end(quantize_frame(frame)); invalidate(); _loop_changed(); } else { - _seek_request(frame); + drag.start(event->x, event->y); + interact_mode = InteractDrag; + } + return false; +} + +bool MeasureView::on_motion_notify_event(GdkEventMotion *event) { + if (interact_mode == InteractDrag) { + drag.update(event->x, event->y); + if (drag.threshold_reached()) { + int x = drag.start_x; + int y = drag.start_y; + flip(x,y); + if (hit_loop_begin(x)) + interact_mode = InteractDragLoopBegin; + else if (hit_loop_end(x)) + interact_mode = InteractDragLoopEnd; + else + interact_mode = InteractSeek; + } + } + if (interact_mode == InteractDragLoopBegin) { + int frame = quantize_frame(get_frame(event->x,event->y)); + if (frame != model->loop.get_begin()) { + model->loop.set_begin(frame); + invalidate(); + _loop_changed(); + return true; + } + } else if (interact_mode == InteractDragLoopEnd) { + int frame = quantize_frame(get_frame(event->x,event->y)); + if (frame != model->loop.get_end()) { + model->loop.set_end(frame); + invalidate(); + _loop_changed(); + return true; + } + } else if (interact_mode == InteractNone) { + int x = event->x; + int y = event->y; + flip(x,y); + if (hit_loop_begin(x)||hit_loop_end(x)) + window->set_cursor(Gdk::Cursor(Gdk::HAND1)); + else + window->set_cursor(Gdk::Cursor(Gdk::ARROW)); } return false; } bool MeasureView::on_button_release_event(GdkEventButton* event) { + if ((interact_mode == InteractSeek)||(interact_mode == InteractDrag)) { + int frame = get_frame(event->x,event->y); + _seek_request(quantize_frame(frame)); + } + interact_mode = InteractNone; return false; } diff --git a/measure.hpp b/measure.hpp index a197762..036057f 100644 --- a/measure.hpp +++ b/measure.hpp @@ -6,6 +6,8 @@ #error GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED must be set. #endif +#include "drag.hpp" + namespace Jacker { //============================================================================= @@ -17,6 +19,14 @@ public: OrientationVertical, }; + enum InteractMode { + InteractNone = 0, + InteractDrag, + InteractDragLoopBegin, + InteractDragLoopEnd, + InteractSeek, + }; + typedef sigc::signal type_seek_request; typedef sigc::signal type_loop_changed; @@ -49,13 +59,22 @@ public: protected: void on_adjustment_value_changed(); void flip(int &x, int &y); + Gdk::Point flip(const Gdk::Point &pt); void flip(int &x, int &y, int &w, int &h); + int get_pixel(int frame); int get_frame(int x, int y); + int quantize_frame(int frame); + bool hit_loop_begin(int x); + bool hit_loop_end(int x); + void render_arrow(int x, int y, bool begin); Orientation orientation; Gtk::Adjustment *adjustment; Model *model; + InteractMode interact_mode; + Drag drag; + type_seek_request _seek_request; type_loop_changed _loop_changed; }; diff --git a/model.cpp b/model.cpp index 9528e0b..5c8cf6e 100644 --- a/model.cpp +++ b/model.cpp @@ -331,20 +331,20 @@ Loop::Loop() { } void Loop::set(int begin, int end) { - this->begin = begin; - this->end = end; + this->begin = std::max(begin,0); + this->end = std::max(end,0); if (this->begin > this->end) std::swap(this->begin, this->end); } void Loop::set_begin(int begin) { - this->begin = begin; + this->begin = std::max(begin,0); if (this->begin > this->end) this->end = this->begin; } void Loop::set_end(int end) { - this->end = end; + this->end = std::max(end,0); if (this->begin > this->end) this->begin = this->end; }