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 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
10 // JLH = James L. Hammons <jlhamm@acm.org>
13 // --- ---------- -----------------------------------------------------------
14 // JLH 05/10/2010 Added this text. :-)
17 #include "qg_graphicview.h"
19 #include "qg_dialogfactory.h"
20 #include "actiondefault.h"
21 #include "actionzoomin.h"
22 #include "actionzoompan.h"
23 #include "actionzoomscroll.h"
24 #include "actionmodifydelete.h"
25 #include "actionselectsingle.h"
26 #include "cadtoolbar.h"
27 #include "dialogfactory.h"
29 #include "graphicview.h"
30 #include "patternlist.h"
31 #include "paintinterface.h"
36 #define QG_SCROLLMARGIN 400
41 QG_GraphicView::QG_GraphicView(QWidget * parent, const char */*name*/, Qt::WindowFlags f):
42 QWidget(parent, f), GraphicView()//, refCount(0)
44 DEBUG->print("QG_GraphicView::QG_GraphicView(parent, name, flags)...");
45 CommonInitialization();
51 QG_GraphicView::QG_GraphicView(Document * doc, QWidget * parent)
53 // QG_GraphicView(parent, "graphicview");
54 CommonInitialization();
56 DEBUG->print("QG_GraphicView::QG_GraphicView(doc, parent)...");
58 DEBUG->print(" Setting Container...");
63 DEBUG->print(" container set.");
67 setBorders(10, 10, 10, 10);
70 setDefaultAction(new ActionDefault(*doc, *this));
73 void QG_GraphicView::CommonInitialization(void)
75 setBackground(background);
82 DEBUG->print("--> Setting up layout...");
83 QGridLayout * layout = new QGridLayout(this);
84 layout->setColumnStretch(0, 1);
85 layout->setColumnStretch(1, 0);
86 layout->setColumnStretch(2, 0);
87 layout->setRowStretch(0, 1);
88 layout->setRowStretch(1, 0);
91 void QGridLayout::addMultiCellWidget ( QWidget * w, int fromRow, int toRow, int fromCol, int toCol, int alignment = 0 )
92 Adds the widget w to the cell grid, spanning multiple rows/columns. The cell will span from fromRow, fromCol to toRow, toCol.
94 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.
96 A non-zero alignment indicates that the widget should not grow to fill the available space but should be sized according to sizeHint().
98 DEBUG->print("--> Creating hScrollBar...");
99 // hScrollBar = new QG_ScrollBar(Qt::Horizontal, this);
100 hScrollBar = new QScrollBar(Qt::Horizontal, this);
101 // hScrollBar->setLineStep(50);
102 hScrollBar->setSingleStep(50);
103 // layout->addMultiCellWidget(hScrollBar, 1, 1, 0, 0);
104 layout->addWidget(hScrollBar, 1, 0);
105 // layout->addRowSpacing(1, hScrollBar->sizeHint().height());
106 layout->addItem(new QSpacerItem(0, hScrollBar->sizeHint().height()), 1, 0);
108 DEBUG->print("--> Creating vScrollBar...");
109 // vScrollBar = new QG_ScrollBar(Qt::Vertical, this);
110 vScrollBar = new QScrollBar(Qt::Vertical, this);
111 // vScrollBar->setLineStep(50);
112 vScrollBar->setSingleStep(50);
113 // layout->addMultiCellWidget(vScrollBar, 0, 0, 2, 2);
114 layout->addWidget(vScrollBar, 0, 2);
115 // layout->addColSpacing(2, vScrollBar->sizeHint().width());
116 layout->addItem(new QSpacerItem(vScrollBar->sizeHint().width(), 0), 0, 2);
118 connect(hScrollBar, SIGNAL(valueChanged(int)), this, SLOT(slotHScrolled(int)));
119 connect(vScrollBar, SIGNAL(valueChanged(int)), this, SLOT(slotVScrolled(int)));
124 QPixmap cur1(":/res/cur_cad_bmp.xpm");
125 bmp = QPixmap(":/res/cur_cad_mask.xpm");
127 curCad = new QCursor(cur1, 15, 15);
129 QPixmap cur2(":/res/cur_glass_bmp.xpm");
130 bmp = QPixmap(":/res/cur_glass_mask.xpm");
132 curMagnifier = new QCursor(cur2, 12, 12);
134 QPixmap cur3(":/res/cur_del_bmp.xpm");
135 bmp = QPixmap(":/res/cur_del_mask.xpm");
137 curDel = new QCursor(cur3, 15, 15);
139 QPixmap cur4(":/res/cur_select_bmp.xpm");
140 bmp = QPixmap(":/res/cur_select_mask.xpm");
142 curSelect = new QCursor(cur4, 15, 15);
144 QPixmap cur5(":/res/cur_hand_bmp.xpm");
145 bmp = QPixmap(":/res/cur_hand_mask.xpm");
147 curHand = new QCursor(cur5, 15, 15);
149 // No individual cursors for the Mac
157 // Dummy widgets for scrollbar corners:
158 //layout->addWidget(new QWidget(this), 1, 1);
159 //QWidget* w = new QWidget(this);
160 //w->setEraseColor(QColor(255,0,0));
161 gridStatus = new QLabel("-", this);
162 gridStatus->setAlignment(Qt::AlignRight);
163 // gridStatus->setAutoFillBackground(true);
164 // layout->addMultiCellWidget(gridStatus, 1, 1, 1, 2);
165 layout->addWidget(gridStatus, 1, 1, 1, 2);
166 // layout->addColSpacing(1, 50);
167 layout->addItem(new QSpacerItem(50, 0), 0, 1);
169 setMouseTracking(true);
170 // flickering under win:
171 //setFocusPolicy(WheelFocus);
173 #warning "QG_GraphicView constructor is setting Qt::NoFocus!!!"
174 // A-HAH! THIS IS THE PROBLEM!!!
175 // setFocusPolicy(Qt::NoFocus);
177 // setFocusPolicy(Qt::StrongFocus);
183 QG_GraphicView::~QG_GraphicView()
198 * @return width of widget.
200 int QG_GraphicView::getWidth()
202 return width() - vScrollBar->sizeHint().width();
206 * @return height of widget.
208 int QG_GraphicView::getHeight()
210 return height() - hScrollBar->sizeHint().height();
214 * Changes the current background color of this view.
216 void QG_GraphicView::setBackground(const Color & bg)
218 GraphicView::setBackground(bg);
220 // setBackgroundColor(bg);
222 palette.setColor(backgroundRole(), bg);
227 * Sets the mouse cursor to the given type.
229 void QG_GraphicView::setMouseCursor(RS2::CursorType c)
234 case RS2::ArrowCursor:
235 setCursor(Qt::ArrowCursor);
237 case RS2::UpArrowCursor:
238 setCursor(Qt::UpArrowCursor);
240 case RS2::CrossCursor:
241 setCursor(Qt::CrossCursor);
243 case RS2::WaitCursor:
244 setCursor(Qt::WaitCursor);
246 case RS2::IbeamCursor:
247 setCursor(Qt::IBeamCursor);
249 case RS2::SizeVerCursor:
250 setCursor(Qt::SizeVerCursor);
252 case RS2::SizeHorCursor:
253 setCursor(Qt::SizeHorCursor);
255 case RS2::SizeBDiagCursor:
256 setCursor(Qt::SizeBDiagCursor);
258 case RS2::SizeFDiagCursor:
259 setCursor(Qt::SizeFDiagCursor);
261 case RS2::SizeAllCursor:
262 setCursor(Qt::SizeAllCursor);
264 case RS2::BlankCursor:
265 setCursor(Qt::BlankCursor);
267 case RS2::SplitVCursor:
268 setCursor(Qt::SplitVCursor);
270 case RS2::SplitHCursor:
271 setCursor(Qt::SplitHCursor);
273 case RS2::PointingHandCursor:
274 setCursor(Qt::PointingHandCursor);
276 case RS2::ForbiddenCursor:
277 setCursor(Qt::ForbiddenCursor);
279 case RS2::WhatsThisCursor:
280 setCursor(Qt::WhatsThisCursor);
290 case RS2::SelectCursor:
291 setCursor(*curSelect);
293 case RS2::MagnifierCursor:
294 setCursor(*curMagnifier);
296 case RS2::MovingHandCursor:
300 // Reduced cursor selection for the Mac:
302 setCursor(Qt::CrossCursor);
305 setCursor(Qt::CrossCursor);
307 case RS2::SelectCursor:
308 setCursor(Qt::CrossCursor);
310 case RS2::MagnifierCursor:
311 setCursor(Qt::CrossCursor);
313 case RS2::MovingHandCursor:
314 setCursor(Qt::PointingHandCursor);
321 * Sets the text for the grid status widget in the left bottom corner.
323 void QG_GraphicView::updateGridStatusWidget(const QString & text)
325 gridStatus->setText(text);
329 * Redraws the widget.
331 void QG_GraphicView::redraw()
333 DEBUG->print("QG_GraphicView::redraw begin 1");
335 if (simulationRunning)
338 //Also, this stuff is properly taken care of by the Qt toolkit--
339 //so we don't have to be concerned with shit like this as long
340 //as we use update() instead of repaint()
342 static bool running = false;
347 DEBUG->print("QG_GraphicView::redraw begin 2");
350 This is the only place in the entire codebase that a proper update is
353 if (isUpdateEnabled())
357 DEBUG->print("QG_GraphicView::redraw end 2");
361 DEBUG->print("QG_GraphicView::redraw end 1");
364 void QG_GraphicView::resizeEvent(QResizeEvent * /*e*/)
366 DEBUG->print("QG_GraphicView::resizeEvent begin");
367 adjustOffsetControls();
368 adjustZoomControls();
370 DEBUG->print("QG_GraphicView::resizeEvent end");
374 This is retarded. There is absolutely no reason to have this shit in here.
375 This could be easily called from whoever calls this crap.
377 And so... We remove it.
380 // Next three methods from LayerListListener Interface:
381 void QG_GraphicView::layerEdited(Layer *)
386 void QG_GraphicView::layerRemoved(Layer *)
391 void QG_GraphicView::layerToggled(Layer *)
397 void QG_GraphicView::emulateMouseMoveEvent()
399 // QMouseEvent e(QEvent::MouseMove, QPoint(mx, my), Qt::NoButton, Qt::NoButton);
400 //mouseMoveEvent(&e);
403 void QG_GraphicView::mousePressEvent(QMouseEvent * e)
405 // pan zoom with middle mouse button
406 if (e->button() == Qt::MidButton /*|| (e->state()==Qt::LeftButton|Qt::AltButton)*/)
407 setCurrentAction(new ActionZoomPan(*container, *this));
409 GraphicView::mousePressEvent(e);
410 QWidget::mousePressEvent(e);
413 void QG_GraphicView::mouseReleaseEvent(QMouseEvent * e)
415 DEBUG->print("QG_GraphicView::mouseReleaseEvent");
416 GraphicView::mouseReleaseEvent(e);
417 //QWidget::mouseReleaseEvent(e);
419 if (!e->isAccepted())
421 if (QG_DIALOGFACTORY && QG_DIALOGFACTORY->getCadToolBar())
423 DEBUG->print("QG_GraphicView::mouseReleaseEvent: fwd to cadtoolbar");
424 QG_DIALOGFACTORY->getCadToolBar()->mouseReleaseEvent(e);
428 DEBUG->print("QG_GraphicView::mouseReleaseEvent: OK");
431 void QG_GraphicView::mouseMoveEvent(QMouseEvent * e)
433 GraphicView::mouseMoveEvent(e);
434 QWidget::mouseMoveEvent(e);
438 * support for the wacom graphic tablet.
440 void QG_GraphicView::tabletEvent(QTabletEvent * e)
443 if (testAttribute(Qt::WA_UnderMouse))
447 case QTabletEvent::Eraser:
448 if (e->type() == QEvent::TabletRelease)
450 if (container != NULL)
452 ActionSelectSingle * a = new ActionSelectSingle(*container, *this);
455 // QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(), Qt::LeftButton, Qt::LeftButton);
456 // mouseReleaseEvent(&ev);
459 if (container->countSelected() > 0)
460 setCurrentAction(new ActionModifyDelete(*container, *this));
465 case QTabletEvent::Stylus:
466 case QTabletEvent::Puck:
467 if (e->type() == QEvent::TabletPress)
470 // QMouseEvent ev(QEvent::MouseButtonPress, e->pos(), Qt::LeftButton, Qt::LeftButton);
471 // mousePressEvent(&ev);
473 else if (e->type() == QEvent::TabletRelease)
476 // QMouseEvent ev(QEvent::MouseButtonRelease, e->pos(), Qt::LeftButton, Qt::LeftButton);
477 // mouseReleaseEvent(&ev);
479 else if (e->type() == QEvent::TabletMove)
482 // QMouseEvent ev(QEvent::MouseMove, e->pos(), Qt::NoButton, 0);
483 // mouseMoveEvent(&ev);
493 /*if (e->pressure()>10 && lastPressure<10) {
494 QMouseEvent e(QEvent::MouseButtonPress, e->pos(),
495 Qt::LeftButton, Qt::LeftButton);
498 else if (e->pressure()<10 && lastPressure>10) {
499 QMouseEvent e(QEvent::MouseButtonRelease, e->pos(),
500 Qt::LeftButton, Qt::LeftButton);
501 mouseReleaseEvent(&e);
502 } else if (lastPos!=e->pos()) {
503 QMouseEvent e(QEvent::MouseMove, e->pos(),
508 lastPressure = e->pressure();
513 void QG_GraphicView::leaveEvent(QEvent * e)
515 GraphicView::mouseLeaveEvent();
516 QWidget::leaveEvent(e);
519 void QG_GraphicView::enterEvent(QEvent * e)
521 GraphicView::mouseEnterEvent();
522 QWidget::enterEvent(e);
525 void QG_GraphicView::focusOutEvent(QFocusEvent * e)
527 QWidget::focusOutEvent(e);
530 void QG_GraphicView::focusInEvent(QFocusEvent * e)
532 //printf("-->QG_GraphicView::focusInEvent(): Start. this %s focus...\n", (hasFocus() ? "has" : "DOES NOT HAVE"));
533 GraphicView::mouseEnterEvent();
534 QWidget::focusInEvent(e);
535 //printf("-->QG_GraphicView::focusInEvent(): End. this %s focus...\n", (hasFocus() ? "has" : "DOES NOT HAVE"));
539 * mouse wheel event. zooms in/out or scrolls when
540 * shift or ctrl is pressed.
542 void QG_GraphicView::wheelEvent(QWheelEvent * e)
544 //DEBUG->print("wheel: %d", e->delta());
546 //printf("state: %d\n", e->state());
547 //printf("ctrl: %d\n", Qt::ControlButton);
551 Qt::KeyboardModifiers keyState = QApplication::keyboardModifiers();
552 Vector mouse = toGraph(Vector(e->x(), e->y()));
555 RS2::Direction direction = RS2::Up;
558 // if (e->state() == Qt::ControlModifier)
559 if (keyState == Qt::ControlModifier)
566 direction = RS2::Down;
568 // scroll left / right:
569 // else if (e->state() == Qt::ShiftModifier)
570 else if (keyState == Qt::ShiftModifier)
575 direction = RS2::Right;
577 direction = RS2::Left;
581 setCurrentAction(new ActionZoomScroll(direction, *container, *this));
583 // else if (e->state() == 0)
584 else if (keyState == 0)
587 setCurrentAction(new ActionZoomIn(*container, *this, RS2::In, RS2::Both, mouse));
589 setCurrentAction(new ActionZoomIn(*container, *this, RS2::Out, RS2::Both, mouse));
595 void QG_GraphicView::keyPressEvent(QKeyEvent * e)
601 RS2::Direction direction = RS2::Up;
607 direction = RS2::Right;
611 direction = RS2::Left;
619 direction = RS2::Down;
627 setCurrentAction(new ActionZoomScroll(direction, *container, *this));
629 GraphicView::keyPressEvent(e);
632 void QG_GraphicView::keyReleaseEvent(QKeyEvent * e)
634 GraphicView::keyReleaseEvent(e);
638 * Called whenever the graphic view has changed.
639 * Adjusts the scrollbar ranges / steps.
641 void QG_GraphicView::adjustOffsetControls()
643 static bool running = false;
650 DEBUG->print("QG_GraphicView::adjustOffsetControls() begin");
652 if (!container || !hScrollBar || !vScrollBar)
656 int ox = getOffsetX();
657 int oy = getOffsetY();
659 Vector min = container->getMin();
660 Vector max = container->getMax();
662 // no drawing yet - still allow to scroll
663 if (max.x < min.x + 1.0e-6 || max.y < min.y + 1.0e-6
664 || max.x > RS_MAXDOUBLE || max.x < RS_MINDOUBLE
665 || min.x > RS_MAXDOUBLE || min.x < RS_MINDOUBLE
666 || max.y > RS_MAXDOUBLE || max.y < RS_MINDOUBLE
667 || min.y > RS_MAXDOUBLE || min.y < RS_MINDOUBLE)
669 min = Vector(-10, -10);
670 max = Vector(100, 100);
673 int minVal = (int)(min.x * getFactor().x - QG_SCROLLMARGIN - getBorderLeft());
674 int maxVal = (int)(max.x * getFactor().x - getWidth() + QG_SCROLLMARGIN + getBorderRight());
676 hScrollBar->setValue(0);
678 if (minVal <= maxVal)
679 hScrollBar->setRange(minVal, maxVal);
681 minVal = (int)(getHeight() - max.y * getFactor().y
682 - QG_SCROLLMARGIN - getBorderTop());
683 maxVal = (int)(QG_SCROLLMARGIN + getBorderBottom()
684 - (min.y * getFactor().y));
686 if (minVal <= maxVal)
687 vScrollBar->setRange(minVal, maxVal);
689 hScrollBar->setPageStep((int)(getWidth()));
690 vScrollBar->setPageStep((int)(getHeight()));
692 hScrollBar->setValue(-ox);
693 vScrollBar->setValue(oy);
698 DEBUG->print("H min: %d / max: %d / step: %d / value: %d\n",
699 hScrollBar->minimum(), hScrollBar->maximum(), hScrollBar->pageStep(), ox);
700 DEBUG->print("V min: %d / max: %d / step: %d / value: %d\n",
701 vScrollBar->minimum(), vScrollBar->maximum(), vScrollBar->pageStep(), oy);
705 DEBUG->print("QG_GraphicView::adjustOffsetControls() end");
711 * override this to adjust controls and widgets that
712 * control the zoom factor of the graphic.
714 void QG_GraphicView::adjustZoomControls()
719 * Slot for horizontal scroll events.
721 void QG_GraphicView::slotHScrolled(int value)
723 // Scrollbar behaviour tends to change with every Qt version..
724 // so let's keep old code in here for now
726 //static int running = false;
729 ////DEBUG->print("value x: %d\n", value);
730 if (hScrollBar->maximum() == hScrollBar->minimum())
735 //if (isUpdateEnabled()) {
745 * Slot for vertical scroll events.
747 void QG_GraphicView::slotVScrolled(int value)
749 // Scrollbar behaviour tends to change with every Qt version..
750 // so let's keep old code in here for now
752 //static int running = false;
755 ////DEBUG->print("value y: %d\n", value);
756 if (vScrollBar->maximum() == vScrollBar->minimum())
761 //if (isUpdateEnabled()) {
771 * Handles paint events by redrawing the graphic in this view.
772 * usually that's very fast since we only paint the buffer we
773 * have from the last call..
775 * This is not true anymore--even if it was true in the first place, which
776 * seems doubtful to me. Everything is redrawn every time; we need to see if
777 * relying on Qt do to the clipping is wise or not... Also, we need to clarify
778 * all rendering paths since they are not all logical or consistent.
780 void QG_GraphicView::paintEvent(QPaintEvent *)
782 DEBUG->print("QG_GraphicView::paintEvent begin");
784 if (simulationRunning)
788 To fix the broken rendering here, we need to do one of the following:
790 1) Put preview object here and fix draw code to do an update() instead
791 of trying to draw directly to a widget context
792 2) Use a QPixmap as a randomly drawable surface and fix createDirectPainter()
793 to do an update() after drawing to the pixmap which will eventually end
796 I think 1) is cleaner in the long run, and will make the codebase much more
797 maintainable. However, 2) would make it possible to fix things fairly easily
798 without having to rearrange classes too much.
800 The way the UI is written is crap. It tries to be clever by doing direct
801 painting, bypassing the toolkit's built in rendering pipeline. Also, there
802 are tons of references to Qt stuff in what is supposed to be a toolkit
803 independent manner, resulting in unnecessary divisions of code and rendundant
804 class definitions. Still, the core is still useful and worth fixing just to
805 have something back in portage. We can make it better, faster, stronger. ;-)
807 If we can make the UI more like Inkscape we'll be in good shape. Plus elements
808 of VectorWorks & etc. as well...
811 // Qt4 handles double buffering of screen writes now, so this needs
813 //#warning "!!! Need to pass a valid QPainter to drawIt() !!!"
816 pntr.setBackgroundMode(Qt::OpaqueMode);
817 pntr.setBackground(QBrush(background));
819 painter = new PaintInterface(&pntr);
821 //Note that we do drawIt() regardless here because paintEvent() clears the background
822 //*unless* we create the window with a specific style. Depending on our draw code, we
823 //just may go that way...
826 // Draw the snapper first, because we want to be able to see the preview on
828 // if (snapper.Visible())
829 // snapper.Draw(this, painter);
830 // Actually, this code can go into drawIt() instead of here...
832 DrawSnapper(painter);
834 if (preview.Visible())
835 preview.Draw(this, painter);
840 DEBUG->print("QG_GraphicView::paintEvent end");