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_actionzoomin.h"
18 #include "rs_actionzoompan.h"
19 #include "rs_actionzoomscroll.h"
20 #include "rs_actionmodifydelete.h"
21 #include "rs_actionselectsingle.h"
22 #include "rs_dialogfactory.h"
23 #include "graphicview.h"
24 #include "rs_preview.h"
27 #include "rs_system.h"
28 #include "rs_patternlist.h"
29 #include "cadtoolbar.h"
30 #include "paintintf.h"
31 #include "qg_dialogfactory.h"
33 #define QG_SCROLLMARGIN 400
38 QG_GraphicView::QG_GraphicView(QWidget * parent, const char */*name*/, Qt::WindowFlags f):
39 QWidget(parent, f), GraphicView()//, refCount(0)
41 setBackground(background);
48 layout = new QGridLayout(this);
49 layout->setColumnStretch(0, 1);
50 layout->setColumnStretch(1, 0);
51 layout->setColumnStretch(2, 0);
52 layout->setRowStretch(0, 1);
53 layout->setRowStretch(1, 0);
56 void QGridLayout::addMultiCellWidget ( QWidget * w, int fromRow, int toRow, int fromCol, int toCol, int alignment = 0 )
57 Adds the widget w to the cell grid, spanning multiple rows/columns. The cell will span from fromRow, fromCol to toRow, toCol.
59 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.
61 A non-zero alignment indicates that the widget should not grow to fill the available space but should be sized according to sizeHint().
63 hScrollBar = new QG_ScrollBar(Qt::Horizontal, this);
64 // hScrollBar->setLineStep(50);
65 hScrollBar->setSingleStep(50);
66 // layout->addMultiCellWidget(hScrollBar, 1, 1, 0, 0);
67 layout->addWidget(hScrollBar, 1, 0);
68 // layout->addRowSpacing(1, hScrollBar->sizeHint().height());
69 layout->addItem(new QSpacerItem(0, hScrollBar->sizeHint().height()), 1, 0);
70 connect(hScrollBar, SIGNAL(valueChanged(int)), this, SLOT(slotHScrolled(int)));
72 vScrollBar = new QG_ScrollBar(Qt::Vertical, this);
73 // vScrollBar->setLineStep(50);
74 vScrollBar->setSingleStep(50);
75 // layout->addMultiCellWidget(vScrollBar, 0, 0, 2, 2);
76 layout->addWidget(vScrollBar, 0, 2);
77 // layout->addColSpacing(2, vScrollBar->sizeHint().width());
78 layout->addItem(new QSpacerItem(vScrollBar->sizeHint().width(), 0), 0, 2);
79 connect(vScrollBar, SIGNAL(valueChanged(int)), this, SLOT(slotVScrolled(int)));
84 QPixmap cur1(":/res/cur_cad_bmp.xpm");
85 bmp = QPixmap(":/res/cur_cad_mask.xpm");
87 curCad = new QCursor(cur1, 15, 15);
89 QPixmap cur2(":/res/cur_glass_bmp.xpm");
90 bmp = QPixmap(":/res/cur_glass_mask.xpm");
92 curMagnifier = new QCursor(cur2, 12, 12);
94 QPixmap cur3(":/res/cur_del_bmp.xpm");
95 bmp = QPixmap(":/res/cur_del_mask.xpm");
97 curDel = new QCursor(cur3, 15, 15);
99 QPixmap cur4(":/res/cur_select_bmp.xpm");
100 bmp = QPixmap(":/res/cur_select_mask.xpm");
102 curSelect = new QCursor(cur4, 15, 15);
104 QPixmap cur5(":/res/cur_hand_bmp.xpm");
105 bmp = QPixmap(":/res/cur_hand_mask.xpm");
107 curHand = new QCursor(cur5, 15, 15);
109 // No individual cursors for the Mac
117 // Dummy widgets for scrollbar corners:
118 //layout->addWidget(new QWidget(this), 1, 1);
119 //QWidget* w = new QWidget(this);
120 //w->setEraseColor(QColor(255,0,0));
121 gridStatus = new QLabel("-", this);
122 gridStatus->setAlignment(Qt::AlignRight);
123 // layout->addMultiCellWidget(gridStatus, 1, 1, 1, 2);
124 layout->addWidget(gridStatus, 1, 1, 1, 2);
125 // layout->addColSpacing(1, 50);
126 layout->addItem(new QSpacerItem(50, 0), 0, 1);
128 setMouseTracking(true);
129 // flickering under win:
130 //setFocusPolicy(WheelFocus);
132 #warning "QG_GraphicView constructor is setting Qt::NoFocus!!!"
133 // A-HAH! THIS IS THE PROBLEM!!!
134 // setFocusPolicy(Qt::NoFocus);
136 // setFocusPolicy(Qt::StrongFocus);
142 QG_GraphicView::~QG_GraphicView()
146 //Not sure about this...
147 // ((RS_PainterQt *)painter)->end();
161 * @return width of widget.
163 int QG_GraphicView::getWidth()
165 return width() - vScrollBar->sizeHint().width();
169 * @return height of widget.
171 int QG_GraphicView::getHeight()
173 return height() - hScrollBar->sizeHint().height();
177 * Changes the current background color of this view.
179 void QG_GraphicView::setBackground(const RS_Color & bg)
181 GraphicView::setBackground(bg);
183 // setBackgroundColor(bg);
185 palette.setColor(backgroundRole(), bg);
190 * Sets the mouse cursor to the given type.
192 void QG_GraphicView::setMouseCursor(RS2::CursorType c)
197 case RS2::ArrowCursor:
198 setCursor(Qt::ArrowCursor);
200 case RS2::UpArrowCursor:
201 setCursor(Qt::UpArrowCursor);
203 case RS2::CrossCursor:
204 setCursor(Qt::CrossCursor);
206 case RS2::WaitCursor:
207 setCursor(Qt::WaitCursor);
209 case RS2::IbeamCursor:
210 setCursor(Qt::IBeamCursor);
212 case RS2::SizeVerCursor:
213 setCursor(Qt::SizeVerCursor);
215 case RS2::SizeHorCursor:
216 setCursor(Qt::SizeHorCursor);
218 case RS2::SizeBDiagCursor:
219 setCursor(Qt::SizeBDiagCursor);
221 case RS2::SizeFDiagCursor:
222 setCursor(Qt::SizeFDiagCursor);
224 case RS2::SizeAllCursor:
225 setCursor(Qt::SizeAllCursor);
227 case RS2::BlankCursor:
228 setCursor(Qt::BlankCursor);
230 case RS2::SplitVCursor:
231 setCursor(Qt::SplitVCursor);
233 case RS2::SplitHCursor:
234 setCursor(Qt::SplitHCursor);
236 case RS2::PointingHandCursor:
237 setCursor(Qt::PointingHandCursor);
239 case RS2::ForbiddenCursor:
240 setCursor(Qt::ForbiddenCursor);
242 case RS2::WhatsThisCursor:
243 setCursor(Qt::WhatsThisCursor);
253 case RS2::SelectCursor:
254 setCursor(*curSelect);
256 case RS2::MagnifierCursor:
257 setCursor(*curMagnifier);
259 case RS2::MovingHandCursor:
263 // Reduced cursor selection for the Mac:
265 setCursor(Qt::CrossCursor);
268 setCursor(Qt::CrossCursor);
270 case RS2::SelectCursor:
271 setCursor(Qt::CrossCursor);
273 case RS2::MagnifierCursor:
274 setCursor(Qt::CrossCursor);
276 case RS2::MovingHandCursor:
277 setCursor(Qt::PointingHandCursor);
284 * Sets the text for the grid status widget in the left bottom corner.
286 void QG_GraphicView::updateGridStatusWidget(const QString & text)
288 gridStatus->setText(text);
292 * Redraws the widget.
294 void QG_GraphicView::redraw()
296 RS_DEBUG->print("QG_GraphicView::redraw begin 1");
298 if (simulationRunning)
301 //Also, this stuff is properly taken care of by the Qt toolkit--
302 //so we don't have to be concerned with shit like this as long
303 //as we use update() instead of repaint()
305 static bool running = false;
310 RS_DEBUG->print("QG_GraphicView::redraw begin 2");
313 This is the only place in the entire codebase that a proper update is
316 if (isUpdateEnabled())
320 RS_DEBUG->print("QG_GraphicView::redraw end 2");
324 RS_DEBUG->print("QG_GraphicView::redraw end 1");
327 void QG_GraphicView::resizeEvent(QResizeEvent * /*e*/)
329 RS_DEBUG->print("QG_GraphicView::resizeEvent begin");
330 adjustOffsetControls();
331 adjustZoomControls();
333 RS_DEBUG->print("QG_GraphicView::resizeEvent end");
336 // Next three methods from RS_LayerListListener Interface:
337 void QG_GraphicView::layerEdited(RS_Layer *)
342 void QG_GraphicView::layerRemoved(RS_Layer *)
347 void QG_GraphicView::layerToggled(RS_Layer *)
352 void QG_GraphicView::emulateMouseMoveEvent()
354 // QMouseEvent e(QEvent::MouseMove, QPoint(mx, my), Qt::NoButton, Qt::NoButton);
355 //mouseMoveEvent(&e);
358 void QG_GraphicView::mousePressEvent(QMouseEvent * e)
360 // pan zoom with middle mouse button
361 if (e->button() == Qt::MidButton /*|| (e->state()==Qt::LeftButton|Qt::AltButton)*/)
362 setCurrentAction(new RS_ActionZoomPan(*container, *this));
364 GraphicView::mousePressEvent(e);
365 QWidget::mousePressEvent(e);
368 void QG_GraphicView::mouseReleaseEvent(QMouseEvent * e)
370 RS_DEBUG->print("QG_GraphicView::mouseReleaseEvent");
371 GraphicView::mouseReleaseEvent(e);
372 //QWidget::mouseReleaseEvent(e);
374 if (!e->isAccepted())
376 if (QG_DIALOGFACTORY != NULL && QG_DIALOGFACTORY->getCadToolBar() != NULL)
378 RS_DEBUG->print("QG_GraphicView::mouseReleaseEvent: fwd to cadtoolbar");
379 QG_DIALOGFACTORY->getCadToolBar()->mouseReleaseEvent(e);
383 RS_DEBUG->print("QG_GraphicView::mouseReleaseEvent: OK");
386 void QG_GraphicView::mouseMoveEvent(QMouseEvent * e)
388 //RS_DEBUG->print("QG_GraphicView::mouseMoveEvent begin");
389 //QMouseEvent rsm = QG_Qt2Rs::mouseEvent(e);
391 GraphicView::mouseMoveEvent(e);
392 QWidget::mouseMoveEvent(e);
394 //What kind of cacamamie crap is this???
396 // // make sure that we can still use hotkeys and the mouse wheel
397 // if (parent() != NULL)
398 // ((QWidget *)parent())->setFocus();
401 //RS_DEBUG->print("QG_GraphicView::mouseMoveEvent end");
405 * support for the wacom graphic tablet.
407 void QG_GraphicView::tabletEvent(QTabletEvent * e)
410 if (testAttribute(Qt::WA_UnderMouse))
414 case QTabletEvent::Eraser:
415 if (e->type() == QEvent::TabletRelease)
417 if (container != NULL)
419 RS_ActionSelectSingle * a = new RS_ActionSelectSingle(*container, *this);
422 // QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(), Qt::LeftButton, Qt::LeftButton);
423 // mouseReleaseEvent(&ev);
426 if (container->countSelected() > 0)
427 setCurrentAction(new RS_ActionModifyDelete(*container, *this));
432 case QTabletEvent::Stylus:
433 case QTabletEvent::Puck:
434 if (e->type() == QEvent::TabletPress)
437 // QMouseEvent ev(QEvent::MouseButtonPress, e->pos(), Qt::LeftButton, Qt::LeftButton);
438 // mousePressEvent(&ev);
440 else if (e->type() == QEvent::TabletRelease)
443 // QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(), Qt::LeftButton, Qt::LeftButton);
444 // mouseReleaseEvent(&ev);
446 else if (e->type() == QEvent::TabletMove)
449 // QMouseEvent ev(QEvent::MouseMove, e->pos(), Qt::NoButton, 0);
450 // mouseMoveEvent(&ev);
460 /*if (e->pressure()>10 && lastPressure<10) {
461 QMouseEvent e(QEvent::MouseButtonPress, e->pos(),
462 Qt::LeftButton, Qt::LeftButton);
465 else if (e->pressure()<10 && lastPressure>10) {
466 QMouseEvent e(QEvent::MouseButtonRelease, e->pos(),
467 Qt::LeftButton, Qt::LeftButton);
468 mouseReleaseEvent(&e);
469 } else if (lastPos!=e->pos()) {
470 QMouseEvent e(QEvent::MouseMove, e->pos(),
475 lastPressure = e->pressure();
480 void QG_GraphicView::leaveEvent(QEvent * e)
482 GraphicView::mouseLeaveEvent();
483 QWidget::leaveEvent(e);
486 void QG_GraphicView::enterEvent(QEvent * e)
488 GraphicView::mouseEnterEvent();
489 QWidget::enterEvent(e);
492 void QG_GraphicView::focusOutEvent(QFocusEvent * e)
494 QWidget::focusOutEvent(e);
497 void QG_GraphicView::focusInEvent(QFocusEvent * e)
499 //printf("-->QG_GraphicView::focusInEvent(): Start. this %s focus...\n", (hasFocus() ? "has" : "DOES NOT HAVE"));
500 GraphicView::mouseEnterEvent();
501 QWidget::focusInEvent(e);
502 //printf("-->QG_GraphicView::focusInEvent(): End. this %s focus...\n", (hasFocus() ? "has" : "DOES NOT HAVE"));
506 * mouse wheel event. zooms in/out or scrolls when
507 * shift or ctrl is pressed.
509 void QG_GraphicView::wheelEvent(QWheelEvent * e)
511 //RS_DEBUG->print("wheel: %d", e->delta());
513 //printf("state: %d\n", e->state());
514 //printf("ctrl: %d\n", Qt::ControlButton);
515 Qt::KeyboardModifiers keyState = QApplication::keyboardModifiers();
517 if (container == NULL)
520 Vector mouse = toGraph(Vector(e->x(), e->y()));
523 RS2::Direction direction = RS2::Up;
526 // if (e->state() == Qt::ControlModifier)
527 if (keyState == Qt::ControlModifier)
534 direction = RS2::Down;
536 // scroll left / right:
537 // else if (e->state() == Qt::ShiftModifier)
538 else if (keyState == Qt::ShiftModifier)
543 direction = RS2::Right;
545 direction = RS2::Left;
549 setCurrentAction(new RS_ActionZoomScroll(direction, *container, *this));
551 // else if (e->state() == 0)
552 else if (keyState == 0)
555 setCurrentAction(new RS_ActionZoomIn(*container, *this, RS2::In, RS2::Both, mouse));
557 setCurrentAction(new RS_ActionZoomIn(*container, *this, RS2::Out, RS2::Both, mouse));
563 void QG_GraphicView::keyPressEvent(QKeyEvent * e)
565 if (container == NULL)
569 RS2::Direction direction = RS2::Up;
575 direction = RS2::Right;
579 direction = RS2::Left;
587 direction = RS2::Down;
595 setCurrentAction(new RS_ActionZoomScroll(direction, *container, *this));
597 GraphicView::keyPressEvent(e);
600 void QG_GraphicView::keyReleaseEvent(QKeyEvent * e)
602 GraphicView::keyReleaseEvent(e);
606 * Called whenever the graphic view has changed.
607 * Adjusts the scrollbar ranges / steps.
609 void QG_GraphicView::adjustOffsetControls()
611 static bool running = false;
618 RS_DEBUG->print("QG_GraphicView::adjustOffsetControls() begin");
620 if (container == NULL || hScrollBar == NULL || vScrollBar == NULL)
624 int ox = getOffsetX();
625 int oy = getOffsetY();
627 Vector min = container->getMin();
628 Vector max = container->getMax();
630 // no drawing yet - still allow to scroll
631 if (max.x < min.x+1.0e-6 || max.y < min.y+1.0e-6 ||
632 max.x > RS_MAXDOUBLE || max.x < RS_MINDOUBLE ||
633 min.x > RS_MAXDOUBLE || min.x < RS_MINDOUBLE ||
634 max.y > RS_MAXDOUBLE || max.y < RS_MINDOUBLE ||
635 min.y > RS_MAXDOUBLE || min.y < RS_MINDOUBLE)
637 min = Vector(-10, -10);
638 max = Vector(100, 100);
641 int minVal = (int)(min.x * getFactor().x - QG_SCROLLMARGIN - getBorderLeft());
642 int maxVal = (int)(max.x * getFactor().x - getWidth() + QG_SCROLLMARGIN + getBorderRight());
644 hScrollBar->setValue(0);
646 if (minVal <= maxVal)
647 hScrollBar->setRange(minVal, maxVal);
649 minVal = (int)(getHeight() - max.y * getFactor().y
650 - QG_SCROLLMARGIN - getBorderTop());
651 maxVal = (int)(QG_SCROLLMARGIN + getBorderBottom()
652 - (min.y * getFactor().y));
654 if (minVal <= maxVal)
655 vScrollBar->setRange(minVal, maxVal);
657 hScrollBar->setPageStep((int)(getWidth()));
658 vScrollBar->setPageStep((int)(getHeight()));
660 hScrollBar->setValue(-ox);
661 vScrollBar->setValue(oy);
666 RS_DEBUG->print("H min: %d / max: %d / step: %d / value: %d\n",
667 hScrollBar->minimum(), hScrollBar->maximum(), hScrollBar->pageStep(), ox);
668 RS_DEBUG->print("V min: %d / max: %d / step: %d / value: %d\n",
669 vScrollBar->minimum(), vScrollBar->maximum(), vScrollBar->pageStep(), oy);
673 RS_DEBUG->print("QG_GraphicView::adjustOffsetControls() end");
679 * override this to adjust controls and widgets that
680 * control the zoom factor of the graphic.
682 void QG_GraphicView::adjustZoomControls()
687 * Slot for horizontal scroll events.
689 void QG_GraphicView::slotHScrolled(int value)
691 // Scrollbar behaviour tends to change with every Qt version..
692 // so let's keep old code in here for now
694 //static int running = false;
697 ////RS_DEBUG->print("value x: %d\n", value);
698 if (hScrollBar->maximum() == hScrollBar->minimum())
703 //if (isUpdateEnabled()) {
713 * Slot for vertical scroll events.
715 void QG_GraphicView::slotVScrolled(int value)
717 // Scrollbar behaviour tends to change with every Qt version..
718 // so let's keep old code in here for now
720 //static int running = false;
723 ////RS_DEBUG->print("value y: %d\n", value);
724 if (vScrollBar->maximum() == vScrollBar->minimum())
729 //if (isUpdateEnabled()) {
739 * Handles paint events by redrawing the graphic in this view.
740 * usually that's very fast since we only paint the buffer we
741 * have from the last call..
743 * This is not true anymore--even if it was true in the first place, which
744 * seems doubtful to me. Everything is redrawn every time; we need to see if
745 * relying on Qt do to the clipping is wise or not... Also, we need to clarify
746 * all rendering paths since they are not all logical or consistent.
748 void QG_GraphicView::paintEvent(QPaintEvent *)
750 RS_DEBUG->print("QG_GraphicView::paintEvent begin");
752 if (simulationRunning)
756 To fix the broken rendering here, we need to do one of the following:
758 1) Put preview object here and fix draw code to do an update() instead
759 of trying to draw directly to a widget context
760 2) Use a QPixmap as a randomly drawable surface and fix createDirectPainter()
761 to do an update() after drawing to the pixmap which will eventually end
764 I think 1) is cleaner in the long run, and will make the codebase much more
765 maintainable. However, 2) would make it possible to fix things fairly easily
766 without having to rearrange classes too much.
768 The way the UI is written is crap. It tries to be clever by doing direct
769 painting, bypassing the toolkit's built in rendering pipeline. Also, there
770 are tons of references to Qt stuff in what is supposed to be a toolkit
771 independent manner, resulting in unnecessary divisions of code and rendundant
772 class definitions. Still, the core is still useful and worth fixing just to
773 have something back in portage. We can make it better, faster, stronger. ;-)
775 If we can make the UI more like Inkscape we'll be in good shape. Plus elements
776 of VectorWorks & etc. as well...
779 // Qt4 handles double buffering of screen writes now, so this needs
781 #warning "!!! Need to pass a valid QPainter to drawIt() !!!"
785 //#warning "!!! Double buffering is out... !!!"
786 // Seems most rendering is going through this path... Hrm.
790 wPainter.begin(this);
791 wPainter.drawPixmap(0, 0, *buffer);
798 //// RS_DEBUG->print(RS_Debug::D_CRITICAL, "paintEvent seems to be called recursively: refCount=%u", refCount);
799 // printf("paintEvent seems to be called recursively: refCount=%u\n", refCount);
803 pntr.setBackgroundMode(Qt::OpaqueMode);
804 // pntr.setBackgroundColor(background);
805 pntr.setBackground(QBrush(background));
807 painter = new PaintInterface(&pntr);
812 painter = new RS_PainterQt(this);
814 //Nope, this didn't fix the problem...
815 //The problem is, unless we specifically tell Qt NOT to
816 //clear the window every time, it will...
820 painter->setDrawingMode(drawingMode);
821 ((RS_PainterQt *)painter)->setBackgroundMode(Qt::OpaqueMode);
822 ((RS_PainterQt *)painter)->setBackgroundColor(background);
823 // ((RS_PainterQt *)painter)->eraseRect(0, 0, getWidth(), getHeight());
828 printf("QG_GraphicView::paintEvent: painter is NOT NULL!\n");
833 //Note that we do drawIt() regardless here because paintEvent() clears the background
834 //*unless* we create the window with a specific style. Depending on our draw code, we
835 //just may go that way...
837 //Need some logic here to do drawing in preview mode, since it'll be calling
838 //update now instead of trying to do a direct draw...
841 //hrm. painter->setCompositionMode(QPainter::CompositionMode_Xor);
842 //needed anymore? painter->setXORMode();
843 painter->setOffset(previewOffset);
844 drawEntity(previewEntity);//meh -> , 1.0, false);
845 painter->setOffset(Vector(0, 0));
846 // We'll set previewMode to false here, just because we can
856 if (snapCoord1.valid)
859 //This is causing segfaults in the Qt::Painter code...
860 //*This* is causing the segfault!
861 //Actually, it looks like buggy painting code in PaintInterface()...
862 painter->drawCircle(toGui(snapCoord1), 4);
866 if (showCrosshairs1 == true)
868 painter->setPen(RS_Pen(RS_Color(0, 255, 255), RS2::Width00, RS2::DashLine));
869 painter->drawLine(Vector(0, toGuiY(snapCoord1.y)),
870 Vector(getWidth(), toGuiY(snapCoord1.y)));
871 painter->drawLine(Vector(toGuiX(snapCoord1.x), 0),
872 Vector(toGuiX(snapCoord1.x), getHeight()));
877 if (snapCoord1.valid && snapCoord1 != snapSpot1)
879 painter->drawLine(toGui(snapSpot1) + Vector(-5, 0), toGui(snapSpot1) + Vector(-1, 4));
880 painter->drawLine(toGui(snapSpot1) + Vector(0, 5), toGui(snapSpot1) + Vector(4, 1));
881 painter->drawLine(toGui(snapSpot1) + Vector(5, 0), toGui(snapSpot1) + Vector(1, -4));
882 painter->drawLine(toGui(snapSpot1) + Vector(0, -5), toGui(snapSpot1) + Vector(-4, -1));
893 RS_DEBUG->print("QG_GraphicView::paintEvent end");
898 * Previews the given url for the file open dialog.
900 void QG_GraphicView::previewUrl(const Q3Url & u)
902 //static Drawing* gr = new Drawing();
904 RS_DEBUG->print("QG_GraphicView::previewUrl");
906 if (container != NULL && container->rtti() == RS2::EntityGraphic)
908 ((Drawing *)container)->open(u.path(), RS2::FormatUnknown);
913 RS_DEBUG->print("QG_GraphicView::previewUrl: OK");