/*************************************************************************** * Copyright (c) 2005 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ # include # include # include # include # include # include # include # include # include # include # include #endif #include #include "MouseSelection.h" #include "View3DInventor.h" #include "View3DInventorViewer.h" #include "GLPainter.h" using namespace Gui; AbstractMouseSelection::AbstractMouseSelection() : _pcView3D(0) { m_bInner = true; mustRedraw = false; } void AbstractMouseSelection::grabMouseModel( Gui::View3DInventorViewer* viewer ) { _pcView3D = viewer; m_cPrevCursor = _pcView3D->getWidget()->cursor(); m_antiAliasing = (int)_pcView3D->getAntiAliasingMode(); _pcView3D->setAntiAliasingMode(View3DInventorViewer::None); // do initialization of your mousemodel initialize(); } void AbstractMouseSelection::releaseMouseModel() { if (_pcView3D) { // do termination of your mousemodel terminate(); _pcView3D->getWidget()->setCursor(m_cPrevCursor); _pcView3D->setAntiAliasingMode(View3DInventorViewer::AntiAliasing(m_antiAliasing)); _pcView3D = 0; } } void AbstractMouseSelection::redraw() { // Note: For any reason it does not work to do a redraw in the actualRedraw() method of the // viewer class. So, we do the redraw when the user continues moving the cursor. E.g. have // a look to PolyPickerSelection::draw() mustRedraw = true; } int AbstractMouseSelection::handleEvent(const SoEvent * const ev, const SbViewportRegion& vp) { int ret=Continue; const SbVec2s& sz = vp.getWindowSize(); short w,h; sz.getValue(w,h); SbVec2s loc = ev->getPosition(); short x,y; loc.getValue(x,y); y = h-y; // the origin is at the left bottom corner (instead of left top corner) if (ev->getTypeId().isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) { const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev; const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; if (press) { _clPoly.push_back(ev->getPosition()); ret = mouseButtonEvent(static_cast(ev), QPoint(x,y)); } else { ret = mouseButtonEvent(static_cast(ev), QPoint(x,y)); } } else if (ev->getTypeId().isDerivedFrom(SoLocation2Event::getClassTypeId())) { ret = locationEvent(static_cast(ev), QPoint(x,y)); } else if (ev->getTypeId().isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { ret = keyboardEvent(static_cast(ev)); } if (ret == Restart) _clPoly.clear(); return ret; } // ----------------------------------------------------------------------------------- BaseMouseSelection::BaseMouseSelection() : AbstractMouseSelection() { } // ----------------------------------------------------------------------------------- #if 0 /* XPM */ static const char *cursor_polypick[]={ "32 32 2 1", "# c #646464", ". c None", "................................", "................................", ".......#........................", ".......#........................", ".......#........................", "................................", ".......#........................", "..###.###.###...................", ".......#...............#........", "......................##........", ".......#..............#.#.......", ".......#.............#..#.......", ".......#............#...#.......", "....................#....#......", "...................#.....#......", "..................#......#......", "............#.....#.......#.....", "...........#.##..#........#.....", "..........#....##.........#.....", ".........#...............#......", "........#................#......", ".......#................#.......", "......#.................#.......", ".....#.................#........", "....#####..............#........", ".........#########....#.........", "..................#####.........", "................................", "................................", "................................", "................................", "................................"}; /* XPM */ static const char *cursor_scissors[]={ "32 32 3 1", "# c #000000", "+ c #ffffff", ". c None", "....+...........................", "....+...........................", "....+...........................", "................................", "+++.+.+++.......................", "................................", "....+...........................", "....+...................#####...", "....+.................########..", ".....................#########..", ".....###............##########..", "....##++##.........#####...###..", "...#++++++##.......####...####..", "...##+++++++#......####.######..", ".....#+++++++##....##########...", "......##+++++++##.##########....", "........##+++++++#########......", "..........#+++++++#####.........", "...........##+++++####..........", "...........##+++++###...........", ".........##+++++++########......", "........##+++++++###########....", "......##+++++++##.###########...", "....##+++++++##....##########...", "...#+++++++##......####..#####..", "...#++++++#........#####..####..", "....##++##..........#####..###..", "......#.............##########..", ".....................#########..", ".......................######...", "................................", "................................"}; #endif static const char *cursor_cut_scissors[]={ "32 32 6 1", "a c #800000", "c c #808080", "+ c #c0c0c0", "b c #ff0000", "# c #ffffff", ". c None", "....#...........................", "....#...........................", "....#...........................", "................................", "###.#.###.......................", "................................", "....#...........................", "....#...................aaaaa...", "....#.................aabbbbba..", ".....................abbbbbbba..", ".....ccc............abbaaaaabb..", "....cc++cc.........babaa...aba..", "...c+#++++cc.......abba...abba..", "...cc+#+++++c......abba.aabbaa..", ".....c+++++#+cc....abbaaabbaa...", "......cc+#+++#+cc.aabbbbbbaa....", "........cc+#+++#+cabbbaaaa......", "..........c+++++++abbaa.........", "...........cc+++#+aaaa..........", "...........cc+#+++caa...........", ".........cc+++++#+cbbaaaaa......", "........cc+#+++#+cabbabbbaaa....", "......cc+#+++#+cc.aaabbbbbbaa...", "....cc+#+++#+cc....abbaaaabba...", "...c++#++#+cc......abba..aabba..", "...c+###++c........aabaa..aaba..", "....cc++cc..........abbaa..aba..", "......c.............aabbaaaaba..", ".....................baabbbbba..", ".......................aaaaaa...", "................................", "................................"}; PolyPickerSelection::PolyPickerSelection() { m_iRadius = 2; m_iNodes = 0; m_bWorking = false; } void PolyPickerSelection::initialize() { QPixmap p(cursor_cut_scissors); QCursor cursor(p, 4, 4); _pcView3D->getWidget()->setCursor(cursor); } void PolyPickerSelection::terminate() { // _pcView3D->getGLWidget()->releaseMouse(); } void PolyPickerSelection::draw () { if (mustRedraw){ if (_cNodeVector.size() > 1) { QPoint start = _cNodeVector.front(); GLPainter p; p.begin(_pcView3D); p.setColor(1.0f,1.0f,1.0f); p.setLogicOp(GL_XOR); for (std::vector::iterator it = _cNodeVector.begin()+1; it != _cNodeVector.end(); ++it) { p.drawLine(start.x(),start.y(),it->x(), it->y()); start = *it; } p.end(); } // recursive call, but no infinite loop mustRedraw = false; draw(); } if (m_bWorking) { if (m_iNodes < int(_cNodeVector.size())) { m_iNodes = int(_cNodeVector.size()); if (_cNodeVector.size() > 2) { QPoint start = _cNodeVector.front(); GLPainter p; p.begin(_pcView3D); p.setColor(1.0f,1.0f,1.0f); p.setLogicOp(GL_XOR); p.drawLine(m_iXnew,m_iYnew,start.x(), start.y()); p.end(); } } else { GLPainter p; p.begin(_pcView3D); p.setColor(1.0f,1.0f,1.0f); p.setLogicOp(GL_XOR); p.drawLine(m_iXnew,m_iYnew,m_iXold,m_iYold); if (_cNodeVector.size() > 1) { QPoint start = _cNodeVector.front(); p.drawLine(m_iXnew,m_iYnew,start.x(), start.y()); } p.end(); } } } PolyPickerSelection::~PolyPickerSelection() { } int PolyPickerSelection::popupMenu() { QMenu menu; QAction* fi = menu.addAction(QObject::tr("Finish")); menu.addAction(QObject::tr("Clear")); QAction* ca = menu.addAction(QObject::tr("Cancel")); if (getPositions().size() < 3) fi->setEnabled(false); QAction* id = menu.exec(QCursor::pos()); if (id == fi) return Finish; else if (id == ca) return Cancel; else return Restart; } int PolyPickerSelection::mouseButtonEvent( const SoMouseButtonEvent * const e, const QPoint& pos ) { const int button = e->getButton(); const SbBool press = e->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; if (press) { switch (button) { case SoMouseButtonEvent::BUTTON1: { // start working from now on if (!m_bWorking) { m_bWorking = true; // clear the old polygon _cNodeVector.clear(); _pcView3D->getGLWidget()->update(); // _pcView3D->getGLWidget()->grabMouse(); } _cNodeVector.push_back(pos); m_iXnew = pos.x(); m_iYnew = pos.y(); m_iXold = pos.x(); m_iYold = pos.y(); } break; case SoMouseButtonEvent::BUTTON2: { if (_cNodeVector.size() > 0) { if (_cNodeVector.back() != pos) _cNodeVector.push_back(pos); m_iXnew = pos.x(); m_iYnew = pos.y(); m_iXold = pos.x(); m_iYold = pos.y(); } } break; default: { } break; } } // release else { switch (button) { case SoMouseButtonEvent::BUTTON2: { QCursor cur = _pcView3D->getWidget()->cursor(); _pcView3D->getWidget()->setCursor(m_cPrevCursor); // The pop-up menu should be shown when releasing mouse button because // otherwise the navigation style doesn't get the UP event and gets into // an inconsistent state. int id = popupMenu(); if (id == Finish || id == Cancel) { releaseMouseModel(); } else if (id == Restart) { m_bWorking = false; m_iNodes = 0; _pcView3D->getWidget()->setCursor(cur); } return id; } break; default: { } break; } } return Continue; } int PolyPickerSelection::locationEvent( const SoLocation2Event * const e, const QPoint& pos ) { // do all the drawing stuff for us QPoint clPoint = pos; if (m_bWorking) { // check the position QRect r = _pcView3D->getGLWidget()->rect(); if (!r.contains(clPoint)) { if (clPoint.x() < r.left()) clPoint.setX( r.left()); if (clPoint.x() > r.right()) clPoint.setX(r.right()); if (clPoint.y() < r.top()) clPoint.setY(r.top()); if (clPoint.y() > r.bottom()) clPoint.setY(r.bottom()); #ifdef FC_OS_WINDOWS QPoint newPos = _pcView3D->getGLWidget()->mapToGlobal(clPoint); QCursor::setPos(newPos); #endif } } draw(); m_iXnew = clPoint.x(); m_iYnew = clPoint.y(); draw(); return Continue; } int PolyPickerSelection::keyboardEvent( const SoKeyboardEvent * const e ) { return Continue; } // ----------------------------------------------------------------------------------- PolyClipSelection::PolyClipSelection() { } PolyClipSelection::~PolyClipSelection() { } int PolyClipSelection::popupMenu() { QMenu menu; QAction* ci = menu.addAction(QObject::tr("Inner")); QAction* co = menu.addAction(QObject::tr("Outer")); QAction* ca = menu.addAction(QObject::tr("Cancel")); if (getPositions().size() < 3) { ci->setEnabled(false); co->setEnabled(false); } QAction* id = menu.exec(QCursor::pos()); if (id == ci) { m_bInner = true; return Finish; } else if (id == co) { m_bInner = false; return Finish; } else if (id == ca) return Cancel; else return Restart; } // ----------------------------------------------------------------------------------- BrushSelection::BrushSelection() : r(1.0f), g(0.0f), b(0.0f), a(0.0f), l(2.0f) { m_iNodes = 0; m_bWorking = false; m_bClose = false; } void BrushSelection::initialize() { QPixmap p(cursor_cut_scissors); QCursor cursor(p, 4, 4); _pcView3D->getWidget()->setCursor(cursor); } void BrushSelection::terminate() { } void BrushSelection::setColor(float r, float g, float b, float a) { this->r = r; this->g = g; this->b = b; this->a = a; } void BrushSelection::setLineWidth(float l) { this->l = l; } void BrushSelection::setClosed(bool on) { this->m_bClose = on; } void BrushSelection::draw () { if (mustRedraw){ if (_cNodeVector.size() > 1) { QPoint start = _cNodeVector.front(); GLPainter p; p.begin(_pcView3D); p.setLineWidth(this->l); p.setColor(this->r, this->g, this->b, this->a); for (std::vector::iterator it = _cNodeVector.begin()+1; it != _cNodeVector.end(); ++it) { p.drawLine(start.x(),start.y(),it->x(), it->y()); start = *it; } p.end(); } // recursive call, but no infinite loop mustRedraw = false; draw(); } if (m_bWorking) { GLPainter p; p.begin(_pcView3D); p.setLineWidth(this->l); p.setColor(this->r, this->g, this->b, this->a); if (m_bClose && !_cNodeVector.empty()) { // We have to redraw the whole polyline when using XOR because otherwise the curve looks 'dirty'. QPoint start = _cNodeVector.front(); for (std::vector::iterator it = _cNodeVector.begin()+1; it != _cNodeVector.end(); ++it) { p.drawLine(start.x(),start.y(),it->x(), it->y()); start = *it; } start = _cNodeVector.front(); p.setLineStipple(2, 0x3F3F); p.setLogicOp(GL_XOR); p.drawLine(m_iXold, m_iYold, start.x(),start.y()); p.drawLine(m_iXnew, m_iYnew, start.x(),start.y()); p.resetLogicOp(); p.resetLineStipple(); } p.drawLine(m_iXnew, m_iYnew, m_iXold, m_iYold); p.end(); } } BrushSelection::~BrushSelection() { } int BrushSelection::popupMenu() { QMenu menu; QAction* fi = menu.addAction(QObject::tr("Finish")); menu.addAction(QObject::tr("Clear")); QAction* ca = menu.addAction(QObject::tr("Cancel")); if (getPositions().size() < 3) fi->setEnabled(false); QAction* id = menu.exec(QCursor::pos()); if (id == fi) return Finish; else if (id == ca) return Cancel; else return Restart; } int BrushSelection::mouseButtonEvent(const SoMouseButtonEvent * const e, const QPoint& pos) { const int button = e->getButton(); const SbBool press = e->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; if (press) { switch (button) { case SoMouseButtonEvent::BUTTON1: { // start working from now on if (!m_bWorking) { m_bWorking = true; // clear the old polygon _cNodeVector.clear(); _pcView3D->getGLWidget()->update(); _cNodeVector.push_back(pos); m_iXnew = pos.x(); m_iYnew = pos.y(); m_iXold = pos.x(); m_iYold = pos.y(); } } break; case SoMouseButtonEvent::BUTTON2: { if (_cNodeVector.size() > 0) { if (_cNodeVector.back() != pos) _cNodeVector.push_back(pos); m_iXnew = pos.x(); m_iYnew = pos.y(); m_iXold = pos.x(); m_iYold = pos.y(); } } break; default: { } break; } } // release else { switch (button) { case SoMouseButtonEvent::BUTTON1: return Finish; case SoMouseButtonEvent::BUTTON2: { QCursor cur = _pcView3D->getWidget()->cursor(); _pcView3D->getWidget()->setCursor(m_cPrevCursor); // The pop-up menu should be shown when releasing mouse button because // otherwise the navigation style doesn't get the UP event and gets into // an inconsistent state. int id = popupMenu(); if (id == Finish || id == Cancel) { releaseMouseModel(); } else if (id == Restart) { m_bWorking = false; m_iNodes = 0; _pcView3D->getWidget()->setCursor(cur); } return id; } break; default: { } break; } } return Continue; } int BrushSelection::locationEvent(const SoLocation2Event * const e, const QPoint& pos) { // do all the drawing stuff for us QPoint clPoint = pos; if (m_bWorking) { // check the position QRect r = _pcView3D->getGLWidget()->rect(); if (!r.contains(clPoint)) { if (clPoint.x() < r.left()) clPoint.setX( r.left()); if (clPoint.x() > r.right()) clPoint.setX(r.right()); if (clPoint.y() < r.top()) clPoint.setY(r.top()); if (clPoint.y() > r.bottom()) clPoint.setY(r.bottom()); } SbVec2s last = _clPoly.back(); SbVec2s curr = e->getPosition(); if (abs(last[0]-curr[0]) > 20 || abs(last[1]-curr[1]) > 20) _clPoly.push_back(curr); _cNodeVector.push_back(clPoint); } m_iXnew = clPoint.x(); m_iYnew = clPoint.y(); draw(); m_iXold = clPoint.x(); m_iYold = clPoint.y(); return Continue; } int BrushSelection::keyboardEvent( const SoKeyboardEvent * const e ) { return Continue; } // ----------------------------------------------------------------------------------- RectangleSelection::RectangleSelection() { m_bWorking = false; } RectangleSelection::~RectangleSelection() { } void RectangleSelection::initialize() { } void RectangleSelection::terminate() { } void RectangleSelection::draw () { if (m_bWorking) { GLPainter p; p.begin(_pcView3D); p.setColor(1.0, 1.0, 0.0, 0.0); p.setLogicOp(GL_XOR); p.setLineWidth(3.0f); p.setLineStipple(2, 0x3F3F); p.drawRect(m_iXold, m_iYold, m_iXnew, m_iYnew); p.end(); } } int RectangleSelection::mouseButtonEvent( const SoMouseButtonEvent * const e, const QPoint& pos ) { const int button = e->getButton(); const SbBool press = e->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; int ret = Continue; if (press) { switch ( button ) { case SoMouseButtonEvent::BUTTON1: { m_bWorking = true; m_iXold = m_iXnew = pos.x(); m_iYold = m_iYnew = pos.y(); } break; default: { } break; } } else { switch (button) { case SoMouseButtonEvent::BUTTON1: { releaseMouseModel(); m_bWorking = false; _clPoly.push_back(e->getPosition()); ret = Finish; } break; default: { } break; } } return ret; } int RectangleSelection::locationEvent( const SoLocation2Event * const e, const QPoint& pos ) { draw(); m_iXnew = pos.x(); m_iYnew = pos.y(); draw(); return Continue; } int RectangleSelection::keyboardEvent( const SoKeyboardEvent * const e ) { return Continue; } // ----------------------------------------------------------------------------------- class RubberbandSelection::Private : public Gui::GLGraphicsItem { Gui::View3DInventorViewer* viewer; int x_old, y_old, x_new, y_new; bool working; public: Private(Gui::View3DInventorViewer* v) : viewer(v) { x_old = y_old = x_new = y_new = 0; working = false; } ~Private() { } void setWorking(bool on) { working = on; } void setCoords(int x1, int y1, int x2, int y2) { x_old = x1; y_old = y1; x_new = x2; y_new = y2; } void paintGL() { if (!working) return; const SbViewportRegion vp = viewer->getViewportRegion(); SbVec2s size = vp.getViewportSizePixels(); glMatrixMode(GL_PROJECTION); glOrtho(0, size[0], size[1], 0, 0, 100); glMatrixMode(GL_MODELVIEW); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glLineWidth(4.0); glColor4f(1.0f, 1.0f, 1.0f, 0.2f); glRecti(x_old, y_old, x_new, y_new); glColor4f(1.0, 1.0, 0.0, 0.5); glLineStipple(3, 0xAAAA); glEnable(GL_LINE_STIPPLE); glBegin(GL_LINE_LOOP); glVertex2i(x_old, y_old); glVertex2i(x_new, y_old); glVertex2i(x_new, y_new); glVertex2i(x_old, y_new); glEnd(); glLineWidth(1.0); glDisable(GL_LINE_STIPPLE); glDisable(GL_BLEND); } }; RubberbandSelection::RubberbandSelection() { d = 0; } RubberbandSelection::~RubberbandSelection() { } void RubberbandSelection::initialize() { d = new Private(_pcView3D); _pcView3D->addGraphicsItem(d); if (QGLFramebufferObject::hasOpenGLFramebufferObjects()) { _pcView3D->setRenderFramebuffer(true); } _pcView3D->scheduleRedraw(); } void RubberbandSelection::terminate() { _pcView3D->removeGraphicsItem(d); delete d; d = 0; if (QGLFramebufferObject::hasOpenGLFramebufferObjects()) { _pcView3D->setRenderFramebuffer(false); } _pcView3D->scheduleRedraw(); } void RubberbandSelection::draw () { } int RubberbandSelection::mouseButtonEvent(const SoMouseButtonEvent * const e, const QPoint& pos) { const int button = e->getButton(); const SbBool press = e->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; int ret = Continue; if (press) { switch (button) { case SoMouseButtonEvent::BUTTON1: { d->setWorking(true); m_iXold = m_iXnew = pos.x(); m_iYold = m_iYnew = pos.y(); } break; default: { } break; } } else { switch (button) { case SoMouseButtonEvent::BUTTON1: { d->setWorking(false); releaseMouseModel(); _clPoly.push_back(e->getPosition()); ret = Finish; } break; default: { } break; } } return ret; } int RubberbandSelection::locationEvent(const SoLocation2Event * const e, const QPoint& pos) { m_iXnew = pos.x(); m_iYnew = pos.y(); d->setCoords(m_iXold, m_iYold, m_iXnew, m_iYnew); _pcView3D->render(); return Continue; } int RubberbandSelection::keyboardEvent(const SoKeyboardEvent * const e) { return Continue; } // ----------------------------------------------------------------------------------- BoxZoomSelection::BoxZoomSelection() { } BoxZoomSelection::~BoxZoomSelection() { } void BoxZoomSelection::terminate() { RubberbandSelection::terminate(); int xmin = std::min(m_iXold, m_iXnew); int xmax = std::max(m_iXold, m_iXnew); int ymin = std::min(m_iYold, m_iYnew); int ymax = std::max(m_iYold, m_iYnew); SbBox2s box(xmin, ymin, xmax, ymax); _pcView3D->boxZoom(box); }