Mercurial > octave-nkf
view libgui/graphics/Canvas.cc @ 20044:f2dea119369e
Qt Canvas: set selection type to 'open' on double click (Bug #44669)
* libgui/graphics/Canvas.cc
(canvasMouseDoubleClickEvent): move autoscale if left mouse and zoom,pan.rotate mode call to canvasMousePressEvent
and call canvasMousePressEvent on double click.
(canvasMousePressEvent) set isdlbclick flag if even type was MouseDlbClickEvent, pass doubleclick to figureSelectionType calls,
and if double click on left mouse occured in zoom,rotate,pan mode, then autoscale.
author | John Donoghue |
---|---|
date | Wed, 01 Apr 2015 15:16:12 -0400 |
parents | c3a40003aa42 |
children | 055ad6fbc910 |
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 <QList> #include <QMouseEvent> #include <QWheelEvent> #include <QRectF> #include "Backend.h" #include "Canvas.h" #include "ContextMenu.h" #include "GLCanvas.h" #include "QtHandlesUtils.h" #include "gl2ps-renderer.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 ()); } } 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::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); if (ap.handlevisibility_is ("on") && ap.is_visible ()) { 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); if (ap.handlevisibility_is ("on") && ap.is_visible ()) { 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 ()) 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 rotate_enabled (const graphics_object figObj) { // Getting rotate mode property: octave_value ov_rm = Utils::properties<figure> (figObj).get___rotate_mode__ (); octave_scalar_map rm = ov_rm.scalar_map_value (); return rm.contents ("Enable").string_value () == "on"; } 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 (); } static std::string zoom_direction (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 ("Direction").string_value (); } 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 ()) { axes::properties& ap = Utils::properties<axes> (ax); switch (m_mouseMode) { case RotateMode: { 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 ZoomInMode: case ZoomOutMode: m_mouseCurrent = event->pos (); redraw (true); break; case PanMode: { 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")); updateCurrentPoint (figObj, obj, event); gh_manager::post_callback (figObj.get_handle (), "windowbuttonmotionfcn"); } } } void Canvas::canvasMouseDoubleClickEvent (QMouseEvent* event) { // same processing as nornal 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; QList<graphics_object> axesList; 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")) 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 (! 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) break; } 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)); 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: // Handle text insertion here. break; case PanMode: case RotateMode: case ZoomInMode: case ZoomOutMode: if (axesObj) { 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; break; case Qt::RightButton: Utils::properties<axes> (axesObj).unzoom (); 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: Utils::properties<axes> (axesObj).unzoom (); 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_mouseMode == ZoomInMode ? 2.0 : 0.5; ColumnVector p1 = ap.pixel2coord (event->x (), event->y ()); ap.zoom_about_point (zm, p1(0), p1(1), factor); } else { 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"); } } 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) { 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