3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // (C) 2010 Underground Software
8 // JLH = James L. Hammons <jlhamm@acm.org>
11 // --- ---------- -----------------------------------------------------------
12 // JLH 05/10/2010 Added this text. :-)
15 #include "qg_graphicview.h"
17 #include "rs_actiondefault.h"
18 #include "rs_actionzoomin.h"
19 #include "rs_actionzoompan.h"
20 #include "rs_actionzoomscroll.h"
21 #include "rs_actionmodifydelete.h"
22 #include "rs_actionselectsingle.h"
23 #include "rs_dialogfactory.h"
24 #include "graphicview.h"
25 #include "rs_preview.h"
28 #include "rs_system.h"
29 #include "rs_patternlist.h"
30 #include "cadtoolbar.h"
31 #include "paintintf.h"
32 #include "qg_dialogfactory.h"
34 #define QG_SCROLLMARGIN 400
39 QG_GraphicView::QG_GraphicView(QWidget * parent, const char */*name*/, Qt::WindowFlags f):
40 QWidget(parent, f), GraphicView()//, refCount(0)
42 RS_DEBUG->print("QG_GraphicView::QG_GraphicView(parent, name, flags)...");
43 CommonInitialization();
49 QG_GraphicView::QG_GraphicView(RS_Document * doc, QWidget * parent)
51 // QG_GraphicView(parent, "graphicview");
52 CommonInitialization();
54 RS_DEBUG->print("QG_GraphicView::QG_GraphicView(doc, parent)...");
56 RS_DEBUG->print(" Setting Container...");
61 RS_DEBUG->print(" container set.");
65 setBorders(10, 10, 10, 10);
68 setDefaultAction(new RS_ActionDefault(*doc, *this));
71 void QG_GraphicView::CommonInitialization(void)
73 setBackground(background);
80 RS_DEBUG->print("--> Setting up layout...");
81 QGridLayout * layout = new QGridLayout(this);
82 layout->setColumnStretch(0, 1);
83 layout->setColumnStretch(1, 0);
84 layout->setColumnStretch(2, 0);
85 layout->setRowStretch(0, 1);
86 layout->setRowStretch(1, 0);
89 void QGridLayout::addMultiCellWidget ( QWidget * w, int fromRow, int toRow, int fromCol, int toCol, int alignment = 0 )
90 Adds the widget w to the cell grid, spanning multiple rows/columns. The cell will span from fromRow, fromCol to toRow, toCol.
92 Alignment is specified by alignment, which is a bitwise OR of Qt::AlignmentFlags values. The default alignment is 0, which means that the widget fills the entire cell.
94 A non-zero alignment indicates that the widget should not grow to fill the available space but should be sized according to sizeHint().
96 RS_DEBUG->print("--> Creating hScrollBar...");
97 // hScrollBar = new QG_ScrollBar(Qt::Horizontal, this);
98 hScrollBar = new QScrollBar(Qt::Horizontal, this);
99 // hScrollBar->setLineStep(50);
100 hScrollBar->setSingleStep(50);
101 // layout->addMultiCellWidget(hScrollBar, 1, 1, 0, 0);
102 layout->addWidget(hScrollBar, 1, 0);
103 // layout->addRowSpacing(1, hScrollBar->sizeHint().height());
104 layout->addItem(new QSpacerItem(0, hScrollBar->sizeHint().height()), 1, 0);
106 RS_DEBUG->print("--> Creating vScrollBar...");
107 // vScrollBar = new QG_ScrollBar(Qt::Vertical, this);
108 vScrollBar = new QScrollBar(Qt::Vertical, this);
109 // vScrollBar->setLineStep(50);
110 vScrollBar->setSingleStep(50);
111 // layout->addMultiCellWidget(vScrollBar, 0, 0, 2, 2);
112 layout->addWidget(vScrollBar, 0, 2);
113 // layout->addColSpacing(2, vScrollBar->sizeHint().width());
114 layout->addItem(new QSpacerItem(vScrollBar->sizeHint().width(), 0), 0, 2);
116 connect(hScrollBar, SIGNAL(valueChanged(int)), this, SLOT(slotHScrolled(int)));
117 connect(vScrollBar, SIGNAL(valueChanged(int)), this, SLOT(slotVScrolled(int)));
122 QPixmap cur1(":/res/cur_cad_bmp.xpm");
123 bmp = QPixmap(":/res/cur_cad_mask.xpm");
125 curCad = new QCursor(cur1, 15, 15);
127 QPixmap cur2(":/res/cur_glass_bmp.xpm");
128 bmp = QPixmap(":/res/cur_glass_mask.xpm");
130 curMagnifier = new QCursor(cur2, 12, 12);
132 QPixmap cur3(":/res/cur_del_bmp.xpm");
133 bmp = QPixmap(":/res/cur_del_mask.xpm");
135 curDel = new QCursor(cur3, 15, 15);
137 QPixmap cur4(":/res/cur_select_bmp.xpm");
138 bmp = QPixmap(":/res/cur_select_mask.xpm");
140 curSelect = new QCursor(cur4, 15, 15);
142 QPixmap cur5(":/res/cur_hand_bmp.xpm");
143 bmp = QPixmap(":/res/cur_hand_mask.xpm");
145 curHand = new QCursor(cur5, 15, 15);
147 // No individual cursors for the Mac
155 // Dummy widgets for scrollbar corners:
156 //layout->addWidget(new QWidget(this), 1, 1);
157 //QWidget* w = new QWidget(this);
158 //w->setEraseColor(QColor(255,0,0));
159 gridStatus = new QLabel("-", this);
160 gridStatus->setAlignment(Qt::AlignRight);
161 // gridStatus->setAutoFillBackground(true);
162 // layout->addMultiCellWidget(gridStatus, 1, 1, 1, 2);
163 layout->addWidget(gridStatus, 1, 1, 1, 2);
164 // layout->addColSpacing(1, 50);
165 layout->addItem(new QSpacerItem(50, 0), 0, 1);
167 setMouseTracking(true);
168 // flickering under win:
169 //setFocusPolicy(WheelFocus);
171 #warning "QG_GraphicView constructor is setting Qt::NoFocus!!!"
172 // A-HAH! THIS IS THE PROBLEM!!!
173 // setFocusPolicy(Qt::NoFocus);
175 // setFocusPolicy(Qt::StrongFocus);
181 QG_GraphicView::~QG_GraphicView()
196 * @return width of widget.
198 int QG_GraphicView::getWidth()
200 return width() - vScrollBar->sizeHint().width();
204 * @return height of widget.
206 int QG_GraphicView::getHeight()
208 return height() - hScrollBar->sizeHint().height();
212 * Changes the current background color of this view.
214 void QG_GraphicView::setBackground(const RS_Color & bg)
216 GraphicView::setBackground(bg);
218 // setBackgroundColor(bg);
220 palette.setColor(backgroundRole(), bg);
225 * Sets the mouse cursor to the given type.
227 void QG_GraphicView::setMouseCursor(RS2::CursorType c)
232 case RS2::ArrowCursor:
233 setCursor(Qt::ArrowCursor);
235 case RS2::UpArrowCursor:
236 setCursor(Qt::UpArrowCursor);
238 case RS2::CrossCursor:
239 setCursor(Qt::CrossCursor);
241 case RS2::WaitCursor:
242 setCursor(Qt::WaitCursor);
244 case RS2::IbeamCursor:
245 setCursor(Qt::IBeamCursor);
247 case RS2::SizeVerCursor:
248 setCursor(Qt::SizeVerCursor);
250 case RS2::SizeHorCursor:
251 setCursor(Qt::SizeHorCursor);
253 case RS2::SizeBDiagCursor:
254 setCursor(Qt::SizeBDiagCursor);
256 case RS2::SizeFDiagCursor:
257 setCursor(Qt::SizeFDiagCursor);
259 case RS2::SizeAllCursor:
260 setCursor(Qt::SizeAllCursor);
262 case RS2::BlankCursor:
263 setCursor(Qt::BlankCursor);
265 case RS2::SplitVCursor:
266 setCursor(Qt::SplitVCursor);
268 case RS2::SplitHCursor:
269 setCursor(Qt::SplitHCursor);
271 case RS2::PointingHandCursor:
272 setCursor(Qt::PointingHandCursor);
274 case RS2::ForbiddenCursor:
275 setCursor(Qt::ForbiddenCursor);
277 case RS2::WhatsThisCursor:
278 setCursor(Qt::WhatsThisCursor);
288 case RS2::SelectCursor:
289 setCursor(*curSelect);
291 case RS2::MagnifierCursor:
292 setCursor(*curMagnifier);
294 case RS2::MovingHandCursor:
298 // Reduced cursor selection for the Mac:
300 setCursor(Qt::CrossCursor);
303 setCursor(Qt::CrossCursor);
305 case RS2::SelectCursor:
306 setCursor(Qt::CrossCursor);
308 case RS2::MagnifierCursor:
309 setCursor(Qt::CrossCursor);
311 case RS2::MovingHandCursor:
312 setCursor(Qt::PointingHandCursor);
319 * Sets the text for the grid status widget in the left bottom corner.
321 void QG_GraphicView::updateGridStatusWidget(const QString & text)
323 gridStatus->setText(text);
327 * Redraws the widget.
329 void QG_GraphicView::redraw()
331 RS_DEBUG->print("QG_GraphicView::redraw begin 1");
333 if (simulationRunning)
336 //Also, this stuff is properly taken care of by the Qt toolkit--
337 //so we don't have to be concerned with shit like this as long
338 //as we use update() instead of repaint()
340 static bool running = false;
345 RS_DEBUG->print("QG_GraphicView::redraw begin 2");
348 This is the only place in the entire codebase that a proper update is
351 if (isUpdateEnabled())
355 RS_DEBUG->print("QG_GraphicView::redraw end 2");
359 RS_DEBUG->print("QG_GraphicView::redraw end 1");
362 void QG_GraphicView::resizeEvent(QResizeEvent * /*e*/)
364 RS_DEBUG->print("QG_GraphicView::resizeEvent begin");
365 adjustOffsetControls();
366 adjustZoomControls();
368 RS_DEBUG->print("QG_GraphicView::resizeEvent end");
372 This is retarded. There is absolutely no reason to have this shit in here.
373 This could be easily called from whoever calls this crap.
375 And so... We remove it.
378 // Next three methods from RS_LayerListListener Interface:
379 void QG_GraphicView::layerEdited(RS_Layer *)
384 void QG_GraphicView::layerRemoved(RS_Layer *)
389 void QG_GraphicView::layerToggled(RS_Layer *)
395 void QG_GraphicView::emulateMouseMoveEvent()
397 // QMouseEvent e(QEvent::MouseMove, QPoint(mx, my), Qt::NoButton, Qt::NoButton);
398 //mouseMoveEvent(&e);
401 void QG_GraphicView::mousePressEvent(QMouseEvent * e)
403 // pan zoom with middle mouse button
404 if (e->button() == Qt::MidButton /*|| (e->state()==Qt::LeftButton|Qt::AltButton)*/)
405 setCurrentAction(new RS_ActionZoomPan(*container, *this));
407 GraphicView::mousePressEvent(e);
408 QWidget::mousePressEvent(e);
411 void QG_GraphicView::mouseReleaseEvent(QMouseEvent * e)
413 RS_DEBUG->print("QG_GraphicView::mouseReleaseEvent");
414 GraphicView::mouseReleaseEvent(e);
415 //QWidget::mouseReleaseEvent(e);
417 if (!e->isAccepted())
419 if (QG_DIALOGFACTORY && QG_DIALOGFACTORY->getCadToolBar())
421 RS_DEBUG->print("QG_GraphicView::mouseReleaseEvent: fwd to cadtoolbar");
422 QG_DIALOGFACTORY->getCadToolBar()->mouseReleaseEvent(e);
426 RS_DEBUG->print("QG_GraphicView::mouseReleaseEvent: OK");
429 void QG_GraphicView::mouseMoveEvent(QMouseEvent * e)
431 //RS_DEBUG->print("QG_GraphicView::mouseMoveEvent begin");
432 //QMouseEvent rsm = QG_Qt2Rs::mouseEvent(e);
434 GraphicView::mouseMoveEvent(e);
435 QWidget::mouseMoveEvent(e);
437 //What kind of cacamamie crap is this???
439 // // make sure that we can still use hotkeys and the mouse wheel
440 // if (parent() != NULL)
441 // ((QWidget *)parent())->setFocus();
444 //RS_DEBUG->print("QG_GraphicView::mouseMoveEvent end");
448 * support for the wacom graphic tablet.
450 void QG_GraphicView::tabletEvent(QTabletEvent * e)
453 if (testAttribute(Qt::WA_UnderMouse))
457 case QTabletEvent::Eraser:
458 if (e->type() == QEvent::TabletRelease)
460 if (container != NULL)
462 RS_ActionSelectSingle * a = new RS_ActionSelectSingle(*container, *this);
465 // QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(), Qt::LeftButton, Qt::LeftButton);
466 // mouseReleaseEvent(&ev);
469 if (container->countSelected() > 0)
470 setCurrentAction(new RS_ActionModifyDelete(*container, *this));
475 case QTabletEvent::Stylus:
476 case QTabletEvent::Puck:
477 if (e->type() == QEvent::TabletPress)
480 // QMouseEvent ev(QEvent::MouseButtonPress, e->pos(), Qt::LeftButton, Qt::LeftButton);
481 // mousePressEvent(&ev);
483 else if (e->type() == QEvent::TabletRelease)
486 // QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(), Qt::LeftButton, Qt::LeftButton);
487 // mouseReleaseEvent(&ev);
489 else if (e->type() == QEvent::TabletMove)
492 // QMouseEvent ev(QEvent::MouseMove, e->pos(), Qt::NoButton, 0);
493 // mouseMoveEvent(&ev);
503 /*if (e->pressure()>10 && lastPressure<10) {
504 QMouseEvent e(QEvent::MouseButtonPress, e->pos(),
505 Qt::LeftButton, Qt::LeftButton);
508 else if (e->pressure()<10 && lastPressure>10) {
509 QMouseEvent e(QEvent::MouseButtonRelease, e->pos(),
510 Qt::LeftButton, Qt::LeftButton);
511 mouseReleaseEvent(&e);
512 } else if (lastPos!=e->pos()) {
513 QMouseEvent e(QEvent::MouseMove, e->pos(),
518 lastPressure = e->pressure();
523 void QG_GraphicView::leaveEvent(QEvent * e)
525 GraphicView::mouseLeaveEvent();
526 QWidget::leaveEvent(e);
529 void QG_GraphicView::enterEvent(QEvent * e)
531 GraphicView::mouseEnterEvent();
532 QWidget::enterEvent(e);
535 void QG_GraphicView::focusOutEvent(QFocusEvent * e)
537 QWidget::focusOutEvent(e);
540 void QG_GraphicView::focusInEvent(QFocusEvent * e)
542 //printf("-->QG_GraphicView::focusInEvent(): Start. this %s focus...\n", (hasFocus() ? "has" : "DOES NOT HAVE"));
543 GraphicView::mouseEnterEvent();
544 QWidget::focusInEvent(e);
545 //printf("-->QG_GraphicView::focusInEvent(): End. this %s focus...\n", (hasFocus() ? "has" : "DOES NOT HAVE"));
549 * mouse wheel event. zooms in/out or scrolls when
550 * shift or ctrl is pressed.
552 void QG_GraphicView::wheelEvent(QWheelEvent * e)
554 //RS_DEBUG->print("wheel: %d", e->delta());
556 //printf("state: %d\n", e->state());
557 //printf("ctrl: %d\n", Qt::ControlButton);
561 Qt::KeyboardModifiers keyState = QApplication::keyboardModifiers();
562 Vector mouse = toGraph(Vector(e->x(), e->y()));
565 RS2::Direction direction = RS2::Up;
568 // if (e->state() == Qt::ControlModifier)
569 if (keyState == Qt::ControlModifier)
576 direction = RS2::Down;
578 // scroll left / right:
579 // else if (e->state() == Qt::ShiftModifier)
580 else if (keyState == Qt::ShiftModifier)
585 direction = RS2::Right;
587 direction = RS2::Left;
591 setCurrentAction(new RS_ActionZoomScroll(direction, *container, *this));
593 // else if (e->state() == 0)
594 else if (keyState == 0)
597 setCurrentAction(new RS_ActionZoomIn(*container, *this, RS2::In, RS2::Both, mouse));
599 setCurrentAction(new RS_ActionZoomIn(*container, *this, RS2::Out, RS2::Both, mouse));
605 void QG_GraphicView::keyPressEvent(QKeyEvent * e)
611 RS2::Direction direction = RS2::Up;
617 direction = RS2::Right;
621 direction = RS2::Left;
629 direction = RS2::Down;
637 setCurrentAction(new RS_ActionZoomScroll(direction, *container, *this));
639 GraphicView::keyPressEvent(e);
642 void QG_GraphicView::keyReleaseEvent(QKeyEvent * e)
644 GraphicView::keyReleaseEvent(e);
648 * Called whenever the graphic view has changed.
649 * Adjusts the scrollbar ranges / steps.
651 void QG_GraphicView::adjustOffsetControls()
653 static bool running = false;
660 RS_DEBUG->print("QG_GraphicView::adjustOffsetControls() begin");
662 if (!container || !hScrollBar || !vScrollBar)
666 int ox = getOffsetX();
667 int oy = getOffsetY();
669 Vector min = container->getMin();
670 Vector max = container->getMax();
672 // no drawing yet - still allow to scroll
673 if (max.x < min.x + 1.0e-6 || max.y < min.y + 1.0e-6
674 || max.x > RS_MAXDOUBLE || max.x < RS_MINDOUBLE
675 || min.x > RS_MAXDOUBLE || min.x < RS_MINDOUBLE
676 || max.y > RS_MAXDOUBLE || max.y < RS_MINDOUBLE
677 || min.y > RS_MAXDOUBLE || min.y < RS_MINDOUBLE)
679 min = Vector(-10, -10);
680 max = Vector(100, 100);
683 int minVal = (int)(min.x * getFactor().x - QG_SCROLLMARGIN - getBorderLeft());
684 int maxVal = (int)(max.x * getFactor().x - getWidth() + QG_SCROLLMARGIN + getBorderRight());
686 hScrollBar->setValue(0);
688 if (minVal <= maxVal)
689 hScrollBar->setRange(minVal, maxVal);
691 minVal = (int)(getHeight() - max.y * getFactor().y
692 - QG_SCROLLMARGIN - getBorderTop());
693 maxVal = (int)(QG_SCROLLMARGIN + getBorderBottom()
694 - (min.y * getFactor().y));
696 if (minVal <= maxVal)
697 vScrollBar->setRange(minVal, maxVal);
699 hScrollBar->setPageStep((int)(getWidth()));
700 vScrollBar->setPageStep((int)(getHeight()));
702 hScrollBar->setValue(-ox);
703 vScrollBar->setValue(oy);
708 RS_DEBUG->print("H min: %d / max: %d / step: %d / value: %d\n",
709 hScrollBar->minimum(), hScrollBar->maximum(), hScrollBar->pageStep(), ox);
710 RS_DEBUG->print("V min: %d / max: %d / step: %d / value: %d\n",
711 vScrollBar->minimum(), vScrollBar->maximum(), vScrollBar->pageStep(), oy);
715 RS_DEBUG->print("QG_GraphicView::adjustOffsetControls() end");
721 * override this to adjust controls and widgets that
722 * control the zoom factor of the graphic.
724 void QG_GraphicView::adjustZoomControls()
729 * Slot for horizontal scroll events.
731 void QG_GraphicView::slotHScrolled(int value)
733 // Scrollbar behaviour tends to change with every Qt version..
734 // so let's keep old code in here for now
736 //static int running = false;
739 ////RS_DEBUG->print("value x: %d\n", value);
740 if (hScrollBar->maximum() == hScrollBar->minimum())
745 //if (isUpdateEnabled()) {
755 * Slot for vertical scroll events.
757 void QG_GraphicView::slotVScrolled(int value)
759 // Scrollbar behaviour tends to change with every Qt version..
760 // so let's keep old code in here for now
762 //static int running = false;
765 ////RS_DEBUG->print("value y: %d\n", value);
766 if (vScrollBar->maximum() == vScrollBar->minimum())
771 //if (isUpdateEnabled()) {
781 * Handles paint events by redrawing the graphic in this view.
782 * usually that's very fast since we only paint the buffer we
783 * have from the last call..
785 * This is not true anymore--even if it was true in the first place, which
786 * seems doubtful to me. Everything is redrawn every time; we need to see if
787 * relying on Qt do to the clipping is wise or not... Also, we need to clarify
788 * all rendering paths since they are not all logical or consistent.
790 void QG_GraphicView::paintEvent(QPaintEvent *)
792 RS_DEBUG->print("QG_GraphicView::paintEvent begin");
794 if (simulationRunning)
798 To fix the broken rendering here, we need to do one of the following:
800 1) Put preview object here and fix draw code to do an update() instead
801 of trying to draw directly to a widget context
802 2) Use a QPixmap as a randomly drawable surface and fix createDirectPainter()
803 to do an update() after drawing to the pixmap which will eventually end
806 I think 1) is cleaner in the long run, and will make the codebase much more
807 maintainable. However, 2) would make it possible to fix things fairly easily
808 without having to rearrange classes too much.
810 The way the UI is written is crap. It tries to be clever by doing direct
811 painting, bypassing the toolkit's built in rendering pipeline. Also, there
812 are tons of references to Qt stuff in what is supposed to be a toolkit
813 independent manner, resulting in unnecessary divisions of code and rendundant
814 class definitions. Still, the core is still useful and worth fixing just to
815 have something back in portage. We can make it better, faster, stronger. ;-)
817 If we can make the UI more like Inkscape we'll be in good shape. Plus elements
818 of VectorWorks & etc. as well...
821 // Qt4 handles double buffering of screen writes now, so this needs
823 #warning "!!! Need to pass a valid QPainter to drawIt() !!!"
826 pntr.setBackgroundMode(Qt::OpaqueMode);
827 pntr.setBackground(QBrush(background));
829 painter = new PaintInterface(&pntr);
831 //Note that we do drawIt() regardless here because paintEvent() clears the background
832 //*unless* we create the window with a specific style. Depending on our draw code, we
833 //just may go that way...
836 // Draw the snapper first, we want to be able to see the preview on top of
838 if (snapper.Visible())
839 snapper.Draw(this, painter);
841 if (preview.Visible())
843 painter->setPen(RS_Pen(RS_Color(60, 255, 80), RS2::Width00, RS2::SolidLine));
844 painter->setOffset(preview.Offset());
846 // We have to traverse the container ourselves, because RS_Container::draw()
847 // uses drawEntity() instead of drawEntityPlain()...
848 for(RS_Entity * e=preview.firstEntity(RS2::ResolveNone); e!=NULL;
849 e = preview.nextEntity(RS2::ResolveNone))
854 painter->setOffset(Vector(0, 0));
860 RS_DEBUG->print("QG_GraphicView::paintEvent end");