view libgui/graphics/Canvas.cc @ 21301:40de9f8f23a6

Use '#include "config.h"' rather than <config.h>. * mk-octave-config-h.sh, mk-opts.pl, Backend.cc, BaseControl.cc, ButtonControl.cc, Canvas.cc, CheckBoxControl.cc, Container.cc, ContextMenu.cc, EditControl.cc, Figure.cc, FigureWindow.cc, GLCanvas.cc, KeyMap.cc, ListBoxControl.cc, Logger.cc, Menu.cc, MouseModeActionGroup.cc, Object.cc, ObjectFactory.cc, ObjectProxy.cc, Panel.cc, PopupMenuControl.cc, PushButtonControl.cc, PushTool.cc, QtHandlesUtils.cc, RadioButtonControl.cc, SliderControl.cc, TextControl.cc, TextEdit.cc, ToggleButtonControl.cc, ToggleTool.cc, ToolBar.cc, ToolBarButton.cc, __init_qt__.cc, annotation-dialog.cc, gl-select.cc, module.mk, kpty.cpp, color-picker.cc, dialog.cc, documentation-dock-widget.cc, files-dock-widget.cc, find-files-dialog.cc, find-files-model.cc, history-dock-widget.cc, file-editor-tab.cc, file-editor-tab.h, file-editor.cc, find-dialog.cc, marker.cc, octave-qscintilla.cc, octave-txt-lexer.cc, main-window.cc, octave-cmd.cc, octave-dock-widget.cc, octave-gui.cc, octave-interpreter.cc, octave-qt-link.cc, parser.cc, webinfo.cc, resource-manager.cc, settings-dialog.cc, shortcut-manager.cc, terminal-dock-widget.cc, thread-manager.cc, welcome-wizard.cc, workspace-model.cc, workspace-view.cc, build-env-features.sh, build-env.in.cc, Cell.cc, __contourc__.cc, __dispatch__.cc, __dsearchn__.cc, __ichol__.cc, __ilu__.cc, __lin_interpn__.cc, __pchip_deriv__.cc, __qp__.cc, balance.cc, besselj.cc, betainc.cc, bitfcns.cc, bsxfun.cc, c-file-ptr-stream.cc, cdisplay.c, cellfun.cc, coct-hdf5-types.c, colloc.cc, comment-list.cc, conv2.cc, daspk.cc, dasrt.cc, dassl.cc, data.cc, debug.cc, defaults.cc, defun.cc, det.cc, dirfns.cc, display.cc, dlmread.cc, dot.cc, dynamic-ld.cc, eig.cc, ellipj.cc, error.cc, errwarn.cc, event-queue.cc, fft.cc, fft2.cc, fftn.cc, file-io.cc, filter.cc, find.cc, ft-text-renderer.cc, gammainc.cc, gcd.cc, getgrent.cc, getpwent.cc, getrusage.cc, givens.cc, gl-render.cc, gl2ps-print.cc, graphics.cc, gripes.cc, hash.cc, help.cc, hess.cc, hex2num.cc, hook-fcn.cc, input.cc, inv.cc, jit-ir.cc, jit-typeinfo.cc, jit-util.cc, kron.cc, load-path.cc, load-save.cc, lookup.cc, ls-ascii-helper.cc, ls-hdf5.cc, ls-mat-ascii.cc, ls-mat4.cc, ls-mat5.cc, ls-oct-binary.cc, ls-oct-text.cc, ls-utils.cc, lsode.cc, lu.cc, luinc.cc, mappers.cc, matrix_type.cc, max.cc, mex.cc, mgorth.cc, nproc.cc, oct-errno.in.cc, oct-fstrm.cc, oct-hdf5-types.cc, oct-hist.cc, oct-iostrm.cc, oct-lvalue.cc, oct-map.cc, oct-prcstrm.cc, oct-procbuf.cc, oct-stream.cc, oct-strstrm.cc, oct-tex-lexer.in.ll, oct-tex-parser.in.yy, octave-link.cc, ordschur.cc, pager.cc, pinv.cc, pr-output.cc, procstream.cc, profiler.cc, psi.cc, pt-jit.cc, quad.cc, quadcc.cc, qz.cc, rand.cc, rcond.cc, regexp.cc, schur.cc, sighandlers.cc, siglist.c, sparse-xdiv.cc, sparse-xpow.cc, sparse.cc, spparms.cc, sqrtm.cc, str2double.cc, strfind.cc, strfns.cc, sub2ind.cc, svd.cc, sylvester.cc, symtab.cc, syscalls.cc, sysdep.cc, text-renderer.cc, time.cc, toplev.cc, tril.cc, tsearch.cc, txt-eng.cc, typecast.cc, urlwrite.cc, utils.cc, variables.cc, xdiv.cc, xgl2ps.c, xnorm.cc, xpow.cc, zfstream.cc, __delaunayn__.cc, __eigs__.cc, __fltk_uigetfile__.cc, __glpk__.cc, __init_fltk__.cc, __init_gnuplot__.cc, __magick_read__.cc, __osmesa_print__.cc, __voronoi__.cc, amd.cc, audiodevinfo.cc, audioread.cc, ccolamd.cc, chol.cc, colamd.cc, convhulln.cc, dmperm.cc, fftw.cc, qr.cc, symbfact.cc, symrcm.cc, mkbuiltins, mkops, ov-base-diag.cc, ov-base-int.cc, ov-base-mat.cc, ov-base-scalar.cc, ov-base-sparse.cc, ov-base.cc, ov-bool-mat.cc, ov-bool-sparse.cc, ov-bool.cc, ov-builtin.cc, ov-cell.cc, ov-ch-mat.cc, ov-class.cc, ov-classdef.cc, ov-colon.cc, ov-complex.cc, ov-cs-list.cc, ov-cx-diag.cc, ov-cx-mat.cc, ov-cx-sparse.cc, ov-dld-fcn.cc, ov-fcn-handle.cc, ov-fcn-inline.cc, ov-fcn.cc, ov-float.cc, ov-flt-complex.cc, ov-flt-cx-diag.cc, ov-flt-cx-mat.cc, ov-flt-re-diag.cc, ov-flt-re-mat.cc, ov-int16.cc, ov-int32.cc, ov-int64.cc, ov-int8.cc, ov-java.cc, ov-lazy-idx.cc, ov-mex-fcn.cc, ov-null-mat.cc, ov-oncleanup.cc, ov-perm.cc, ov-range.cc, ov-re-diag.cc, ov-re-mat.cc, ov-re-sparse.cc, ov-scalar.cc, ov-str-mat.cc, ov-struct.cc, ov-typeinfo.cc, ov-uint16.cc, ov-uint32.cc, ov-uint64.cc, ov-uint8.cc, ov-usr-fcn.cc, ov.cc, ovl.cc, octave.cc, op-b-b.cc, op-b-bm.cc, op-b-sbm.cc, op-bm-b.cc, op-bm-bm.cc, op-bm-sbm.cc, op-cdm-cdm.cc, op-cell.cc, op-chm.cc, op-class.cc, op-cm-cm.cc, op-cm-cs.cc, op-cm-m.cc, op-cm-s.cc, op-cm-scm.cc, op-cm-sm.cc, op-cs-cm.cc, op-cs-cs.cc, op-cs-m.cc, op-cs-s.cc, op-cs-scm.cc, op-cs-sm.cc, op-dm-dm.cc, op-dm-scm.cc, op-dm-sm.cc, op-dm-template.cc, op-dms-template.cc, op-double-conv.cc, op-fcdm-fcdm.cc, op-fcdm-fdm.cc, op-fcm-fcm.cc, op-fcm-fcs.cc, op-fcm-fm.cc, op-fcm-fs.cc, op-fcn.cc, op-fcs-fcm.cc, op-fcs-fcs.cc, op-fcs-fm.cc, op-fcs-fs.cc, op-fdm-fdm.cc, op-float-conv.cc, op-fm-fcm.cc, op-fm-fcs.cc, op-fm-fm.cc, op-fm-fs.cc, op-fs-fcm.cc, op-fs-fcs.cc, op-fs-fm.cc, op-fs-fs.cc, op-i16-i16.cc, op-i32-i32.cc, op-i64-i64.cc, op-i8-i8.cc, op-int-concat.cc, op-int-conv.cc, op-m-cm.cc, op-m-cs.cc, op-m-m.cc, op-m-s.cc, op-m-scm.cc, op-m-sm.cc, op-pm-pm.cc, op-pm-scm.cc, op-pm-sm.cc, op-pm-template.cc, op-range.cc, op-s-cm.cc, op-s-cs.cc, op-s-m.cc, op-s-s.cc, op-s-scm.cc, op-s-sm.cc, op-sbm-b.cc, op-sbm-bm.cc, op-sbm-sbm.cc, op-scm-cm.cc, op-scm-cs.cc, op-scm-m.cc, op-scm-s.cc, op-scm-scm.cc, op-scm-sm.cc, op-sm-cm.cc, op-sm-cs.cc, op-sm-m.cc, op-sm-s.cc, op-sm-scm.cc, op-sm-sm.cc, op-str-m.cc, op-str-s.cc, op-str-str.cc, op-struct.cc, op-ui16-ui16.cc, op-ui32-ui32.cc, op-ui64-ui64.cc, op-ui8-ui8.cc, lex.ll, oct-parse.in.yy, pt-arg-list.cc, pt-array-list.cc, pt-assign.cc, pt-binop.cc, pt-bp.cc, pt-cbinop.cc, pt-cell.cc, pt-check.cc, pt-classdef.cc, pt-cmd.cc, pt-colon.cc, pt-const.cc, pt-decl.cc, pt-eval.cc, pt-except.cc, pt-exp.cc, pt-fcn-handle.cc, pt-funcall.cc, pt-id.cc, pt-idx.cc, pt-jump.cc, pt-loop.cc, pt-mat.cc, pt-misc.cc, pt-pr-code.cc, pt-select.cc, pt-stmt.cc, pt-unop.cc, pt.cc, token.cc, Array-jit.cc, Array-os.cc, Array-sym.cc, Array-tc.cc, version.cc, Array-C.cc, Array-b.cc, Array-ch.cc, Array-d.cc, Array-f.cc, Array-fC.cc, Array-i.cc, Array-idx-vec.cc, Array-s.cc, Array-str.cc, Array-util.cc, Array-voidp.cc, Array.cc, CColVector.cc, CDiagMatrix.cc, CMatrix.cc, CNDArray.cc, CRowVector.cc, CSparse.cc, DiagArray2.cc, MArray-C.cc, MArray-d.cc, MArray-f.cc, MArray-fC.cc, MArray-i.cc, MArray-s.cc, MArray.cc, MDiagArray2.cc, MSparse-C.cc, MSparse-d.cc, MatrixType.cc, PermMatrix.cc, Range.cc, Sparse-C.cc, Sparse-b.cc, Sparse-d.cc, Sparse.cc, boolMatrix.cc, boolNDArray.cc, boolSparse.cc, chMatrix.cc, chNDArray.cc, dColVector.cc, dDiagMatrix.cc, dMatrix.cc, dNDArray.cc, dRowVector.cc, dSparse.cc, dim-vector.cc, fCColVector.cc, fCDiagMatrix.cc, fCMatrix.cc, fCNDArray.cc, fCRowVector.cc, fColVector.cc, fDiagMatrix.cc, fMatrix.cc, fNDArray.cc, fRowVector.cc, idx-vector.cc, int16NDArray.cc, int32NDArray.cc, int64NDArray.cc, int8NDArray.cc, intNDArray.cc, uint16NDArray.cc, uint32NDArray.cc, uint64NDArray.cc, uint8NDArray.cc, blaswrap.c, cquit.c, f77-extern.cc, f77-fcn.c, lo-error.c, quit.cc, CollocWt.cc, DASPK.cc, DASRT.cc, DASSL.cc, EIG.cc, LSODE.cc, ODES.cc, Quad.cc, aepbalance.cc, chol.cc, eigs-base.cc, fEIG.cc, gepbalance.cc, hess.cc, lo-mappers.cc, lo-specfun.cc, lu.cc, oct-convn.cc, oct-fftw.cc, oct-norm.cc, oct-rand.cc, oct-spparms.cc, qr.cc, qrp.cc, randgamma.c, randmtzig.c, randpoisson.c, schur.cc, sparse-chol.cc, sparse-dmsolve.cc, sparse-lu.cc, sparse-qr.cc, svd.cc, mk-ops.awk, dir-ops.cc, file-ops.cc, file-stat.cc, lo-sysdep.cc, mach-info.cc, oct-env.cc, oct-group.cc, oct-passwd.cc, oct-syscalls.cc, oct-time.cc, oct-uname.cc, cmd-edit.cc, cmd-hist.cc, data-conv.cc, f2c-main.c, glob-match.cc, kpse.cc, lo-array-errwarn.cc, lo-array-gripes.cc, lo-cutils.c, lo-ieee.cc, lo-regexp.cc, lo-utils.cc, oct-base64.cc, oct-glob.cc, oct-inttypes.cc, oct-locbuf.cc, oct-mutex.cc, oct-rl-edit.c, oct-rl-hist.c, oct-shlib.cc, oct-sort.cc, pathsearch.cc, singleton-cleanup.cc, sparse-sort.cc, sparse-util.cc, str-vec.cc, unwind-prot.cc, url-transfer.cc, display-available.c, main-cli.cc, main-gui.cc, main.in.cc, mkoctfile.in.cc, octave-config.in.cc: Use '#include "config.h"' rather than <config.h>.
author Rik <rik@octave.org>
date Thu, 18 Feb 2016 13:34:50 -0800
parents 710e700cdd7f
children 7bdfa98a98a3
line wrap: on
line source

