cheshirekow  v0.1.0
pan_zoom_view.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014 Josh Bialkowski (josh.bialkowski@gmail.com)
3  *
4  * This file is part of mpblocks.
5  *
6  * mpblocks is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * mpblocks is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with mpblocks. If not, see <http://www.gnu.org/licenses/>.
18  */
26 #ifndef MPBLOCKS_GTK_PAN_ZOOM_VIEW_H_
27 #define MPBLOCKS_GTK_PAN_ZOOM_VIEW_H_
28 
29 #include <Eigen/Dense>
30 #include <gtkmm.h>
31 
32 namespace mpblocks {
33 namespace gtk {
34 
36 class PanZoomView : public Gtk::DrawingArea {
37  private:
38  Glib::RefPtr<Gtk::Adjustment> offset_x_;
39  Glib::RefPtr<Gtk::Adjustment> offset_y_;
40 
42  Glib::RefPtr<Gtk::Adjustment> scale_;
43 
46  Glib::RefPtr<Gtk::Adjustment> scale_rate_;
47 
50 
53  bool active_;
54 
58 
59  public:
62  sigc::signal<void, const Cairo::RefPtr<Cairo::Context>&> sig_draw;
63 
65  sigc::signal<void, GdkEventMotion*> sig_motion;
66 
68  sigc::signal<void, GdkEventButton*> sig_button;
69 
71  add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_MOTION_MASK |
72  Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK |
73  Gdk::SCROLL_MASK);
74  offset_x_ = Gtk::Adjustment::create(0, -1e9, 1e9);
75  offset_y_ = Gtk::Adjustment::create(0, -1e9, 1e9);
76  scale_ = Gtk::Adjustment::create(1, 1e-9, 1e9);
77  scale_rate_ = Gtk::Adjustment::create(1.05, 0, 10);
78  active_ = true;
79  pan_button_ = 3;
80  pan_button_mask_ = GDK_BUTTON3_MASK;
81  }
82 
83  void SetOffsetAdjustments(Glib::RefPtr<Gtk::Adjustment> offset_x,
84  Glib::RefPtr<Gtk::Adjustment> offset_y) {
85  offset_x_ = offset_x;
86  offset_y_ = offset_y;
87  offset_x_->signal_value_changed().connect(
88  sigc::mem_fun(this, &Gtk::DrawingArea::queue_draw));
89  offset_y_->signal_value_changed().connect(
90  sigc::mem_fun(this, &Gtk::DrawingArea::queue_draw));
91  }
92 
93  void SetScaleAdjustments(Glib::RefPtr<Gtk::Adjustment> scale,
94  Glib::RefPtr<Gtk::Adjustment> scale_rate) {
95  scale_ = scale;
96  scale_rate_ = scale_rate;
97  scale_->signal_value_changed().connect(
98  sigc::mem_fun(this, &Gtk::DrawingArea::queue_draw));
99  }
100 
101  void SetOffset(const Eigen::Vector2d& offset) {
102  if (offset_x_) {
103  offset_x_->set_value(offset[0]);
104  }
105  if (offset_y_) {
106  offset_y_->set_value(offset[1]);
107  }
108  }
109 
111  return (offset_x_ && offset_y_)
112  ? Eigen::Vector2d(offset_x_->get_value(), offset_y_->get_value())
113  : Eigen::Vector2d(0, 0);
114  }
115 
116  void SetScale(double scale) {
117  if (scale_) {
118  scale_->set_value(scale);
119  }
120  }
121 
122  double GetScale() { return scale_ ? scale_->get_value() : 1; }
123 
124  void SetScaleRate(double scale_rate) {
125  if (scale_rate_) {
126  scale_rate_->set_value(scale_rate);
127  }
128  }
129 
130  double GetScaleRate() {
131  return scale_rate_ ? scale_rate_->get_value() : 1.05;
132  }
133 
135  double GetMaxDim() {
136  return std::max(get_allocated_width(), get_allocated_height());
137  }
138 
142  Eigen::Vector2d RawPoint(double x, double y) {
143  return Eigen::Vector2d(x, get_allocated_height() - y);
144  }
145 
148  Eigen::Vector2d TransformPoint(double x, double y) {
149  return GetOffset() + RawPoint(x, y) * (GetScale() / GetMaxDim());
150  }
151 
152  template <typename Event>
153  void TransformEvent(Event* event) {
154  Eigen::Vector2d transformed_point = TransformPoint(event->x, event->y);
155  event->x = transformed_point[0];
156  event->y = transformed_point[1];
157  }
158 
159  virtual bool on_motion_notify_event(GdkEventMotion* event) {
160  if (active_ && (event->state & pan_button_mask_)) {
161  Eigen::Vector2d loc = RawPoint(event->x, event->y);
162  Eigen::Vector2d delta = loc - last_pos_;
163  Eigen::Vector2d new_offset =
164  GetOffset() - (GetScale() / GetMaxDim()) * delta;
165  SetOffset(new_offset);
166  last_pos_ = loc;
167  } else {
168  Gtk::DrawingArea::on_motion_notify_event(event);
169  GdkEventMotion transformed = *event;
170  TransformEvent(&transformed);
171  sig_motion.emit(&transformed);
172  }
173 
174  return true;
175  }
176 
177  virtual bool on_button_press_event(GdkEventButton* event) {
178  if (active_ && (event->button == 3)) {
179  last_pos_ = RawPoint(event->x, event->y);
180  } else {
181  Gtk::DrawingArea::on_button_press_event(event);
182  GdkEventButton transformed = *event;
183  TransformEvent(&transformed);
184  sig_button.emit(&transformed);
185  }
186  return true;
187  }
188 
189  virtual bool on_scroll_event(GdkEventScroll* event) {
190  if (active_) {
191  // after the change in scale, we want the mouse pointer to be over
192  // the same location in the scaled view
193  Eigen::Vector2d raw_point = RawPoint(event->x, event->y);
194  Eigen::Vector2d center_point = TransformPoint(event->x, event->y);
195 
196  if (event->direction == GDK_SCROLL_UP) {
197  if (scale_) {
198  scale_->set_value(GetScale() * GetScaleRate());
199  }
200  } else if (event->direction == GDK_SCROLL_DOWN) {
201  if (scale_) {
202  scale_->set_value(GetScale() / GetScaleRate());
203  }
204  } else {
205  Gtk::DrawingArea::on_scroll_event(event);
206  return true;
207  }
208 
209  // center_point = offset + raw_point * scale / max_dim
210  // offset = center_point - raw_point * scale / max_dim
211  SetOffset(center_point - raw_point * (GetScale() / GetMaxDim()));
212  }
213  return true;
214  }
215 
216  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& ctx) {
217  // draw a white rectangle for the background
218  ctx->rectangle(0, 0, get_allocated_width(), get_allocated_height());
219  ctx->set_source_rgb(1, 1, 1);
220  ctx->fill_preserve();
221  ctx->set_source_rgb(0, 0, 0);
222  ctx->stroke();
223 
224  // scale and translate so that we can draw in cartesian coordinates
225  ctx->save();
226  // make it so that drawing commands in the virtual space map to pixel
227  // coordinates
228  ctx->scale(GetMaxDim() / GetScale(), -GetMaxDim() / GetScale());
229  ctx->translate(0, -GetScale() * get_allocated_height() / GetMaxDim());
230  ctx->translate(-GetOffset()[0], -GetOffset()[1]);
231 
232  ctx->set_line_width(0.001);
233 
234  sig_draw.emit(ctx);
235  ctx->restore();
236 
237  return true;
238  }
239 };
240 
241 } // curves
242 } // gtk
243 
244 #endif // MPBLOCKS_GTK_PAN_ZOOM_VIEW_H_
void SetScaleAdjustments(Glib::RefPtr< Gtk::Adjustment > scale, Glib::RefPtr< Gtk::Adjustment > scale_rate)
Definition: pan_zoom_view.h:93
void SetScale(double scale)
sigc::signal< void, const Cairo::RefPtr< Cairo::Context > & > sig_draw
Signal is emitted by the on_draw handler, and sends out the context with appropriate scaling and...
Definition: pan_zoom_view.h:62
void SetOffsetAdjustments(Glib::RefPtr< Gtk::Adjustment > offset_x, Glib::RefPtr< Gtk::Adjustment > offset_y)
Definition: pan_zoom_view.h:83
Glib::RefPtr< Gtk::Adjustment > scale_
Size (in virtual units) of the largest dimension of the widget.
Definition: pan_zoom_view.h:42
void SetOffset(const Eigen::Vector2d &offset)
sigc::signal< void, GdkEventButton * > sig_button
button event with transformed coordinates
Definition: pan_zoom_view.h:68
bool active_
If true, controls are enabled, if false, controls are disabled and events are passed through...
Definition: pan_zoom_view.h:53
Eigen::Vector2d last_pos_
The last mouse position (used during pan)
Definition: pan_zoom_view.h:49
virtual bool on_scroll_event(GdkEventScroll *event)
virtual bool on_draw(const Cairo::RefPtr< Cairo::Context > &ctx)
A drawing area with built in mouse handlers for pan-zoom control.
Definition: pan_zoom_view.h:36
double GetMaxDim()
return the maximum dimension of the
Eigen::Vector2d TransformPoint(double x, double y)
Return a point in the virtual cartesian plane by applying the offset and scaling of the viewport...
void SetScaleRate(double scale_rate)
Eigen::Vector2d GetOffset()
sigc::signal< void, GdkEventMotion * > sig_motion
motion event with transformed coordinates
Definition: pan_zoom_view.h:65
Matrix< double, 2, 1 > Vector2d
Definition: matrix.h:144
int pan_button_
Which mouse button is used for pan.
Definition: pan_zoom_view.h:56
virtual bool on_motion_notify_event(GdkEventMotion *event)
virtual bool on_button_press_event(GdkEventButton *event)
Eigen::Vector2d RawPoint(double x, double y)
Convert the point (x,y) in GTK coordinates, with the origin at the top left, to a point in traditiona...
void TransformEvent(Event *event)
Glib::RefPtr< Gtk::Adjustment > offset_x_
offset of viewport
Definition: pan_zoom_view.h:38
Glib::RefPtr< Gtk::Adjustment > scale_rate_
When the mouse wheel is turned multiply or divide the scale by this much.
Definition: pan_zoom_view.h:46
Glib::RefPtr< Gtk::Adjustment > offset_y_
offset of viewport
Definition: pan_zoom_view.h:39