File indexing completed on 2024-03-03 05:58:26

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2006 David Saxton                                  *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "itemview.h"
0012 #include "canvasitemparts.h"
0013 #include "canvasmanipulator.h"
0014 #include "cnitem.h"
0015 #include "component.h"
0016 #include "connector.h"
0017 #include "docmanager.h"
0018 #include "drawpart.h"
0019 #include "ecnode.h"
0020 #include "itemdocument.h"
0021 #include "itemlibrary.h"
0022 #include "ktechlab.h"
0023 #include "utils.h"
0024 
0025 //#include <kaccel.h>
0026 #include <KLocalizedString>
0027 #include <KStandardAction>
0028 // #include <k3popupmenu.h>
0029 #include <KActionCollection>
0030 #include <KToolBarPopupAction>
0031 
0032 #include <QApplication>
0033 #include <QCursor>
0034 #include <QMenu>
0035 #include <QMimeData>
0036 #include <QMouseEvent>
0037 #include <QTimer>
0038 #include <QVBoxLayout>
0039 
0040 #include <cmath>
0041 
0042 #include <ktlconfig.h>
0043 #include <ktechlab_debug.h>
0044 
0045 // BEGIN class ItemView
0046 ItemView::ItemView(ItemDocument *itemDocument, ViewContainer *viewContainer, uint viewAreaId)
0047     : View(itemDocument, viewContainer, viewAreaId)
0048 {
0049     KActionCollection *ac = actionCollection();
0050 
0051     KStandardAction::selectAll(itemDocument, SLOT(selectAll()), ac);
0052     KStandardAction::zoomIn(this, SLOT(zoomIn()), ac);
0053     KStandardAction::zoomOut(this, SLOT(zoomOut()), ac);
0054     KStandardAction::actualSize(this, SLOT(actualSize()), ac)->setEnabled(false);
0055 
0056     // KAccel *pAccel = new KAccel(this);
0057     // pAccel->insert( "Cancel", i18n("Cancel"), i18n("Cancel the current operation"), Qt::Key_Escape, itemDocument, SLOT(cancelCurrentOperation()) );
0058     // pAccel->readSettings(); // TODO what does this do?
0059     QAction *pAccel = new QAction(QIcon::fromTheme("process-stop"), i18n("Cancel"), ac);
0060     pAccel->setObjectName("cancelCurrentOperation");
0061     pAccel->setShortcut(Qt::Key_Escape);
0062     connect(pAccel, &QAction::triggered, itemDocument, &ItemDocument::cancelCurrentOperation);
0063     ac->addAction("cancelCurrentOperation", pAccel);
0064 
0065     {
0066         // new KAction( i18n("Delete"), "edit-delete", Qt::Key_Delete, itemDocument, SLOT(deleteSelection()), ac, "edit_delete" );
0067         QAction *action = new QAction(QIcon::fromTheme("edit-delete"), i18n("Delete"), ac);
0068         action->setObjectName("edit_delete");
0069         action->setShortcut(Qt::Key_Delete);
0070         connect(action, &QAction::triggered, itemDocument, &ItemDocument::deleteSelection);
0071         ac->addAction("edit_delete", action);
0072     }
0073     {
0074         // new KAction( i18n("Export as Image..."), 0, 0, itemDocument, SLOT(exportToImage()), ac, "file_export_image");
0075         QAction *action = new QAction(QIcon::fromTheme("document-export"), i18n("Export as Image..."), ac);
0076         action->setObjectName("file_export_image");
0077         connect(action, &QAction::triggered, itemDocument, &ItemDocument::exportToImage);
0078         ac->addAction("file_export_image", action);
0079     }
0080 
0081     // BEGIN Item Alignment actions
0082     {
0083         // new KAction( i18n("Align Horizontally"), 0, 0, itemDocument, SLOT(alignHorizontally()), ac, "align_horizontally" );
0084         QAction *action = new QAction(QIcon::fromTheme("align-horizontal-center"), i18n("Align Horizontally"), ac);
0085         action->setObjectName("align_horizontally");
0086         connect(action, &QAction::triggered, itemDocument, &ItemDocument::alignHorizontally);
0087         ac->addAction("align_horizontally", action);
0088     }
0089     {
0090         // new KAction( i18n("Align Vertically"), 0, 0, itemDocument, SLOT(alignVertically()), ac, "align_vertically" );
0091         QAction *action = new QAction(QIcon::fromTheme("align-vertical-center"), i18n("Align Vertically"), ac);
0092         action->setObjectName("align_vertically");
0093         connect(action, &QAction::triggered, itemDocument, &ItemDocument::alignVertically);
0094         ac->addAction("align_vertically", action);
0095     }
0096     {
0097         // new KAction( i18n("Distribute Horizontally"), 0, 0, itemDocument, SLOT(distributeHorizontally()), ac, "distribute_horizontally" );
0098         QAction *action = new QAction(QIcon::fromTheme("distribute-horizontal-x"), i18n("Distribute Horizontally"), ac);
0099         action->setObjectName("distribute_horizontally");
0100         connect(action, &QAction::triggered, itemDocument, &ItemDocument::distributeHorizontally);
0101         ac->addAction("distribute_horizontally", action);
0102     }
0103     {
0104         // new KAction( i18n("Distribute Vertically"), 0, 0, itemDocument, SLOT(distributeVertically()), ac, "distribute_vertically" );
0105         QAction *action = new QAction(QIcon::fromTheme("distribute-vertical-y"), i18n("Distribute Vertically"), ac);
0106         action->setObjectName("distribute_vertically");
0107         connect(action, &QAction::triggered, itemDocument, &ItemDocument::distributeVertically);
0108         ac->addAction("distribute_vertically", action);
0109     }
0110     // END Item Alignment actions
0111 
0112     // BEGIN Draw actions
0113     // KToolBarPopupAction * pa = new KToolBarPopupAction( i18n("Draw"), "paintbrush", 0, 0, 0, ac, "edit_draw" );
0114     KToolBarPopupAction *pa = new KToolBarPopupAction(QIcon::fromTheme("draw-brush"), i18n("Draw"), ac);
0115     pa->setObjectName("edit_draw");
0116     pa->setDelayed(false);
0117     ac->addAction("edit_draw", pa);
0118 
0119     QMenu *m = pa->menu();
0120     m->setTitle(i18n("Draw"));
0121 
0122     m->addAction(QIcon::fromTheme("draw-text"), i18n("Text"))->setData(DrawPart::da_text);
0123     m->addAction(QIcon::fromTheme("draw-line"), i18n("Line"))->setData(DrawPart::da_line);
0124     m->addAction(QIcon::fromTheme("draw-arrow"), i18n("Arrow"))->setData(DrawPart::da_arrow);
0125     m->addAction(QIcon::fromTheme("draw-ellipse"), i18n("Ellipse"))->setData(DrawPart::da_ellipse);
0126     m->addAction(QIcon::fromTheme("draw-rectangle"), i18n("Rectangle"))->setData(DrawPart::da_rectangle);
0127     m->addAction(QIcon::fromTheme("insert-image"), i18n("Image"))->setData(DrawPart::da_image);
0128     connect(m, &QMenu::triggered, itemDocument, &ItemDocument::slotSetDrawAction);
0129     // END Draw actions
0130 
0131     // BEGIN Item Control actions
0132     {
0133         // new KAction( i18n("Raise Selection"), "object-order-raise", Qt::Key_PageUp,   itemDocument, SLOT(raiseZ()), ac, "edit_raise" );
0134         QAction *action = new QAction(QIcon::fromTheme("object-order-raise"), i18n("Raise Selection"), ac);
0135         action->setObjectName("edit_raise");
0136         action->setShortcut(Qt::Key_PageUp);
0137         connect(action, &QAction::triggered, itemDocument, qOverload<>(&ItemDocument::raiseZ));
0138         ac->addAction("edit_raise", action);
0139     }
0140     {
0141         // new KAction( i18n("Lower Selection"), "object-order-lower", Qt::Key_PageDown, itemDocument, SLOT(lowerZ()), ac, "edit_lower" );
0142         QAction *action = new QAction(QIcon::fromTheme("object-order-lower"), i18n("Lower Selection"), ac);
0143         action->setObjectName("edit_lower");
0144         action->setShortcut(Qt::Key_PageDown);
0145         connect(action, &QAction::triggered, itemDocument, qOverload<>(&ItemDocument::lowerZ));
0146         ac->addAction("edit_lower", action);
0147     }
0148     // END Item Control actions
0149 
0150     {
0151         // KAction * na = new KAction( "", 0, 0, 0, 0, ac, "null_action" );
0152         QAction *na = new QAction("", ac);
0153         na->setObjectName("null_action");
0154         na->setEnabled(false);
0155     }
0156 
0157     setXMLFile("ktechlabitemviewui.rc");
0158 
0159     m_pUpdateStatusTmr = new QTimer(this);
0160     connect(m_pUpdateStatusTmr, &QTimer::timeout, this, &ItemView::updateStatus);
0161     connect(this, &ItemView::unfocused, this, &ItemView::stopUpdatingStatus);
0162 
0163     m_pDragItem = nullptr;
0164     p_itemDocument = itemDocument;
0165     m_zoomLevel = 1.;
0166     m_CVBEditor = new CVBEditor(p_itemDocument->canvas(), this);
0167     m_CVBEditor->setObjectName("cvbEditor");
0168     m_CVBEditor->setLineWidth(1);
0169 
0170     // connect(m_CVBEditor, SIGNAL(horizontalSliderReleased()), itemDocument, SLOT(requestCanvasResize()));
0171     connect(m_CVBEditor, &CVBEditor::horizontalSliderReleased, itemDocument, &ItemDocument::requestCanvasResize);
0172     connect(m_CVBEditor, &CVBEditor::verticalSliderReleased, itemDocument, &ItemDocument::requestCanvasResize);
0173 
0174     m_layout->insertWidget(0, m_CVBEditor);
0175 
0176     setAcceptDrops(true);
0177 
0178     setFocusWidget(m_CVBEditor->viewport());
0179 }
0180 
0181 ItemView::~ItemView()
0182 {
0183 }
0184 
0185 bool ItemView::canZoomIn() const
0186 {
0187     return true;
0188 }
0189 bool ItemView::canZoomOut() const
0190 {
0191     return int(std::floor((100 * m_zoomLevel) + 0.5)) > 25;
0192 }
0193 
0194 QPoint ItemView::mousePosToCanvasPos(const QPoint &contentsClick) const
0195 {
0196     QPoint offsetPos = contentsClick + QPoint(cvbEditor()->contentsX(), cvbEditor()->contentsY());
0197     return (offsetPos / zoomLevel()) + p_itemDocument->canvas()->rect().topLeft();
0198 }
0199 
0200 void ItemView::zoomIn(const QPoint &center)
0201 {
0202     // NOTE The code in this function is nearly the same as that in zoomOut.
0203     // Any updates to this code should also be done to zoomOut
0204 
0205     // Previous position of center in widget coordinates
0206     QPoint previous = center * zoomLevel() - QPoint(cvbEditor()->contentsX(), cvbEditor()->contentsY());
0207 
0208     // Don't repaint the view until we've also shifted it
0209     cvbEditor()->viewport()->setUpdatesEnabled(false);
0210 
0211     zoomIn();
0212 
0213     // Adjust the contents' position to ensure that "previous" remains fixed
0214     QPoint offset = center * zoomLevel() - previous;
0215     cvbEditor()->setContentsPos(offset.x(), offset.y());
0216 
0217     cvbEditor()->viewport()->setUpdatesEnabled(true);
0218     cvbEditor()->viewport()->update();
0219 }
0220 
0221 void ItemView::zoomOut(const QPoint &center)
0222 {
0223     // NOTE The code in this function is nearly the same as that in zoomIn.
0224     // Any updates to this code should also be done to zoomIn
0225 
0226     // Previous position of center in widget coordinates
0227     QPoint previous = center * zoomLevel() - QPoint(cvbEditor()->contentsX(), cvbEditor()->contentsY());
0228 
0229     // Don't repaint the view until we've also shifted it
0230     cvbEditor()->viewport()->setUpdatesEnabled(false);
0231 
0232     zoomOut();
0233 
0234     // Adjust the contents' position to ensure that "previous" remains fixed
0235     QPoint offset = center * zoomLevel() - previous;
0236     cvbEditor()->setContentsPos(offset.x(), offset.y());
0237 
0238     cvbEditor()->viewport()->setUpdatesEnabled(true);
0239     cvbEditor()->viewport()->update();
0240 }
0241 
0242 void ItemView::zoomIn()
0243 {
0244     int currentZoomPercent = int(std::floor((100 * m_zoomLevel) + 0.5));
0245     int newZoom = currentZoomPercent;
0246 
0247     if (currentZoomPercent < 100)
0248         newZoom += 25;
0249     else if (currentZoomPercent < 200)
0250         newZoom += 50;
0251     else
0252         newZoom += 100;
0253 
0254     m_zoomLevel = newZoom / 100.0;
0255     m_CVBEditor->updateWorldMatrix();
0256 
0257     p_itemDocument->requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
0258     updateZoomActions();
0259 }
0260 
0261 void ItemView::zoomOut()
0262 {
0263     int currentZoomPercent = int(std::floor((100 * m_zoomLevel) + 0.5));
0264     int newZoom = currentZoomPercent;
0265 
0266     if (currentZoomPercent <= 25)
0267         return;
0268     if (currentZoomPercent <= 100)
0269         newZoom -= 25;
0270     else if (currentZoomPercent <= 200)
0271         newZoom -= 50;
0272     else
0273         newZoom -= 100;
0274 
0275     m_zoomLevel = newZoom / 100.0;
0276     m_CVBEditor->updateWorldMatrix();
0277 
0278     p_itemDocument->requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
0279     updateZoomActions();
0280 }
0281 
0282 void ItemView::actualSize()
0283 {
0284     m_zoomLevel = 1.0;
0285     QTransform m(m_zoomLevel, 0.0, 0.0, m_zoomLevel, 1.0, 1.0);
0286     m_CVBEditor->setWorldTransform(m);
0287 
0288     p_itemDocument->requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
0289     updateZoomActions();
0290 }
0291 
0292 void ItemView::updateZoomActions()
0293 {
0294     actionByName("view_zoom_in")->setEnabled(canZoomIn());
0295     actionByName("view_zoom_out")->setEnabled(canZoomOut());
0296     actionByName("view_actual_size")->setEnabled(m_zoomLevel != 1.0);
0297 }
0298 
0299 void ItemView::dropEvent(QDropEvent *event)
0300 {
0301     removeDragItem();
0302     const QMimeData *mimeData = event->mimeData();
0303 
0304     if (mimeData->hasUrls()) {
0305         // Then it is URLs that we can decode :)
0306         const QList<QUrl> urls = mimeData->urls();
0307         for (const QUrl &u : urls) {
0308             DocManager::self()->openURL(u);
0309         }
0310         return;
0311     }
0312 
0313     QString matchingFormat;
0314     for (QString format : mimeData->formats()) {
0315         if (format.startsWith("ktechlab/")) {
0316             matchingFormat = format;
0317             break;
0318         }
0319     }
0320     if (matchingFormat.isEmpty()) {
0321         return;
0322     }
0323     event->acceptProposedAction();
0324 
0325     QString text;
0326     // QDataStream stream( event->encodedData(event->format()), QIODevice::ReadOnly );
0327     QByteArray byteArray(mimeData->data(matchingFormat));
0328 
0329     QDataStream stream(&byteArray, QIODevice::ReadOnly);
0330     stream >> text;
0331 
0332     // Get a new component item
0333     p_itemDocument->addItem(text, mousePosToCanvasPos(event->pos()), true);
0334 
0335     setFocus();
0336 }
0337 
0338 void ItemView::scrollToMouse(const QPoint &pos)
0339 {
0340     QPoint viewPos = pos - p_itemDocument->canvas()->rect().topLeft();
0341     viewPos *= m_zoomLevel;
0342     int x = viewPos.x();
0343     int y = viewPos.y();
0344 
0345     int left = m_CVBEditor->contentsX();
0346     int top = m_CVBEditor->contentsY();
0347     int width = m_CVBEditor->contentsWidth();
0348     int height = m_CVBEditor->contentsHeight();
0349     int right = left + m_CVBEditor->visibleWidth();
0350     int bottom = top + m_CVBEditor->visibleHeight();
0351 
0352     // A magic "snap" region whereby if the mouse is near the edge of the canvas,
0353     // then assume that we want to scroll right up to it
0354     int snapMargin = 32;
0355 
0356     if (x < snapMargin)
0357         x = 0;
0358     else if (x > width - snapMargin)
0359         x = width;
0360 
0361     if (y < snapMargin)
0362         y = 0;
0363     else if (y > height - snapMargin)
0364         y = height;
0365 
0366     if (x < left)
0367         m_CVBEditor->scrollBy(x - left, 0);
0368     else if (x > right)
0369         m_CVBEditor->scrollBy(x - right, 0);
0370 
0371     if (y < top)
0372         m_CVBEditor->scrollBy(0, y - top);
0373     else if (y > bottom)
0374         m_CVBEditor->scrollBy(0, y - bottom);
0375 }
0376 
0377 void ItemView::contentsMousePressEvent(QMouseEvent *e)
0378 {
0379     if (!e)
0380         return;
0381 
0382     e->accept();
0383 
0384     if (!p_itemDocument)
0385         return;
0386 
0387     EventInfo eventInfo(this, e);
0388 
0389     if (eventInfo.isRightClick && m_pDragItem) {
0390         // We are dragging an item, and the user has right clicked.
0391         // Therefore, we want to rotate the item.
0392         /// @todo we should implement a virtual method in item for "rotating the item by one"
0393         /// - whatever that one may be (e.g. by 90 degrees, or changing the pin layout for
0394         /// flowparts, or nothing if the item isn't rotatable).
0395         if (Component *c = dynamic_cast<Component *>(m_pDragItem))
0396             c->setAngleDegrees(c->angleDegrees() + 90);
0397 
0398         return;
0399     }
0400 
0401     p_itemDocument->canvas()->setMessage(QString());
0402     p_itemDocument->m_cmManager->mousePressEvent(eventInfo);
0403 }
0404 
0405 void ItemView::contentsMouseDoubleClickEvent(QMouseEvent *e)
0406 {
0407     if (!e)
0408         return;
0409 
0410     e->accept();
0411 
0412     // HACK: Pass this of as a single press event if widget underneath
0413     KtlQCanvasItem *atTop = p_itemDocument->itemAtTop(e->pos() / zoomLevel());
0414     if (dynamic_cast<Widget *>(atTop))
0415         contentsMousePressEvent(e);
0416     else
0417         p_itemDocument->m_cmManager->mouseDoubleClickEvent(EventInfo(this, e));
0418 }
0419 
0420 void ItemView::contentsMouseMoveEvent(QMouseEvent *e)
0421 {
0422     //  qCDebug(KTL_LOG) << "state = " << e->state();
0423 
0424     if (!e || !p_itemDocument)
0425         return;
0426 
0427     e->accept();
0428 
0429     EventInfo eventInfo(this, e);
0430 
0431     p_itemDocument->m_cmManager->mouseMoveEvent(eventInfo);
0432     if (!m_pUpdateStatusTmr->isActive())
0433         startUpdatingStatus();
0434 }
0435 
0436 void ItemView::contentsMouseReleaseEvent(QMouseEvent *e)
0437 {
0438     if (!e)
0439         return;
0440 
0441     e->accept();
0442 
0443     p_itemDocument->m_cmManager->mouseReleaseEvent(EventInfo(this, e));
0444 }
0445 
0446 void ItemView::contentsWheelEvent(QWheelEvent *e)
0447 {
0448     if (!e)
0449         return;
0450 
0451     e->accept();
0452     EventInfo eventInfo(this, e);
0453     if (eventInfo.ctrlPressed) {
0454         // Zooming in or out
0455 
0456         if (eventInfo.pixelDelta.y() > 0)
0457             zoomIn(eventInfo.pos);
0458         else
0459             zoomOut(eventInfo.pos);
0460 
0461         return;
0462     }
0463 
0464     p_itemDocument->m_cmManager->wheelEvent(eventInfo);
0465 }
0466 
0467 void ItemView::dragEnterEvent(QDragEnterEvent *event)
0468 {
0469     startUpdatingStatus();
0470 
0471     if (event->mimeData()->hasUrls()) {
0472         event->setAccepted(true);
0473         // Then it is URLs that we can decode later :)
0474         return;
0475     }
0476 }
0477 
0478 void ItemView::createDragItem(QDragEnterEvent *e)
0479 {
0480     removeDragItem();
0481 
0482     const QMimeData *mimeData = e->mimeData();
0483     QString matchingFormat;
0484     for (QString format : mimeData->formats()) {
0485         if (format.startsWith("ktechlab/")) {
0486             matchingFormat = format;
0487             break;
0488         }
0489     }
0490     if (matchingFormat.isEmpty()) {
0491         qCWarning(KTL_LOG) << "Invalid mime data" << mimeData->formats();
0492         return;
0493     }
0494 
0495     e->accept();
0496 
0497     QString text;
0498     // QDataStream stream( e->encodedData(e->format()), QIODevice::ReadOnly );
0499     // QByteArray byteArray( e->encodedData(e->format()) );
0500     QByteArray byteArray(mimeData->data(matchingFormat));
0501     QDataStream stream(&byteArray, QIODevice::ReadOnly);
0502     stream >> text;
0503 
0504     QPoint p = mousePosToCanvasPos(e->pos());
0505 
0506     m_pDragItem = itemLibrary()->createItem(text, p_itemDocument, true);
0507 
0508     if (CNItem *cnItem = dynamic_cast<CNItem *>(m_pDragItem)) {
0509         cnItem->move(snapToCanvas(p.x()), snapToCanvas(p.y()));
0510     } else {
0511         m_pDragItem->move(p.x(), p.y());
0512     }
0513 
0514     m_pDragItem->show();
0515 }
0516 
0517 void ItemView::removeDragItem()
0518 {
0519     if (!m_pDragItem)
0520         return;
0521 
0522     m_pDragItem->removeItem();
0523     p_itemDocument->flushDeleteList();
0524     m_pDragItem = nullptr;
0525 }
0526 
0527 void ItemView::dragMoveEvent(QDragMoveEvent *e)
0528 {
0529     if (!m_pDragItem)
0530         return;
0531 
0532     QPoint p = mousePosToCanvasPos(e->pos());
0533 
0534     if (CNItem *cnItem = dynamic_cast<CNItem *>(m_pDragItem)) {
0535         cnItem->move(snapToCanvas(p.x()), snapToCanvas(p.y()));
0536     } else {
0537         m_pDragItem->move(p.x(), p.y());
0538     }
0539 }
0540 
0541 void ItemView::dragLeaveEvent(QDragLeaveEvent *)
0542 {
0543     removeDragItem();
0544 }
0545 
0546 void ItemView::enterEvent(QEvent *e)
0547 {
0548     Q_UNUSED(e);
0549     startUpdatingStatus();
0550 }
0551 
0552 void ItemView::leaveEvent(QEvent *e)
0553 {
0554     Q_UNUSED(e);
0555     stopUpdatingStatus();
0556 
0557     // Cleanup
0558     setCursor(Qt::ArrowCursor);
0559 
0560     if (KTechlab::self())
0561         KTechlab::self()->slotChangeStatusbar(QString());
0562 
0563     if (p_itemDocument)
0564         p_itemDocument->m_canvasTip->setVisible(false);
0565 }
0566 
0567 void ItemView::requestDocumentResizeToCanvasItems()
0568 {
0569     p_itemDocument->requestEvent(ItemDocument::ItemDocumentEvent::ResizeCanvasToItems);
0570 }
0571 
0572 void ItemView::slotUpdateConfiguration()
0573 {
0574     //  m_CVBEditor->setEraseColor( KTLConfig::bgColor() );
0575     // m_CVBEditor->setEraseColor( Qt::white ); // 2018.12.02
0576     QPalette pe;
0577     pe.setColor(m_CVBEditor->backgroundRole(), Qt::white);
0578     m_CVBEditor->setPalette(pe);
0579 
0580     if (m_pUpdateStatusTmr->isActive())
0581         startUpdatingStatus();
0582 }
0583 
0584 void ItemView::startUpdatingStatus()
0585 {
0586     m_pUpdateStatusTmr->stop();
0587     m_pUpdateStatusTmr->start(int(1000. / KTLConfig::refreshRate()));
0588 }
0589 
0590 void ItemView::stopUpdatingStatus()
0591 {
0592     m_pUpdateStatusTmr->stop();
0593 }
0594 
0595 void ItemView::updateStatus()
0596 {
0597     QPoint pos = mousePosToCanvasPos(m_CVBEditor->mapFromGlobal(QCursor::pos()));
0598 
0599     ItemDocument *itemDocument = static_cast<ItemDocument *>(document());
0600     if (!itemDocument)
0601         return;
0602 
0603     CMManager *cmManager = itemDocument->m_cmManager;
0604     CanvasTip *canvasTip = itemDocument->m_canvasTip;
0605 
0606     bool displayTip = false;
0607     QCursor cursor = Qt::ArrowCursor;
0608     QString statusbar;
0609 
0610     if (cmManager->cmState() & CMManager::cms_repeated_add) {
0611         cursor = Qt::CrossCursor;
0612         statusbar = i18n("Left click to add. Right click to resume normal editing");
0613     } else if (cmManager->cmState() & CMManager::cms_draw) {
0614         cursor = Qt::CrossCursor;
0615         statusbar = i18n("Click and hold to start drawing.");
0616     } else if (cmManager->currentManipulator()) {
0617         switch (cmManager->currentManipulator()->type()) {
0618         case CanvasManipulator::RepeatedItemAdd:
0619             cursor = Qt::CrossCursor;
0620             statusbar = i18n("Left click to add. Right click to resume normal editing");
0621             break;
0622         case CanvasManipulator::ManualConnector:
0623             statusbar = i18n("Right click to cancel the connector");
0624             cursor = Qt::CrossCursor;
0625             break;
0626         case CanvasManipulator::AutoConnector:
0627             cursor = Qt::CrossCursor;
0628             break;
0629         case CanvasManipulator::ItemMove:
0630         case CanvasManipulator::MechItemMove:
0631             cursor = Qt::ClosedHandCursor;
0632             break;
0633         case CanvasManipulator::Draw:
0634             cursor = Qt::CrossCursor;
0635             break;
0636         default:
0637             break;
0638         }
0639 
0640     } else if (KtlQCanvasItem *qcanvasItem = itemDocument->itemAtTop(pos)) {
0641         if (Connector *con = qobject_cast<Connector *>(qcanvasItem)) {
0642             cursor = Qt::CrossCursor;
0643             if (itemDocument->type() == Document::dt_circuit) {
0644                 canvasTip->displayVI(con, pos);
0645                 displayTip = true;
0646             }
0647         } else if (Node *node = qobject_cast<Node *>(qcanvasItem)) {
0648             cursor = Qt::CrossCursor;
0649             if (ECNode *ecnode = qobject_cast<ECNode *>(node)) {
0650                 canvasTip->displayVI(ecnode, pos);
0651                 displayTip = true;
0652             }
0653         } else if (CNItem *item = qobject_cast<CNItem *>(qcanvasItem)) {
0654             cursor = Qt::OpenHandCursor;
0655             statusbar = item->name();
0656         }
0657     }
0658     setCursor(cursor);
0659 
0660     if (KTechlab::self())
0661         KTechlab::self()->slotChangeStatusbar(statusbar);
0662 
0663     canvasTip->setVisible(displayTip);
0664 }
0665 
0666 // END class ItemView
0667 
0668 // BEGIN class CVBEditor
0669 CVBEditor::CVBEditor(Canvas *canvas, ItemView *itemView)
0670     : KtlQCanvasView(canvas, itemView /*,Qt::WNoAutoErase | Qt::WA_StaticContents */)
0671 {
0672     setAttribute(Qt::WA_StaticContents);
0673     m_pCanvas = canvas;
0674     b_ignoreEvents = false;
0675     b_passEventsToView = true;
0676     p_itemView = itemView;
0677 
0678     setMouseTracking(true);
0679     viewport()->setMouseTracking(true);
0680     setAcceptDrops(true);
0681     setFrameShape(NoFrame);
0682     //  setEraseColor( KTLConfig::bgColor() );
0683     // setEraseColor( Qt::white );
0684     // setPaletteBackgroundColor( Qt::white );
0685     {
0686         QPalette p;
0687         p.setColor(backgroundRole(), Qt::white);
0688         setPalette(p);
0689     }
0690     // viewport()->setEraseColor( Qt::white );
0691     // viewport()->setPaletteBackgroundColor( Qt::white ); // 2018.12.02
0692     {
0693         QPalette pv;
0694         pv.setColor(viewport()->backgroundRole(), Qt::white);
0695         viewport()->setPalette(pv);
0696     }
0697 
0698     connect(canvas, &Canvas::resized, this, &CVBEditor::canvasResized);
0699 }
0700 
0701 void CVBEditor::canvasResized(const QRect &oldSize, const QRect &newSize)
0702 {
0703     updateWorldMatrix();
0704 
0705     return;
0706 
0707     qCDebug(KTL_LOG);
0708 
0709     QPoint delta = oldSize.topLeft() - newSize.topLeft();
0710     delta *= p_itemView->zoomLevel();
0711     scrollBy(delta.x(), delta.y());
0712 }
0713 
0714 void CVBEditor::updateWorldMatrix()
0715 {
0716     double z = p_itemView->zoomLevel();
0717     QRect r = m_pCanvas->rect();
0718     //  QTransform m( z, 0.0, 0.0, z, -r.left(), -r.top() );
0719     //  QTransform m( z, 0.0, 0.0, z, 0.0, 0.0 );
0720     QTransform m;
0721     m.scale(z, z);
0722     m.translate(-r.left(), -r.top());
0723     setWorldTransform(m);
0724 }
0725 
0726 void CVBEditor::contentsWheelEvent(QWheelEvent *e)
0727 {
0728     QWheelEvent ce(viewport()->mapFromGlobal(e->globalPosition().toPoint()),
0729                     e->globalPosition(),
0730                     e->pixelDelta(),
0731                     e->angleDelta(),
0732                     e->buttons(),
0733                     e->modifiers(),
0734                     e->phase(),
0735                     e->inverted());
0736 
0737     if ((e->angleDelta().x() != 0) && horizontalScrollBar())
0738         QApplication::sendEvent(horizontalScrollBar(), e);
0739     else if ((e->angleDelta().y() != 0) && verticalScrollBar())
0740         QApplication::sendEvent(verticalScrollBar(), e);
0741 
0742 #if 0
0743     if ( b_ignoreEvents )
0744         return;
0745     b_ignoreEvents = true;
0746     KtlQCanvasView::wheelEvent( e );
0747     b_ignoreEvents = false;
0748 #endif
0749 }
0750 
0751 bool CVBEditor::event(QEvent *e)
0752 {
0753     if (!b_passEventsToView) {
0754         bool isWheel = e->type() == QEvent::Wheel;
0755         if (isWheel && b_ignoreEvents)
0756             return false;
0757 
0758         b_ignoreEvents = isWheel;
0759         bool accepted = KtlQCanvasView::event(e);
0760         b_ignoreEvents = false;
0761         return accepted;
0762     }
0763 
0764     switch (e->type()) {
0765     case QEvent::MouseButtonPress:
0766         p_itemView->contentsMousePressEvent(static_cast<QMouseEvent *>(e));
0767         return (static_cast<QMouseEvent *>(e))->isAccepted();
0768 
0769     case QEvent::MouseButtonRelease:
0770         p_itemView->contentsMouseReleaseEvent(static_cast<QMouseEvent *>(e));
0771         return (static_cast<QMouseEvent *>(e))->isAccepted();
0772 
0773     case QEvent::MouseButtonDblClick:
0774         p_itemView->contentsMouseDoubleClickEvent(static_cast<QMouseEvent *>(e));
0775         return (static_cast<QMouseEvent *>(e))->isAccepted();
0776 
0777     case QEvent::MouseMove:
0778         p_itemView->contentsMouseMoveEvent(static_cast<QMouseEvent *>(e));
0779         return (static_cast<QMouseEvent *>(e))->isAccepted();
0780 
0781     case QEvent::DragEnter:
0782         p_itemView->dragEnterEvent(static_cast<QDragEnterEvent *>(e));
0783         return true;
0784 
0785     case QEvent::DragMove:
0786         p_itemView->dragMoveEvent(static_cast<QDragMoveEvent *>(e));
0787         return true;
0788 
0789     case QEvent::DragLeave:
0790         p_itemView->dragLeaveEvent(static_cast<QDragLeaveEvent *>(e));
0791         return true;
0792 
0793     case QEvent::Drop:
0794         p_itemView->dropEvent(static_cast<QDropEvent *>(e));
0795         return true;
0796 
0797     case QEvent::Enter:
0798         p_itemView->enterEvent(e);
0799         return true;
0800 
0801     case QEvent::Leave:
0802         p_itemView->leaveEvent(e);
0803         return true;
0804 
0805     case QEvent::Wheel:
0806         p_itemView->contentsWheelEvent(static_cast<QWheelEvent *>(e));
0807         return (static_cast<QWheelEvent *>(e))->isAccepted();
0808 
0809     default:
0810         return KtlQCanvasView::event(e);
0811     }
0812 }
0813 
0814 void CVBEditor::viewportResizeEvent(QResizeEvent *e)
0815 {
0816     KtlQCanvasView::viewportResizeEvent(e);
0817     // 2018.09.26 - move to explicit method
0818     // p_itemView->p_itemDocument->requestEvent( ItemDocument::ItemDocumentEvent::ResizeCanvasToItems );
0819     p_itemView->requestDocumentResizeToCanvasItems();
0820 }
0821 // END class CVBEditor
0822 
0823 #include "moc_itemview.cpp"