1079 lines
29 KiB
C++
1079 lines
29 KiB
C++
/****************************************************************************
|
|
**
|
|
** This file is part of the LibreCAD project, a 2D CAD program
|
|
**
|
|
** Copyright (C) 2010-2011 R. van Twisk (librecad@rvt.dds.nl)
|
|
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
|
|
**
|
|
**
|
|
** This file may be distributed and/or modified under the terms of the
|
|
** GNU General Public License version 2 as published by the Free Software
|
|
** Foundation and appearing in the file gpl-2.0.txt included in the
|
|
** packaging of this file.
|
|
**
|
|
** This program 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 this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
**
|
|
** This copyright notice MUST APPEAR in all copies of the script!
|
|
**
|
|
**********************************************************************/
|
|
|
|
#include "qg_graphicview.h"
|
|
|
|
#include <QGridLayout>
|
|
#include <QLabel>
|
|
#include <QMenu>
|
|
#include <QDebug>
|
|
#include <QNativeGestureEvent>
|
|
|
|
#include "rs_actionzoomin.h"
|
|
#include "rs_actionzoompan.h"
|
|
#include "rs_actionzoomscroll.h"
|
|
#include "rs_actionzoomauto.h"
|
|
#include "rs_actionmodifydelete.h"
|
|
#include "rs_actionselectsingle.h"
|
|
#include "rs_settings.h"
|
|
#include "rs_painterqt.h"
|
|
#include "rs_dialogfactory.h"
|
|
#include "qg_dialogfactory.h"
|
|
#include "rs_eventhandler.h"
|
|
#include "rs_actiondefault.h"
|
|
|
|
|
|
#include "qg_scrollbar.h"
|
|
#include "rs_modification.h"
|
|
#include "rs_debug.h"
|
|
#include "rs_graphic.h"
|
|
|
|
#ifdef Q_OS_WIN32
|
|
#define CURSOR_SIZE 16
|
|
#else
|
|
#define CURSOR_SIZE 15
|
|
#endif
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
QG_GraphicView::QG_GraphicView(QWidget* parent, Qt::WindowFlags f, RS_Document* doc)
|
|
:RS_GraphicView(parent, f)
|
|
,device("Mouse")
|
|
,curCad(new QCursor(QPixmap(":ui/cur_cad_bmp.png"), CURSOR_SIZE, CURSOR_SIZE))
|
|
,curDel(new QCursor(QPixmap(":ui/cur_del_bmp.png"), CURSOR_SIZE, CURSOR_SIZE))
|
|
,curSelect(new QCursor(QPixmap(":ui/cur_select_bmp.png"), CURSOR_SIZE, CURSOR_SIZE))
|
|
,curMagnifier(new QCursor(QPixmap(":ui/cur_glass_bmp.png"), CURSOR_SIZE, CURSOR_SIZE))
|
|
,curHand(new QCursor(QPixmap(":ui/cur_hand_bmp.png"), CURSOR_SIZE, CURSOR_SIZE))
|
|
,redrawMethod(RS2::RedrawAll)
|
|
,isSmoothScrolling(false)
|
|
{
|
|
RS_DEBUG->print("QG_GraphicView::QG_GraphicView()..");
|
|
|
|
if (doc)
|
|
{
|
|
setContainer(doc);
|
|
doc->setGraphicView(this);
|
|
setDefaultAction(new RS_ActionDefault(*doc, *this));
|
|
}
|
|
|
|
setFactorX(4.0);
|
|
setFactorY(4.0);
|
|
setBorders(10, 10, 10, 10);
|
|
|
|
setMouseTracking(true);
|
|
setFocusPolicy(Qt::NoFocus);
|
|
|
|
// SourceForge issue 45 (Left-mouse drag shrinks window)
|
|
setAttribute(Qt::WA_NoMousePropagation);
|
|
|
|
view_rect = LC_Rect(toGraph(0, 0), toGraph(getWidth(), getHeight()));
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Destructor
|
|
*/
|
|
QG_GraphicView::~QG_GraphicView() {
|
|
cleanUp();
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @return width of widget.
|
|
*/
|
|
int QG_GraphicView::getWidth() const
|
|
{
|
|
if (scrollbars)
|
|
return width() - vScrollBar->sizeHint().width();
|
|
else
|
|
return width();
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @return height of widget.
|
|
*/
|
|
int QG_GraphicView::getHeight() const
|
|
{
|
|
if (scrollbars)
|
|
return height() - hScrollBar->sizeHint().height();
|
|
else
|
|
return height();
|
|
}
|
|
|
|
|
|
/**
|
|
* Changes the current background color of this view.
|
|
*/
|
|
void QG_GraphicView::setBackground(const RS_Color& bg) {
|
|
RS_GraphicView::setBackground(bg);
|
|
|
|
QPalette palette;
|
|
palette.setColor(backgroundRole(), bg);
|
|
setPalette(palette);
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the mouse cursor to the given type.
|
|
*/
|
|
void QG_GraphicView::setMouseCursor(RS2::CursorType c) {
|
|
|
|
switch (c) {
|
|
default:
|
|
case RS2::ArrowCursor:
|
|
setCursor(Qt::ArrowCursor);
|
|
break;
|
|
case RS2::UpArrowCursor:
|
|
setCursor(Qt::UpArrowCursor);
|
|
break;
|
|
case RS2::CrossCursor:
|
|
setCursor(Qt::CrossCursor);
|
|
break;
|
|
case RS2::WaitCursor:
|
|
setCursor(Qt::WaitCursor);
|
|
break;
|
|
case RS2::IbeamCursor:
|
|
setCursor(Qt::IBeamCursor);
|
|
break;
|
|
case RS2::SizeVerCursor:
|
|
setCursor(Qt::SizeVerCursor);
|
|
break;
|
|
case RS2::SizeHorCursor:
|
|
setCursor(Qt::SizeHorCursor);
|
|
break;
|
|
case RS2::SizeBDiagCursor:
|
|
setCursor(Qt::SizeBDiagCursor);
|
|
break;
|
|
case RS2::SizeFDiagCursor:
|
|
setCursor(Qt::SizeFDiagCursor);
|
|
break;
|
|
case RS2::SizeAllCursor:
|
|
setCursor(Qt::SizeAllCursor);
|
|
break;
|
|
case RS2::BlankCursor:
|
|
setCursor(Qt::BlankCursor);
|
|
break;
|
|
case RS2::SplitVCursor:
|
|
setCursor(Qt::SplitVCursor);
|
|
break;
|
|
case RS2::SplitHCursor:
|
|
setCursor(Qt::SplitHCursor);
|
|
break;
|
|
case RS2::PointingHandCursor:
|
|
setCursor(Qt::PointingHandCursor);
|
|
break;
|
|
case RS2::ForbiddenCursor:
|
|
setCursor(Qt::ForbiddenCursor);
|
|
break;
|
|
case RS2::WhatsThisCursor:
|
|
setCursor(Qt::WhatsThisCursor);
|
|
break;
|
|
case RS2::OpenHandCursor:
|
|
setCursor(Qt::OpenHandCursor);
|
|
break;
|
|
case RS2::ClosedHandCursor:
|
|
setCursor(Qt::ClosedHandCursor);
|
|
break;
|
|
case RS2::CadCursor:
|
|
cursor_hiding
|
|
? setCursor(Qt::BlankCursor)
|
|
: setCursor(*curCad);
|
|
break;
|
|
case RS2::DelCursor:
|
|
setCursor(*curDel);
|
|
break;
|
|
case RS2::SelectCursor:
|
|
setCursor(*curSelect);
|
|
break;
|
|
case RS2::MagnifierCursor:
|
|
setCursor(*curMagnifier);
|
|
break;
|
|
case RS2::MovingHandCursor:
|
|
setCursor(*curHand);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Sets the text for the grid status widget in the left bottom corner.
|
|
*/
|
|
void QG_GraphicView::updateGridStatusWidget(const QString& text)
|
|
{
|
|
emit gridStatusChanged(text);
|
|
}
|
|
|
|
|
|
/**
|
|
* Redraws the widget.
|
|
*/
|
|
void QG_GraphicView::redraw(RS2::RedrawMethod method) {
|
|
redrawMethod=(RS2::RedrawMethod ) (redrawMethod | method);
|
|
update(); // Paint when reeady to pain
|
|
// repaint(); //Paint immediate
|
|
}
|
|
|
|
|
|
void QG_GraphicView::resizeEvent(QResizeEvent* /*e*/) {
|
|
RS_DEBUG->print("QG_GraphicView::resizeEvent begin");
|
|
adjustOffsetControls();
|
|
adjustZoomControls();
|
|
// updateGrid();
|
|
// Small hack, delete the snapper during resizes
|
|
getOverlayContainer(RS2::Snapper)->clear();
|
|
redraw();
|
|
RS_DEBUG->print("QG_GraphicView::resizeEvent end");
|
|
}
|
|
|
|
void QG_GraphicView::mousePressEvent(QMouseEvent* event)
|
|
{
|
|
// pan zoom with middle mouse button
|
|
if (event->button()==Qt::MiddleButton)
|
|
{
|
|
setCurrentAction(new RS_ActionZoomPan(*container, *this));
|
|
}
|
|
eventHandler->mousePressEvent(event);
|
|
}
|
|
|
|
void QG_GraphicView::mouseDoubleClickEvent(QMouseEvent* e)
|
|
{
|
|
switch(e->button())
|
|
{
|
|
default:
|
|
break;
|
|
case Qt::MiddleButton:
|
|
setCurrentAction(new RS_ActionZoomAuto(*container, *this));
|
|
break;
|
|
case Qt::LeftButton:
|
|
if (menus.contains("Double-Click"))
|
|
{
|
|
killAllActions();
|
|
menus["Double-Click"]->popup(mapToGlobal(e->pos()));
|
|
}
|
|
break;
|
|
}
|
|
e->accept();
|
|
}
|
|
|
|
|
|
void QG_GraphicView::mouseReleaseEvent(QMouseEvent* event)
|
|
{
|
|
RS_DEBUG->print("QG_GraphicView::mouseReleaseEvent");
|
|
|
|
event->accept();
|
|
|
|
switch (event->button())
|
|
{
|
|
case Qt::RightButton:
|
|
if (event->modifiers()==Qt::ControlModifier)
|
|
{
|
|
if (menus.contains("Ctrl+Right-Click"))
|
|
{
|
|
menus["Ctrl+Right-Click"]->popup(mapToGlobal(event->pos()));
|
|
break;
|
|
}
|
|
}
|
|
if (event->modifiers()==Qt::ShiftModifier)
|
|
{
|
|
if (menus.contains("Shift+Right-Click"))
|
|
{
|
|
menus["Shift+Right-Click"]->popup(mapToGlobal(event->pos()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!eventHandler->hasAction())
|
|
{
|
|
if (menus.contains("Right-Click"))
|
|
{
|
|
menus["Right-Click"]->popup(mapToGlobal(event->pos()));
|
|
}
|
|
else if (!recent_actions.isEmpty())
|
|
{
|
|
QMenu* context_menu = new QMenu(this);
|
|
context_menu->setAttribute(Qt::WA_DeleteOnClose);
|
|
context_menu->addActions(recent_actions);
|
|
context_menu->exec(mapToGlobal(event->pos()));
|
|
}
|
|
}
|
|
else back();
|
|
break;
|
|
|
|
case Qt::XButton1:
|
|
enter();
|
|
emit xbutton1_released();
|
|
break;
|
|
|
|
default:
|
|
eventHandler->mouseReleaseEvent(event);
|
|
break;
|
|
}
|
|
RS_DEBUG->print("QG_GraphicView::mouseReleaseEvent: OK");
|
|
}
|
|
|
|
|
|
void QG_GraphicView::mouseMoveEvent(QMouseEvent* event)
|
|
{
|
|
event->accept();
|
|
eventHandler->mouseMoveEvent(event);
|
|
}
|
|
|
|
bool QG_GraphicView::event(QEvent *event)
|
|
{
|
|
if (event->type() == QEvent::NativeGesture) {
|
|
QNativeGestureEvent *nge = static_cast<QNativeGestureEvent *>(event);
|
|
|
|
if (nge->gestureType() == Qt::ZoomNativeGesture) {
|
|
double v = nge->value();
|
|
RS2::ZoomDirection direction;
|
|
double factor;
|
|
|
|
if (v < 0) {
|
|
direction = RS2::Out;
|
|
factor = 1-v;
|
|
} else {
|
|
direction = RS2::In;
|
|
factor = 1+v;
|
|
}
|
|
|
|
// It seems the NativeGestureEvent::pos() incorrectly reports global coordinates
|
|
QPoint g = mapFromGlobal(nge->globalPos());
|
|
RS_Vector mouse = toGraph(g.x(), g.y());
|
|
setCurrentAction(new RS_ActionZoomIn(*container, *this, direction,
|
|
RS2::Both, &mouse, factor));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return QWidget::event(event);
|
|
}
|
|
|
|
/**
|
|
* support for the wacom graphic tablet.
|
|
*/
|
|
void QG_GraphicView::tabletEvent(QTabletEvent* e) {
|
|
if (testAttribute(Qt::WA_UnderMouse)) {
|
|
switch (e->device()) {
|
|
case QTabletEvent::Eraser:
|
|
if (e->type()==QEvent::TabletRelease) {
|
|
if (container) {
|
|
|
|
RS_ActionSelectSingle* a =
|
|
new RS_ActionSelectSingle(*container, *this);
|
|
setCurrentAction(a);
|
|
QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(),
|
|
Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);//RLZ
|
|
mouseReleaseEvent(&ev);
|
|
a->finish();
|
|
|
|
if (container->countSelected()>0) {
|
|
setCurrentAction(
|
|
new RS_ActionModifyDelete(*container, *this));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case QTabletEvent::Stylus:
|
|
case QTabletEvent::Puck:
|
|
if (e->type()==QEvent::TabletPress) {
|
|
QMouseEvent ev(QEvent::MouseButtonPress, e->pos(),
|
|
Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);//RLZ
|
|
mousePressEvent(&ev);
|
|
} else if (e->type()==QEvent::TabletRelease) {
|
|
QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(),
|
|
Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);//RLZ
|
|
mouseReleaseEvent(&ev);
|
|
} else if (e->type()==QEvent::TabletMove) {
|
|
QMouseEvent ev(QEvent::MouseMove, e->pos(),
|
|
Qt::NoButton, 0, Qt::NoModifier);//RLZ
|
|
mouseMoveEvent(&ev);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// a 'mouse' click:
|
|
/*if (e->pressure()>10 && lastPressure<10) {
|
|
QMouseEvent e(QEvent::MouseButtonPress, e->pos(),
|
|
Qt::LeftButton, Qt::LeftButton);
|
|
mousePressEvent(&e);
|
|
}
|
|
else if (e->pressure()<10 && lastPressure>10) {
|
|
QMouseEvent e(QEvent::MouseButtonRelease, e->pos(),
|
|
Qt::LeftButton, Qt::LeftButton);
|
|
mouseReleaseEvent(&e);
|
|
} else if (lastPos!=e->pos()) {
|
|
QMouseEvent e(QEvent::MouseMove, e->pos(),
|
|
Qt::NoButton, 0);
|
|
mouseMoveEvent(&e);
|
|
}
|
|
|
|
lastPressure = e->pressure();
|
|
lastPos = e->pos();
|
|
*/
|
|
}
|
|
|
|
void QG_GraphicView::leaveEvent(QEvent* e) {
|
|
eventHandler->mouseLeaveEvent();
|
|
QWidget::leaveEvent(e);
|
|
}
|
|
|
|
|
|
void QG_GraphicView::enterEvent(QEvent* e) {
|
|
eventHandler->mouseEnterEvent();
|
|
QWidget::enterEvent(e);
|
|
}
|
|
|
|
|
|
void QG_GraphicView::focusOutEvent(QFocusEvent* e) {
|
|
QWidget::focusOutEvent(e);
|
|
}
|
|
|
|
|
|
void QG_GraphicView::focusInEvent(QFocusEvent* e) {
|
|
eventHandler->mouseEnterEvent();
|
|
QWidget::focusInEvent(e);
|
|
}
|
|
|
|
/**
|
|
* mouse wheel event. zooms in/out or scrolls when
|
|
* shift or ctrl is pressed.
|
|
*/
|
|
void QG_GraphicView::wheelEvent(QWheelEvent *e) {
|
|
//RS_DEBUG->print("wheel: %d", e->delta());
|
|
|
|
//printf("state: %d\n", e->state());
|
|
//printf("ctrl: %d\n", Qt::ControlButton);
|
|
|
|
if (container==NULL) {
|
|
return;
|
|
}
|
|
|
|
RS_Vector mouse = toGraph(e->x(), e->y());
|
|
|
|
if (device == "Trackpad")
|
|
{
|
|
QPoint numPixels = e->pixelDelta();
|
|
|
|
// high-resolution scrolling triggers Pan instead of Zoom logic
|
|
isSmoothScrolling |= !numPixels.isNull();
|
|
|
|
if (isSmoothScrolling)
|
|
{
|
|
if (e->phase() == Qt::ScrollEnd) isSmoothScrolling = false;
|
|
}
|
|
else // Trackpads that without high-resolution scrolling
|
|
// e.g. libinput-XWayland trackpads
|
|
{
|
|
numPixels = e->angleDelta() / 4;
|
|
}
|
|
|
|
if (!numPixels.isNull())
|
|
{
|
|
if (e->modifiers()==Qt::ControlModifier)
|
|
{
|
|
RS_SETTINGS->beginGroup("/Defaults");
|
|
bool invZoom = (RS_SETTINGS->readNumEntry("/InvertZoomDirection", 0) == 1);
|
|
RS_SETTINGS->endGroup();
|
|
|
|
// Hold ctrl to zoom. 1 % per pixel
|
|
double v = (invZoom) ? (numPixels.y() / 100.) : (-numPixels.y() / 100.);
|
|
RS2::ZoomDirection direction;
|
|
double factor;
|
|
|
|
if (v < 0) {
|
|
direction = RS2::Out; factor = 1-v;
|
|
} else {
|
|
direction = RS2::In; factor = 1+v;
|
|
}
|
|
|
|
setCurrentAction(new RS_ActionZoomIn(*container, *this, direction,
|
|
RS2::Both, &mouse, factor));
|
|
}
|
|
else
|
|
{
|
|
RS_SETTINGS->beginGroup("/Defaults");
|
|
bool inv_h = (RS_SETTINGS->readNumEntry("/WheelScrollInvertH", 0) == 1);
|
|
bool inv_v = (RS_SETTINGS->readNumEntry("/WheelScrollInvertV", 0) == 1);
|
|
RS_SETTINGS->endGroup();
|
|
|
|
int hDelta = (inv_h) ? -numPixels.x() : numPixels.x();
|
|
int vDelta = (inv_v) ? -numPixels.y() : numPixels.y();
|
|
|
|
// scroll by scrollbars: issue #479 (it has its own issues)
|
|
if (scrollbars)
|
|
{
|
|
hScrollBar->setValue(hScrollBar->value() - hDelta);
|
|
vScrollBar->setValue(vScrollBar->value() - vDelta);
|
|
}
|
|
else
|
|
{
|
|
setCurrentAction(new RS_ActionZoomScroll(hDelta, vDelta,
|
|
*container, *this));
|
|
}
|
|
}
|
|
redraw();
|
|
}
|
|
e->accept();
|
|
return;
|
|
}
|
|
|
|
if (e->delta() == 0) {
|
|
// A zero delta event occurs when smooth scrolling is ended. Ignore this
|
|
e->accept();
|
|
return;
|
|
}
|
|
|
|
bool scroll = false;
|
|
RS2::Direction direction = RS2::Up;
|
|
|
|
// scroll up / down:
|
|
if (e->modifiers()==Qt::ControlModifier) {
|
|
scroll = true;
|
|
switch(e->orientation()){
|
|
case Qt::Horizontal:
|
|
direction=(e->delta()>0)?RS2::Left:RS2::Right;
|
|
break;
|
|
default:
|
|
case Qt::Vertical:
|
|
direction=(e->delta()>0)?RS2::Up:RS2::Down;
|
|
}
|
|
}
|
|
|
|
// scroll left / right:
|
|
else if (e->modifiers()==Qt::ShiftModifier) {
|
|
scroll = true;
|
|
switch(e->orientation()){
|
|
case Qt::Horizontal:
|
|
direction=(e->delta()>0)?RS2::Up:RS2::Down;
|
|
break;
|
|
default:
|
|
case Qt::Vertical:
|
|
direction=(e->delta()>0)?RS2::Left:RS2::Right;
|
|
}
|
|
}
|
|
|
|
if (scroll && scrollbars) {
|
|
//scroll by scrollbars: issue #479
|
|
|
|
RS_SETTINGS->beginGroup("/Defaults");
|
|
bool inv_h = (RS_SETTINGS->readNumEntry("/WheelScrollInvertH", 0) == 1);
|
|
bool inv_v = (RS_SETTINGS->readNumEntry("/WheelScrollInvertV", 0) == 1);
|
|
RS_SETTINGS->endGroup();
|
|
|
|
int delta;
|
|
|
|
switch(direction){
|
|
case RS2::Left:
|
|
case RS2::Right:
|
|
delta = (inv_h) ? -e->delta() : e->delta();
|
|
hScrollBar->setValue(hScrollBar->value()+delta);
|
|
break;
|
|
default:
|
|
delta = (inv_v) ? -e->delta() : e->delta();
|
|
vScrollBar->setValue(vScrollBar->value()+delta);
|
|
}
|
|
|
|
// setCurrentAction(new RS_ActionZoomScroll(direction,
|
|
// *container, *this));
|
|
}
|
|
|
|
// zoom in / out:
|
|
else if (e->modifiers()==0) {
|
|
|
|
/*
|
|
* The zoomFactor effects how quickly the scroll wheel will zoom in & out.
|
|
*
|
|
* Benchmarks:
|
|
* 1.250 - the original; fast & usable, but seems a choppy & a bit 'jarring'
|
|
* 1.175 - still a bit choppy
|
|
* 1.150 - smoother than the original, but still 'quick' enough for good navigation.
|
|
* 1.137 - seems to work well for me
|
|
* 1.125 - about the lowest that would be acceptable and useful, a tad on the slow side for me
|
|
* 1.100 - a very slow & deliberate zooming, but feels very "cautious", "controlled", "safe", and "precise".
|
|
* 1.000 - goes nowhere. :)
|
|
*/
|
|
const double zoomFactor=1.137;
|
|
|
|
RS_Vector mainViewCenter = toGraph(getWidth()/2, getHeight()/2);
|
|
|
|
RS_SETTINGS->beginGroup("/Defaults");
|
|
bool invZoom = (RS_SETTINGS->readNumEntry("/InvertZoomDirection", 0) == 1);
|
|
RS_SETTINGS->endGroup();
|
|
|
|
if ((e->delta()>0 && !invZoom) || (e->delta()<0 && invZoom)) {
|
|
const double zoomInOvershoot=1.20;
|
|
|
|
RS_Vector effect{mouse};
|
|
{
|
|
effect-=mainViewCenter;
|
|
effect.scale(zoomInOvershoot);
|
|
effect+=mainViewCenter;
|
|
}
|
|
|
|
setCurrentAction(new RS_ActionZoomIn(*container, *this,
|
|
RS2::In, RS2::Both,
|
|
&effect,
|
|
zoomFactor
|
|
));
|
|
} else {
|
|
const double zoomOutUndershoot=0.30;
|
|
|
|
RS_Vector effect{mouse};
|
|
{
|
|
effect-=mainViewCenter;
|
|
effect.scale(zoomOutUndershoot);
|
|
effect+=mainViewCenter;
|
|
}
|
|
|
|
setCurrentAction(new RS_ActionZoomIn(*container, *this,
|
|
RS2::Out, RS2::Both,
|
|
&effect,
|
|
zoomFactor
|
|
));
|
|
}
|
|
}
|
|
redraw();
|
|
|
|
QMouseEvent* event = new QMouseEvent(QEvent::MouseMove,
|
|
QPoint(e->x(), e->y()),
|
|
Qt::NoButton, Qt::NoButton,
|
|
Qt::NoModifier);
|
|
eventHandler->mouseMoveEvent(event);
|
|
delete event;
|
|
|
|
e->accept();
|
|
}
|
|
|
|
|
|
void QG_GraphicView::keyPressEvent(QKeyEvent* e)
|
|
{
|
|
if (container==NULL) {
|
|
return;
|
|
}
|
|
|
|
bool scroll = false;
|
|
RS2::Direction direction = RS2::Up;
|
|
|
|
switch (e->key()) {
|
|
case Qt::Key_Left:
|
|
scroll = true;
|
|
direction = RS2::Right;
|
|
break;
|
|
case Qt::Key_Right:
|
|
scroll = true;
|
|
direction = RS2::Left;
|
|
break;
|
|
case Qt::Key_Up:
|
|
scroll = true;
|
|
direction = RS2::Up;
|
|
break;
|
|
case Qt::Key_Down:
|
|
scroll = true;
|
|
direction = RS2::Down;
|
|
break;
|
|
default:
|
|
scroll = false;
|
|
break;
|
|
}
|
|
|
|
if (scroll) {
|
|
setCurrentAction(new RS_ActionZoomScroll(direction,
|
|
*container, *this));
|
|
}
|
|
eventHandler->keyPressEvent(e);
|
|
}
|
|
|
|
|
|
void QG_GraphicView::keyReleaseEvent(QKeyEvent* e)
|
|
{
|
|
eventHandler->keyReleaseEvent(e);
|
|
}
|
|
|
|
/**
|
|
* Called whenever the graphic view has changed.
|
|
* Adjusts the scrollbar ranges / steps.
|
|
*/
|
|
void QG_GraphicView::adjustOffsetControls()
|
|
{
|
|
if (scrollbars)
|
|
{
|
|
static bool running = false;
|
|
|
|
if (running) {
|
|
return;
|
|
}
|
|
|
|
running = true;
|
|
|
|
if (container==NULL || hScrollBar==NULL || vScrollBar==NULL) {
|
|
return;
|
|
}
|
|
|
|
int ox = getOffsetX();
|
|
int oy = getOffsetY();
|
|
|
|
RS_Vector min = container->getMin();
|
|
RS_Vector max = container->getMax();
|
|
|
|
// no drawing yet - still allow to scroll
|
|
if (max.x < min.x+1.0e-6 ||
|
|
max.y < min.y+1.0e-6 ||
|
|
max.x > RS_MAXDOUBLE ||
|
|
max.x < RS_MINDOUBLE ||
|
|
min.x > RS_MAXDOUBLE ||
|
|
min.x < RS_MINDOUBLE ||
|
|
max.y > RS_MAXDOUBLE ||
|
|
max.y < RS_MINDOUBLE ||
|
|
min.y > RS_MAXDOUBLE ||
|
|
min.y < RS_MINDOUBLE ) {
|
|
min = RS_Vector(-10,-10);
|
|
max = RS_Vector(100,100);
|
|
}
|
|
|
|
auto factor = getFactor();
|
|
|
|
int minVal = (int)(-getWidth()*0.75
|
|
+ std::min(min.x, 0.)*factor.x);
|
|
int maxVal = (int)(-getWidth()*0.25
|
|
+ std::max(max.x, 0.)*factor.x);
|
|
|
|
if (minVal<=maxVal) {
|
|
hScrollBar->setRange(minVal, maxVal);
|
|
}
|
|
|
|
minVal = (int)(+getHeight()*0.25
|
|
- std::max(max.y, 0.)*factor.y);
|
|
maxVal = (int)(+getHeight()*0.75
|
|
- std::min(min.y, 0.)*factor.y);
|
|
|
|
if (minVal<=maxVal) {
|
|
vScrollBar->setRange(minVal, maxVal);
|
|
}
|
|
|
|
hScrollBar->setPageStep(getWidth());
|
|
vScrollBar->setPageStep(getHeight());
|
|
|
|
hScrollBar->setValue(-ox);
|
|
vScrollBar->setValue(oy);
|
|
|
|
|
|
slotHScrolled(-ox);
|
|
slotVScrolled(oy);
|
|
|
|
|
|
// RS_DEBUG->print("H min: %d / max: %d / step: %d / value: %d\n",
|
|
// hScrollBar->minimum(), hScrollBar->maximum(),
|
|
// hScrollBar->pageStep(), ox);
|
|
|
|
// RS_DEBUG->print(/*RS_Debug::D_WARNING, */"V min: %d / max: %d / step: %d / value: %d\n",
|
|
// vScrollBar->minimum(), vScrollBar->maximum(),
|
|
// vScrollBar->pageStep(), oy);
|
|
|
|
|
|
running = false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* override this to adjust controls and widgets that
|
|
* control the zoom factor of the graphic.
|
|
*/
|
|
void QG_GraphicView::adjustZoomControls() {}
|
|
|
|
|
|
/**
|
|
* Slot for horizontal scroll events.
|
|
*/
|
|
void QG_GraphicView::slotHScrolled(int value) {
|
|
// Scrollbar behaviour tends to change with every Qt version..
|
|
// so let's keep old code in here for now
|
|
|
|
//static int running = false;
|
|
//if (!running) {
|
|
//running = true;
|
|
////RS_DEBUG->print("value x: %d\n", value);
|
|
if (hScrollBar->maximum()==hScrollBar->minimum()) {
|
|
centerOffsetX();
|
|
} else {
|
|
setOffsetX(-value);
|
|
}
|
|
//if (isUpdateEnabled()) {
|
|
// updateGrid();
|
|
redraw();
|
|
}
|
|
|
|
|
|
/**
|
|
* Slot for vertical scroll events.
|
|
*/
|
|
void QG_GraphicView::slotVScrolled(int value) {
|
|
// Scrollbar behaviour tends to change with every Qt version..
|
|
// so let's keep old code in here for now
|
|
|
|
//static int running = false;
|
|
//if (!running) {
|
|
//running = true;
|
|
// DEBUG_HEADER
|
|
// RS_DEBUG->print(/*RS_Debug::D_WARNING,*/ "%s %s(): set vertical offset from %d to %d\n",
|
|
// __FILE__, __func__, getOffsetY(), value);
|
|
if (vScrollBar->maximum()==vScrollBar->minimum()) {
|
|
centerOffsetY();
|
|
} else {
|
|
setOffsetY(value);
|
|
}
|
|
//if (isUpdateEnabled()) {
|
|
// updateGrid();
|
|
redraw();
|
|
}
|
|
/**
|
|
* @brief setOffset
|
|
* @param ox, offset X
|
|
* @param oy, offset Y
|
|
*/
|
|
void QG_GraphicView::setOffset(int ox, int oy) {
|
|
// DEBUG_HEADER
|
|
// qDebug()<<"adjusting offset from ("<<getOffsetX()<<","<<getOffsetY()<<") to ("<<ox<<" , "<<oy<<")";
|
|
RS_GraphicView::setOffset(ox, oy);
|
|
// need to adjust offset control for scrollbars when setting graphicview offset
|
|
adjustOffsetControls();
|
|
}
|
|
|
|
RS_Vector QG_GraphicView::getMousePosition() const
|
|
{
|
|
//find mouse position
|
|
QPoint vp=mapFromGlobal(QCursor::pos());
|
|
//if cursor is not on widget, return the widget center position
|
|
if(!rect().contains(vp))
|
|
vp=QPoint(width()/2, height()/2);
|
|
return toGraph(vp.x(), vp.y());
|
|
}
|
|
|
|
void QG_GraphicView::getPixmapForView(std::unique_ptr<QPixmap>& pm)
|
|
{
|
|
QSize const s0(getWidth(), getHeight());
|
|
if(pm && pm->size()==s0)
|
|
return;
|
|
pm.reset(new QPixmap(getWidth(), getHeight()));
|
|
}
|
|
|
|
void QG_GraphicView::layerActivated(RS_Layer *layer) {
|
|
RS_SETTINGS->beginGroup("/Modify");
|
|
bool toActivated= (RS_SETTINGS->readNumEntry("/ModifyEntitiesToActiveLayer", 0)==1);
|
|
RS_SETTINGS->endGroup();
|
|
|
|
if(!toActivated) return;
|
|
RS_EntityContainer *container = this->getContainer();
|
|
RS_Graphic* graphic = this->getGraphic();
|
|
QList<RS_Entity*> clones;
|
|
|
|
if (graphic) {
|
|
graphic->startUndoCycle();
|
|
}
|
|
|
|
for (auto en: *container) {
|
|
if (!en) continue;
|
|
if (!en->isSelected()) continue;
|
|
|
|
RS_Entity* cl = en->clone();
|
|
cl->setLayer(layer);
|
|
this->deleteEntity(en);
|
|
en->setSelected(false);
|
|
cl->setSelected(false);
|
|
clones << cl;
|
|
|
|
if (!graphic) continue;
|
|
|
|
en->setUndoState(true);
|
|
graphic->addUndoable(en);
|
|
}
|
|
|
|
for (auto cl: clones) {
|
|
container->addEntity(cl);
|
|
this->drawEntity(cl);
|
|
|
|
if (!graphic) continue;
|
|
|
|
graphic->addUndoable(cl);
|
|
}
|
|
|
|
if (graphic) {
|
|
graphic->endUndoCycle();
|
|
graphic->updateInserts();
|
|
}
|
|
|
|
container->calculateBorders();
|
|
container->setSelected(false);
|
|
redraw(RS2::RedrawDrawing);
|
|
}
|
|
|
|
|
|
/**
|
|
* Handles paint events by redrawing the graphic in this view.
|
|
* usually that's very fast since we only paint the buffer we
|
|
* have from the last call..
|
|
*/
|
|
void QG_GraphicView::paintEvent(QPaintEvent *)
|
|
{
|
|
|
|
// Re-Create or get the layering pixmaps
|
|
getPixmapForView(PixmapLayer1);
|
|
getPixmapForView(PixmapLayer2);
|
|
getPixmapForView(PixmapLayer3);
|
|
|
|
// Draw Layer 1
|
|
if (redrawMethod & RS2::RedrawGrid)
|
|
{
|
|
PixmapLayer1->fill(background);
|
|
RS_PainterQt painter1(PixmapLayer1.get());
|
|
drawLayer1((RS_Painter*)&painter1);
|
|
painter1.end();
|
|
}
|
|
|
|
if (redrawMethod & RS2::RedrawDrawing)
|
|
{
|
|
view_rect = LC_Rect(toGraph(0, 0),
|
|
toGraph(getWidth(), getHeight()));
|
|
// DRaw layer 2
|
|
PixmapLayer2->fill(Qt::transparent);
|
|
RS_PainterQt painter2(PixmapLayer2.get());
|
|
if (antialiasing)
|
|
{
|
|
painter2.setRenderHint(QPainter::Antialiasing);
|
|
}
|
|
painter2.setDrawingMode(drawingMode);
|
|
painter2.setDrawSelectedOnly(false);
|
|
drawLayer2((RS_Painter*)&painter2);
|
|
painter2.setDrawSelectedOnly(true);
|
|
drawLayer2((RS_Painter*)&painter2);
|
|
painter2.end();
|
|
}
|
|
|
|
if (redrawMethod & RS2::RedrawOverlay)
|
|
{
|
|
PixmapLayer3->fill(Qt::transparent);
|
|
RS_PainterQt painter3(PixmapLayer3.get());
|
|
if (antialiasing)
|
|
{
|
|
painter3.setRenderHint(QPainter::Antialiasing);
|
|
}
|
|
drawLayer3((RS_Painter*)&painter3);
|
|
painter3.end();
|
|
}
|
|
|
|
// Finally paint the layers back on the screen, bitblk to the rescue!
|
|
RS_PainterQt wPainter(this);
|
|
wPainter.drawPixmap(0,0,*PixmapLayer1);
|
|
wPainter.drawPixmap(0,0,*PixmapLayer2);
|
|
wPainter.drawPixmap(0,0,*PixmapLayer3);
|
|
wPainter.end();
|
|
|
|
redrawMethod=RS2::RedrawNone;
|
|
}
|
|
|
|
void QG_GraphicView::setAntialiasing(bool state)
|
|
{
|
|
antialiasing = state;
|
|
}
|
|
|
|
void QG_GraphicView::addScrollbars()
|
|
{
|
|
scrollbars = true;
|
|
|
|
hScrollBar = new QG_ScrollBar(Qt::Horizontal, this);
|
|
vScrollBar = new QG_ScrollBar(Qt::Vertical, this);
|
|
layout = new QGridLayout(this);
|
|
|
|
setOffset(50, 50);
|
|
|
|
layout->setMargin(0);
|
|
layout->setSpacing(0);
|
|
layout->setColumnStretch(0, 1);
|
|
layout->setColumnStretch(1, 0);
|
|
layout->setColumnStretch(2, 0);
|
|
layout->setRowStretch(0, 1);
|
|
layout->setRowStretch(1, 0);
|
|
|
|
hScrollBar->setSingleStep(50);
|
|
hScrollBar->setCursor(Qt::ArrowCursor);
|
|
layout->addWidget(hScrollBar, 1, 0);
|
|
connect(hScrollBar, SIGNAL(valueChanged(int)),
|
|
this, SLOT(slotHScrolled(int)));
|
|
|
|
vScrollBar->setSingleStep(50);
|
|
vScrollBar->setCursor(Qt::ArrowCursor);
|
|
layout->addWidget(vScrollBar, 0, 1);
|
|
connect(vScrollBar, SIGNAL(valueChanged(int)),
|
|
this, SLOT(slotVScrolled(int)));
|
|
}
|
|
|
|
bool QG_GraphicView::hasScrollbars()
|
|
{
|
|
return scrollbars;
|
|
}
|
|
|
|
void QG_GraphicView::setCursorHiding(bool state)
|
|
{
|
|
cursor_hiding = state;
|
|
}
|
|
|
|
void QG_GraphicView::setCurrentQAction(QAction* q_action)
|
|
{
|
|
eventHandler->setQAction(q_action);
|
|
|
|
if (recent_actions.contains(q_action))
|
|
{
|
|
recent_actions.removeOne(q_action);
|
|
}
|
|
recent_actions.prepend(q_action);
|
|
}
|
|
|
|
void QG_GraphicView::destroyMenu(const QString& activator)
|
|
{
|
|
if (menus.contains(activator))
|
|
{
|
|
auto menu = menus.take(activator);
|
|
delete menu;
|
|
}
|
|
}
|
|
|
|
void QG_GraphicView::setMenu(const QString& activator, QMenu* menu)
|
|
{
|
|
destroyMenu(activator);
|
|
menus[activator] = menu;
|
|
}
|