File indexing completed on 2024-12-08 11:07:17

0001 /***************************************************************************
0002  *   Copyright (C) 2003-2005 by 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 "picitem.h"
0012 #include "canvasitemparts.h"
0013 #include "eventinfo.h"
0014 #include "flowcodedocument.h"
0015 #include "microinfo.h"
0016 #include "micropackage.h"
0017 #include "microsettings.h"
0018 #include "microsettingsdlg.h"
0019 
0020 #include <KLocalizedString>
0021 
0022 #include <QIcon>
0023 #include <QMouseEvent>
0024 #include <QPainter>
0025 
0026 #include <ktechlab_debug.h>
0027 
0028 static const int InnerWidth = 88;
0029 static const int SidePadding = 24;
0030 static const int TopPadding = 36;
0031 static const int BottomPadding = 42;
0032 static const int PinWidth = 12;
0033 static const int PinSeparation = 16 - PinWidth;
0034 static const int PinLength = 8;
0035 static const int ArcWidth = 22;
0036 static const int PinDirArrow = 3;
0037 
0038 // BEGIN class PinItem
0039 PinItem::PinItem(FlowCodeDocument *_view, QPoint position, bool _onLeft, PinSettings *pinSettings)
0040     : KtlQCanvasRectangle(nullptr)
0041 {
0042     m_pinSettings = pinSettings;
0043     view = _view;
0044     onLeft = _onLeft;
0045 
0046     connect(m_pinSettings, &PinSettings::settingsChanged, this, &PinItem::updateDrawing);
0047 
0048     if (QFontInfo(m_font).pixelSize() > 11) // It has to be > 11, not > 12, as (I think) pixelSize() rounds off the actual size
0049         m_font.setPixelSize(12);
0050 
0051     setCanvas(view->canvas());
0052 
0053     move(position.x(), position.y());
0054     initItem();
0055     setZ((ICNDocument::Z::RaisedItem + ICNDocument::Z::ResizeHandle) / 2 + 1); // Hackish, but whatever
0056 }
0057 
0058 void PinItem::updateDrawing()
0059 {
0060     update();
0061 }
0062 
0063 void PinItem::initItem()
0064 {
0065     setSize(PinLength, PinWidth);
0066     setSelected(false);
0067     setPen(QPen(Qt::black));
0068     calcTextRect();
0069     show();
0070 }
0071 
0072 void PinItem::drawShape(QPainter &p)
0073 {
0074     if (!m_pinSettings)
0075         return;
0076 
0077     if (m_pinSettings->state() == PinSettings::ps_on) {
0078         if (m_pinSettings->type() == PinSettings::pt_output)
0079             setBrush(QColor(255, 127, 127));
0080         else
0081             setBrush(QColor(255, 191, 191));
0082     } else
0083         setBrush(Qt::white);
0084 
0085     p.drawRect(rect());
0086 
0087     p.setFont(m_font);
0088     p.setBrush(Qt::NoBrush);
0089     QRect r = m_textRect;
0090     if (onLeft)
0091         p.drawText(r, Qt::AlignLeft, m_pinSettings->id());
0092     else
0093         p.drawText(r, Qt::AlignRight, m_pinSettings->id());
0094     // QRect br = p.boundingRect( r, Qt::AlignLeft, m_pinSettings->id() ); // 2017.10.01 - comment out unused variable
0095 
0096     int left;
0097     int right;
0098     if (onLeft) {
0099         right = int(x());
0100         left = right - 8;
0101     } else {
0102         left = int(x()) + PinLength;
0103         right = left + 8;
0104     }
0105 
0106     int midY = int(y()) + PinWidth / 2;
0107     QPolygon pa(3);
0108     int midLeft = left + (8 - PinDirArrow) / 2;
0109     int midRight = left + (8 + PinDirArrow) / 2;
0110 
0111     if (onLeft) {
0112         midLeft--;
0113         midRight--;
0114     } else {
0115         midLeft++;
0116         midRight++;
0117     }
0118 
0119     p.setBrush(Qt::black);
0120 
0121     // Right facing arrow
0122     if ((m_pinSettings->type() == PinSettings::pt_input && onLeft) || (m_pinSettings->type() == PinSettings::pt_output && !onLeft)) {
0123         pa[0] = QPoint(midRight, midY);
0124         pa[1] = QPoint(midLeft, midY - PinDirArrow);
0125         pa[2] = QPoint(midLeft, midY + PinDirArrow);
0126         p.drawPolygon(pa);
0127         p.drawLine(left, midY, right, midY);
0128     } else // Left facing arrow
0129     {
0130         pa[0] = QPoint(midLeft, midY);
0131         pa[1] = QPoint(midRight, midY - PinDirArrow);
0132         pa[2] = QPoint(midRight, midY + PinDirArrow);
0133         p.drawPolygon(pa);
0134         p.drawLine(left, midY, right, midY);
0135     }
0136 }
0137 
0138 QRect PinItem::boundingRect() const
0139 {
0140     QRect r = m_textRect;
0141     if (onLeft)
0142         r.setLeft(int(x()) - 10);
0143     else
0144         r.setRight(int(x()) + PinLength + 10);
0145 
0146     return r;
0147 }
0148 
0149 QString PinItem::id()
0150 {
0151     return m_pinSettings->id();
0152 }
0153 
0154 void PinItem::switchState()
0155 {
0156     if (m_pinSettings->state() == PinSettings::ps_on)
0157         m_pinSettings->setState(PinSettings::ps_off);
0158     else
0159         m_pinSettings->setState(PinSettings::ps_on);
0160 
0161     update();
0162 }
0163 
0164 void PinItem::dragged(int dx)
0165 {
0166     if ((onLeft && dx > 0) || (!onLeft && dx < 0)) {
0167         m_pinSettings->setType(PinSettings::pt_input);
0168     } else
0169         m_pinSettings->setType(PinSettings::pt_output);
0170 
0171     update();
0172 }
0173 
0174 void PinItem::moveBy(double dx, double dy)
0175 {
0176     KtlQCanvasRectangle::moveBy(dx, dy);
0177     calcTextRect();
0178 }
0179 
0180 void PinItem::calcTextRect()
0181 {
0182     m_textRect = rect();
0183     m_textRect.moveTop(m_textRect.top() - 2);
0184     QRect br;
0185 
0186     //  QWidget tmpWidget;
0187     //     //tmpWidget.setAttribute(Qt::WA_PaintOutsidePaintEvent, true); //note: add this if needed
0188     //  //QPainter p(&tmpWidget); // 2016.05.03 - initialize painter explicitly
0189     //     QPainter p;
0190     //     const bool isBeginSuccess = p.begin(&tmpWidget);
0191     //     {
0192     //         qCWarning(KTL_LOG) << " painter not active";
0193     //     }
0194     //
0195     //  p.setFont(m_font);
0196 
0197     QFontMetrics fontMetrics(m_font);
0198 
0199     if (!m_pinSettings) {
0200         qCDebug(KTL_LOG) << "PinItem::textRect: No pinSettings!";
0201         return;
0202     }
0203     // note: br is assigned but not used; here might be some bug...
0204     if (onLeft) {
0205         m_textRect.setLeft(int(x()) + PinLength + 2);
0206         m_textRect.setRight(int(x()) + InnerWidth / 2);
0207         // br = p.boundingRect( m_textRect, Qt::AlignLeft, m_pinSettings->id() ); // 2016.05.03 - do not create dummy widget
0208         br = fontMetrics.boundingRect(m_textRect, Qt::AlignLeft, m_pinSettings->id());
0209     } else {
0210         m_textRect.setLeft(m_textRect.right() - InnerWidth / 2);
0211         m_textRect.setRight(int(x()) - 2);
0212         // br = p.boundingRect( m_textRect, Qt::AlignRight, m_pinSettings->id() ); // 2016.05.03 - do not create dummy widget
0213         br = fontMetrics.boundingRect(m_textRect, Qt::AlignRight, m_pinSettings->id());
0214     }
0215 }
0216 // END class PinItem
0217 
0218 // BEGIN class PicItem
0219 PicItem::PicItem(ICNDocument *icnDocument, bool newItem, const char *id, MicroSettings *_microSettings)
0220     : CNItem(icnDocument, newItem, id ? id : "picitem")
0221 {
0222     m_name = "PIC";
0223     m_type = typeString();
0224     m_pressed = false;
0225     p_icnDocument = icnDocument;
0226     icnDocument->registerItem(this);
0227 
0228     microSettings = _microSettings;
0229     const int numPins = microSettings->microInfo()->package()->pinCount(PicPin::type_bidir | PicPin::type_input | PicPin::type_open);
0230     const int numSide = (numPins / 2) + (numPins % 2);
0231 
0232     m_bExpanded = true;
0233     m_innerHeight = (numSide + 2) * PinWidth + (numSide - 1) * PinSeparation;
0234     updateVisibility();
0235 
0236     addButton("settings", QRect(SidePadding - 8, m_innerHeight + TopPadding + (BottomPadding - 24) / 2 - 1, InnerWidth + 16, 24), i18n("Advanced..."));
0237     addButton("expandBtn", QRect((TopPadding - 22) / 2, (TopPadding - 22) / 2, 22, 22), QIcon::fromTheme("go-down"), true);
0238     button("expandBtn")->setState(true);
0239 
0240     move(12, 12);
0241 
0242     QStringList pinIDs = microSettings->microInfo()->package()->pinIDs(PicPin::type_bidir | PicPin::type_input | PicPin::type_open);
0243     QStringList::iterator it = pinIDs.begin();
0244 
0245     for (int i = 0; i < numSide; ++i, ++it) {
0246         QPoint position(int(this->x()) + SidePadding - PinLength + 1, int(y()) + TopPadding + (i + 1) * PinWidth + i * PinSeparation);
0247         const QString id = *it;
0248         PinSettings *settings = microSettings->pinWithID(id);
0249         m_pinItemList.append(new PinItem(dynamic_cast<FlowCodeDocument *>(icnDocument), position, true, settings));
0250     }
0251 
0252     for (int i = 0; i < numPins / 2; ++i, ++it) {
0253         QPoint position(int(this->x()) + SidePadding + InnerWidth - 1, int(y()) + TopPadding + m_innerHeight - ((i + 2) * PinWidth + i * PinSeparation));
0254         const QString id = *it;
0255         PinSettings *settings = microSettings->pinWithID(id);
0256         m_pinItemList.append(new PinItem(dynamic_cast<FlowCodeDocument *>(icnDocument), position, false, settings));
0257     }
0258 
0259     setSelected(false);
0260     setPen(QPen(Qt::black));
0261     updateZ(-1);
0262     update();
0263     show();
0264 }
0265 
0266 PicItem::~PicItem()
0267 {
0268     qDeleteAll(m_pinItemList);
0269     m_pinItemList.clear();
0270 }
0271 
0272 void PicItem::updateZ(int baseZ)
0273 {
0274     (void)baseZ;
0275     setZ((ICNDocument::Z::RaisedItem + ICNDocument::Z::ResizeHandle) / 2); // Hackish, but whatever
0276     button("settings")->setZ(z() + 1);
0277     button("expandBtn")->setZ(z() + 1);
0278 }
0279 
0280 void PicItem::drawShape(QPainter &p)
0281 {
0282     int _x = int(x());
0283     int _y = int(y());
0284 
0285     p.setBrush(QColor(0xef, 0xff, 0xef));
0286     p.setFont(font());
0287 
0288     p.drawRoundedRect(_x, _y, width(), height(), 2000 / width(), 2000 / height(), Qt::RelativeSize);
0289 
0290     p.drawText(_x + TopPadding - 2, _y, width() - TopPadding + 2, TopPadding, Qt::AlignVCenter, i18n("PIC Settings"));
0291 
0292     if (!m_bExpanded)
0293         return;
0294 
0295     // Draw rectangle to cut off pins
0296     p.setBrush(QColor(239, 255, 255));
0297     QRect r(_x + SidePadding, _y + TopPadding, InnerWidth, m_innerHeight);
0298     p.drawRect(r);
0299 
0300     // Draw dimple thingy at end of pic
0301     p.drawArc(r.x() + (r.width() - ArcWidth) / 2, r.y() + 1 - ArcWidth / 2, ArcWidth, ArcWidth, 180 * 16, 180 * 16);
0302 
0303     // Draw vertical text centered in PIC
0304     p.translate(r.width() / 2 + r.x(), r.height() / 2 + r.y());
0305     p.rotate(90);
0306     QRect textRect(r.width() / -2, r.height() / -2, r.width(), r.height());
0307     p.drawText(textRect, Qt::AlignCenter, microSettings->microInfo()->id());
0308 
0309     p.rotate(-90);
0310     p.translate(r.width() / -2 - r.x(), r.height() / -2 - r.y());
0311 }
0312 
0313 void PicItem::buttonStateChanged(const QString &id, bool state)
0314 {
0315     if (id == "expandBtn") {
0316         m_bExpanded = state;
0317         updateVisibility();
0318     }
0319 
0320     else if (id == "settings") {
0321         if (!state)
0322             return;
0323 
0324         // Redraw button
0325         button("settings")->setState(false);
0326         update();
0327 
0328         MicroSettingsDlg *dlg = new MicroSettingsDlg(microSettings, nullptr);
0329         dlg->setObjectName("microSettingsDlg");
0330         connect(dlg, &MicroSettingsDlg::accepted, this, &PicItem::slotMicroSettingsDlgAccepted);
0331         connect(dlg, &MicroSettingsDlg::applyClicked, this, &PicItem::slotMicroSettingsDlgAccepted);
0332         dlg->show();
0333         // At this point the PIC is selected but this does not appear to the
0334         // user so we must deselect it when done.
0335         p_icnDocument->unselectAll();
0336     }
0337 }
0338 
0339 void PicItem::updateVisibility()
0340 {
0341     if (m_bExpanded)
0342         setSize(0, 0, InnerWidth + (2 * SidePadding), m_innerHeight + TopPadding + BottomPadding, true);
0343 
0344     else
0345         setSize(0, 0, InnerWidth + (2 * SidePadding), TopPadding, true);
0346 
0347     const PinItemList::iterator end = m_pinItemList.end();
0348     for (PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it)
0349         (*it)->setVisible(m_bExpanded);
0350 
0351     if (Button *btn = button("settings"))
0352         btn->setVisible(m_bExpanded);
0353 }
0354 
0355 void PicItem::slotMicroSettingsDlgAccepted()
0356 {
0357     const PinItemList::iterator end = m_pinItemList.end();
0358     for (PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it)
0359         canvas()->setChanged((*it)->boundingRect());
0360 
0361     p_icnDocument->requestStateSave();
0362 }
0363 
0364 bool PicItem::mousePressEvent(const EventInfo &info)
0365 {
0366     QMouseEvent *e = info.mousePressEvent(0, 0);
0367     const PinItemList::iterator end = m_pinItemList.end();
0368     for (PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it)
0369         if (e->isAccepted() && (*it)->boundingRect().contains(info.pos)) {
0370             // reset mouse-gesture state
0371             m_pressed = true;
0372             m_pos = info.pos;
0373             m_dragged = false;
0374             m_dx = 0;
0375             delete e;
0376             return true;
0377         }
0378 
0379     m_pressed = false;
0380     delete e;
0381     return CNItem::mousePressEvent(info);
0382 }
0383 
0384 bool PicItem::mouseReleaseEvent(const EventInfo &info)
0385 {
0386     QMouseEvent *e = info.mouseReleaseEvent(0, 0);
0387     if (!m_pressed) {
0388         delete e;
0389         return CNItem::mouseReleaseEvent(info);
0390     }
0391 
0392     const PinItemList::iterator end = m_pinItemList.end();
0393     for (PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it)
0394         if (!e->isAccepted()) {
0395             continue;
0396         } else if ((*it)->boundingRect().contains(m_pos) && (*it)->boundingRect().contains(info.pos)) {
0397             if (m_dragged) {
0398                 (*it)->dragged(m_dx);
0399             } else {
0400                 (*it)->switchState();
0401             }
0402             m_pressed = false;
0403             m_dragged = false;
0404             m_pos = QPoint();
0405             m_dx = 0;
0406             delete e;
0407             return true;
0408         }
0409     delete e;
0410     return CNItem::mouseReleaseEvent(info);
0411 }
0412 
0413 bool PicItem::mouseMoveEvent(const EventInfo &info)
0414 {
0415     QMouseEvent *e = info.mouseMoveEvent(0, 0);
0416     const PinItemList::iterator end = m_pinItemList.end();
0417     for (PinItemList::iterator it = m_pinItemList.begin(); it != end; ++it)
0418         if (e->isAccepted() && (*it)->boundingRect().contains(info.pos)) {
0419             QPoint vec = info.pos - m_pos;
0420             if (m_pressed && vec.manhattanLength() > 4) {
0421                 m_dragged = true;
0422                 m_dx = vec.x();
0423             }
0424             delete e;
0425             return true;
0426         }
0427     delete e;
0428     return CNItem::mouseMoveEvent(info);
0429 }
0430 
0431 // END class PicItem
0432 
0433 #include "moc_picitem.cpp"