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"