GNU Octave  4.4.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
Canvas.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2011-2018 Michael Goffioul
4 
5 This file is part of Octave.
6 
7 Octave is free software: you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 #if defined (HAVE_CONFIG_H)
24 # include "config.h"
25 #endif
26 
27 #include <QApplication>
28 #include <QBitmap>
29 #include <QCursor>
30 #include <QInputDialog>
31 #include <QList>
32 #include <QMouseEvent>
33 #include <QWheelEvent>
34 #include <QRectF>
35 
36 #include "Backend.h"
37 #include "Canvas.h"
38 #include "ContextMenu.h"
39 #include "GLCanvas.h"
40 #include "QtHandlesUtils.h"
41 
42 #include "annotation-dialog.h"
43 
44 #include "oct-opengl.h"
45 #include "octave-qt-link.h"
46 
47 #include "builtin-defun-decls.h"
48 
49 namespace QtHandles
50 {
51 
52  void
53  Canvas::redraw (bool sync)
54  {
55  QWidget *w = qWidget ();
56 
57  if (w)
58  {
59  if (sync)
60  w->repaint ();
61  else
62  w->update ();
63  }
64  }
65 
66  void
67  Canvas::blockRedraw (bool block)
68  {
69  m_redrawBlocked = block;
70  }
71 
72  void
74  {
75  QWidget *w = qWidget ();
76 
77  if (w)
78  {
79  static QCursor origCursor = w->cursor ();
80 
81  switch (mode)
82  {
83  case PanMode:
84  case RotateMode:
85  w->setCursor (Qt::OpenHandCursor);
86  break;
87 
88  case ZoomInMode:
89  w->setCursor (QPixmap (":/images/zoom-in.png"));
90  break;
91 
92  case ZoomOutMode:
93  w->setCursor (QPixmap (":/images/zoom-out.png"));
94  break;
95 
96  default:
97  w->setCursor (origCursor);
98  break;
99  }
100  }
101  }
102 
103  /*
104  Two updateCurrentPoint() routines are required:
105  1) Used for QMouseEvents where cursor position data is in callback from Qt.
106  2) Used for QKeyEvents where cursor position must be determined.
107  */
108  void
110  const graphics_object& obj, QMouseEvent *event)
111  {
113 
114  gh_manager::post_set (fig.get_handle (), "currentpoint",
115  Utils::figureCurrentPoint (fig, event), false);
116 
117  Matrix children = obj.get_properties ().get_children ();
118  octave_idx_type num_children = children.numel ();
119 
120  for (int i = 0; i < num_children; i++)
121  {
122  graphics_object childObj (gh_manager::get_object (children(i)));
123 
124  if (childObj.isa ("axes"))
125  {
126  axes::properties& ap = Utils::properties<axes> (childObj);
127  Matrix x_zlim = ap.get_transform_zlim ();
128  graphics_xform x_form = ap.get_transform ();
129 
130  ColumnVector p1 = x_form.untransform (event->x (), event->y (),
131  x_zlim(0));
132  ColumnVector p2 = x_form.untransform (event->x (), event->y (),
133  x_zlim(1));
134 
135  Matrix cp (2, 3, 0.0);
136 
137  cp(0,0) = p1(0); cp(0,1) = p1(1); cp(0,2) = p1(2);
138  cp(1,0) = p2(0); cp(1,1) = p2(1); cp(1,2) = p2(2);
139 
140  gh_manager::post_set (childObj.get_handle (), "currentpoint", cp,
141  false);
142  }
143  }
144  }
145 
146  void
148  const graphics_object& obj)
149  {
151 
152  gh_manager::post_set (fig.get_handle (), "currentpoint",
153  Utils::figureCurrentPoint (fig), false);
154 
155  Matrix children = obj.get_properties ().get_children ();
156  octave_idx_type num_children = children.numel ();
157 
158  for (int i = 0; i < num_children; i++)
159  {
160  graphics_object childObj (gh_manager::get_object (children(i)));
161 
162  if (childObj.isa ("axes"))
163  {
164  // FIXME: QCursor::pos() may give inaccurate results with
165  // asynchronous window systems like X11 over ssh.
166  QWidget *w = qWidget ();
167  QPoint p = w->mapFromGlobal (QCursor::pos ());
168  axes::properties& ap = Utils::properties<axes> (childObj);
169  Matrix x_zlim = ap.get_transform_zlim ();
170  graphics_xform x_form = ap.get_transform ();
171 
172  ColumnVector p1 = x_form.untransform (p.x (), p.y (), x_zlim(0));
173  ColumnVector p2 = x_form.untransform (p.x (), p.y (), x_zlim(1));
174 
175  Matrix cp (2, 3, 0.0);
176 
177  cp(0,0) = p1(0); cp(0,1) = p1(1); cp(0,2) = p1(2);
178  cp(1,0) = p2(0); cp(1,1) = p2(1); cp(1,2) = p2(2);
179 
180  gh_manager::post_set (childObj.get_handle (), "currentpoint", cp,
181  false);
182  }
183  }
184  }
185 
186  void
188  {
189  Ffeval (ovl ("annotation").append (args));
190 
191  redraw ();
192  }
193 
194  void
196  {
198 
200 
201  if (go.valid_object ())
202  {
203  figure::properties& fp = Utils::properties<figure> (go);
204 
205  graphics_handle ah = fp.get_currentaxes ();
206 
208 
209  if (ax.valid_object ())
210  {
211  axes::properties& ap = Utils::properties<axes> (ax);
212 
213  if (ap.handlevisibility_is ("on"))
214  {
215  ap.set_visible (! ap.is_visible ());
216 
217  redraw (true);
218  }
219  }
220  }
221  }
222 
223  void
225  {
227 
229 
230  if (go.valid_object ())
231  {
232  figure::properties& fp = Utils::properties<figure> (go);
233 
234  graphics_handle ah = fp.get_currentaxes ();
235 
237 
238  if (ax.valid_object ())
239  {
240  axes::properties& ap = Utils::properties<axes> (ax);
241 
243 
244  // If any grid is off, then turn them all on. If they are all
245  // on, then turn them off.
246 
247  std::string state = ((ap.get_xgrid () == "off"
248  || ap.get_ygrid () == "off"
249  || ap.get_zgrid () == "off")
250  ? "on" : "off");
251 
252  ap.set_xgrid (state);
253  ap.set_ygrid (state);
254  ap.set_zgrid (state);
255 
256  redraw (true);
257  }
258  }
259  }
260 
261  static void
263  {
264  ap.clear_zoom_stack ();
265 
266  ap.set_xlimmode ("auto");
267  ap.set_ylimmode ("auto");
268  ap.set_zlimmode ("auto");
269  }
270 
271  void
273  {
275 
277 
278  if (go.valid_object ())
279  {
280  figure::properties& fp = Utils::properties<figure> (go);
281 
282  graphics_handle ah = fp.get_currentaxes ();
283 
285 
286  if (ax.valid_object ())
287  {
288  axes::properties& ap = Utils::properties<axes> (ax);
289 
290  autoscale_axes (ap);
291 
292  redraw (true);
293  }
294  }
295  }
296 
297  void
299  {
300  if (! m_redrawBlocked)
301  {
303 
304  draw (m_handle);
305 
306  if ((m_mouseMode == ZoomInMode && m_mouseAxes.ok ()) || m_rectMode)
308  }
309  }
310 
311  static bool
313  {
314  // Getting pan mode property:
315  octave_value ov_pm
316  = Utils::properties<figure> (figObj).get___pan_mode__ ();
317 
318  octave_scalar_map pm = ov_pm.scalar_map_value ();
319 
320  return pm.contents ("Enable").string_value () == "on";
321  }
322 
323  static std::string
324  pan_mode (const graphics_object figObj)
325  {
326  // Getting pan mode property:
327  octave_value ov_pm
328  = Utils::properties<figure> (figObj).get___pan_mode__ ();
329 
330  octave_scalar_map pm = ov_pm.scalar_map_value ();
331 
332  return pm.contents ("Motion").string_value ();
333  }
334 
335  static bool
337  {
338  // Getting zoom mode property:
339  octave_value ov_zm
340  = Utils::properties<figure> (figObj).get___zoom_mode__ ();
341 
342  octave_scalar_map zm = ov_zm.scalar_map_value ();
343 
344  return zm.contents ("Enable").string_value () == "on";
345  }
346 
347  static std::string
348  zoom_mode (const graphics_object figObj)
349  {
350  // Getting zoom mode property:
351  octave_value ov_zm
352  = Utils::properties<figure> (figObj).get___zoom_mode__ ();
353 
354  octave_scalar_map zm = ov_zm.scalar_map_value ();
355 
356  return zm.contents ("Motion").string_value ();
357  }
358 
359  void
360  Canvas::select_object (graphics_object obj, QMouseEvent *event,
361  graphics_object& currentObj, graphics_object& axesObj,
362  bool axes_only, std::vector<std::string> omit)
363  {
364  QList<graphics_object> axesList;
365  Matrix children = obj.get_properties ().get_all_children ();
366  octave_idx_type num_children = children.numel ();
367 
368  for (int i = 0; i < num_children; i++)
369  {
370  graphics_object childObj (gh_manager::get_object (children(i)));
371 
372  if (childObj.isa ("axes"))
373  {
374  auto p = omit.begin ();
375  bool omitfound = false;
376  while (p != omit.end () && ! omitfound)
377  {
378  omitfound = (childObj.get ("tag").string_value () == *p);
379  p++;
380  }
381  if (! omitfound)
382  axesList.append (childObj);
383  }
384  else if (childObj.isa ("uicontrol") || childObj.isa ("uipanel")
385  || childObj.isa ("uibuttongroup"))
386  {
387  Matrix bb = childObj.get_properties ().get_boundingbox (false);
388  QRectF r (bb(0), bb(1), bb(2), bb(3));
389 
390  r.adjust (-5, -5, 5, 5);
391 
392 #if defined (HAVE_QMOUSEEVENT_LOCALPOS)
393  bool rect_contains_pos = r.contains (event->localPos ());
394 #else
395  bool rect_contains_pos = r.contains (event->posF ());
396 #endif
397  if (rect_contains_pos)
398  {
399  currentObj = childObj;
400  break;
401  }
402  }
403  }
404 
405  if (axes_only)
406  {
407  QPoint pt = event->pos ();
408 
409  for (QList<graphics_object>::ConstIterator it = axesList.begin ();
410  it != axesList.end (); ++it)
411  {
412  const axes::properties& ap =
413  dynamic_cast<const axes::properties&> ((*it).get_properties ());
414 
415  ColumnVector p0 = ap.pixel2coord (pt.x (), pt.y ());
416  Matrix xlim = ap.get_xlim ().matrix_value ();
417  Matrix ylim = ap.get_ylim ().matrix_value ();
418 
419  if (xlim(0) < p0(0) && xlim(1) > p0(0)
420  && ylim(0) < p0(1) && ylim(1) > p0(1))
421  {
422  axesObj = *it;
423  return;
424  }
425  }
426  }
427  else if (! currentObj)
428  {
429  for (QList<graphics_object>::ConstIterator it = axesList.begin ();
430  it != axesList.end (); ++it)
431  {
432  graphics_object go = selectFromAxes (*it, event->pos ());
433 
434  if (go)
435  {
436  currentObj = go;
437  axesObj = *it;
438  }
439  // FIXME: is this really necessary? the axes object should
440  // have been selected through selectFromAxes anyway
441  else if (it->get_properties ().is_hittest ())
442  {
443  Matrix bb = it->get_properties ().get_boundingbox (true);
444  QRectF r (bb(0), bb(1), bb(2), bb(3));
445 
446 #if defined (HAVE_QMOUSEEVENT_LOCALPOS)
447  bool rect_contains_pos = r.contains (event->localPos ());
448 #else
449  bool rect_contains_pos = r.contains (event->posF ());
450 #endif
451  if (rect_contains_pos)
452  axesObj = *it;
453  }
454 
455  if (axesObj && currentObj)
456  break;
457  }
458  }
459  }
460 
461  void
462  Canvas::canvasMouseMoveEvent (QMouseEvent *event)
463  {
466 
467  if (m_mouseMode != NoMode && (ax.valid_object () || m_mouseMode == TextMode))
468  {
469  switch (m_mouseMode)
470  {
471  case RotateMode:
472  {
473  axes::properties& ap = Utils::properties<axes> (ax);
474 
475  ap.rotate3d (m_mouseCurrent.x (), event->x (),
476  m_mouseCurrent.y (), event->y ());
477 
478  // Update current mouse position
479  m_mouseCurrent = event->pos ();
480 
481  // Force immediate redraw
482  redraw (true);
483  }
484  break;
485  case TextMode:
486  case ZoomInMode:
487  case ZoomOutMode:
488  m_mouseCurrent = event->pos ();
489  redraw (true);
490  break;
491 
492  case PanMode:
493  {
494  axes::properties& ap = Utils::properties<axes> (ax);
495 
496  graphics_object figObj (ax.get_ancestor ("figure"));
497 
498  std::string mode = pan_mode (figObj);
499 
501  m_mouseCurrent.y ());
502  ColumnVector p1 = ap.pixel2coord (event->x (),
503  event->y ());
504 
505  ap.translate_view (mode, p0(0), p1(0), p0(1), p1(1));
506 
507  // Update current mouse position
508  m_mouseCurrent = event->pos ();
509 
510  // Force immediate redraw
511  redraw (true);
512  }
513 
514  default:
515  break;
516  }
517  }
518  else if (m_mouseMode == NoMode)
519  {
521 
522  if (obj.valid_object ())
523  {
524  graphics_object figObj (obj.get_ancestor ("figure"));
525 
526  if (figObj.valid_object () &&
527  ! figObj.get ("windowbuttonmotionfcn").isempty ())
528  {
529  updateCurrentPoint (figObj, obj, event);
530  gh_manager::post_callback (figObj.get_handle (),
531  "windowbuttonmotionfcn");
532  }
533  }
534  }
535 
536  // Update mouse coordinates in the figure window status bar
538  graphics_object figObj = obj.get_ancestor ("figure");
539 
540  if (figObj.valid_object () && obj.valid_object ())
541  {
542  graphics_object currentObj, axesObj;
543  std::vector<std::string> omit = {"legend", "colorbar", "scribeoverlay"};
544  select_object (obj, event, currentObj, axesObj, true, omit);
545 
546  if (axesObj.valid_object ())
547  {
548  // FIXME: should we use signal/slot mechanism instead of
549  // directly calling parent fig methods
550  Figure *fig =
551  dynamic_cast<Figure *> (Backend::toolkitObject (figObj));
552  axes::properties& ap = Utils::properties<axes> (axesObj);
553 
554  if (fig)
555  fig->updateStatusBar (ap.pixel2coord (event->x (), event->y ()));
556  }
557  }
558  }
559 
560  void
562  {
563  // same processing as normal click, but event type is MouseButtonDblClick
564  canvasMousePressEvent (event);
565  }
566 
567  static double
568  button_number (QMouseEvent *event)
569  {
570  double retval = 0;
571 
572  switch (event->button ())
573  {
574  case Qt::LeftButton:
575  retval = 1;
576  break;
577 
578  case Qt::MidButton:
579  retval = 2;
580  break;
581 
582  case Qt::RightButton:
583  retval = 3;
584  break;
585 
586  default:
587  break;
588  }
589 
590  return retval;
591  }
592 
593  void
594  Canvas::canvasMousePressEvent (QMouseEvent *event)
595  {
598 
599  bool isdblclick = (event->type () == QEvent::MouseButtonDblClick);
600 
601  if (obj.valid_object ())
602  {
603  graphics_object figObj (obj.get_ancestor ("figure"));
604 
605  // Any click in a figure canvas makes it current
606  if (figObj)
607  {
609  Utils::properties<root_figure> (root)
610  .set_currentfigure (figObj.get_handle ().as_octave_value ());
611  }
612 
613  graphics_object currentObj, axesObj;
614 
615  // Retrieve selected object.
616  select_object (obj, event, currentObj, axesObj);
617 
618  // currentObj may be invalid if, e.g., all objects under the mouse
619  // click had "hittest" -> "off" or "pickableparts" -> "none". In that
620  // case, replace with underlying figObj which always accepts mouse
621  // clicks.
622  if (! currentObj.valid_object ())
623  currentObj = figObj;
624  else if (! currentObj.get_properties ().is_hittest ())
625  {
626  // Objects with "hittest"->"off" pass the mouse event to their
627  // parent and so on.
628  graphics_object tmpgo;
629  tmpgo = gh_manager::get_object (currentObj.get_parent ());
630  while (tmpgo && ! tmpgo.get_properties ().is_hittest ())
631  tmpgo = gh_manager::get_object (tmpgo.get_parent ());
632 
633  if (tmpgo && tmpgo.get_handle () != 0.0)
634  currentObj = tmpgo;
635  else
636  currentObj = graphics_object ();
637  }
638 
639  if (axesObj)
640  {
641  if (axesObj.get_properties ().handlevisibility_is ("on")
642  && axesObj.get_properties ().get_tag () != "legend"
643  && axesObj.get_properties ().get_tag () != "colorbar")
644  Utils::properties<figure> (figObj)
645  .set_currentaxes (axesObj.get_handle ().as_octave_value ());
646  }
647 
648  Figure *fig = dynamic_cast<Figure *> (Backend::toolkitObject (figObj));
649 
650  MouseMode newMouseMode = NoMode;
651 
652  if (fig)
653  newMouseMode = fig->mouseMode ();
654 
655  switch (newMouseMode)
656  {
657  case NoMode:
658  {
659  // Update the figure "currentobject"
660  auto& fprop = Utils::properties<figure> (figObj);
661 
662  if (currentObj
663  && currentObj.get_properties ().handlevisibility_is ("on"))
664  fprop.set_currentobject (currentObj.get_handle ()
665  .as_octave_value ());
666  else
667  fprop.set_currentobject (Matrix ());
668 
669  // Update figure "selectiontype" and "currentpoint"
671  figObj.get_handle (), "selectiontype",
672  Utils::figureSelectionType (event, isdblclick), false);
673 
674  updateCurrentPoint (figObj, obj, event);
675 
676  gh_manager::post_callback (figObj.get_handle (),
677  "windowbuttondownfcn",
678  button_number (event));
679 
680  // Execute the "buttondownfcn" of the selected object. If the
681  // latter is empty then execute the figure "buttondownfcn"
682  if (currentObj && ! currentObj.get ("buttondownfcn").isempty ())
683  gh_manager::post_callback (currentObj.get_handle (),
684  "buttondownfcn",
685  button_number (event));
686  else if (figObj && ! figObj.get ("buttondownfcn").isempty ())
687  gh_manager::post_callback (figObj.get_handle (),
688  "buttondownfcn",
689  button_number (event));
690 
691  // Show context menu of the selected object
692  if (currentObj && event->button () == Qt::RightButton)
693  ContextMenu::executeAt (currentObj.get_properties (),
694  event->globalPos ());
695  }
696  break;
697 
698  case TextMode:
699  {
700  if (event->modifiers () == Qt::NoModifier)
701  {
702  switch (event->buttons ())
703  {
704  case Qt::LeftButton:
705  m_mouseAnchor = m_mouseCurrent = event->pos ();
706  m_mouseMode = newMouseMode;
707  m_rectMode = true;
708  }
709  }
710  redraw (false);
711  }
712  break;
713 
714  case PanMode:
715  case RotateMode:
716  case ZoomInMode:
717  case ZoomOutMode:
718  if (axesObj && axesObj.get_properties ().handlevisibility_is ("on"))
719  {
720  bool redraw_figure = true;
721 
722  if (isdblclick)
723  {
724  if (event->button () == Qt::LeftButton)
725  {
726  axes::properties& ap = Utils::properties<axes> (axesObj);
727 
728  autoscale_axes (ap);
729  }
730  else
731  {
732  redraw_figure = false;
733  }
734  }
735  else if (event->modifiers () == Qt::NoModifier)
736  {
737  switch (event->buttons ())
738  {
739  case Qt::LeftButton:
740  m_mouseAnchor = m_mouseCurrent = event->pos ();
741  m_mouseAxes = axesObj.get_handle ();
742  m_mouseMode = newMouseMode;
743  m_clickMode = newMouseMode == ZoomInMode;
744  break;
745 
746  case Qt::RightButton:
747  if (newMouseMode == ZoomInMode)
748  {
749  m_mouseAnchor = m_mouseCurrent = event->pos ();
750  m_mouseAxes = axesObj.get_handle ();
751  m_mouseMode = newMouseMode;
752  m_clickMode = false;
753  }
754 
755  break;
756 
757  case Qt::MidButton:
758  {
759  axes::properties& ap =
760  Utils::properties<axes> (axesObj);
761 
762  autoscale_axes (ap);
763  }
764  break;
765 
766  default:
767  redraw_figure = false;
768  break;
769  }
770  }
771  else if (event->modifiers () == Qt::ShiftModifier)
772  {
773  switch (event->buttons ())
774  {
775  case Qt::LeftButton:
776  if (newMouseMode == ZoomInMode)
777  {
778  m_mouseAnchor = m_mouseCurrent = event->pos ();
779  m_mouseAxes = axesObj.get_handle ();
780  m_mouseMode = newMouseMode;
781  m_clickMode = false;
782  }
783  break;
784 
785  default:
786  redraw_figure = false;
787  break;
788  }
789  }
790 
791  if (redraw_figure)
792  redraw (false);
793  }
794  break;
795 
796  default:
797  break;
798  }
799  }
800 
801  }
802 
803  void
804  Canvas::canvasMouseReleaseEvent (QMouseEvent *event)
805  {
807  && m_mouseAxes.ok ())
808  {
811 
812  if (ax.valid_object ())
813  {
814  axes::properties& ap = Utils::properties<axes> (ax);
815 
817 
818  graphics_object figObj (obj.get_ancestor ("figure"));
819 
820  std::string zm = zoom_mode (figObj);
821 
822  if (m_mouseAnchor == event->pos ())
823  {
824  double factor = (m_clickMode ? 2.0 : 0.5);
825 
826  ColumnVector p1 = ap.pixel2coord (event->x (), event->y ());
827 
828  ap.zoom_about_point (zm, p1(0), p1(1), factor);
829  }
830  else if (m_mouseMode == ZoomInMode)
831  {
832  ColumnVector p0 = ap.pixel2coord (m_mouseAnchor.x (),
833  m_mouseAnchor.y ());
834  ColumnVector p1 = ap.pixel2coord (event->x (),
835  event->y ());
836 
837  Matrix xl (1, 2, 0.0);
838  Matrix yl (1, 2, 0.0);
839 
840  xl(0) = std::min (p0(0), p1(0));
841  xl(1) = std::max (p0(0), p1(0));
842  yl(0) = std::min (p0(1), p1(1));
843  yl(1) = std::max (p0(1), p1(1));
844 
845  ap.zoom (zm, xl, yl);
846  }
847 
848  redraw (false);
849  }
850  }
851  else if (m_mouseMode == NoMode)
852  {
855 
856  if (obj.valid_object ())
857  {
858  graphics_object figObj (obj.get_ancestor ("figure"));
859 
860  updateCurrentPoint (figObj, obj, event);
861  gh_manager::post_callback (figObj.get_handle (),
862  "windowbuttonupfcn");
863  }
864  }
865  else if (m_mouseMode == TextMode)
866  {
868 
869  graphics_object figObj =
871  if (figObj.valid_object ())
872  {
873  QWidget *w = qWidget ();
874  if (w)
875  {
876  Matrix bb = figObj.get ("position").matrix_value ();
877  bb(0) = m_mouseAnchor.x () / bb(2);
878  bb(1) = 1.0 - (m_mouseAnchor.y () / bb(3));
879  bb(2) = (event->x () - m_mouseAnchor.x ()) / bb(2);
880  bb(3) = (m_mouseAnchor.y () - event->y ()) / bb(3);
881 
882  octave_value_list props = ovl ("textbox", bb);
883 
884  annotation_dialog anno_dlg (w, props);
885 
886  if (anno_dlg.exec () == QDialog::Accepted)
887  {
888  props = anno_dlg.get_properties ();
889  props.prepend (figObj.get_handle ().as_octave_value ());
890 
892  props);
893  }
894  }
895  }
896  }
897  m_rectMode = false;
900  }
901 
902  void
903  Canvas::canvasWheelEvent (QWheelEvent *event)
904  {
907 
908  if (obj.valid_object ())
909  {
911 
912  graphics_object axesObj;
913 
914  Matrix children = obj.get_properties ().get_children ();
915  octave_idx_type num_children = children.numel ();
916 
917  for (int i = 0; i < num_children; i++)
918  {
919  graphics_object childObj (gh_manager::get_object (children(i)));
920 
921  if (childObj.isa ("axes"))
922  {
923  graphics_object go = selectFromAxes (childObj, event->pos ());
924 
925  if (go)
926  {
927  axesObj = childObj;
928  break;
929  }
930  }
931  }
932 
933  if (axesObj)
934  {
935  MouseMode newMouseMode = NoMode;
936 
937  graphics_object figObj (obj.get_ancestor ("figure"));
938 
939  Figure *fig = dynamic_cast<Figure *> (Backend::toolkitObject (figObj));
940 
941  if (fig)
942  newMouseMode = fig->mouseMode ();
943 
944  if (axesObj.get_properties ().handlevisibility_is ("on"))
945  {
946  Utils::properties<figure> (figObj)
947  .set_currentaxes (axesObj.get_handle ().as_octave_value ());
948 
949  if (zoom_enabled (figObj))
950  {
951  if (event->delta () > 0)
952  newMouseMode = ZoomInMode;
953  else
954  newMouseMode = ZoomOutMode;
955 
956  mode = zoom_mode (figObj);
957  }
958  else if (pan_enabled (figObj))
959  {
960  newMouseMode = PanMode;
961 
962  mode = pan_mode (figObj);
963  }
964  }
965 
966  bool redrawFigure = true;
967 
968  switch (newMouseMode)
969  {
970  case ZoomInMode:
971  case ZoomOutMode:
972  {
973  axes::properties& ap = Utils::properties<axes> (axesObj);
974 
975  // Control how fast to zoom when using scroll wheel.
976  double wheel_zoom_speed = ap.get_mousewheelzoom ();
977 
978  // Determine if we're zooming in or out.
979  double factor = (newMouseMode == ZoomInMode
980  ? 1 / (1.0 - wheel_zoom_speed)
981  : 1.0 - wheel_zoom_speed);
982 
983  // FIXME: should we zoom about point for 2D plots?
984 
985  ap.zoom (mode, factor);
986  }
987  break;
988 
989  case PanMode:
990  {
991  axes::properties& ap = Utils::properties<axes> (axesObj);
992 
993  double factor = (event->delta () > 0 ? 0.1 : -0.1);
994 
995  if (event->modifiers () == Qt::NoModifier
996  && mode != "horizontal")
997  ap.pan ("vertical", factor);
998  else if (event->modifiers () == Qt::ShiftModifier
999  && mode != "vertical")
1000  ap.pan ("horizontal", factor);
1001  }
1002  break;
1003 
1004  default:
1005  redrawFigure = false;
1006  break;
1007  }
1008 
1009  if (redrawFigure)
1010  redraw (false);
1011  }
1012  }
1013  }
1014 
1015  bool
1016  Canvas::canvasKeyPressEvent (QKeyEvent *event)
1017  {
1018  if (m_eventMask & KeyPress)
1019  {
1020  gh_manager::auto_lock lock;
1022 
1023  if (obj.valid_object ())
1024  {
1025  graphics_object figObj (obj.get_ancestor ("figure"));
1026 
1027  updateCurrentPoint (figObj, obj);
1028  }
1029 
1030  octave_scalar_map eventData = Utils::makeKeyEventStruct (event);
1031 
1032  gh_manager::post_set (m_handle, "currentcharacter",
1033  eventData.getfield ("Character"), false);
1034  gh_manager::post_callback (m_handle, "keypressfcn", eventData);
1035 
1036  return true;
1037  }
1038 
1039  return false;
1040  }
1041 
1042  bool
1044  {
1045  if (! event->isAutoRepeat () && (m_eventMask & KeyRelease))
1046  {
1047  gh_manager::post_callback (m_handle, "keyreleasefcn",
1048  Utils::makeKeyEventStruct (event));
1049 
1050  return true;
1051  }
1052 
1053  return false;
1054  }
1055 
1056  Canvas*
1057  Canvas::create (const std::string& /* name */, QWidget *parent,
1058  const graphics_handle& handle)
1059  {
1060  // Only OpenGL
1061  return new GLCanvas (parent, handle);
1062  }
1063 
1064 }
graphics_handle m_mouseAxes
Definition: Canvas.h:129
virtual QWidget * qWidget(void)=0
bool ok(void) const
Definition: oct-handle.h:109
void rotate3d(double x0, double x1, double y0, double y1, bool push_to_zoom_stack=true)
Definition: graphics.cc:8471
static void post_callback(const graphics_handle &h, const std::string &name, const octave_value &data=Matrix())
Definition: graphics.in.h:6214
octave_value_list get_properties() const
std::string string_value(bool force=false) const
Definition: ov.h:955
bool isempty(void) const
Definition: ov.h:529
void select_object(graphics_object obj, QMouseEvent *event, graphics_object &currentObj, graphics_object &axesObj, bool axes_only=false, std::vector< std::string > omit=std::vector< std::string >())
Definition: Canvas.cc:360
graphics_handle get_handle(void) const
Definition: graphics.in.h:2774
MouseMode
Definition: Figure.h:39
Return the CPU time used by your Octave session The first output is the total time spent executing your process and is equal to the sum of second and third which are the number of CPU seconds spent executing in user mode and the number of CPU seconds spent executing in system mode
Definition: data.cc:6348
graphics_handle m_handle
Definition: Canvas.h:123
void updateCurrentPoint(const graphics_object &fig, const graphics_object &obj, QMouseEvent *event)
Definition: Canvas.cc:109
void zoom(const std::string &mode, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8301
void translate_view(const std::string &mode, double x0, double x1, double y0, double y1, bool push_to_zoom_stack=true)
Definition: graphics.cc:8425
graphics_xform get_transform(void) const
Definition: graphics.in.h:3442
virtual Matrix get_boundingbox(bool=false, const Matrix &=Matrix()) const
Definition: graphics.in.h:2257
Matrix figureCurrentPoint(const graphics_object &fig, QMouseEvent *event)
void redraw(bool sync=false)
Definition: Canvas.cc:53
Matrix get_children(void) const
Definition: graphics.in.h:2275
void canvasToggleGrid(const graphics_handle &handle)
Definition: Canvas.cc:224
ColumnVector pixel2coord(double px, double py) const
Definition: graphics.in.h:3492
virtual void drawZoomBox(const QPoint &p1, const QPoint &p2)=0
static void autoscale_axes(axes::properties &ap)
Definition: Canvas.cc:262
void canvasMouseDoubleClickEvent(QMouseEvent *event)
Definition: Canvas.cc:561
bool isa(const std::string &go_name) const
Definition: graphics.in.h:2786
ColumnVector untransform(double x, double y, double z, bool use_scale=true) const
Definition: graphics.cc:6949
void canvasToggleAxes(const graphics_handle &handle)
Definition: Canvas.cc:195
static void executeAt(const base_properties &props, const QPoint &pt)
Definition: ContextMenu.cc:113
void pan(const std::string &mode, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8454
bool valid_object(void) const
Definition: graphics.in.h:2806
void canvasMousePressEvent(QMouseEvent *event)
Definition: Canvas.cc:594
QPoint m_mouseAnchor
Definition: Canvas.h:127
void canvasWheelEvent(QWheelEvent *event)
Definition: Canvas.cc:903
bool canvasKeyPressEvent(QKeyEvent *event)
Definition: Canvas.cc:1016
void canvasMouseMoveEvent(QMouseEvent *event)
Definition: Canvas.cc:462
virtual octave_value get_xlim(void) const
Definition: graphics.in.h:2330
bool m_clickMode
Definition: Canvas.h:126
octave_scalar_map makeKeyEventStruct(QKeyEvent *event)
octave_value get(bool all=false) const
Definition: graphics.in.h:2711
static bool zoom_enabled(const graphics_object figObj)
Definition: Canvas.cc:336
void canvasAutoAxes(const graphics_handle &handle)
Definition: Canvas.cc:272
std::complex< double > w(std::complex< double > z, double relerr=0)
octave_value as_octave_value(void) const
Definition: oct-handle.h:76
QPoint m_mouseCurrent
Definition: Canvas.h:128
virtual octave_value get_ylim(void) const
Definition: graphics.in.h:2331
double tmp
Definition: data.cc:6252
octave_value retval
Definition: data.cc:6246
bool append
Definition: load-save.cc:1618
base_properties & get_properties(void)
Definition: graphics.in.h:2788
Definition: dMatrix.h:36
void zoom_about_point(const std::string &mode, double x, double y, double factor, bool push_to_zoom_stack=true)
Definition: graphics.cc:8272
MouseMode m_mouseMode
Definition: Canvas.h:125
octave_value getfield(const std::string &key) const
Definition: oct-map.cc:184
void updateStatusBar(ColumnVector pt)
Definition: Figure.cc:615
octave_handle graphics_handle
Matrix get_transform_zlim(void) const
Definition: graphics.in.h:3449
graphics_handle get_parent(void) const
Definition: graphics.in.h:2772
void canvasPaintEvent(void)
Definition: Canvas.cc:298
Matrix get_all_children(void) const
Definition: graphics.in.h:2280
static uint32_t state[624]
Definition: randmtzig.cc:183
void canvasMouseReleaseEvent(QMouseEvent *event)
Definition: Canvas.cc:804
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:227
octave_scalar_map scalar_map_value(void) const
static Object * toolkitObject(const graphics_object &go)
Definition: Backend.cc:212
const octave_value & contents(const_iterator p) const
Definition: oct-map.h:194
std::string figureSelectionType(QMouseEvent *event, bool isDoubleClick)
bool canvasKeyReleaseEvent(QKeyEvent *event)
Definition: Canvas.cc:1043
virtual void draw(const graphics_handle &handle)=0
OCTAVE_EXPORT octave_value_list isa nd deftypefn *return ovl(args(0).isinteger())
void setCursor(MouseMode mode)
Definition: Canvas.cc:73
p
Definition: lu.cc:138
static bool pan_enabled(const graphics_object figObj)
Definition: Canvas.cc:312
virtual graphics_object selectFromAxes(const graphics_object &ax, const QPoint &pt)=0
void clear_zoom_stack(bool do_unzoom=true)
Definition: graphics.cc:8602
static Canvas * create(const std::string &name, QWidget *parent, const graphics_handle &handle)
Definition: Canvas.cc:1057
static graphics_object get_object(double val)
Definition: graphics.in.h:6098
MouseMode mouseMode(void)
Definition: Figure.cc:274
void annotation_callback(const octave_value_list &args)
Definition: Canvas.cc:187
static std::string pan_mode(const graphics_object figObj)
Definition: Canvas.cc:324
for i
Definition: data.cc:5264
bool m_redrawBlocked
Definition: Canvas.h:124
is a function handle
Definition: bsxfun.cc:337
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:366
graphics_object get_ancestor(const std::string &type) const
Definition: graphics.cc:3613
static double button_number(QMouseEvent *event)
Definition: Canvas.cc:568
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888
octave_value_list & prepend(const octave_value &val)
Definition: ovl.cc:65
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:834
static std::string zoom_mode(const graphics_object figObj)
Definition: Canvas.cc:348
void blockRedraw(bool block=true)
Definition: Canvas.cc:67
static void post_set(const graphics_handle &h, const std::string &name, const octave_value &value, bool notify_toolkit=true)
Definition: graphics.in.h:6228
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:204