libcrn  3.9.5
A document image processing library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GtkCRNImage.cpp
Go to the documentation of this file.
1 /* Copyright 2010-2016 CoReNum, INSA-Lyon, ZHAO Xiaojuan, ENS-Lyon
2  *
3  * This file is part of libgtkcrnmm.
4  *
5  * libgtkcrnmm is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * libgtkcrnmm is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with libgtkcrnmm. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * file: GtkCRNImage.cpp
19  * \author Yann LEYDIER, ZHAO Xiaojuan
20  */
21 
22 #include <libgtkcrnmm_config.h>
23 #include <CRNi18n.h>
24 #include <GtkCRNImage.h>
25 #include <CRNIO/CRNIO.h>
26 #include <CRNMath/CRNMath.h>
27 
28 using namespace GtkCRN;
29 
30 #ifdef CRN_USING_GTKMM3
31 # define get_red_p get_red
32 # define get_green_p get_green
33 # define get_blue_p get_blue
34 #endif
35 
36 int Image::selection_margin(5);
37 
40 #ifndef CRN_USING_GTKMM3
41  Table(3, 3, false),
42 #endif
43  mouse_mode(MouseMode::NONE),
44  need_redraw(true),
45  need_recompute(false),
46  dispw(0),
47  disph(0),
48  zoom(1.0),
49 #ifdef CRN_USING_GTKMM3
50  image_actions(Gio::SimpleActionGroup::create()),
51 #else
52  image_actions(Gtk::ActionGroup::create("image")),
53 #endif
54  selection_type(Overlay::None),
55  scroll_cursor(Gdk::FLEUR),
56  select_cursor(Gdk::CROSS),
57  move_cursor(Gdk::HAND1),
58  move_1_cursor(Gdk::FLEUR),
59  drag_left_cursor(Gdk::LEFT_SIDE),
60  drag_bottom_left_cursor(Gdk::BOTTOM_LEFT_CORNER),
61  drag_bottom_cursor(Gdk::BOTTOM_SIDE),
62  drag_bottom_right_cursor(Gdk::BOTTOM_RIGHT_CORNER),
63  drag_right_cursor(Gdk::RIGHT_SIDE),
64  drag_top_right_cursor(Gdk::TOP_RIGHT_CORNER),
65  drag_top_cursor(Gdk::TOP_SIDE),
66  drag_top_left_cursor(Gdk::TOP_LEFT_CORNER),
67  user_cursor(Gdk::TARGET)
68 {
69 #ifndef CRN_USING_GTKMM3
70  attach(hruler, 1, 2, 0, 1, Gtk::FILL, Gtk::FILL, 0, 0);
71  hruler.show();
72  //hruler.set_sensitive(false);
73  attach(vruler, 0, 1, 1, 2, Gtk::FILL, Gtk::FILL, 0, 0);
74  vruler.show();
75  //vruler.set_sensitive(false);
76 #endif /* !CRN_USING_GTKMM3 */
77 
78 #ifdef CRN_USING_GTKMM3
79  attach(da, 1, 1, 1, 1);
80  da.set_hexpand(true);
81  da.set_vexpand(true);
82 #else
83  attach(da, 1, 2, 1, 2, Gtk::FILL | Gtk::EXPAND | Gtk::SHRINK, Gtk::FILL | Gtk::EXPAND | Gtk::SHRINK, 0, 0);
84 #endif
85  da.add_events(Gdk::EXPOSURE_MASK | Gdk::STRUCTURE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::SCROLL_MASK);
86 #ifdef CRN_USING_GTKMM3
87  da.signal_draw().connect(sigc::mem_fun(this, &Image::expose)); // redraw
88 #else /* CRN_USING_GTKMM3 */
89  da.signal_expose_event().connect(sigc::mem_fun(this, &Image::expose)); // redraw
90 #endif /* CRN_USING_GTKMM3 */
91  da.signal_configure_event().connect(sigc::mem_fun(this, &Image::configure)); // size changed
92  da.signal_motion_notify_event().connect(sigc::mem_fun(this, &Image::mouse_motion)); // mouse motion
93  da.signal_button_press_event().connect(sigc::mem_fun(this, &Image::button_clicked));
94  da.signal_button_release_event().connect(sigc::mem_fun(this, &Image::button_clicked));
95  da.signal_scroll_event().connect(sigc::mem_fun(this, &Image::mouse_wheel));
96  da.show();
97  //da.set_sensitive(false);
98  hscrollbar.get_adjustment()->set_lower(0);
99  hscrollbar.get_adjustment()->set_step_increment(10);
100  hscrollbar.get_adjustment()->set_page_increment(100);
101 #ifdef CRN_USING_GTKMM3
102  attach(hscrollbar, 1, 2, 1, 1);
103  hscrollbar.set_hexpand(true);
104  hscrollbar.set_vexpand(false);
105  hscrollbar.set_orientation(Gtk::ORIENTATION_HORIZONTAL),
106 #else
107  attach(hscrollbar, 1, 2, 2, 3, Gtk::FILL, Gtk::FILL, 0, 0);
108 #endif
109  hscrollbar.signal_value_changed().connect(sigc::mem_fun(this, &Image::scrolled));
110  hscrollbar.show();
111  //hscrollbar.set_sensitive(false);
112 
113  vscrollbar.get_adjustment()->set_lower(0);
114  vscrollbar.get_adjustment()->set_step_increment(10);
115  vscrollbar.get_adjustment()->set_page_increment(100);
116 #ifdef CRN_USING_GTKMM3
117  attach(vscrollbar, 2, 1, 1, 1);
118  vscrollbar.set_hexpand(false);
119  vscrollbar.set_vexpand(true);
120  vscrollbar.set_orientation(Gtk::ORIENTATION_VERTICAL),
121 #else
122  attach(vscrollbar, 2, 3, 1, 2, Gtk::FILL, Gtk::FILL, 0, 0);
123 #endif
124  vscrollbar.signal_value_changed().connect(sigc::mem_fun(this, &Image::scrolled));
125  vscrollbar.show();
126  //vscrollbar.set_sensitive(false);
127 
128  set_sensitive(false);
129  refresher = Glib::signal_timeout().connect(sigc::mem_fun(this, &Image::refresh), 100);
130 
131  // actions
132 #ifdef CRN_USING_GTKMM3
133  image_actions->add_action("zoom-in", sigc::mem_fun(this, &Image::zoom_in));
134  image_actions->add_action("zoom-out", sigc::mem_fun(this, &Image::zoom_out));
135  image_actions->add_action("zoom-100", sigc::mem_fun(this, &Image::zoom_100));
136  image_actions->add_action("zoom-fit", sigc::mem_fun(this, &Image::zoom_fit));
137  image_actions->add_action("clear-user-selection", sigc::mem_fun(this, &Image::clear_selection));
138 #else
139  image_actions->add(Gtk::Action::create("image-zoom-in", Gtk::Stock::ZOOM_IN), sigc::mem_fun(this, &Image::zoom_in));
140  image_actions->add(Gtk::Action::create("image-zoom-out", Gtk::Stock::ZOOM_OUT), sigc::mem_fun(this, &Image::zoom_out));
141  image_actions->add(Gtk::Action::create("image-zoom-100", Gtk::Stock::ZOOM_100), sigc::mem_fun(this, &Image::zoom_100));
142  image_actions->add(Gtk::Action::create("image-zoom-fit", Gtk::Stock::ZOOM_FIT), sigc::mem_fun(this, &Image::zoom_fit));
143  image_actions->add(Gtk::Action::create("image-clear-user-selection", Gtk::Stock::CLEAR, _("_Clear User Selection"), _("Clear User Selection")), sigc::mem_fun(this, &Image::clear_selection));
144 #endif
145  overlays[selection_overlay()].config.moveable = true;
146  overlays[selection_overlay()].config.editable = true;
147 }
148 
151 {
152  refresher.disconnect();
153 }
154 
155 #ifndef CRN_USING_GTKMM3
156 
161 void Image::set_rulers_visible(bool is_visible)
162 {
163  if (is_visible)
164  {
165  hruler.show();
166  vruler.show();
167  }
168  else
169  {
170  hruler.hide();
171  vruler.hide();
172  }
173 }
174 #endif /* ! CRN_USING_GTKMM3 */
175 
181 void Image::set_pixbuf(Glib::RefPtr<Gdk::Pixbuf> pb)
182 {
183  image = pb;
184  if (!image)
185  {
186  set_sensitive(false);
187  }
188  else
189  {
190  pos.X = pos.Y = 0;
191  hscrollbar.get_adjustment()->set_upper(image->get_width());
192  vscrollbar.get_adjustment()->set_upper(image->get_height());
193  image_bounds = crn::Rect(0, 0, image->get_width() - 1, image->get_height() - 1);
194  need_recompute = true;
195  set_sensitive(true);
196  }
197 }
198 
206 void Image::set_zoom(double z)
207 {
208  if (z <= 0.0)
209  throw Glib::OptionError(Glib::OptionError::BAD_VALUE, Glib::ustring("void Image::set_zoom(double z): ") + _("null or negative zoom value."));
210  zoom = z;
211  need_recompute = true;
212  zoom_changed.emit();
213  scrolled_event.emit(int(hscrollbar.get_value()), int(vscrollbar.get_value()));
214 }
215 
220 {
221  zoom += 0.1;
222  need_recompute = true;
223  zoom_changed.emit();
224  scrolled_event.emit(int(hscrollbar.get_value()), int(vscrollbar.get_value()));
225 }
226 
231 {
232  zoom -= 0.1;
233  if (zoom <= 0.0)
234  zoom = 0.1;
235  need_recompute = true;
236  zoom_changed.emit();
237  scrolled_event.emit(int(hscrollbar.get_value()), int(vscrollbar.get_value()));
238 }
239 
244 {
245  zoom = 1.0;
246  need_recompute = true;
247  zoom_changed.emit();
248  scrolled_event.emit(int(hscrollbar.get_value()), int(vscrollbar.get_value()));
249 }
250 
255 {
256  if (image)
257  zoom = crn::Min(double(dispw) / image_bounds.GetWidth(), double(disph) / image_bounds.GetHeight());
258  else
259  zoom = 1.0;
260  need_recompute = true;
261  zoom_changed.emit();
262  scrolled_event.emit(int(hscrollbar.get_value()), int(vscrollbar.get_value()));
263 }
264 
271 void Image::focus_on(int x, int y)
272 {
273  if (image_bounds.IsValid())
274  {
275  if (image_bounds.GetLeft() > x) x = image_bounds.GetLeft();
276  if (image_bounds.GetRight() < x) x = image_bounds.GetRight();
277  if (image_bounds.GetTop() > y) y = image_bounds.GetTop();
278  if (image_bounds.GetBottom() < y) y = image_bounds.GetBottom();
279  pos.X = x - int(dispw / zoom / 2.0);
280  hscrollbar.set_value(pos.X);
281  pos.Y = y - int(disph / zoom / 2.0);
282  vscrollbar.set_value(pos.Y);
283  need_recompute = true;
284  }
285 }
286 
290 #ifdef CRN_USING_GTKMM3
291 void Image::set_user_cursor(const Gdk::CursorType &cur)
292 #else /* CRN_USING_GTKMM3 */
293 void Image::set_user_cursor(const Gdk::Cursor &cur)
294 #endif /* CRN_USING_GTKMM3 */
295 {
296  user_cursor = cur;
297 }
298 
300 void Image::scrolled()
301 {
302  need_recompute = true;
303  scrolled_event.emit(int(hscrollbar.get_value()), int(vscrollbar.get_value()));
304 }
305 
306 
312 #ifdef CRN_USING_GTKMM3
313 bool Image::expose(const Cairo::RefPtr<Cairo::Context> &cc)
314 #else /* CRN_USING_GTKMM3 */
315 bool Image::expose(GdkEventExpose *ev)
316 #endif /* CRN_USING_GTKMM3 */
317 {
318  need_redraw = true;
319  return false;
320 }
321 
326 bool Image::configure(GdkEventConfigure *ev)
327 {
328  dispw = ev->width;
329  disph = ev->height;
330  need_recompute = true;
331  return false;
332 }
333 
338 bool Image::mouse_motion(GdkEventMotion* ev)
339 {
340  // update rulers
341 #ifndef CRN_USING_GTKMM3
342  hruler.set_range(pos.X, pos.X + int(dispw / zoom), pos.X + int(ev->x / zoom), pos.X + int(dispw / zoom));
343  vruler.set_range(pos.Y, pos.Y + int(disph / zoom), pos.Y + int(ev->y / zoom), pos.Y + int(disph / zoom));
344 #endif /* ! CRN_USING_GTKMM3 */
345 
346  // check for a click
347  switch (mouse_mode)
348  {
349  case MouseMode::NONE:
350  {
351  crn::String s1, s2;
352  MouseMode mode = find_selection_at(ev->x, ev->y, s1, s2);
353  set_cursor_from_mode(mode);
354  }
355  break;
356  case MouseMode::SCROLL:
357  {
358  double dx = (click_ref.X - ev->x) / zoom;
359  double dy = (click_ref.Y - ev->y) / zoom;
360  hscrollbar.set_value(hscrollbar.get_value() + dx);
361  vscrollbar.set_value(vscrollbar.get_value() + dy);
362  click_ref.X = ev->x;
363  click_ref.Y = ev->y;
364  }
365  break;
366  case MouseMode::DRAW:
367  switch (selection_type)
368  {
369  case Overlay::Rectangle:
370  {
371  crn::Rect newsel(
372  int(crn::Min(click_ref.X, ev->x) / zoom) + pos.X,
373  int(crn::Min(click_ref.Y, ev->y) / zoom) + pos.Y,
374  int(crn::Max(click_ref.X, ev->x) / zoom) + pos.X,
375  int(crn::Max(click_ref.Y, ev->y) / zoom) + pos.Y);
376  if (!overlays[selection_overlay()].config.can_jut_out)
377  newsel &= image_bounds;
378  auto r = dynamic_cast<Rectangle*>(overlays[selection_overlay()].items[selection_overlay()].get());
379  if (r)
380  r->rect = newsel;
381  else
382  overlays[selection_overlay()].items[selection_overlay()].reset(new Rectangle(newsel));
383  need_redraw = true;
384  }
385  break;
386  case Overlay::Point:
387  {
388  int x = int(ev->x / zoom + pos.X);
389  int y = int(ev->y / zoom + pos.Y);
390  if (!overlays[selection_overlay()].config.can_jut_out)
391  {
392  x = crn::Cap(x, 0, image_bounds.GetRight());
393  y = crn::Cap(y, 0, image_bounds.GetBottom());
394  }
395  auto pt = dynamic_cast<crn::Point2DInt*>(overlays[selection_overlay()].items[selection_overlay()].get());
396  if (pt)
397  {
398  pt->X = x;
399  pt->Y = y;
400  }
401  else
402  overlays[selection_overlay()].items[selection_overlay()].reset(new Point(crn::Point2DInt(x, y)));
403  need_redraw = true;
404  }
405  break;
406  case Overlay::Line:
407  {
408  int x = int(ev->x / zoom + pos.X);
409  int y = int(ev->y / zoom + pos.Y);
410  if (!overlays[selection_overlay()].config.can_jut_out)
411  {
412  x = crn::Cap(x, 0, image_bounds.GetRight());
413  y = crn::Cap(y, 0, image_bounds.GetBottom());
414  }
415  auto li = dynamic_cast<Line*>(overlays[selection_overlay()].items[selection_overlay()].get());
416  if (li)
417  {
418  li->p2.X = x;
419  li->p2.Y = y;
420  }
421  else
422  overlays[selection_overlay()].items[selection_overlay()].reset(new Line(crn::Point2DInt(x,y), crn::Point2DInt(x,y)));
423  need_redraw = true;
424  }
425  break;
426  }
427  break;
428  case MouseMode::MOVE:
429  {
430 
431  double dx = (ev->x - click_ref.X) / zoom;
432  double dy = (ev->y - click_ref.Y) / zoom;
433  int ox = int(dx);
434  int oy = int(dy);
435 
436  std::unique_ptr<OverlayItem> &item(overlays[selected_overlay].items[selected_overlay_item]);
437 
438  Line* li = dynamic_cast<Line*>(item.get());
439  if (li != nullptr)
440  {
441  if (!overlays[selected_overlay].config.can_jut_out)
442  {
443  if (ox >= 0)
444  ox = crn::Min(image_bounds.GetRight() - crn::Max(li->p1.X, li->p2.X), ox);
445  else
446  ox = crn::Max(-crn::Min(li->p1.X, li->p2.X), ox);
447  if (oy >= 0)
448  oy = crn::Min(image_bounds.GetBottom() - crn::Max(li->p1.Y, li->p2.Y), oy);
449  else
450  oy = crn::Max(-crn::Min(li->p1.Y, li->p2.Y), oy);
451  }
452  li->p1.X += ox;
453  li->p1.Y += oy;
454  li->p2.X += ox;
455  li->p2.Y += oy;
456  }
457 
458 
459  Rectangle* rec=dynamic_cast<Rectangle*>(item.get());
460  if(rec != nullptr)
461  {
462  if (!overlays[selected_overlay].config.can_jut_out)
463  {
464  if (ox >= 0)
465  ox = crn::Min(image_bounds.GetRight() - crn::Max(rec->rect.GetTopLeft().X, rec->rect.GetBottomRight().X), ox);
466  else
467  ox = crn::Max(-crn::Min(rec->rect.GetTopLeft().X, rec->rect.GetBottomRight().X), ox);
468  if (oy >= 0)
469  oy = crn::Min(image_bounds.GetBottom() - crn::Max(rec->rect.GetTopLeft().Y, rec->rect.GetBottomRight().Y), oy);
470  else
471  oy = crn::Max(-crn::Min(rec->rect.GetTopLeft().Y, rec->rect.GetBottomRight().Y), oy);
472  }
473  rec->rect.SetLeft(rec->rect.GetTopLeft().X + ox);
474  rec->rect.SetTop(rec->rect.GetTopLeft().Y + oy);
475  rec->rect.SetRight(rec->rect.GetBottomRight().X + ox);
476  rec->rect.SetBottom(rec->rect.GetBottomRight().Y + oy);
477  }
478  Polygon* pol=dynamic_cast<Polygon*>(item.get());
479  if(pol != nullptr)
480  {
481  if (!overlays[selected_overlay].config.can_jut_out)
482  {
483  int x_maximum=pol->points[0].X;
484  int y_maximum=pol->points[0].Y;
485  int x_minimum=pol->points[0].X;
486  int y_minimum=pol->points[0].Y;
487  for (size_t nb = 0; nb < pol->points.size(); ++nb)
488  {
489  if(x_maximum < pol->points[nb].X)
490  x_maximum = pol->points[nb].X;
491  if(y_maximum < pol->points[nb].Y)
492  y_maximum = pol->points[nb].Y;
493  if(x_minimum > pol->points[nb].X)
494  x_minimum = pol->points[nb].X;
495  if(y_minimum > pol->points[nb].Y)
496  y_minimum = pol->points[nb].Y;
497  }
498  if (ox >= 0)
499  ox = crn::Min(image_bounds.GetRight() - x_maximum, ox);
500  else
501  ox = crn::Max(-x_minimum, ox);
502  if (oy >= 0)
503  oy = crn::Min(image_bounds.GetBottom() - y_maximum, oy);
504  else
505  oy = crn::Max(-y_minimum, oy);
506  }
507  for (size_t nb = 0; nb < pol->points.size(); ++nb)
508  {
509  pol->points[nb].X += ox;
510  pol->points[nb].Y += oy;
511  }
512  }
513 
514  click_ref.X = ev->x + (ox - dx) * zoom; // don't forget to add the residue to keep the cursor at the same place in the box!
515  click_ref.Y = ev->y + (oy - dy) * zoom;
516  need_redraw = true;
517 
518  }
519  break;
520 
522  {
523  double dx = (ev->x - click_ref.X) / zoom;
524  double dy = (ev->y - click_ref.Y) / zoom;
525  int ox = int(dx);
526  int oy = int(dy);
527  if(movePoint != nullptr)
528  {
529 
530  std:: unique_ptr<OverlayItem> &item(overlays[selected_overlay].items[selected_overlay_item]);
531  Line* li = dynamic_cast<Line*>(item.get());
532  if (li != nullptr)
533  {
534  if (!overlays[selected_overlay].config.can_jut_out)
535  {
536  if (ox >= 0)
537  ox = crn::Min(image_bounds.GetRight() - li->p1.X, ox);
538  else
539  ox = crn::Max(-li->p1.X, ox);
540  if (oy >= 0)
541  oy = crn::Min(image_bounds.GetBottom() - li->p1.Y, oy);
542  else
543  oy = crn::Max(-li->p1.Y, oy);
544  }
545  movePoint->X += int(ox * zoom);
546  movePoint->Y += int(oy * zoom);
547  }
548  Point* po = dynamic_cast<Point*>(item.get());
549  if (po != nullptr)
550  {
551  if (!overlays[selected_overlay].config.can_jut_out)
552  {
553  if (ox >= 0)
554  ox = crn::Min(image_bounds.GetRight() - po->point.X, ox);
555  else
556  ox = crn::Max(-po->point.X, ox);
557  if (oy >= 0)
558  oy = crn::Min(image_bounds.GetBottom() - po->point.Y, oy);
559  else
560  oy = crn::Max(-po->point.Y, oy);
561  }
562  movePoint->X += int(ox * zoom);
563  movePoint->Y += int(oy * zoom);
564  }
565  Polygon* pol = dynamic_cast<Polygon*>(item.get());
566  if(pol != nullptr)
567  {
568  if (!overlays[selected_overlay].config.can_jut_out)
569  {
570  int x_maximum=pol->points[0].X;
571  int y_maximum=pol->points[0].Y;
572  int x_minimum=pol->points[0].X;
573  int y_minimum=pol->points[0].Y;
574  for (size_t nb = 0; nb < pol->points.size(); ++nb)
575  {
576  if(x_maximum < pol->points[nb].X)
577  x_maximum = pol->points[nb].X;
578  if(y_maximum < pol->points[nb].Y)
579  y_maximum = pol->points[nb].Y;
580  if(x_minimum > pol->points[nb].X)
581  x_minimum = pol->points[nb].X;
582  if(y_minimum > pol->points[nb].Y)
583  y_minimum = pol->points[nb].Y;
584  }
585  if (ox >= 0)
586  ox = crn::Min(image_bounds.GetRight() - x_maximum, ox);
587  else
588  ox = crn::Max(-x_minimum, ox);
589  if (oy >= 0)
590  oy = crn::Min(image_bounds.GetBottom() - y_maximum, oy);
591  else
592  oy = crn::Max(-y_minimum, oy);
593  }
594  movePoint->X += ox;
595  movePoint->Y += oy;
596 
597  }
598  if(ev->type == GDK_BUTTON_RELEASE)
599  movePoint = nullptr;
600  }
601 
602  click_ref.X = ev->x + (ox - dx) * zoom; // don't forget to add the residue to keep the cursor at the same place in the box!
603  click_ref.Y = ev->y + (oy - dy) * zoom;
604  need_redraw = true;
605  }
606  break;
607 
609  {
610  int x = pos.X + int(ev->x / zoom);
611 
612 
613  std::unique_ptr<OverlayItem> &item(overlays[selected_overlay].items[selected_overlay_item]);
614 
615  Line* li = dynamic_cast<Line*>(item.get());
616  if (li != nullptr)
617  {
618  if (x < li->p2.X)
619  {
620  if (!overlays[selected_overlay].config.can_jut_out)
621  if (x < 0) x = 0;
622  li->p1.X = x;
623  need_redraw = true;
624  }
625  }
626 
627  Rectangle* rec = dynamic_cast<Rectangle*>(item.get());
628  if (rec != nullptr)
629  {
630  if (x < rec->rect.GetRight())
631  {
632  if (!overlays[selected_overlay].config.can_jut_out)
633  if (x < 0) x = 0;
634  rec->rect.SetLeft(x);
635  need_redraw = true;
636  }
637  }
638 
639  Point* po = dynamic_cast<Point*>(item.get());
640  if (po != nullptr)
641  {
642  if (!overlays[selected_overlay].config.can_jut_out)
643  if (x < 0) x = 0;
644  po->point.X = x;
645  need_redraw = true;
646 
647  }
648 
649  Polygon* pol = dynamic_cast<Polygon*>(item.get());
650  if (pol != nullptr)
651  {
652  int x_maximum=pol->points[0].X;
653  int x_minimum=pol->points[0].X;
654  size_t id_minimim = 0;
655  for (size_t nb = 0; nb < pol->points.size(); ++nb)
656  {
657  if(x_maximum < pol->points[nb].X)
658  x_maximum = pol->points[nb].X;
659  if(x_minimum > pol->points[nb].X)
660  {
661  x_minimum = pol->points[nb].X;
662  id_minimim = nb;
663  }
664  }
665 
666  if (x < x_maximum)
667  {
668  if (!overlays[selected_overlay].config.can_jut_out)
669  if (x < 0) x = 0;
670  pol->points[id_minimim].X = x;
671  need_redraw = true;
672  }
673  }
674 
675  }
676  break;
678  {
679  int x = pos.X + int(ev->x / zoom);
680 
681  std::unique_ptr<OverlayItem> &item(overlays[selected_overlay].items[selected_overlay_item]);
682 
683  Line* li = dynamic_cast<Line*>(item.get());
684  if (li != nullptr)
685  {
686  if (x > li->p1.X)
687  {
688  if (!overlays[selected_overlay].config.can_jut_out)
689  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
690  li->p2.X = x;
691  need_redraw = true;
692  }
693  }
694 
695  Rectangle* rec = dynamic_cast<Rectangle*>(item.get());
696  if (rec != nullptr)
697  {
698 
699  if (x > rec->rect.GetLeft())
700  {
701  if (!overlays[selected_overlay].config.can_jut_out)
702  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
703  rec->rect.SetRight(x);//x2 = x;
704  need_redraw = true;
705  }
706  }
707 
708  Point* po = dynamic_cast<Point*>(item.get());
709  if (po != nullptr)
710  {
711  if (!overlays[selected_overlay].config.can_jut_out)
712  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
713  po->point.X = x;
714  need_redraw = true;
715 
716  }
717 
718  Polygon* pol = dynamic_cast<Polygon*>(item.get());
719  if (pol != nullptr)
720  {
721 
722  int x_minimum=pol->points[0].X;
723  int x_maximum=pol->points[0].X;
724  size_t id_maximum = 0;
725  for (size_t nb = 0; nb < pol->points.size(); ++nb)
726  {
727  if(x_minimum > pol->points[nb].X)
728  x_minimum = pol->points[nb].X;
729  if(x_maximum < pol->points[nb].X)
730  {
731  x_maximum = pol->points[nb].X;
732  id_maximum = nb;
733  }
734  }
735 
736  if (x > x_minimum)
737  {
738  if (!overlays[selected_overlay].config.can_jut_out)
739  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
740  pol->points[id_maximum].X = x;
741  need_redraw = true;
742  }
743  }
744 
745  }
746  break;
748  {
749  int y = pos.Y + int(ev->y / zoom);
750  std::unique_ptr<OverlayItem> &item(overlays[selected_overlay].items[selected_overlay_item]);
751 
752  Line* li = dynamic_cast<Line*>(item.get());
753  if (li != nullptr)
754  {
755 
756  if (y < li->p2.Y)
757  {
758  if (!overlays[selected_overlay].config.can_jut_out)
759  if (y < 0) y = 0;
760  li->p1.Y = y;
761  need_redraw = true;
762  }
763  }
764 
765  Rectangle* rec = dynamic_cast<Rectangle*>(item.get());
766  if (rec != nullptr)
767  {
768 
769  if (y < rec->rect.GetBottom())//y2)
770  {
771  if (!overlays[selected_overlay].config.can_jut_out)
772  if (y < 0) y = 0;
773  rec->rect.SetTop(y);
774  need_redraw = true;
775  }
776  }
777 
778  Point* po = dynamic_cast<Point*>(item.get());
779  if (po != nullptr)
780  {
781  if (!overlays[selected_overlay].config.can_jut_out)
782  if (y < 0) y = 0;
783  po->point.Y = y;
784  need_redraw = true;
785 
786  }
787 
788  Polygon* pol = dynamic_cast<Polygon*>(item.get());
789  if (pol != nullptr)
790  {
791  int y_maximum=pol->points[0].Y;
792  int y_minimum=pol->points[0].Y;
793  size_t id_minimim = 0;
794  for (size_t nb = 0; nb < pol->points.size(); ++nb)
795  {
796  if(y_maximum < pol->points[nb].Y)
797  y_maximum = pol->points[nb].Y;
798  if(y_minimum > pol->points[nb].Y)
799  {
800  y_minimum = pol->points[nb].Y;
801  id_minimim = nb;
802  }
803  }
804 
805  if (y < y_maximum)
806  {
807  if (!overlays[selected_overlay].config.can_jut_out)
808  if (y < 0) y = 0;
809  pol->points[id_minimim].Y = y;
810  need_redraw = true;
811  }
812  }
813 
814  }
815  break;
817  {
818  int y = pos.Y + int(ev->y / zoom);
819  std::unique_ptr<OverlayItem> &item(overlays[selected_overlay].items[selected_overlay_item]);
820 
821  Line* li = dynamic_cast<Line*>(item.get());
822  if (li != nullptr)
823  {
824  if (y > li->p1.Y)//y1)
825  {
826  if (!overlays[selected_overlay].config.can_jut_out)
827  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
828  li->p2.Y = y;
829  need_redraw = true;
830  }
831  }
832 
833  Rectangle* rec = dynamic_cast<Rectangle*>(item.get());
834  if (rec != nullptr)
835  {
836  if (y > rec->rect.GetTop())//y1)
837  {
838  if (!overlays[selected_overlay].config.can_jut_out)
839  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
840  rec->rect.SetBottom(y);//y2 = y;
841  need_redraw = true;
842  }
843  }
844 
845  Point* po = dynamic_cast<Point*>(item.get());
846  if (po != nullptr)
847  {
848  if (!overlays[selected_overlay].config.can_jut_out)
849  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
850  po->point.Y = y;
851  need_redraw = true;
852 
853  }
854 
855  Polygon* pol = dynamic_cast<Polygon*>(item.get());
856  if (pol != nullptr)
857  {
858  int y_minimum = pol->points[0].Y;
859  int y_maximum = pol->points[0].Y;
860  size_t id_maximum = 0;
861  for (size_t nb = 0; nb < pol->points.size(); ++nb)
862  {
863  if (y_minimum > pol->points[nb].Y)
864  y_minimum = pol->points[nb].Y;
865  if (y_maximum < pol->points[nb].Y)
866  {
867  y_maximum = pol->points[nb].Y;
868  id_maximum = nb;
869  }
870  }
871 
872  if (y > y_minimum)
873  {
874  if (!overlays[selected_overlay].config.can_jut_out)
875  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
876  pol->points[id_maximum].Y = y;
877  need_redraw = true;
878  }
879  }
880 
881  }
882  break;
884  {
885  int x = pos.X + int(ev->x / zoom);
886 
887  std::unique_ptr<OverlayItem> &item(overlays[selected_overlay].items[selected_overlay_item]);
888 
889  Line* li = dynamic_cast<Line*>(item.get());
890  if (li != nullptr)
891  {
892 
893  if (x < li->p2.X)
894  {
895  if (!overlays[selected_overlay].config.can_jut_out)
896  if (x < 0) x = 0;
897  li->p1.X = x;
898  need_redraw = true;
899  }
900  }
901 
902  Rectangle* rec = dynamic_cast<Rectangle*>(item.get());
903  if (rec != nullptr)
904  {
905 
906  if (x < rec->rect.GetRight())//x2)
907  {
908  if (!overlays[selected_overlay].config.can_jut_out)
909  if (x < 0) x = 0;
910  rec->rect.SetLeft(x);//x1 = x;
911  need_redraw = true;
912  }
913  }
914 
915  Point* po = dynamic_cast<Point*>(item.get());
916  if (po != nullptr)
917  {
918  if (!overlays[selected_overlay].config.can_jut_out)
919  if (x < 0) x = 0;
920  po->point.X = x;
921  need_redraw = true;
922 
923  }
924 
925  Polygon* pol = dynamic_cast<Polygon*>(item.get());
926  if (pol != nullptr)
927  {
928  int x_maximum=pol->points[0].X;
929  int x_minimum=pol->points[0].X;
930  size_t id_minimim = 0;
931  for(size_t nb = 0; nb < pol->points.size(); ++nb)
932  {
933  if(x_maximum < pol->points[nb].X)
934  x_maximum = pol->points[nb].X;
935  if(x_minimum > pol->points[nb].X)
936  {
937  x_minimum = pol->points[nb].X;
938  id_minimim = nb;
939  }
940  }
941 
942  if (x < x_maximum)
943  {
944  if (!overlays[selected_overlay].config.can_jut_out)
945  if (x < 0) x = 0;
946  pol->points[id_minimim].X = x;
947  need_redraw = true;
948  }
949  }
950 
951  int y = pos.Y + int(ev->y / zoom);
952 
953  if (li != nullptr)
954  {
955 
956  if (y < li->p2.Y)
957  {
958  if (!overlays[selected_overlay].config.can_jut_out)
959  if (y < 0) y = 0;
960  li->p1.Y = y;
961  need_redraw = true;
962  }
963  }
964 
965  if (rec != nullptr)
966  {
967 
968  if (y < rec->rect.GetBottom())//y2)
969  {
970  if (!overlays[selected_overlay].config.can_jut_out)
971  if (y < 0) y = 0;
972  rec->rect.SetTop(y);//y1 = y;
973  need_redraw = true;
974  }
975  }
976 
977  if (po != nullptr)
978  {
979  if (!overlays[selected_overlay].config.can_jut_out)
980  if (y < 0) y = 0;
981  po->point.Y = y;
982  need_redraw = true;
983 
984  }
985 
986  if (pol != nullptr)
987  {
988 
989  int y_maximum = pol->points[0].Y;
990  int y_minimum = pol->points[0].Y;
991  size_t id_minimim = 0;
992  for (size_t nb = 0; nb < pol->points.size(); ++nb)
993  {
994  if (y_maximum < pol->points[nb].Y)
995  y_maximum = pol->points[nb].Y;
996  if (y_minimum > pol->points[nb].Y)
997  {
998  y_minimum = pol->points[nb].Y;
999  id_minimim = nb;
1000  }
1001  }
1002  if (y < y_maximum)
1003  {
1004  if (!overlays[selected_overlay].config.can_jut_out)
1005  if (y < 0) y = 0;
1006  pol->points[id_minimim].Y = y;
1007  need_redraw = true;
1008  }
1009  }
1010  }
1011  break;
1013  {
1014  int x = pos.X + int(ev->x / zoom);
1015 
1016  std::unique_ptr<OverlayItem> &item(overlays[selected_overlay].items[selected_overlay_item]);
1017 
1018  Line* li = dynamic_cast<Line*>(item.get());
1019  if (li != nullptr)
1020  {
1021  if (x > li->p1.X)
1022  {
1023  if (!overlays[selected_overlay].config.can_jut_out)
1024  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
1025  li->p2.X = x;
1026  need_redraw = true;
1027  }
1028  }
1029 
1030  Rectangle* rec = dynamic_cast<Rectangle*>(item.get());
1031  if (rec != nullptr)
1032  {
1033 
1034  if (x > rec->rect.GetLeft())//x1)
1035  {
1036  if (!overlays[selected_overlay].config.can_jut_out)
1037  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
1038  rec->rect.SetRight(x);//x2 = x;
1039  need_redraw = true;
1040  }
1041  }
1042 
1043  Point* po = dynamic_cast<Point*>(item.get());
1044  if (po != nullptr)
1045  {
1046  if (!overlays[selected_overlay].config.can_jut_out)
1047  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
1048  po->point.X = x;
1049  need_redraw = true;
1050 
1051  }
1052 
1053  Polygon* pol = dynamic_cast<Polygon*>(item.get());
1054  if (pol != nullptr)
1055  {
1056  int x_minimum = pol->points[0].X;
1057  int x_maximum = pol->points[0].X;
1058  size_t id_maximum = 0;
1059  for (size_t nb = 0; nb < pol->points.size(); ++nb)
1060  {
1061  if (x_minimum > pol->points[nb].X)
1062  x_minimum = pol->points[nb].X;
1063  if (x_maximum < pol->points[nb].X)
1064  {
1065  x_maximum = pol->points[nb].X;
1066  id_maximum = nb;
1067  }
1068  }
1069 
1070  if (x > x_minimum)
1071  {
1072  if (!overlays[selected_overlay].config.can_jut_out)
1073  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
1074  pol->points[id_maximum].X = x;
1075  need_redraw = true;
1076  }
1077  }
1078 
1079  int y = pos.Y + int(ev->y / zoom);
1080 
1081  if (li != nullptr)
1082  {
1083 
1084  if (y < li->p2.Y)
1085  {
1086  if (!overlays[selected_overlay].config.can_jut_out)
1087  if (y < 0) y = 0;
1088  li->p1.Y = y;
1089  need_redraw = true;
1090  }
1091  }
1092 
1093  if (rec != nullptr)
1094  {
1095 
1096  if (y < rec->rect.GetBottom())//y2)
1097  {
1098  if (!overlays[selected_overlay].config.can_jut_out)
1099  if (y < 0) y = 0;
1100  rec->rect.SetTop(y);//y1 = y;
1101  need_redraw = true;
1102  }
1103  }
1104 
1105  if (po != nullptr)
1106  {
1107  if (!overlays[selected_overlay].config.can_jut_out)
1108  if (y < 0) y = 0;
1109  po->point.Y= y;
1110  need_redraw = true;
1111 
1112  }
1113 
1114  if (pol != nullptr)
1115  {
1116  int y_maximum = pol->points[0].Y;
1117  int y_minimum = pol->points[0].Y;
1118  size_t id_minimim = 0;
1119  for (size_t nb = 0; nb < pol->points.size(); ++nb)
1120  {
1121  if (y_maximum < pol->points[nb].Y)
1122  y_maximum = pol->points[nb].Y;
1123  if (y_minimum > pol->points[nb].Y)
1124  {
1125  y_minimum = pol->points[nb].Y;
1126  id_minimim = nb;
1127  }
1128  }
1129 
1130  if (y < y_maximum)
1131  {
1132  if (!overlays[selected_overlay].config.can_jut_out)
1133  if (y < 0) y = 0;
1134  pol->points[id_minimim].Y = y;
1135  need_redraw = true;
1136  }
1137  }
1138  }
1139  break;
1141  {
1142  int x = pos.X + int(ev->x / zoom);
1143 
1144  std::unique_ptr<OverlayItem> &item(overlays[selected_overlay].items[selected_overlay_item]);
1145 
1146  Line* li = dynamic_cast<Line*>(item.get());
1147  if (li != nullptr)
1148  {
1149 
1150  if (x < li->p2.X)
1151  {
1152  if (!overlays[selected_overlay].config.can_jut_out)
1153  if (x < 0) x = 0;
1154  li->p1.X = x;
1155  need_redraw = true;
1156  }
1157  }
1158  Rectangle* rec = dynamic_cast<Rectangle*>(item.get());
1159  if (rec != nullptr)
1160  {
1161 
1162  if (x < rec->rect.GetRight())
1163  {
1164  if (!overlays[selected_overlay].config.can_jut_out)
1165  if (x < 0) x = 0;
1166  rec->rect.SetLeft(x);
1167  need_redraw = true;
1168  }
1169  }
1170  Point* po = dynamic_cast<Point*>(item.get());
1171  if (po != nullptr)
1172  {
1173  if (!overlays[selected_overlay].config.can_jut_out)
1174  if (x < 0) x = 0;
1175  po->point.X = x;
1176  need_redraw = true;
1177  }
1178  Polygon* pol = dynamic_cast<Polygon*>(item.get());
1179  if (pol != nullptr)
1180  {
1181  int x_maximum = pol->points[0].X;
1182  int x_minimum = pol->points[0].X;
1183  size_t id_minimim = 0;
1184  for (size_t nb = 0; nb < pol->points.size(); ++nb)
1185  {
1186  if (x_maximum < pol->points[nb].X)
1187  x_maximum = pol->points[nb].X;
1188  if (x_minimum > pol->points[nb].X)
1189  {
1190  x_minimum = pol->points[nb].X;
1191  id_minimim = nb;
1192  }
1193  }
1194 
1195  if (x < x_maximum)
1196  {
1197  if (!overlays[selected_overlay].config.can_jut_out)
1198  if (x < 0) x = 0;
1199  pol->points[id_minimim].X = x;
1200  need_redraw = true;
1201  }
1202  }
1203 
1204  int y = pos.Y + int(ev->y / zoom);
1205  if (li != nullptr)
1206  {
1207  if (y > li->p1.Y)
1208  {
1209  if (!overlays[selected_overlay].config.can_jut_out)
1210  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
1211  li->p2.Y = y;
1212  need_redraw = true;
1213  }
1214  }
1215 
1216  if (rec != nullptr)
1217  {
1218  if (y > rec->rect.GetTop())
1219  {
1220  if (!overlays[selected_overlay].config.can_jut_out)
1221  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
1222  rec->rect.SetBottom(y);
1223  need_redraw = true;
1224  }
1225  }
1226 
1227  if (po != nullptr)
1228  {
1229  if (!overlays[selected_overlay].config.can_jut_out)
1230  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
1231  po->point.Y = y;
1232  need_redraw = true;
1233 
1234  }
1235 
1236  if (pol != nullptr)
1237  {
1238  int y_minimum = pol->points[0].Y;
1239  int y_maximum = pol->points[0].Y;
1240  size_t id_maximum = 0;
1241  for (size_t nb = 0; nb < pol->points.size(); ++nb)
1242  {
1243  if (y_minimum > pol->points[nb].Y)
1244  y_minimum = pol->points[nb].Y;
1245  if (y_maximum < pol->points[nb].Y)
1246  {
1247  y_maximum = pol->points[nb].Y;
1248  id_maximum = nb;
1249  }
1250  }
1251 
1252  if (y > y_minimum)
1253  {
1254  if (!overlays[selected_overlay].config.can_jut_out)
1255  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
1256  pol->points[id_maximum].Y = y;
1257  need_redraw = true;
1258  }
1259  }
1260 
1261  }
1262  break;
1264  {
1265  int x = pos.X + int(ev->x / zoom);
1266 
1267  std::unique_ptr<OverlayItem> &item(overlays[selected_overlay].items[selected_overlay_item]);
1268 
1269  Line* li = dynamic_cast<Line*>(item.get());
1270  if (li != nullptr)
1271  {
1272  if (x > li->p1.X)
1273  {
1274  if (!overlays[selected_overlay].config.can_jut_out)
1275  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
1276  li->p2.X = x;
1277  need_redraw = true;
1278  }
1279  }
1280 
1281  Rectangle* rec = dynamic_cast<Rectangle*>(item.get());
1282  if (rec != nullptr)
1283  {
1284  if (x > rec->rect.GetLeft())
1285  {
1286  if (!overlays[selected_overlay].config.can_jut_out)
1287  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
1288  rec->rect.SetRight(x);
1289  need_redraw = true;
1290  }
1291  }
1292 
1293  Point* po = dynamic_cast<Point*>(item.get());
1294  if (po != nullptr)
1295  {
1296  if (!overlays[selected_overlay].config.can_jut_out)
1297  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
1298  po->point.X = x;
1299  need_redraw = true;
1300 
1301  }
1302 
1303  Polygon* pol = dynamic_cast<Polygon*>(item.get());
1304  if (pol != nullptr)
1305  {
1306  int x_minimum = pol->points[0].X;
1307  int x_maximum = pol->points[0].X;
1308  size_t id_maximum = 0;
1309  for (size_t nb = 0; nb < pol->points.size(); ++nb)
1310  {
1311  if(x_minimum > pol->points[nb].X)
1312  x_minimum = pol->points[nb].X;
1313  if(x_maximum < pol->points[nb].X)
1314  {
1315  x_maximum = pol->points[nb].X;
1316  id_maximum = nb;
1317  }
1318  }
1319 
1320  if (x > x_minimum)
1321  {
1322  if (!overlays[selected_overlay].config.can_jut_out)
1323  if (x > image_bounds.GetRight()) x = image_bounds.GetRight();
1324  pol->points[id_maximum].X = x;
1325  need_redraw = true;
1326  }
1327  }
1328 
1329  int y = pos.Y + int(ev->y / zoom);
1330  if (li != nullptr)
1331  {
1332  if (y > li->p1.Y)
1333  {
1334  if (!overlays[selected_overlay].config.can_jut_out)
1335  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
1336  li->p2.Y = y;
1337  need_redraw = true;
1338  }
1339  }
1340 
1341  if (rec != nullptr)
1342  {
1343  if (y > rec->rect.GetTop())
1344  {
1345  if (!overlays[selected_overlay].config.can_jut_out)
1346  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
1347  rec->rect.SetBottom(y);
1348  need_redraw = true;
1349  }
1350  }
1351 
1352  if (po != nullptr)
1353  {
1354  if (!overlays[selected_overlay].config.can_jut_out)
1355  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
1356  po->point.Y = y;
1357  need_redraw = true;
1358 
1359  }
1360 
1361  if (pol != nullptr)
1362  {
1363  int y_minimum = pol->points[0].Y;
1364  int y_maximum = pol->points[0].Y;
1365  size_t id_maximum = 0;
1366  for (size_t nb = 0; nb < pol->points.size(); ++nb)
1367  {
1368  if (y_minimum > pol->points[nb].Y)
1369  y_minimum = pol->points[nb].Y;
1370  if (y_maximum < pol->points[nb].Y)
1371  {
1372  y_maximum = pol->points[nb].Y;
1373  id_maximum = nb;
1374  }
1375  }
1376 
1377  if (y > y_minimum)
1378  {
1379  if (!overlays[selected_overlay].config.can_jut_out)
1380  if (y > image_bounds.GetBottom()) y = image_bounds.GetBottom();
1381  pol->points[id_maximum].Y = y;
1382  need_redraw = true;
1383  }
1384  }
1385 
1386  }
1387  break;
1388  case MouseMode::USER:
1389  {
1390  int x = int(ev->x / zoom) + pos.X;
1391  int y = int(ev->y / zoom) + pos.Y;
1392  user_mouse.emit(x, y);
1393  }
1394  break;
1395  }
1396  return false;
1397 }
1398 
1403 bool Image::button_clicked(GdkEventButton *ev)
1404 {
1405  switch (mouse_mode)
1406  {
1407  case MouseMode::NONE:
1408  if (ev->type == GDK_BUTTON_PRESS)
1409  {
1410  switch (ev->button)
1411  {
1412  case 1:
1413  {
1414  // left button
1415  // look for a selection under the mouse pointer and use it if found
1416  int x = int(ev->x / zoom) + pos.X;
1417  int y = int(ev->y / zoom) + pos.Y;
1418  if (selection_type == Overlay::User)
1419  {
1420  mouse_mode = MouseMode::USER;
1421  user_mouse.emit(x, y);
1422  break;
1423  }
1424  MouseMode mode = find_selection_at(ev->x, ev->y, selected_overlay, selected_overlay_item);
1425  click_ref.X = ev->x;
1426  click_ref.Y = ev->y;
1427  switch (mode)
1428  {
1429  case MouseMode::NONE:
1430  {
1431  // no selection under the mouse pointer, so we start a new user selection
1432  if (!overlays[selection_overlay()].config.can_jut_out && ((x > image_bounds.GetRight() || (y > image_bounds.GetBottom()))))
1433  break; // cannot select out of the image
1434  switch (selection_type)
1435  {
1436  case Overlay::Rectangle:
1438  mouse_mode = MouseMode::DRAW;
1439  selected_overlay = selection_overlay();
1440  selected_overlay_item = selection_overlay();
1441  need_redraw = true;
1442  break;
1443  case Overlay::Point:
1445  mouse_mode = MouseMode::DRAW;
1446  selected_overlay = selection_overlay();
1447  selected_overlay_item = selection_overlay();
1448  need_redraw = true;
1449  break;
1450  case Overlay::Line:
1452  mouse_mode = MouseMode::DRAW;
1453  selected_overlay = selection_overlay();
1454  selected_overlay_item = selection_overlay();
1455  need_redraw = true;
1456  break;
1457  }
1458  }
1459  break;
1460  default: // selection found, stretch or move
1461  mouse_mode = mode;
1462  }
1463  }
1464  break;
1465  case 2:
1466  // middle button: scroll
1467  click_ref.X = ev->x;
1468  click_ref.Y = ev->y;
1469  mouse_mode = MouseMode::SCROLL;
1470  break;
1471  case 3:
1472  {
1473  // send signal
1474  const auto realx = int(ev->x / zoom) + pos.X;
1475  const auto realy = int(ev->y / zoom) + pos.Y;
1476  const auto selection_margin_zoom = int(selection_margin / zoom);
1477 
1478  std::vector<std::pair<crn::String, crn::String> > res;
1479  for (std::map<crn::String, Overlay_internal>::const_iterator lit = overlays.begin(); lit != overlays.end(); ++lit)
1480  {
1481  if (!lit->second.config.show)
1482  continue; // do not include overlay items that are invisible
1483  for (const auto &overlay : lit->second.items)
1484  {
1485  const std::unique_ptr<OverlayItem>& item(overlay.second);
1486  Line* li = dynamic_cast<Line*>(item.get());
1487  if(li != nullptr)
1488  {
1489  crn::Rect r(li->p1.X - selection_margin_zoom, li->p1.Y - selection_margin_zoom, li->p2.X + selection_margin_zoom, li->p2.Y + selection_margin_zoom);
1490  if (r.Contains(realx, realy))
1491  {
1492  double s = (li->p1.Y - li->p2.Y) * (realx - li->p1.X) + (li->p2.X - li->p1.X) * (realy - li->p1.Y); // dot product of p1-mouse by the orthogonal of p1-p2
1493  s /= sqrt(double(crn::Sqr(li->p1.X - li->p2.X) + crn::Sqr(li->p1.Y - li->p2.Y))); // normalize
1494  if (crn::Abs(s) < selection_margin_zoom)
1495  res.push_back(std::make_pair(lit->first, overlay.first));
1496  }
1497  }
1498 
1499  Rectangle* rec = dynamic_cast<Rectangle*>(item.get());
1500  if(rec != nullptr)
1501  {
1502  crn::Rect r=rec->rect;//()rec->x1, rec->y1, rec->x2, rec->y2);
1503  if (r.Contains(realx, realy))
1504  res.push_back(std::make_pair(lit->first, overlay.first));
1505  }
1506  Point* p = dynamic_cast<Point*>(item.get());
1507  if(p != nullptr)
1508  {
1509 
1510  crn::Rect r(p->point.X - selection_margin_zoom, p->point.Y - selection_margin_zoom, p->point.X + selection_margin_zoom, p->point.Y + selection_margin_zoom);
1511  if (r.Contains(realx, realy))
1512  res.push_back(std::make_pair(lit->first, overlay.first));
1513  }
1514 
1515  Polygon* poly = dynamic_cast<Polygon*>(item.get());
1516  if ((poly != nullptr) && !poly->points.empty())
1517  {
1518  int max_x = poly->points[0].X;
1519  int min_x = poly->points[0].X;
1520  int max_y = poly->points[0].Y;
1521  int min_y = poly->points[0].Y;
1522  for (size_t i = 1; i< poly->points.size(); ++i)
1523  {
1524  int x1 = poly->points[i].X;
1525  int y1 = poly->points[i].Y;
1526  if(min_x > x1)
1527  min_x = x1;
1528  if(max_x < x1)
1529  max_x = x1;
1530  if(min_y > y1)
1531  min_y = y1;
1532  if(max_y < y1)
1533  max_y = y1;
1534  }
1535  crn::Rect rec(min_x - selection_margin_zoom, min_y - selection_margin_zoom, max_x + selection_margin_zoom, max_y + selection_margin_zoom);
1536 
1537  if (rec.Contains(realx, realy))
1538  res.push_back(std::make_pair(lit->first, overlay.first));
1539 
1540  }
1541  }
1542  }
1543  rmb_clicked.emit(ev->button, ev->time, res, realx, realy);
1544  }
1545  break;
1546  }
1547  }
1548  else if (ev->type == GDK_BUTTON_RELEASE)
1549  {
1550  switch (ev->button)
1551  {
1552  case 3:
1553  clear_selection();
1554  break;
1555  }
1556  }
1557  break;
1558  case MouseMode::SCROLL:
1559  if ((ev->type == GDK_BUTTON_RELEASE) && (ev->button == 2))
1560  {
1561  mouse_mode = MouseMode::NONE;
1562  }
1563  break;
1564  case MouseMode::USER:
1565  if ((ev->type == GDK_BUTTON_RELEASE) && (ev->button == 1))
1566  {
1567  mouse_mode = MouseMode::NONE;
1568  }
1569  break;
1570  default:
1571  if ((ev->type == GDK_BUTTON_RELEASE) && (ev->button == 1))
1572  {
1573  overlay_changed.emit(selected_overlay, selected_overlay_item, mouse_mode);
1574  mouse_mode = MouseMode::NONE;
1575  }
1576  break;
1577  }
1578  set_cursor_from_mode(mouse_mode);
1579  return false;
1580 }
1581 
1586 bool Image::mouse_wheel(GdkEventScroll *ev)
1587 {
1588  switch (ev->direction)
1589  {
1590  case GDK_SCROLL_UP:
1591  if (ev->state & GDK_CONTROL_MASK)
1592  {
1593  zoom_in();
1594  }
1595  else
1596  {
1597  pos.Y -= int(vscrollbar.get_adjustment()->get_page_increment());
1598  if (pos.Y < 0) pos.Y = 0;
1599  vscrollbar.set_value(pos.Y);
1600  }
1601  break;
1602  case GDK_SCROLL_DOWN:
1603  if (ev->state & GDK_CONTROL_MASK)
1604  {
1605  zoom_out();
1606  }
1607  else
1608  {
1609  pos.Y += int(vscrollbar.get_adjustment()->get_page_increment());
1610  if (pos.Y + disph / zoom > image_bounds.GetHeight())
1611  pos.Y = crn::Max(0, image_bounds.GetHeight() - int(disph / zoom));
1612  vscrollbar.set_value(pos.Y);
1613  }
1614  break;
1615  case GDK_SCROLL_LEFT:
1616  pos.X -= int(hscrollbar.get_adjustment()->get_page_increment());
1617  if (pos.X < 0) pos.X = 0;
1618  hscrollbar.set_value(pos.X);
1619  break;
1620  case GDK_SCROLL_RIGHT:
1621  pos.X += int(hscrollbar.get_adjustment()->get_page_increment());
1622  if (pos.X + dispw / zoom > image_bounds.GetWidth())
1623  pos.X = crn::Max(0, image_bounds.GetWidth() - int(dispw / zoom));
1624  hscrollbar.set_value(pos.X);
1625  break;
1626  }
1627  return false;
1628 }
1629 
1637 Image::MouseMode Image::find_selection_at(double mouse_x, double mouse_y, crn::String &overlay_id, crn::String &overlay_item_id)
1638 {
1639  // coordinates are in screen pixels
1640  int x = int(pos.X * zoom + mouse_x);
1641  int y = int(pos.Y * zoom + mouse_y);
1642 
1643  for (std::map<crn::String, Overlay_internal>::const_iterator lit = overlays.begin(); lit != overlays.end(); ++lit)
1644  {
1645  if (!lit->second.config.show)
1646  continue; // skip invisible overlays
1647 
1648  for (const auto &overlay : lit->second.items)
1649  {
1650  const std::unique_ptr<OverlayItem>& item(overlay.second);
1651  Rectangle* rec = dynamic_cast<Rectangle*>(item.get());
1652  if(rec != nullptr)
1653  {
1654  if (!rec->rect.IsValid())
1655  continue;
1656  int x1 = int(rec->rect.GetLeft() * zoom);
1657  int y1 = int(rec->rect.GetTop() * zoom);
1658  int x2 = int(rec->rect.GetRight() * zoom);
1659  int y2 = int(rec->rect.GetBottom() * zoom);
1660  crn::Rect r(x1 - selection_margin, y1 - selection_margin, x2 + selection_margin, y2 + selection_margin);
1661  if (r.Contains(x, y))
1662  {
1663  overlay_id = lit->first;
1664  overlay_item_id = overlay.first;
1665  if (lit->second.config.editable)
1666  {
1667  // bottom right?
1668  r = crn::Rect(crn::Max(x1, x2 - selection_margin), crn::Max(y1, y2 - selection_margin), x2 + selection_margin, y2 + selection_margin);
1669  if (r.Contains(x, y))
1671  // top left?
1672  r = crn::Rect(x1 - selection_margin, y1 - selection_margin, crn::Min(x2, x1 + selection_margin), crn::Min(y2, y1 + selection_margin));
1673  if (r.Contains(x, y))
1675  // bottom left?
1676  r = crn::Rect(x1 - selection_margin, crn::Max(y1, y2 - selection_margin), crn::Min(x2, x1 + selection_margin), y2 + selection_margin);
1677  if (r.Contains(x, y))
1679  // top right?
1680  r = crn::Rect(crn::Max(x1, x2 - selection_margin), y1 - selection_margin, x2 + selection_margin, crn::Min(y2, y1 + selection_margin));
1681  if (r.Contains(x, y))
1683  // right?
1684  r = crn::Rect(crn::Max(x1, x2 - selection_margin), y1, x2 + selection_margin, y2);
1685  if (r.Contains(x, y))
1686  return MouseMode::STRETCH_RIGHT;
1687  // bottom?
1688  r = crn::Rect(x1, crn::Max(y1, y2 - selection_margin), x2, y2 + selection_margin);
1689  if (r.Contains(x, y))
1690  return MouseMode::STRETCH_BOTTOM;;
1691  // left?
1692  r = crn::Rect(x1 - selection_margin, y1, crn::Min(x2, x1 + selection_margin), y2);
1693  if (r.Contains(x, y))
1694  return MouseMode::STRETCH_LEFT;
1695  // top?
1696  r = crn::Rect(x1, y1 - selection_margin, x2, crn::Min(y2, y1 + selection_margin));
1697  if (r.Contains(x, y))
1698  return MouseMode::STRETCH_TOP;;
1699  }
1700  if (lit->second.config.moveable)
1701  return MouseMode::MOVE;
1702  else
1703  continue;
1704  }
1705  }
1706 
1707  Point* p=dynamic_cast<Point*>(item.get());
1708  if(p != nullptr)
1709  {
1710  int x1 = int(p->point.X * zoom);
1711  int y1 = int(p->point.Y * zoom);
1712  crn::Rect r(x1 - selection_margin, y1 - selection_margin, x1 + selection_margin, y1 + selection_margin);
1713  if (r.Contains(x, y))
1714  {
1715  overlay_id = lit->first;
1716  overlay_item_id = overlay.first;
1717  movePoint = &(p->point);
1718  if (lit->second.config.moveable)
1719  return MouseMode::MOVEPOINT;
1720  else
1721  continue;
1722  }
1723  }
1724 
1725  Text* t=dynamic_cast<Text*>(item.get());
1726  if(t != nullptr)
1727  {
1728  int x1 = int(t->pos.X * zoom);
1729  int y1 = int(t->pos.Y * zoom);
1730  crn::Rect r(x1 - selection_margin, y1 - selection_margin, x1 + selection_margin, y1 + selection_margin);
1731  if (r.Contains(x, y))
1732  {
1733  overlay_id = lit->first;
1734  overlay_item_id = overlay.first;
1735  movePoint = &(t->pos);
1736  if (lit->second.config.moveable)
1737  return MouseMode::MOVEPOINT;
1738  else
1739  continue;
1740  }
1741  }
1742 
1743  Line* li = dynamic_cast<Line*>(item.get());
1744  if(li != nullptr)
1745  {
1746  int x1 = int(li->p1.X * zoom);
1747  int y1 = int(li->p1.Y * zoom);
1748  int x2 = int(li->p2.X * zoom);
1749  int y2 = int(li->p2.Y * zoom);
1750  crn::Rect r(crn::Min(x1, x2) - selection_margin, crn::Min(y1, y2) - selection_margin, crn::Max(x1, x2) + selection_margin, crn::Max(y1, y2) + selection_margin);
1751  if (r.Contains(x, y))
1752  {
1753  if (lit->second.config.editable)
1754  {
1755  // point 1?
1756  r = crn::Rect (x1 - selection_margin, y1 - selection_margin, x1 + selection_margin, y1 + selection_margin);
1757  if (r.Contains(x, y))
1758  {
1759  movePoint=&(li->p1);
1760  overlay_id = lit->first;
1761  overlay_item_id = overlay.first;
1762  return MouseMode::MOVEPOINT;
1763  }
1764  // point 2?
1765  r = crn::Rect (x2 - selection_margin, y2 - selection_margin, x2 + selection_margin, y2 + selection_margin);
1766  if (r.Contains(x, y))
1767  {
1768  movePoint=&(li->p2);
1769  overlay_id = lit->first;
1770  overlay_item_id = overlay.first;
1771  return MouseMode::MOVEPOINT;
1772  }
1773  }
1774  // move the line?
1775  double s = (y1 - y2) * (x - x1) + (x2 - x1) * (y - y1); // dot product of p1-mouse by the orthogonal of p1-p2
1776  s /= sqrt(double(crn::Sqr(x1 - x2) + crn::Sqr(y1 - y2))); // normalize
1777  if (crn::Abs(s) < selection_margin)
1778  {
1779  overlay_id = lit->first;
1780  overlay_item_id = overlay.first;
1781  if (lit->second.config.moveable)
1782  return MouseMode::MOVE;
1783  else
1784  continue;
1785  }
1786  }
1787  }
1788 
1789  Polygon* poly = dynamic_cast<Polygon*>(item.get());
1790  if(poly != nullptr)
1791  {
1792  if (lit->second.config.editable)
1793  {
1794  for (size_t j = 0; j < poly->points.size(); ++j)
1795  {
1796  int x1 = int(poly->points[j].X * zoom);
1797  int y1 = int(poly->points[j].Y * zoom);
1798 
1799  crn::Rect r(x1 - selection_margin, y1 - selection_margin, x1 + selection_margin, y1 + selection_margin);
1800  if (r.Contains(x, y))
1801  {
1802  movePoint=&(poly->points[j]);
1803  overlay_id = lit->first;
1804  overlay_item_id = overlay.first;
1805  return MouseMode::MOVEPOINT;
1806  }
1807  }
1808  }
1809  if (lit->second.config.moveable)
1810  for (size_t i = 0; i < poly->points.size(); ++i)
1811  {
1812  int x1 = int(poly->points[i].X * zoom);
1813  int y1 = int(poly->points[i].Y * zoom);
1814  int x2;
1815  int y2;
1816  if(i == poly->points.size()-1)
1817  {
1818  if( !lit->second.config.closed_polygons)
1819  continue;
1820  else
1821  {
1822  x2 = int(poly->points[0].X * zoom);
1823  y2 = int(poly->points[0].Y * zoom);
1824  }
1825  }
1826  else
1827  {
1828  x2 = int(poly->points[i+1].X * zoom);
1829  y2 = int(poly->points[i+1].Y * zoom);
1830  }
1831  crn::Rect r(crn::Min(x1, x2) - selection_margin, crn::Min(y1, y2) - selection_margin, crn::Max(x1, x2) + selection_margin, crn::Max(y1, y2) + selection_margin);
1832  if (r.Contains(x, y))
1833  {
1834  double s = (y1 - y2) * (x - x1) + (x2 - x1) * (y - y1); // dot product of p1-mouse by the orthogonal of p1-p2
1835  s /= sqrt(double(crn::Sqr(x1 - x2) + crn::Sqr(y1 - y2))); // normalize
1836  if (crn::Abs(s) < selection_margin)
1837  {
1838  overlay_id = lit->first;
1839  overlay_item_id = overlay.first;
1840 
1841  return MouseMode::MOVE;
1842 
1843  }
1844  }
1845  }
1846  else
1847  continue;
1848 
1849  }
1850 
1851  } // for each overlay item
1852  } // for each overlay
1853  return MouseMode::NONE;
1854 }
1855 
1859 void Image::set_cursor_from_mode(Image::MouseMode m)
1860 {
1861 #ifdef CRN_USING_GTKMM3
1862  switch (m)
1863  {
1864  case MouseMode::SCROLL:
1865  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), scroll_cursor));
1866  break;
1867  case MouseMode::DRAW:
1868  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), select_cursor));
1869  break;
1870  case MouseMode::MOVE:
1871  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), move_cursor));
1872  break;
1873  case MouseMode::MOVEPOINT:
1874  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), move_1_cursor));
1875  break;
1877  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), drag_left_cursor));
1878  break;
1880  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), drag_bottom_left_cursor));
1881  break;
1883  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), drag_bottom_cursor));
1884  break;
1886  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), drag_bottom_right_cursor));
1887  break;
1889  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), drag_right_cursor));
1890  break;
1892  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), drag_top_right_cursor));
1893  break;
1895  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), drag_top_cursor));
1896  break;
1898  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), drag_top_left_cursor));
1899  break;
1900  case MouseMode::USER:
1901  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), user_cursor));
1902  break;
1903  case MouseMode::NONE:
1904  default:
1905  if (selection_type == Overlay::User)
1906  da.get_window()->set_cursor(Gdk::Cursor::create(get_display(), user_cursor));
1907  else
1908  da.get_window()->set_cursor();
1909  }
1910 #else
1911  switch (m)
1912  {
1913  case MouseMode::SCROLL:
1914  da.get_window()->set_cursor(scroll_cursor);
1915  break;
1916  case MouseMode::DRAW:
1917  da.get_window()->set_cursor(select_cursor);
1918  break;
1919  case MouseMode::MOVE:
1920  da.get_window()->set_cursor(move_cursor);
1921  break;
1922  case MouseMode::MOVEPOINT:
1923  da.get_window()->set_cursor(move_1_cursor);
1924  break;
1926  da.get_window()->set_cursor(drag_left_cursor);
1927  break;
1929  da.get_window()->set_cursor(drag_bottom_left_cursor);
1930  break;
1932  da.get_window()->set_cursor(drag_bottom_cursor);
1933  break;
1935  da.get_window()->set_cursor(drag_bottom_right_cursor);
1936  break;
1938  da.get_window()->set_cursor(drag_right_cursor);
1939  break;
1941  da.get_window()->set_cursor(drag_top_right_cursor);
1942  break;
1944  da.get_window()->set_cursor(drag_top_cursor);
1945  break;
1947  da.get_window()->set_cursor(drag_top_left_cursor);
1948  break;
1949  case MouseMode::USER:
1950  da.get_window()->set_cursor(user_cursor);
1951  break;
1952  case MouseMode::NONE:
1953  default:
1954  if (selection_type == Overlay::User)
1955  da.get_window()->set_cursor(user_cursor);
1956  else
1957  da.get_window()->set_cursor();
1958  }
1959 #endif
1960 }
1961 
1962 
1965  color1("black"),
1966  color2("white"),
1967  text_color("black"),
1968  show_labels(false),
1969  text_size(15),
1970  editable(false),
1971  moveable(false),
1972  can_jut_out(false),
1973  fill(true),
1974  fill_alpha(0.5),
1975  cross_size(15),
1976  draw_arrows(true),
1977  arrow_size(10),
1978  absolute_text_size(true),
1979  font_family("sans"),
1980  closed_polygons(false),
1981  show(true)
1982 { }
1983 
1989 {
1990  if (typ == Overlay::Text)
1991  throw crn::ExceptionInvalidArgument(_("Cannot set selection type to Text."));
1992  if (typ == Overlay::Polygon)
1993  throw crn::ExceptionInvalidArgument(_("Cannot set selection type to Polygon."));
1994  selection_type = typ;
1995  if (typ == Overlay::User)
1996  {
1997  switch (mouse_mode)
1998  {
1999  case MouseMode::MOVE:
2008  case MouseMode::DRAW:
2009  overlay_changed.emit(selected_overlay, selected_overlay_item, mouse_mode);
2010  }
2011  mouse_mode = MouseMode::NONE;
2012  }
2013  need_redraw = true;
2014 }
2015 
2023 void Image::add_overlay_item(const crn::String &overlay_id, const crn::String &item_id, const crn::Rect &r, const crn::StringUTF8 &label)
2024 {
2025  overlays[overlay_id].items[item_id].reset(new Rectangle(r, label));
2026  need_redraw = true;
2027  movePoint = nullptr;
2028 }
2029 
2036 void Image::add_overlay_item(const crn::String &overlay_id, const crn::String &item_id, const crn::Point2DInt &point, const crn::StringUTF8 &label)
2037 {
2038  overlays[overlay_id].items[item_id].reset(new Point(point,label));
2039  need_redraw = true;
2040  movePoint = nullptr;
2041 }
2042 
2050 void Image::add_overlay_item(const crn::String &overlay_id, const crn::String &item_id, const crn::Point2DInt &p1, const crn::Point2DInt &p2, const crn::StringUTF8 &label)
2051 {
2052  overlays[overlay_id].items[item_id].reset(new Line(p1,p2,label));
2053  need_redraw = true;
2054  movePoint = nullptr;
2055 }
2062 void Image::add_overlay_item(const crn::String &overlay_id, const crn::String &item_id, const crn::StringUTF8 &label, const crn::Point2DInt &position)
2063 {
2064  overlays[overlay_id].items[item_id].reset(new Text(position,label));
2065  need_redraw = true;
2066  movePoint = nullptr;
2067 }
2068 
2077 void Image::add_overlay_item(const crn::String &overlay_id, const crn::String &item_id, const std::vector<crn::Point2DInt>& p, const crn::StringUTF8 &label)
2078 {
2079  if(p.size() < 2)
2080  throw crn::ExceptionDimension(crn::StringUTF8("Image::add_overlay_item(polygon): ") + _("The polygon must have more than two points."));
2081 
2082  overlays[overlay_id].items[item_id].reset(new Polygon(p,label));
2083  need_redraw = true;
2084  movePoint = nullptr;
2085 }
2086 
2095 void Image::add_overlay_item(const crn::String &overlay_id, const crn::String &item_id, std::vector<crn::Point2DInt> &&p, const crn::StringUTF8 &label)
2096 {
2097  if(p.size() < 2)
2098  throw crn::ExceptionDimension(crn::StringUTF8("Image::add_overlay_item(polygon): ") + _("The polygon must have more than two points."));
2099 
2100  overlays[overlay_id].items[item_id].reset(new Polygon(std::move(p),label));
2101  need_redraw = true;
2102  movePoint = nullptr;
2103 }
2104 
2113 {
2114  std::map<crn::String, Overlay_internal>::iterator lit(overlays.find(overlay_id));
2115  if (lit == overlays.end())
2116  throw crn::ExceptionDomain(_("Overlay not found."));
2117  std::map<crn::String, std::unique_ptr<OverlayItem>>::iterator it(lit->second.items.find(item_id));
2118  if (it == lit->second.items.end())
2119  throw crn::ExceptionNotFound(_("Overlay item not found."));
2120  return *it->second;
2121 }
2122 
2130 const Image::OverlayItem& Image::get_overlay_item(const crn::String &overlay_id, const crn::String &item_id) const
2131 {
2132  std::map<crn::String, Overlay_internal>::const_iterator lit(overlays.find(overlay_id));
2133  if (lit == overlays.end())
2134  throw crn::ExceptionDomain(_("Overlay not found."));
2135  std::map<crn::String, std::unique_ptr<OverlayItem>>::const_iterator it(lit->second.items.find(item_id));
2136  if (it == lit->second.items.end())
2137  throw crn::ExceptionNotFound(_("Overlay item not found."));
2138  return *it->second;
2139 }
2140 
2147 void Image::remove_overlay_item(const crn::String &overlay_id, const crn::String &item_id)
2148 {
2149  std::map<crn::String, Overlay_internal>::iterator lit(overlays.find(overlay_id));
2150  if (lit == overlays.end())
2151  throw crn::ExceptionDomain(_("Overlay not found."));
2152  std::map<crn::String, std::unique_ptr<OverlayItem>>::iterator it(lit->second.items.find(item_id));
2153  if (it == lit->second.items.end())
2154  throw crn::ExceptionNotFound(_("Overlay item not found."));
2155  lit->second.items.erase(it);
2156 
2157  if (selected_overlay == overlay_id)
2158  {
2159  if (selected_overlay_item == item_id)
2160  selected_overlay_item = "";
2161  }
2162  need_redraw = true;
2163  movePoint = nullptr;
2164 }
2165 
2168 {
2169  for (std::map<crn::String, Overlay_internal>::iterator it = overlays.begin(); it != overlays.end(); ++it)
2170  if (it->first != selection_overlay())
2171  clear_overlay(it->first);
2172  movePoint = nullptr;
2173 }
2174 
2179 {
2180  overlays[id].items.clear();
2181  need_redraw = true;
2182  if (selected_overlay == id)
2183  {
2184  selected_overlay_item = "";
2185  }
2186  overlay_changed.emit(id, "", MouseMode::NONE);
2187 }
2188 
2193 {
2194  std::map<crn::String, Overlay_internal>::const_iterator it(overlays.find(selection_overlay()));
2195  if (it != overlays.end())
2196  return !it->second.items.empty();
2197  else
2198  return false;
2199 }
2200 
2203 {
2205 }
2206 
2208 {
2209  static const crn::String s(U" 月 سِمسِم coucou");
2210  return s;
2211 }
2212 
2219 {
2220  std::map<crn::String, Overlay_internal>::const_iterator lit(overlays.find(selection_overlay()));
2221  if (lit == overlays.end())
2222  throw crn::ExceptionNotFound(_("No selection found."));
2223  std::map<crn::String, std::unique_ptr<OverlayItem>>::const_iterator it(lit->second.items.find(selection_overlay()));
2224  if (it == lit->second.items.end())
2225  throw crn::ExceptionNotFound(_("No selection found."));
2226  const std::unique_ptr<OverlayItem>& item(it->second);
2227 
2228  Rectangle* rec=dynamic_cast<Rectangle*>(item.get());
2229  if (rec != nullptr)
2230  return rec->rect;
2231  else
2232  throw crn::ExceptionDomain(_("The selection is not a rectangle."));
2233 }
2234 
2241 {
2242  std::map<crn::String, Overlay_internal>::const_iterator lit(overlays.find(selection_overlay()));
2243  if (lit == overlays.end())
2244  throw crn::ExceptionNotFound(_("No selection found."));
2245  std::map<crn::String, std::unique_ptr<OverlayItem>>::const_iterator it(lit->second.items.find(selection_overlay()));
2246  if (it == lit->second.items.end())
2247  throw crn::ExceptionNotFound(_("No selection found."));
2248  const std::unique_ptr<OverlayItem>& item(it->second);
2249 
2250  Point* po=dynamic_cast<Point*>(item.get());
2251  if (po != nullptr)
2252  return po->point;
2253  else
2254  throw crn::ExceptionDomain(_("The selection is not a point."));
2255 }
2256 
2262 std::pair<crn::Point2DInt, crn::Point2DInt> Image::get_selection_as_line() const
2263 {
2264  std::map<crn::String, Overlay_internal>::const_iterator lit(overlays.find(selection_overlay()));
2265  if (lit == overlays.end())
2266  throw crn::ExceptionNotFound(_("No selection found."));
2267  std::map<crn::String, std::unique_ptr<OverlayItem>>::const_iterator it(lit->second.items.find(selection_overlay()));
2268  if (it == lit->second.items.end())
2269  throw crn::ExceptionNotFound(_("No selection found."));
2270  const std::unique_ptr<OverlayItem>& item(it->second);
2271 
2272  Line* li=dynamic_cast<Line*>(item.get());
2273  if (li != nullptr)
2274  return std::make_pair(li->p1, li->p2);
2275  else
2276  throw crn::ExceptionDomain(_("The selection is not a line."));
2277 }
2278 
2284 {
2285  std::unique_ptr<OverlayItem>& item( overlays[selection_overlay()].items[selection_overlay()]);
2286 
2287  Rectangle * rec = dynamic_cast<Rectangle*>(item.get());
2288  if(rec != nullptr)
2289  rec->rect = r;
2290  else
2291  throw crn::ExceptionInvalidArgument(_("The selection is not a rectangle."));
2292 
2293  overlay_changed.emit(selection_overlay(), selection_overlay(), mouse_mode);
2294 }
2295 
2301 {
2302  std::unique_ptr<OverlayItem>& item( overlays[selection_overlay()].items[selection_overlay()]);
2303 
2304  Point * po = dynamic_cast<Point*>(item.get());
2305  if(po != nullptr)
2306  po->point = p;
2307  else
2308  throw crn::ExceptionInvalidArgument(_("The selection is not a point."));
2309 
2310  overlay_changed.emit(selection_overlay(), selection_overlay(), mouse_mode);
2311 }
2312 
2319 {
2320  std::unique_ptr<OverlayItem>& item( overlays[selection_overlay()].items[selection_overlay()]);
2321 
2322  Line * li = dynamic_cast<Line*>(item.get());
2323  if(li != nullptr)
2324  {
2325  li->p1 = p1;
2326  li->p2 = p2;
2327  }
2328  else
2329  throw crn::ExceptionInvalidArgument(_("The selection is not a line."));
2330 
2331  overlay_changed.emit(selection_overlay(), selection_overlay(), mouse_mode);
2332 }
2333 
2338 void Image::set_overlay_visible(const crn::String &id, bool visible)
2339 {
2340  overlays[id].config.show = visible;
2341  need_redraw = true;
2342 }
2343 
2348 bool Image::refresh()
2349 {
2350  if (!image)
2351  return true;
2352 
2353  Glib::RefPtr<Gdk::Window> win = da.get_window();
2354  if (win)
2355  {
2356  // if the drawing area is fully created
2357  if (need_recompute && dispw && disph)
2358  {
2359  // need to recompute the buffer
2360 
2361  // set the scrollbars' scale
2362  hscrollbar.get_adjustment()->set_page_size(dispw / zoom);
2363  vscrollbar.get_adjustment()->set_page_size(disph / zoom);
2364  pos.X = int(hscrollbar.get_value());
2365  pos.Y = int(vscrollbar.get_value());
2366  if (pos.X + dispw / zoom > image_bounds.GetWidth())
2367  {
2368  hscrollbar.set_value(crn::Max(0.0, image_bounds.GetWidth() - dispw / zoom));
2369  pos.X = int(hscrollbar.get_value());
2370  }
2371  if (pos.Y + disph / zoom > image_bounds.GetHeight())
2372  {
2373  vscrollbar.set_value(crn::Max(0.0, image_bounds.GetHeight() - disph / zoom));
2374  pos.Y = int(vscrollbar.get_value());
2375  }
2376 
2377  // compute size
2378  int srcw = crn::Min(int(dispw / zoom), image->get_width() - pos.X);
2379  int srch = crn::Min(int(disph / zoom), image->get_height() - pos.Y);
2380  Glib::RefPtr<Gdk::Pixbuf> crop = Gdk::Pixbuf::create_subpixbuf(image, pos.X, pos.Y, srcw, srch);
2381  if (!crop)
2382  return true;
2383  buffer = crop->scale_simple(int(crop->get_width() * zoom), int(crop->get_height() * zoom), Gdk::INTERP_BILINEAR);
2384  // allows the user to modify the image
2385  drawing.emit(buffer);
2386 
2387 #ifndef CRN_USING_GTKMM3
2388  // set the rulers' scale
2389  hruler.set_range(pos.X, pos.X + int(dispw / zoom), pos.X, pos.X + int(dispw / zoom));
2390  vruler.set_range(pos.Y, pos.Y + int(disph / zoom), pos.Y, pos.Y + int(disph / zoom));
2391 
2392  // update the GC
2393  da_gc = Gdk::GC::create(win);
2394  da_gc->set_rgb_fg_color(Gdk::Color("white"));
2395 #endif /* ! CRN_USING_GTKMM3 */
2396 
2397  need_recompute = false;
2398  need_redraw = true;
2399  }
2400  if (need_redraw)
2401  {
2402  // need to redraw the image
2403  // create a buffer
2404 #ifndef CRN_USING_GTKMM3
2405  Glib::RefPtr<Gdk::Pixmap> pm = Gdk::Pixmap::create(win, dispw, disph);
2406  pm->draw_rectangle(da_gc, true, 0, 0, dispw, disph);
2407  pm->draw_pixbuf(da_gc, buffer, 0, 0, 0, 0, buffer->get_width(), buffer->get_height(), Gdk::RGB_DITHER_NONE, 0, 0);
2408 #endif /* ! CRN_USING_GTKMM3 */
2409 
2410  // now draw the rectangles!
2411 #ifdef CRN_USING_GTKMM3
2412  //Cairo::RefPtr<Cairo::Context> cc = pm->create_cairo_context();
2413  auto cc = win->create_cairo_context();
2414  cc->set_source_rgb(1, 1, 1);
2415  cc->paint();
2416  Gdk::Cairo::set_source_pixbuf(cc, buffer);
2417  cc->paint();
2418 #else /* CRN_USING_GTKMM3 */
2419  Cairo::RefPtr<Cairo::Context> cc = pm->create_cairo_context();
2420  if (!cc)
2421  {
2422  std::cerr << "cannot create cairo context to refresh image" << std::endl; // XXX
2423  return true;
2424  }
2425 #endif /* CRN_USING_GTKMM3 */
2426  crn::Rect screen(0, 0, dispw, disph);
2427  if (!screen.IsValid())
2428  return true;
2429 
2430  for (std::map<crn::String, Overlay_internal>::reverse_iterator lit = overlays.rbegin(); lit != overlays.rend(); ++lit) //dynamic_cast
2431  {
2432  if (!lit->second.config.show)
2433  continue;
2434  double fontsize = lit->second.config.text_size * (lit->second.config.absolute_text_size ? 1.0 : zoom);
2435  Pango::FontDescription fdesc;
2436  fdesc.set_family(lit->second.config.font_family);
2437  fdesc.set_absolute_size(fontsize * Pango::SCALE);
2438  for (std::map<crn::String, std::unique_ptr<OverlayItem>>::const_iterator it = lit->second.items.begin(); it != lit->second.items.end(); ++it)
2439  {
2440  const std::unique_ptr<OverlayItem>& item(it->second);
2441  const Point* p = dynamic_cast<const Point*>(item.get());
2442  if(p != nullptr)
2443  {
2444  int x = int((p->point.X - pos.X) * zoom);
2445  int y = int((p->point.Y - pos.Y) * zoom);
2446  if (!screen.Contains(x, y)) // clipping
2447  continue;
2448  cc->set_source_rgb(lit->second.config.color2.get_red_p(), lit->second.config.color2.get_green_p(), lit->second.config.color2.get_blue_p());
2449  cc->rectangle(x - 1, y - 1, 2, 2);
2450  cc->stroke();
2451  cc->set_source_rgb(lit->second.config.color1.get_red_p(), lit->second.config.color1.get_green_p(), lit->second.config.color1.get_blue_p());
2452  cc->move_to(x - lit->second.config.cross_size / 2, y);
2453  cc->line_to(x + lit->second.config.cross_size / 2, y);
2454  cc->move_to(x, y - lit->second.config.cross_size / 2);
2455  cc->line_to(x, y + lit->second.config.cross_size / 2);
2456  cc->stroke();
2457  if (lit->second.config.show_labels && it->second->label.IsNotEmpty())
2458  {
2459  Glib::RefPtr<Pango::Layout> pl(Pango::Layout::create(cc));
2460  pl->set_font_description(fdesc);
2461  pl->set_text(p->label.Std());
2462  cc->move_to(x, y);
2463  cc->set_source_rgb(lit->second.config.text_color.get_red_p(), lit->second.config.text_color.get_green_p(), lit->second.config.text_color.get_blue_p());
2464  pl->show_in_cairo_context(cc);
2465  }
2466  continue;
2467  }
2468  Rectangle* rec=dynamic_cast<Rectangle*> (item.get());
2469  if(rec != nullptr)
2470  {
2471  crn::Rect z=rec->rect;//(rec->x1, rec->y1, rec->x2, rec->y2);
2472  if (!z.IsValid())
2473  continue;
2474  z.Translate(-pos.X, -pos.Y);
2475  z = z * zoom;
2476  if (!(screen & z).IsValid()) // clipping
2477  continue;
2478  cc->rectangle(z.GetLeft(), z.GetTop(), z.GetWidth(), z.GetHeight());
2479  cc->set_source_rgb(lit->second.config.color1.get_red_p(), lit->second.config.color1.get_green_p(), lit->second.config.color1.get_blue_p());
2480  if (lit->second.config.fill)
2481  {
2482  cc->stroke_preserve();
2483  cc->set_source_rgba(lit->second.config.color2.get_red_p(), lit->second.config.color2.get_green_p(), lit->second.config.color2.get_blue_p(), lit->second.config.fill_alpha);
2484  cc->fill();
2485  }
2486  else
2487  {
2488  cc->stroke();
2489  z.Translate(1, 1);
2490  cc->rectangle(z.GetLeft(), z.GetTop(), z.GetWidth(), z.GetHeight());
2491  cc->set_source_rgb(lit->second.config.color2.get_red_p(), lit->second.config.color2.get_green_p(), lit->second.config.color2.get_blue_p());
2492  cc->stroke();
2493  }
2494  if (lit->second.config.show_labels && it->second->label.IsNotEmpty())
2495  {
2496  Glib::RefPtr<Pango::Layout> pl(Pango::Layout::create(cc));
2497  pl->set_font_description(fdesc);
2498  pl->set_text(it->second->label.Std());
2499  //cc->move_to(z.GetLeft(), z.GetTop());
2501  auto w = 0, h = 0;
2502  pl->get_pixel_size(w, h);
2503  auto posy = z.GetTop();
2504  if (h > z.GetHeight())
2505  posy = z.GetBottom() - h;
2506  cc->move_to(z.GetLeft(), posy);
2508  cc->set_source_rgb(lit->second.config.text_color.get_red_p(), lit->second.config.text_color.get_green_p(), lit->second.config.text_color.get_blue_p());
2509  pl->show_in_cairo_context(cc);
2510  }
2511  continue;
2512  }
2513  Line* li=dynamic_cast<Line*> (item.get());
2514  if(li != nullptr)
2515  {
2516  int x1 = int((li->p1.X - pos.X) * zoom);
2517  int y1 = int((li->p1.Y - pos.Y) * zoom);
2518  int x2 = int((li->p2.X - pos.X) * zoom);
2519  int y2 = int((li->p2.Y - pos.Y) * zoom);
2520  if (!(screen & crn::Rect(crn::Min(x1, x2), crn::Min(y1, y2), crn::Max(x1, x2), crn::Max(y1, y2))).IsValid()) // clipping
2521  continue;
2522  cc->set_source_rgb(lit->second.config.color2.get_red_p(), lit->second.config.color2.get_green_p(), lit->second.config.color2.get_blue_p());
2523  cc->rectangle(x1 - 1, y1 - 1, 2, 2);
2524  cc->rectangle(x2 - 1, y2 - 1, 2, 2);
2525  cc->stroke();
2526  cc->set_source_rgb(lit->second.config.color1.get_red_p(), lit->second.config.color1.get_green_p(), lit->second.config.color1.get_blue_p());
2527  cc->move_to(x1, y1);
2528  cc->line_to(x2, y2);
2529  int w = x2 - x1;
2530  int h = y2 - y1;
2531  double l = sqrt(double(w * w + h * h));
2532  if (lit->second.config.draw_arrows)
2533  {
2534  double l2 = l - lit->second.config.arrow_size;
2535  int w2 = int(w * l2 / l);
2536  int h2 = int(h * l2 / l);
2537  int dx = w - w2;
2538  int dy = h - h2;
2539  cc->move_to(x1 + w2 - dy / 2, y1 + h2 + dx /2);
2540  cc->line_to(x2, y2);
2541  cc->move_to(x1 + w2 + dy / 2, y1 + h2 - dx /2);
2542  cc->line_to(x2, y2);
2543  }
2544  cc->stroke();
2545  if (lit->second.config.show_labels && it->second->label.IsNotEmpty())
2546  {
2547  Glib::RefPtr<Pango::Layout> pl(Pango::Layout::create(cc));
2548  pl->set_font_description(fdesc);
2550  pl->set_text(it->second->label.Std());
2551  pl->set_alignment(Pango::ALIGN_CENTER);
2552  pl->set_width(int(l));
2553  if ((a.value > 90) && (a.value < 270))
2554  {
2555  a.value += 180;
2556  }
2557  cc->move_to(x1 + w / 2, y1 + h / 2);
2558  cc->rotate_degrees(a.value);
2559  cc->set_source_rgb(lit->second.config.text_color.get_red_p(), lit->second.config.text_color.get_green_p(), lit->second.config.text_color.get_blue_p());
2560  pl->show_in_cairo_context(cc);
2561  cc->rotate_degrees(-a.value);
2562  }
2563  continue;
2564  }
2565  Text* te = dynamic_cast<Text*> (item.get());
2566  if(te != nullptr)
2567  {
2568  Glib::RefPtr<Pango::Layout> pl(Pango::Layout::create(cc));
2569  pl->set_font_description(fdesc);
2570  pl->set_text(it->second->label.Std());
2571  int x = int((te->pos.X - pos.X) * zoom);
2572  int y = int((te->pos.Y - pos.Y) * zoom);
2573  if (!screen.Contains(x, y)) // clipping
2574  continue;
2575  cc->move_to(x, y);
2576  cc->set_source_rgb(lit->second.config.text_color.get_red_p(), lit->second.config.text_color.get_green_p(), lit->second.config.text_color.get_blue_p());
2577  pl->show_in_cairo_context(cc);
2578  continue;
2579  }
2580  Polygon * po = dynamic_cast<Polygon*> (item.get());
2581  if(po != nullptr)
2582  {
2583  double x_centre = po->points[0].X;
2584  double y_centre = po->points[0].Y;
2585  int x_big = po->points[0].X;
2586  int x_small = po->points[0].X;
2587  int y_big = po->points[0].Y;
2588  int y_small = po->points[0].Y;
2589 
2590 
2591  for (size_t i = 1; i < po->points.size(); ++ i)
2592  {
2593  x_centre += po->points[i].X;
2594  y_centre += po->points[i].Y;
2595 
2596  if(po->points[i].X > x_big)
2597  x_big = po->points[i].X;
2598  if(po->points[i].X < x_small)
2599  x_small = po->points[i].X;
2600 
2601  if(po->points[i].Y > y_big)
2602  y_big = po->points[i].Y;
2603  if(po->points[i].Y < y_small)
2604  y_small = po->points[i].Y;
2605  }
2606 
2607  if (!(screen & crn::Rect(int((x_small- pos.X) * zoom), int((y_small- pos.Y) * zoom), int((x_big- pos.X) * zoom), int((y_big- pos.Y) * zoom))).IsValid()) // clipping
2608  continue;
2609 
2610  cc->set_source_rgb(lit->second.config.color1.get_red_p(), lit->second.config.color1.get_green_p(), lit->second.config.color1.get_blue_p());
2611  cc->move_to( int(po->points[0].X - pos.X) * zoom, int( po->points[0].Y - pos.Y) * zoom);
2612 
2613  for (size_t i = 1; i < po->points.size(); ++ i)
2614  {
2615  int x1 = int((po->points[i].X - pos.X) * zoom);
2616  int y1 = int((po->points[i].Y - pos.Y) * zoom);
2617  cc->line_to(x1, y1);
2618  }
2619 
2620 
2621  if(lit->second.config.closed_polygons)
2622  {
2623  cc->line_to(int((po->points[0].X - pos.X) * zoom), int(( po->points[0].Y - pos.Y) * zoom));
2624  }
2625  if (lit->second.config.closed_polygons && lit->second.config.fill)
2626  {
2627  cc->stroke_preserve();
2628  cc->set_source_rgba(lit->second.config.color2.get_red_p(), lit->second.config.color2.get_green_p(), lit->second.config.color2.get_blue_p(), lit->second.config.fill_alpha);
2629  cc->fill();
2630  }
2631  else
2632  cc->stroke();
2633 
2634 
2635  for (size_t i = 0; i < po->points.size(); ++ i)
2636  {
2637  int x1 = int((po->points[i].X - pos.X) * zoom);
2638  int y1 = int((po->points[i].Y - pos.Y) * zoom);
2639  cc->set_source_rgb(lit->second.config.color2.get_red_p(), lit->second.config.color2.get_green_p(), lit->second.config.color2.get_blue_p());
2640  cc->rectangle(x1 - 1, y1 - 1, 2, 2);
2641  }
2642  cc->stroke();
2643  if (lit->second.config.show_labels && it->second->label.IsNotEmpty())
2644  {
2645  int h = int((y_big - y_small) * zoom);
2646  int w = int ((x_big - x_small)* zoom);
2647 
2648  double l = sqrt(double(w * w + h * h));
2649 
2650  Glib::RefPtr<Pango::Layout> pl(Pango::Layout::create(cc));
2651  pl->set_font_description(fdesc);
2652  pl->set_text(it->second->label.Std());
2653  // cc->move_to(x_small*zoom, y_small*zoom);
2654  pl->set_alignment(Pango::ALIGN_CENTER);
2655  pl->set_width(int(l));
2656 
2657  cc->move_to((x_centre/int(po->points.size()))*zoom, (y_centre/int(po->points.size()))*zoom);
2658  cc->set_source_rgb(lit->second.config.text_color.get_red_p(), lit->second.config.text_color.get_green_p(), lit->second.config.text_color.get_blue_p());
2659  pl->show_in_cairo_context(cc);
2660  }
2661  continue;
2662  }
2663  }// for each overlay item
2664  } //for each overlay
2665 
2666  // copy pixmap to drawing area
2667 #ifndef CRN_USING_GTKMM3
2668  win->draw_drawable(da_gc, pm, 0, 0, 0, 0);
2669 #endif /* ! CRN_USING_GTKMM3 */
2670 
2671  // done!
2672  need_redraw = false;
2673  }
2674  }
2675  return true;
2676 }
2677 
void clear_overlays()
Clears all overlays.
OverlayConfig()
Default constructor.
void set_overlay_visible(const crn::String &id, bool visible)
Shows or hides an overlay.
An overlay item.
Definition: GtkCRNImage.h:146
void set_user_cursor(const Gdk::Cursor &cur)
Sets the cursor in user mouse mode.
const T & Cap(const T &v, const T &min, const T &max)
Bounds a value to an interval.
Definition: CRNMath.h:75
#define _(String)
Definition: CRNi18n.h:51
void zoom_in()
Increments the zoom level by 10%.
const T & Max(const T &a, const T &b)
Returns the max of two values.
Definition: CRNMath.h:47
int GetBottom() const
Returns the bottommost coordinate.
Definition: CRNRect.h:111
const crn::Rect & get_selection_as_rect() const
Gets the mouse selection as a rectangle.
Image()
Constructor.
Definition: GtkCRNImage.cpp:39
int GetTop() const
Returns the topmost coordinate.
Definition: CRNRect.h:107
int GetLeft() const
Returns the leftmost coordinate.
Definition: CRNRect.h:99
A convenience class for angles units.
void zoom_out()
Decrements the zoom level by 10%.
A UTF32 character string class.
Definition: CRNString.h:61
const crn::Point2DInt & get_selection_as_point() const
Gets the mouse selection as a point.
void zoom_100()
Sets the zoom level to 100%.
void set_selection_type(Overlay typ)
Sets the mouse selection type.
A generic domain error.
Definition: CRNException.h:83
#define false
Definition: ConvertUTF.cpp:56
void clear_overlay(const crn::String &id)
Clears an overlay.
std::pair< crn::Point2DInt, crn::Point2DInt > get_selection_as_line() const
Gets the mouse selection as a line.
crn::Point2DInt p2
Definition: GtkCRNImage.h:163
bool Contains(int x, int y) const noexcept
Checks if the rectangle contains a point.
Definition: CRNRect.cpp:514
value_type X
Definition: CRNPoint2D.h:63
constexpr SumType< T > Sqr(const T &v) noexcept(noexcept(v *v))
Returns the square of a value.
Definition: CRNMath.h:61
bool has_selection() const
Is there a mouse selection?
A dimension error.
Definition: CRNException.h:119
#define true
Definition: ConvertUTF.cpp:57
bool IsValid() const noexcept
Returns whether the rect is valid.
Definition: CRNRect.h:94
crn::Point2DInt p1
Definition: GtkCRNImage.h:163
virtual ~Image() override
Destructor.
void focus_on(int x, int y)
Focus the image on a point.
void add_overlay_item(const crn::String &overlay_id, const crn::String &item_id, const crn::Rect &r, const crn::StringUTF8 &label="")
Adds a rectangle to an overlay.
void Translate(int x, int y)
Translates the rectangle.
Definition: CRNRect.cpp:598
int GetHeight() const
Returns the height of the rectangle.
Definition: CRNRect.h:119
crn::Point2DInt point
Definition: GtkCRNImage.h:181
void Abs(Image< T > &img, typename std::enable_if< std::is_arithmetic< T >::value >::type *dummy=nullptr) noexcept
Replaces each pixel by its absolute value.
Definition: CRNImageGray.h:47
const T & Min(const T &a, const T &b)
Returns the min of two values.
Definition: CRNMath.h:49
void set_rulers_visible(bool is_visible)
Are the rulers visible?
value_type Y
Definition: CRNPoint2D.h:63
static const crn::String & selection_overlay()
Returns the name of the mouse selection overlay.
void set_zoom(double z)
Sets the zoom level.
int GetWidth() const
Returns the width of the rectangle.
Definition: CRNRect.h:115
MouseMode
State of the mouse activity.
Definition: GtkCRNImage.h:116
void set_pixbuf(Glib::RefPtr< Gdk::Pixbuf > pb)
Sets the new image to display.
void clear_selection()
Clears the mouse selection.
A character string class.
Definition: CRNStringUTF8.h:49
void zoom_fit()
Sets the zoom level to fit the image's size.
OverlayItem & get_overlay_item(const crn::String &overlay_id, const crn::String &item_id)
Gets an overlay item.
A 2D point class.
Definition: CRNPoint2DInt.h:39
int GetRight() const
Returns the rightmost coordinate.
Definition: CRNRect.h:103
void remove_overlay_item(const crn::String &overlay_id, const crn::String &item_id)
Removes an overlay item.
Invalid argument error (e.g.: nullptr pointer)
Definition: CRNException.h:107
Overlay
Overlay types and mouse selection modes.
Definition: GtkCRNImage.h:144
An item was not found in a container.
Definition: CRNException.h:95
void set_selection(const crn::Rect &r)
Sets the mouse selection.
A rectangle class.
Definition: CRNRect.h:46