/*

Copyright (C) 2011-2015 Michael Goffioul

This file is part of Octave.

Octave is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

Octave is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING.  If not, see
<http://www.gnu.org/licenses/>.

*/

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <QApplication>
#include <QBitmap>
#include <QCursor>
#include <QInputDialog>
#include <QList>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QRectF>

#include "Backend.h"
#include "Canvas.h"
#include "ContextMenu.h"
#include "GLCanvas.h"
#include "QtHandlesUtils.h"

#include "annotation-dialog.h"

#include "gl2ps-print.h"
#include "oct-opengl.h"
#include "octave-qt-link.h"

#include "builtin-defun-decls.h"

namespace QtHandles
{

void
Canvas::redraw (bool sync)
{
  QWidget *w = qWidget ();

  if (w)
    {
      if (sync)
        w->repaint ();
      else
        w->update ();
    }
}

void
Canvas::blockRedraw (bool block)
{
  m_redrawBlocked = block;
}

void
Canvas::setCursor (MouseMode mode)
{
  QWidget *w = qWidget ();

  if (w)
    {
      static QCursor origCursor = w->cursor ();

      switch (mode)
        {
        case PanMode:
        case RotateMode:
          w->setCursor (Qt::OpenHandCursor);
          break;

        case ZoomInMode:
          w->setCursor (QPixmap (":/images/zoom-in.png"));
          break;

        case ZoomOutMode:
          w->setCursor (QPixmap (":/images/zoom-out.png"));
          break;

        default:
          w->setCursor (origCursor);
          break;
        }
    }
}

void
Canvas::print (const QString& file_cmd, const QString& term)
{
  gh_manager::auto_lock lock;
  graphics_object obj = gh_manager::get_object (m_handle);

  if (obj.valid_object ())
    {
      graphics_object figObj (obj.get_ancestor ("figure"));

      gl2ps_print (figObj, file_cmd.toStdString (), term.toStdString ());
    }
}

/*
   Two updateCurrentPoint() routines are required:
   1) Used for QMouseEvents where cursor position data is in callback from Qt.
   2) Used for QKeyEvents where cursor position must be determined.
*/
void
Canvas::updateCurrentPoint(const graphics_object& fig,
                           const graphics_object& obj, QMouseEvent* event)
{
  gh_manager::auto_lock lock;

  gh_manager::post_set (fig.get_handle (), "currentpoint",
                        Utils::figureCurrentPoint (fig, event), false);

  Matrix children = obj.get_properties ().get_children ();
  octave_idx_type num_children = children.numel ();

  for (int i = 0; i < num_children; i++)
    {
      graphics_object childObj (gh_manager::get_object (children(i)));

      if (childObj.isa ("axes"))
        {
          axes::properties& ap = Utils::properties<axes> (childObj);
          Matrix x_zlim = ap.get_transform_zlim ();
          graphics_xform x_form = ap.get_transform ();

          ColumnVector p1 = x_form.untransform (event->x (), event->y (),
                                                x_zlim(0));
          ColumnVector p2 = x_form.untransform (event->x (), event->y (),
                                                x_zlim(1));

          Matrix cp (2, 3, 0.0);

          cp(0,0) = p1(0); cp(0,1) = p1(1); cp(0,2) = p1(2);
          cp(1,0) = p2(0); cp(1,1) = p2(1); cp(1,2) = p2(2);

          gh_manager::post_set (childObj.get_handle (), "currentpoint", cp,
                                false);
        }
    }
}

void
Canvas::updateCurrentPoint(const graphics_object& fig,
                           const graphics_object& obj)
{
  gh_manager::auto_lock lock;

  gh_manager::post_set (fig.get_handle (), "currentpoint",
                        Utils::figureCurrentPoint (fig), false);

  Matrix children = obj.get_properties ().get_children ();
  octave_idx_type num_children = children.numel ();

  for (int i = 0; i < num_children; i++)
    {
      graphics_object childObj (gh_manager::get_object (children(i)));

      if (childObj.isa ("axes"))
        {
          // FIXME: QCursor::pos() may give inaccurate results with asynchronous
          //        window systems like X11 over ssh.
          QWidget *w = qWidget ();
          QPoint p = w->mapFromGlobal (QCursor::pos ());
          axes::properties& ap = Utils::properties<axes> (childObj);
          Matrix x_zlim = ap.get_transform_zlim ();
          graphics_xform x_form = ap.get_transform ();

          ColumnVector p1 = x_form.untransform (p.x (), p.y (), x_zlim(0));
          ColumnVector p2 = x_form.untransform (p.x (), p.y (), x_zlim(1));

          Matrix cp (2, 3, 0.0);

          cp(0,0) = p1(0); cp(0,1) = p1(1); cp(0,2) = p1(2);
          cp(1,0) = p2(0); cp(1,1) = p2(1); cp(1,2) = p2(2);

          gh_manager::post_set (childObj.get_handle (), "currentpoint", cp,
                                false);
        }
    }
}

void
Canvas::annotation_callback (const octave_value_list& args)
{
  Ffeval (ovl ("annotation").append (args));

  redraw ();
}

void
Canvas::canvasToggleAxes (const graphics_handle& handle)
{
  gh_manager::auto_lock lock;

  graphics_object go = gh_manager::get_object (handle);

  if (go.valid_object ())
    {
      figure::properties& fp = Utils::properties<figure> (go);

      graphics_handle ah = fp.get_currentaxes ();

      graphics_object ax = gh_manager::get_object (ah);

      if (ax.valid_object ())
        {
          axes::properties& ap = Utils::properties<axes> (ax);

          if (ap.handlevisibility_is ("on"))
            {
              ap.set_visible (! ap.is_visible ());

              redraw (true);
            }
        }
    }
}

void
Canvas::canvasToggleGrid (const graphics_handle& handle)
{
  gh_manager::auto_lock lock;

  graphics_object go = gh_manager::get_object (handle);

  if (go.valid_object ())
    {
      figure::properties& fp = Utils::properties<figure> (go);

      graphics_handle ah = fp.get_currentaxes ();

      graphics_object ax = gh_manager::get_object (ah);

      if (ax.valid_object ())
        {
          axes::properties& ap = Utils::properties<axes> (ax);

          std::string tmp;

          // If any grid is off, then turn them all on.  If they are all
          // on, then turn them off.

          std::string state = ((ap.get_xgrid () == "off"
                                || ap.get_ygrid () == "off"
                                || ap.get_zgrid () == "off")
                               ? "on" : "off");

          ap.set_xgrid (state);
          ap.set_ygrid (state);
          ap.set_zgrid (state);

          redraw (true);
        }
    }
}

static void
autoscale_axes (axes::properties& ap)
{
  ap.clear_zoom_stack ();

  ap.set_xlimmode ("auto");
  ap.set_ylimmode ("auto");
  ap.set_zlimmode ("auto");
}

void
Canvas::canvasAutoAxes (const graphics_handle& handle)
{
  gh_manager::auto_lock lock;

  graphics_object go = gh_manager::get_object (handle);

  if (go.valid_object ())
    {
      figure::properties& fp = Utils::properties<figure> (go);

      graphics_handle ah = fp.get_currentaxes ();

      graphics_object ax = gh_manager::get_object (ah);

      if (ax.valid_object ())
        {
          axes::properties& ap = Utils::properties<axes> (ax);

          autoscale_axes (ap);

          redraw (true);
        }
    }
}

void
Canvas::canvasPaintEvent (void)
{
  if (! m_redrawBlocked)
    {
      gh_manager::auto_lock lock;

      draw (m_handle);

      if ((m_mouseMode == ZoomInMode && m_mouseAxes.ok ()) || m_rectMode)
        drawZoomBox (m_mouseAnchor, m_mouseCurrent);
    }
}

static bool
pan_enabled (const graphics_object figObj)
{
  // Getting pan mode property:
  octave_value ov_pm
    = Utils::properties<figure> (figObj).get___pan_mode__ ();

  octave_scalar_map pm = ov_pm.scalar_map_value ();

  return pm.contents ("Enable").string_value () == "on";
}

static std::string
pan_mode (const graphics_object figObj)
{
  // Getting pan mode property:
  octave_value ov_pm
    = Utils::properties<figure> (figObj).get___pan_mode__ ();

  octave_scalar_map pm = ov_pm.scalar_map_value ();

  return pm.contents ("Motion").string_value ();
}

static bool
zoom_enabled (const graphics_object figObj)
{
  // Getting zoom mode property:
  octave_value ov_zm
    = Utils::properties<figure> (figObj).get___zoom_mode__ ();

  octave_scalar_map zm = ov_zm.scalar_map_value ();

  return zm.contents ("Enable").string_value () == "on";
}

static std::string
zoom_mode (const graphics_object figObj)
{
  // Getting zoom mode property:
  octave_value ov_zm
    = Utils::properties<figure> (figObj).get___zoom_mode__ ();

  octave_scalar_map zm = ov_zm.scalar_map_value ();

  return zm.contents ("Motion").string_value ();
}

void
Canvas::select_object (graphics_object obj, QMouseEvent* event,
                       graphics_object &currentObj, graphics_object &axesObj,
                       bool axes_only)
{
  QList<graphics_object> axesList;
  Matrix children = obj.get_properties ().get_all_children ();
  octave_idx_type num_children = children.numel ();

  for (int i = 0; i < num_children; i++)
    {
      graphics_object childObj (gh_manager::get_object (children(i)));

      if (childObj.isa ("axes"))
        axesList.append (childObj);
      else if (childObj.isa ("uicontrol") || childObj.isa ("uipanel"))
        {
          Matrix bb = childObj.get_properties ().get_boundingbox (false);
          QRectF r (bb(0), bb(1), bb(2), bb(3));

          r.adjust (-5, -5, 5, 5);
          if (r.contains (event->posF ()))
            {
              currentObj = childObj;
              break;
            }
        }
    }

  if (axes_only)
    {
      QPoint pt = event->pos ();

      for (QList<graphics_object>::ConstIterator it = axesList.begin ();
           it != axesList.end (); ++it)
        {
          const axes::properties& ap =
            dynamic_cast<const axes::properties&> ((*it).get_properties ());

          ColumnVector p0 = ap.pixel2coord (pt.x (), pt.y ());
          Matrix xlim = ap.get_xlim ().matrix_value ();
          Matrix ylim = ap.get_ylim ().matrix_value ();

          if (xlim(0) < p0(0) && xlim(1) > p0(0)
              && ylim(0) < p0(1) && ylim(1) > p0(1))
            {
              axesObj = *it;
              return;
            }
        }
    }
  else if (! currentObj)
    {
      for (QList<graphics_object>::ConstIterator it = axesList.begin ();
           it != axesList.end (); ++it)
        {
          graphics_object go = selectFromAxes (*it, event->pos ());

          if (go)
            {
              currentObj = go;
              axesObj = *it;
            }
          // FIXME: is this really necessary? the axes object should
          //        have been selected through selectFromAxes anyway
          else if (it->get_properties ().is_hittest ())
            {
              Matrix bb = it->get_properties ().get_boundingbox (true);
              QRectF r (bb(0), bb(1), bb(2), bb(3));

              if (r.contains (event->posF ()))
                axesObj = *it;
            }

          if (axesObj && currentObj)
            break;
        }
    }
}

void
Canvas::canvasMouseMoveEvent (QMouseEvent* event)
{
  gh_manager::auto_lock lock;
  graphics_object ax = gh_manager::get_object (m_mouseAxes);

  if (m_mouseMode != NoMode && (ax.valid_object () || m_mouseMode == TextMode))
    {
      switch (m_mouseMode)
        {
        case RotateMode:
          {
            axes::properties& ap = Utils::properties<axes> (ax);

            ap.rotate3d (m_mouseCurrent.x (), event->x (),
                         m_mouseCurrent.y (), event->y ());

            // Update current mouse position
            m_mouseCurrent = event->pos ();

            // Force immediate redraw
            redraw (true);
          }
          break;
        case TextMode:
        case ZoomInMode:
        case ZoomOutMode:
          m_mouseCurrent = event->pos ();
          redraw (true);
          break;

        case PanMode:
          {
            axes::properties& ap = Utils::properties<axes> (ax);

            graphics_object figObj (ax.get_ancestor ("figure"));

            std::string mode = pan_mode (figObj);

            ColumnVector p0 = ap.pixel2coord (m_mouseCurrent.x (),
                                              m_mouseCurrent.y ());
            ColumnVector p1 = ap.pixel2coord (event->x (),
                                              event->y ());

            ap.translate_view (mode, p0(0), p1(0), p0(1), p1(1));

            // Update current mouse position
            m_mouseCurrent = event->pos ();

            // Force immediate redraw
            redraw (true);
          }

        default:
          break;
        }
    }
  else if (m_mouseMode == NoMode)
    {
      graphics_object obj = gh_manager::get_object (m_handle);

      if (obj.valid_object ())
        {
          graphics_object figObj (obj.get_ancestor ("figure"));

          if (figObj.valid_object () &&
              ! figObj.get ("windowbuttonmotionfcn").is_empty ())
            {
              updateCurrentPoint (figObj, obj, event);
              gh_manager::post_callback (figObj.get_handle (),
                                         "windowbuttonmotionfcn");
            }
        }
    }

  // Update mouse coordinates in the figure window status bar
  graphics_object obj = gh_manager::get_object (m_handle);
  graphics_object figObj = obj.get_ancestor ("figure");

  if (figObj.valid_object () && obj.valid_object ())
    {
      graphics_object currentObj, axesObj;
      select_object (obj, event, currentObj, axesObj, true);

      if (axesObj.valid_object ())
        {
          // FIXME: should we use signal/slot mechanism instead of
          //        directly calling parent fig methods
          Figure* fig =
            dynamic_cast<Figure*> (Backend::toolkitObject (figObj));
          axes::properties& ap = Utils::properties<axes> (axesObj);

          if (fig)
            fig->updateStatusBar (ap.pixel2coord (event->x (), event->y ()));
        }
    }
}

void
Canvas::canvasMouseDoubleClickEvent (QMouseEvent* event)
{
  // same processing as normal click, but event type is MouseButtonDblClick
  canvasMousePressEvent (event);
}

static double
button_number (QMouseEvent *event)
{
  double retval = 0;

  switch (event->button ())
    {
    case Qt::LeftButton:
      retval = 1;
      break;

    case Qt::MidButton:
      retval = 2;
      break;

    case Qt::RightButton:
      retval = 3;
      break;

    default:
      break;
    }

  return retval;
}

void
Canvas::canvasMousePressEvent (QMouseEvent* event)
{
  gh_manager::auto_lock lock;
  graphics_object obj = gh_manager::get_object (m_handle);

  bool isdblclick = (event->type () == QEvent::MouseButtonDblClick);

  if (obj.valid_object ())
    {
      graphics_object figObj (obj.get_ancestor ("figure"));
      graphics_object currentObj, axesObj;

      select_object (obj, event, currentObj, axesObj);

      if (axesObj)
        {
          if (axesObj.get_properties ().handlevisibility_is ("on"))
            Utils::properties<figure> (figObj)
              .set_currentaxes (axesObj.get_handle ().as_octave_value ());
          if (! currentObj)
            currentObj = axesObj;
        }

      if (! currentObj)
        currentObj = obj;

      if (currentObj.get_properties ().handlevisibility_is ("on"))
        Utils::properties<figure> (figObj)
          .set_currentobject (currentObj.get_handle ().as_octave_value ());
      else
        Utils::properties<figure> (figObj).set_currentobject (octave_NaN);

      Figure* fig = dynamic_cast<Figure*> (Backend::toolkitObject (figObj));

      MouseMode newMouseMode = NoMode;

      if (fig)
        newMouseMode = fig->mouseMode ();

      switch (newMouseMode)
        {
        case NoMode:
          gh_manager::post_set (figObj.get_handle (), "selectiontype",
                                Utils::figureSelectionType (event, isdblclick), false);

          updateCurrentPoint (figObj, obj, event);

          gh_manager::post_callback (figObj.get_handle (),
                                     "windowbuttondownfcn",
                                     button_number (event));

          if (currentObj.get ("buttondownfcn").is_empty ())
            {
              graphics_object parentObj =
                gh_manager::get_object (currentObj.get_parent ());

              if (parentObj.valid_object () && parentObj.isa ("hggroup"))
                gh_manager::post_callback (parentObj.get_handle (),
                                           "buttondownfcn",
                                           button_number (event));
            }
          else
            gh_manager::post_callback (currentObj.get_handle (),
                                       "buttondownfcn",
                                       button_number (event));

          if (event->button () == Qt::RightButton)
            ContextMenu::executeAt (currentObj.get_properties (),
                                    event->globalPos ());
          break;

        case TextMode:
          {
            if (event->modifiers () == Qt::NoModifier)
              {
                switch (event->buttons ())
                  {
                  case Qt::LeftButton:
                    m_mouseAnchor = m_mouseCurrent = event->pos ();
                    m_mouseMode = newMouseMode;
                    m_rectMode = true;
                  }
              }
            redraw (false);
          }
          break;

        case PanMode:
        case RotateMode:
        case ZoomInMode:
        case ZoomOutMode:
          if (axesObj && axesObj.get_properties ().handlevisibility_is ("on"))
            {
              bool redraw_figure = true;

              if (isdblclick)
                {
                  if (event->button () == Qt::LeftButton)
                    {
                      axes::properties& ap = Utils::properties<axes> (axesObj);

                      autoscale_axes (ap);
                    }
                  else
                    {
                      redraw_figure = false;
                    }
                }
              else if (event->modifiers () == Qt::NoModifier)
                {
                  switch (event->buttons ())
                    {
                    case Qt::LeftButton:
                      m_mouseAnchor = m_mouseCurrent = event->pos ();
                      m_mouseAxes = axesObj.get_handle ();
                      m_mouseMode = newMouseMode;
                      m_clickMode = newMouseMode == ZoomInMode;
                      break;

                    case Qt::RightButton:
                      if (newMouseMode == ZoomInMode)
                        {
                          m_mouseAnchor = m_mouseCurrent = event->pos ();
                          m_mouseAxes = axesObj.get_handle ();
                          m_mouseMode = newMouseMode;
                          m_clickMode = false;
                        }

                      break;

                    case Qt::MidButton:
                        {
                          axes::properties& ap =
                            Utils::properties<axes> (axesObj);

                          autoscale_axes (ap);
                        }
                      break;

                    default:
                      redraw_figure = false;
                      break;
                    }
                }
              else if (event->modifiers () == Qt::ShiftModifier)
                {
                  switch (event->buttons ())
                    {
                    case Qt::LeftButton:
                      if (newMouseMode == ZoomInMode)
                        {
                          m_mouseAnchor = m_mouseCurrent = event->pos ();
                          m_mouseAxes = axesObj.get_handle ();
                          m_mouseMode = newMouseMode;
                          m_clickMode = false;
                        }
                      break;

                    default:
                      redraw_figure = false;
                      break;
                    }
                }

              if (redraw_figure)
                redraw (false);
            }
          break;

        default:
          break;
        }
    }

}

void
Canvas::canvasMouseReleaseEvent (QMouseEvent* event)
{
  if ((m_mouseMode == ZoomInMode || m_mouseMode == ZoomOutMode)
      && m_mouseAxes.ok ())
    {
      gh_manager::auto_lock lock;
      graphics_object ax = gh_manager::get_object (m_mouseAxes);

      if (ax.valid_object ())
        {
          axes::properties& ap = Utils::properties<axes> (ax);

          graphics_object obj = gh_manager::get_object (m_handle);

          graphics_object figObj (obj.get_ancestor ("figure"));

          std::string zm = zoom_mode (figObj);

          if (m_mouseAnchor == event->pos ())
            {
              double factor = m_clickMode ? 2.0 : 0.5;

              ColumnVector p1 = ap.pixel2coord (event->x (), event->y ());

              ap.zoom_about_point (zm, p1(0), p1(1), factor);
            }
          else if (m_mouseMode == ZoomInMode)
            {
              ColumnVector p0 = ap.pixel2coord (m_mouseAnchor.x (),
                                                m_mouseAnchor.y ());
              ColumnVector p1 = ap.pixel2coord (event->x (),
                                                event->y ());

              Matrix xl (1, 2, 0.0);
              Matrix yl (1, 2, 0.0);

              xl(0) = std::min (p0(0), p1(0));
              xl(1) = std::max (p0(0), p1(0));
              yl(0) = std::min (p0(1), p1(1));
              yl(1) = std::max (p0(1), p1(1));

              ap.zoom (zm, xl, yl);
            }

          redraw (false);
        }
    }
  else if (m_mouseMode == NoMode)
    {
      gh_manager::auto_lock lock;
      graphics_object obj = gh_manager::get_object (m_handle);

      if (obj.valid_object ())
        {
          graphics_object figObj (obj.get_ancestor ("figure"));

          updateCurrentPoint (figObj, obj, event);
          gh_manager::post_callback (figObj.get_handle (),
                                     "windowbuttonupfcn");
        }
    }
  else if (m_mouseMode == TextMode)
    {
      gh_manager::auto_lock lock;

      graphics_object figObj =
        gh_manager::get_object (m_handle).get_ancestor ("figure");
      if (figObj.valid_object ())
        {
          QWidget *w = qWidget ();
          if (w)
            {
              Matrix bb = figObj.get ("position").matrix_value ();
              bb(0) = m_mouseAnchor.x () / bb(2);
              bb(1) = 1.0 - (m_mouseAnchor.y () / bb(3));
              bb(2) = (event->x () - m_mouseAnchor.x ()) / bb(2);
              bb(3) = (m_mouseAnchor.y () - event->y ()) / bb(3);

              octave_value_list props = ovl ("textbox", bb);

              annotation_dialog anno_dlg (w, props);

              if (anno_dlg.exec () == QDialog::Accepted)
                {
                  props = anno_dlg.get_properties ();
                  props.prepend (figObj.get_handle ().as_octave_value ());

                  octave_link::post_event (this, &Canvas::annotation_callback,
                                           props);
                }
            }
        }
    }
  m_rectMode = false;
  m_mouseAxes = graphics_handle ();
  m_mouseMode = NoMode;
}

void
Canvas::canvasWheelEvent (QWheelEvent* event)
{
  gh_manager::auto_lock lock;
  graphics_object obj = gh_manager::get_object (m_handle);

  if (obj.valid_object ())
    {
      std::string mode;

      graphics_object axesObj;

      Matrix children = obj.get_properties ().get_children ();
      octave_idx_type num_children = children.numel ();

      for (int i = 0; i < num_children; i++)
        {
          graphics_object childObj (gh_manager::get_object (children(i)));

          if (childObj.isa ("axes"))
            {
              graphics_object go = selectFromAxes (childObj, event->pos ());

              if (go)
                {
                  axesObj = childObj;
                  break;
                }
            }
        }

      if (axesObj)
        {
          MouseMode newMouseMode = NoMode;

          graphics_object figObj (obj.get_ancestor ("figure"));

          Figure* fig = dynamic_cast<Figure*> (Backend::toolkitObject (figObj));

          if (fig)
            newMouseMode = fig->mouseMode ();

          if (axesObj.get_properties ().handlevisibility_is ("on"))
            {
              Utils::properties<figure> (figObj)
                .set_currentaxes (axesObj.get_handle ().as_octave_value ());

              if (zoom_enabled (figObj))
                {
                  if (event->delta () > 0)
                    newMouseMode = ZoomInMode;
                  else
                    newMouseMode = ZoomOutMode;

                  mode = zoom_mode (figObj);
                }
              else if (pan_enabled (figObj))
                {
                  newMouseMode = PanMode;

                  mode = pan_mode (figObj);
                }
            }

          bool redrawFigure = true;

          switch (newMouseMode)
            {
            case ZoomInMode:
            case ZoomOutMode:
              {
                axes::properties& ap = Utils::properties<axes> (axesObj);

                // Control how fast to zoom when using scroll wheel.
                double wheel_zoom_speed = ap.get_mousewheelzoom ();

                // Determine if we're zooming in or out.
                double factor = (newMouseMode == ZoomInMode
                                 ? 1 / (1.0 - wheel_zoom_speed)
                                 : 1.0 - wheel_zoom_speed);

                // FIXME: should we zoom about point for 2D plots?

                ap.zoom (mode, factor);
              }
              break;

            case PanMode:
              {
                axes::properties& ap = Utils::properties<axes> (axesObj);

                double factor = event->delta () > 0 ? 0.1 : -0.1;

                ap.pan (mode, factor);
              }
              break;

            default:
              redrawFigure = false;
              break;
            }

          if (redrawFigure)
            redraw (false);
        }
    }
}

bool
Canvas::canvasKeyPressEvent (QKeyEvent* event)
{
  if (m_eventMask & KeyPress)
    {
      gh_manager::auto_lock lock;
      graphics_object obj = gh_manager::get_object (m_handle);

      if (obj.valid_object ())
        {
          graphics_object figObj (obj.get_ancestor ("figure"));

          updateCurrentPoint (figObj, obj);
        }

      octave_scalar_map eventData = Utils::makeKeyEventStruct (event);

      gh_manager::post_set (m_handle, "currentcharacter",
                            eventData.getfield ("Character"), false);
      gh_manager::post_callback (m_handle, "keypressfcn", eventData);

      return true;
    }

  return false;
}

bool
Canvas::canvasKeyReleaseEvent (QKeyEvent* event)
{
  if (! event->isAutoRepeat () && (m_eventMask & KeyRelease))
    {
      gh_manager::post_callback (m_handle, "keyreleasefcn",
                                 Utils::makeKeyEventStruct (event));

      return true;
    }

  return false;
}

Canvas*
Canvas::create (const std::string& /* name */, QWidget* parent,
                const graphics_handle& handle)
{
  // Only OpenGL
  return new GLCanvas (parent, handle);
}

}; // namespace QtHandles