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 "qg_dialogfactory.h"
18 #include "actiondefault.h"
19 #include "actionzoomin.h"
20 #include "actionzoompan.h"
21 #include "actionzoomscroll.h"
22 #include "actionmodifydelete.h"
23 #include "actionselectsingle.h"
24 #include "cadtoolbar.h"
25 #include "rs_dialogfactory.h"
27 #include "graphicview.h"
28 #include "rs_patternlist.h"
29 #include "paintinterface.h"
30 #include "rs_preview.h"
32 #include "rs_system.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 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 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 GraphicView::mouseMoveEvent(e);
432 QWidget::mouseMoveEvent(e);
436 * support for the wacom graphic tablet.
438 void QG_GraphicView::tabletEvent(QTabletEvent * e)
441 if (testAttribute(Qt::WA_UnderMouse))
445 case QTabletEvent::Eraser:
446 if (e->type() == QEvent::TabletRelease)
448 if (container != NULL)
450 ActionSelectSingle * a = new ActionSelectSingle(*container, *this);
453 // QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(), Qt::LeftButton, Qt::LeftButton);
454 // mouseReleaseEvent(&ev);
457 if (container->countSelected() > 0)
458 setCurrentAction(new ActionModifyDelete(*container, *this));
463 case QTabletEvent::Stylus:
464 case QTabletEvent::Puck:
465 if (e->type() == QEvent::TabletPress)
468 // QMouseEvent ev(QEvent::MouseButtonPress, e->pos(), Qt::LeftButton, Qt::LeftButton);
469 // mousePressEvent(&ev);
471 else if (e->type() == QEvent::TabletRelease)
474 // QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(), Qt::LeftButton, Qt::LeftButton);
475 // mouseReleaseEvent(&ev);
477 else if (e->type() == QEvent::TabletMove)
480 // QMouseEvent ev(QEvent::MouseMove, e->pos(), Qt::NoButton, 0);
481 // mouseMoveEvent(&ev);
491 /*if (e->pressure()>10 && lastPressure<10) {
492 QMouseEvent e(QEvent::MouseButtonPress, e->pos(),
493 Qt::LeftButton, Qt::LeftButton);
496 else if (e->pressure()<10 && lastPressure>10) {
497 QMouseEvent e(QEvent::MouseButtonRelease, e->pos(),
498 Qt::LeftButton, Qt::LeftButton);
499 mouseReleaseEvent(&e);
500 } else if (lastPos!=e->pos()) {
501 QMouseEvent e(QEvent::MouseMove, e->pos(),
506 lastPressure = e->pressure();
511 void QG_GraphicView::leaveEvent(QEvent * e)
513 GraphicView::mouseLeaveEvent();
514 QWidget::leaveEvent(e);
517 void QG_GraphicView::enterEvent(QEvent * e)
519 GraphicView::mouseEnterEvent();
520 QWidget::enterEvent(e);
523 void QG_GraphicView::focusOutEvent(QFocusEvent * e)
525 QWidget::focusOutEvent(e);
528 void QG_GraphicView::focusInEvent(QFocusEvent * e)
530 //printf("-->QG_GraphicView::focusInEvent(): Start. this %s focus...\n", (hasFocus() ? "has" : "DOES NOT HAVE"));
531 GraphicView::mouseEnterEvent();
532 QWidget::focusInEvent(e);
533 //printf("-->QG_GraphicView::focusInEvent(): End. this %s focus...\n", (hasFocus() ? "has" : "DOES NOT HAVE"));
537 * mouse wheel event. zooms in/out or scrolls when
538 * shift or ctrl is pressed.
540 void QG_GraphicView::wheelEvent(QWheelEvent * e)
542 //RS_DEBUG->print("wheel: %d", e->delta());
544 //printf("state: %d\n", e->state());
545 //printf("ctrl: %d\n", Qt::ControlButton);
549 Qt::KeyboardModifiers keyState = QApplication::keyboardModifiers();
550 Vector mouse = toGraph(Vector(e->x(), e->y()));
553 RS2::Direction direction = RS2::Up;
556 // if (e->state() == Qt::ControlModifier)
557 if (keyState == Qt::ControlModifier)
564 direction = RS2::Down;
566 // scroll left / right:
567 // else if (e->state() == Qt::ShiftModifier)
568 else if (keyState == Qt::ShiftModifier)
573 direction = RS2::Right;
575 direction = RS2::Left;
579 setCurrentAction(new ActionZoomScroll(direction, *container, *this));
581 // else if (e->state() == 0)
582 else if (keyState == 0)
585 setCurrentAction(new ActionZoomIn(*container, *this, RS2::In, RS2::Both, mouse));
587 setCurrentAction(new ActionZoomIn(*container, *this, RS2::Out, RS2::Both, mouse));
593 void QG_GraphicView::keyPressEvent(QKeyEvent * e)
599 RS2::Direction direction = RS2::Up;
605 direction = RS2::Right;
609 direction = RS2::Left;
617 direction = RS2::Down;
625 setCurrentAction(new ActionZoomScroll(direction, *container, *this));
627 GraphicView::keyPressEvent(e);
630 void QG_GraphicView::keyReleaseEvent(QKeyEvent * e)
632 GraphicView::keyReleaseEvent(e);
636 * Called whenever the graphic view has changed.
637 * Adjusts the scrollbar ranges / steps.
639 void QG_GraphicView::adjustOffsetControls()
641 static bool running = false;
648 RS_DEBUG->print("QG_GraphicView::adjustOffsetControls() begin");
650 if (!container || !hScrollBar || !vScrollBar)
654 int ox = getOffsetX();
655 int oy = getOffsetY();
657 Vector min = container->getMin();
658 Vector max = container->getMax();
660 // no drawing yet - still allow to scroll
661 if (max.x < min.x + 1.0e-6 || max.y < min.y + 1.0e-6
662 || max.x > RS_MAXDOUBLE || max.x < RS_MINDOUBLE
663 || min.x > RS_MAXDOUBLE || min.x < RS_MINDOUBLE
664 || max.y > RS_MAXDOUBLE || max.y < RS_MINDOUBLE
665 || min.y > RS_MAXDOUBLE || min.y < RS_MINDOUBLE)
667 min = Vector(-10, -10);
668 max = Vector(100, 100);
671 int minVal = (int)(min.x * getFactor().x - QG_SCROLLMARGIN - getBorderLeft());
672 int maxVal = (int)(max.x * getFactor().x - getWidth() + QG_SCROLLMARGIN + getBorderRight());
674 hScrollBar->setValue(0);
676 if (minVal <= maxVal)
677 hScrollBar->setRange(minVal, maxVal);
679 minVal = (int)(getHeight() - max.y * getFactor().y
680 - QG_SCROLLMARGIN - getBorderTop());
681 maxVal = (int)(QG_SCROLLMARGIN + getBorderBottom()
682 - (min.y * getFactor().y));
684 if (minVal <= maxVal)
685 vScrollBar->setRange(minVal, maxVal);
687 hScrollBar->setPageStep((int)(getWidth()));
688 vScrollBar->setPageStep((int)(getHeight()));
690 hScrollBar->setValue(-ox);
691 vScrollBar->setValue(oy);
696 RS_DEBUG->print("H min: %d / max: %d / step: %d / value: %d\n",
697 hScrollBar->minimum(), hScrollBar->maximum(), hScrollBar->pageStep(), ox);
698 RS_DEBUG->print("V min: %d / max: %d / step: %d / value: %d\n",
699 vScrollBar->minimum(), vScrollBar->maximum(), vScrollBar->pageStep(), oy);
703 RS_DEBUG->print("QG_GraphicView::adjustOffsetControls() end");
709 * override this to adjust controls and widgets that
710 * control the zoom factor of the graphic.
712 void QG_GraphicView::adjustZoomControls()
717 * Slot for horizontal scroll events.
719 void QG_GraphicView::slotHScrolled(int value)
721 // Scrollbar behaviour tends to change with every Qt version..
722 // so let's keep old code in here for now
724 //static int running = false;
727 ////RS_DEBUG->print("value x: %d\n", value);
728 if (hScrollBar->maximum() == hScrollBar->minimum())
733 //if (isUpdateEnabled()) {
743 * Slot for vertical scroll events.
745 void QG_GraphicView::slotVScrolled(int value)
747 // Scrollbar behaviour tends to change with every Qt version..
748 // so let's keep old code in here for now
750 //static int running = false;
753 ////RS_DEBUG->print("value y: %d\n", value);
754 if (vScrollBar->maximum() == vScrollBar->minimum())
759 //if (isUpdateEnabled()) {
769 * Handles paint events by redrawing the graphic in this view.
770 * usually that's very fast since we only paint the buffer we
771 * have from the last call..
773 * This is not true anymore--even if it was true in the first place, which
774 * seems doubtful to me. Everything is redrawn every time; we need to see if
775 * relying on Qt do to the clipping is wise or not... Also, we need to clarify
776 * all rendering paths since they are not all logical or consistent.
778 void QG_GraphicView::paintEvent(QPaintEvent *)
780 RS_DEBUG->print("QG_GraphicView::paintEvent begin");
782 if (simulationRunning)
786 To fix the broken rendering here, we need to do one of the following:
788 1) Put preview object here and fix draw code to do an update() instead
789 of trying to draw directly to a widget context
790 2) Use a QPixmap as a randomly drawable surface and fix createDirectPainter()
791 to do an update() after drawing to the pixmap which will eventually end
794 I think 1) is cleaner in the long run, and will make the codebase much more
795 maintainable. However, 2) would make it possible to fix things fairly easily
796 without having to rearrange classes too much.
798 The way the UI is written is crap. It tries to be clever by doing direct
799 painting, bypassing the toolkit's built in rendering pipeline. Also, there
800 are tons of references to Qt stuff in what is supposed to be a toolkit
801 independent manner, resulting in unnecessary divisions of code and rendundant
802 class definitions. Still, the core is still useful and worth fixing just to
803 have something back in portage. We can make it better, faster, stronger. ;-)
805 If we can make the UI more like Inkscape we'll be in good shape. Plus elements
806 of VectorWorks & etc. as well...
809 // Qt4 handles double buffering of screen writes now, so this needs
811 #warning "!!! Need to pass a valid QPainter to drawIt() !!!"
814 pntr.setBackgroundMode(Qt::OpaqueMode);
815 pntr.setBackground(QBrush(background));
817 painter = new PaintInterface(&pntr);
819 //Note that we do drawIt() regardless here because paintEvent() clears the background
820 //*unless* we create the window with a specific style. Depending on our draw code, we
821 //just may go that way...
824 // Draw the snapper first, we want to be able to see the preview on top of
826 if (snapper.Visible())
827 snapper.Draw(this, painter);
829 if (preview.Visible())
832 painter->setPen(RS_Pen(RS_Color(60, 255, 80), RS2::Width00, RS2::SolidLine));
833 painter->setOffset(preview.Offset());
835 // We have to traverse the container ourselves, because RS_Container::draw()
836 // uses drawEntity() instead of drawEntityPlain()...
837 for(RS_Entity * e=preview.firstEntity(RS2::ResolveNone); e!=NULL;
838 e = preview.nextEntity(RS2::ResolveNone))
843 painter->setOffset(Vector(0, 0));
846 preview.Draw(this, painter);
852 RS_DEBUG->print("QG_GraphicView::paintEvent end");