File indexing completed on 2024-11-10 11:08:04
0001 /*************************************************************************** 0002 * Copyright (C) 2004-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 "iteminterface.h" 0012 #include "circuitview.h" 0013 #include "cnitem.h" 0014 #include "cnitemgroup.h" 0015 #include "colorutils.h" 0016 #include "contexthelp.h" 0017 #include "doublespinbox.h" 0018 #include "itemdocument.h" 0019 #include "itemeditor.h" 0020 #include "itemview.h" 0021 #include "ktechlab.h" 0022 0023 #include <KColorCombo> 0024 #include <KComboBox> 0025 #include <KLineEdit> 0026 #include <KToolBar> 0027 #include <KUrlRequester> 0028 #include <KXMLGUIFactory> 0029 #include <kio_version.h> 0030 0031 #include <QApplication> 0032 #include <QCheckBox> 0033 #include <QLabel> 0034 #include <QSpinBox> 0035 0036 #include <cassert> 0037 0038 #include <ktechlab_debug.h> 0039 0040 ItemInterface *ItemInterface::m_pSelf = nullptr; 0041 0042 ItemInterface *ItemInterface::self() 0043 { 0044 if (!m_pSelf) 0045 m_pSelf = new ItemInterface(); 0046 0047 return m_pSelf; 0048 } 0049 0050 ItemInterface::ItemInterface() 0051 : QObject(KTechlab::self()) 0052 , m_isInTbDataChanged(false) 0053 { 0054 m_pActiveItemEditorToolBar = nullptr; 0055 p_cvb = nullptr; 0056 p_itemGroup = nullptr; 0057 p_lastItem = nullptr; 0058 m_currentActionTicket = -1; 0059 m_toolBarWidgetID = -1; 0060 } 0061 0062 ItemInterface::~ItemInterface() 0063 { 0064 } 0065 0066 void ItemInterface::slotGetActionTicket() 0067 { 0068 m_currentActionTicket = p_cvb ? p_cvb->getActionTicket() : -1; 0069 } 0070 0071 void ItemInterface::slotItemDocumentChanged(ItemDocument *doc) 0072 { 0073 slotClearAll(); 0074 if (ItemDocument *itemDocument = dynamic_cast<ItemDocument *>(static_cast<Document *>(p_cvb))) { 0075 disconnect(itemDocument, &ItemDocument::selectionChanged, this, &ItemInterface::slotUpdateItemInterface); 0076 } 0077 0078 p_itemGroup = nullptr; 0079 p_cvb = doc; 0080 0081 slotGetActionTicket(); 0082 0083 if (!p_cvb) 0084 return; 0085 0086 connect(p_cvb, &ItemDocument::selectionChanged, this, &ItemInterface::slotUpdateItemInterface); 0087 0088 p_itemGroup = p_cvb->selectList(); 0089 0090 slotUpdateItemInterface(); 0091 } 0092 0093 void ItemInterface::clearItemEditorToolBar() 0094 { 0095 if (m_pActiveItemEditorToolBar && m_toolBarWidgetID != -1) { 0096 // m_pActiveItemEditorToolBar->removeItem(m_toolBarWidgetID); // TODO add proper replacmenet 0097 m_pActiveItemEditorToolBar->clear(); 0098 } 0099 m_toolBarWidgetID = -1; 0100 itemEditTBCleared(); 0101 } 0102 0103 void ItemInterface::slotClearAll() 0104 { 0105 ContextHelp::self()->slotClear(); 0106 ItemEditor::self()->slotClear(); 0107 clearItemEditorToolBar(); 0108 p_lastItem = nullptr; 0109 } 0110 0111 void ItemInterface::slotMultipleSelected() 0112 { 0113 ContextHelp::self()->slotMultipleSelected(); 0114 ItemEditor::self()->slotMultipleSelected(); 0115 clearItemEditorToolBar(); 0116 p_lastItem = nullptr; 0117 } 0118 0119 void ItemInterface::slotUpdateItemInterface() 0120 { 0121 if (!p_itemGroup) 0122 return; 0123 0124 slotGetActionTicket(); 0125 updateItemActions(); 0126 0127 if (!p_itemGroup->itemsAreSameType()) { 0128 slotMultipleSelected(); 0129 return; 0130 } 0131 if (p_lastItem && p_itemGroup->activeItem()) { 0132 ItemEditor::self()->itemGroupUpdated(p_itemGroup); 0133 return; 0134 } 0135 0136 p_lastItem = p_itemGroup->activeItem(); 0137 if (!p_lastItem) { 0138 slotClearAll(); 0139 return; 0140 } 0141 0142 ContextHelp::self()->slotUpdate(p_lastItem); 0143 ItemEditor::self()->slotUpdate(p_itemGroup); 0144 if (CNItem *cnItem = dynamic_cast<CNItem *>(static_cast<Item *>(p_lastItem))) { 0145 ItemEditor::self()->slotUpdate(cnItem); 0146 } 0147 0148 // Update item editor toolbar 0149 if (ItemView *itemView = dynamic_cast<ItemView *>(p_cvb->activeView())) { 0150 if (KTechlab *ktl = KTechlab::self()) { 0151 if ((m_pActiveItemEditorToolBar = dynamic_cast<KToolBar *>(ktl->factory()->container("itemEditorTB", itemView)))) { 0152 // m_pActiveItemEditorToolBar->setFullSize( true ); // TODO proper replacement 0153 m_pActiveItemEditorToolBar->adjustSize(); 0154 QWidget *widget = configWidget(); 0155 m_toolBarWidgetID = 1; 0156 // m_pActiveItemEditorToolBar->insertWidget( m_toolBarWidgetID, 0, widget ); // TODO properly fix 0157 m_pActiveItemEditorToolBar->addWidget(widget); 0158 } 0159 } 0160 } 0161 } 0162 0163 void ItemInterface::updateItemActions() 0164 { 0165 ItemView *itemView = (static_cast<ItemDocument *>(p_cvb)) ? dynamic_cast<ItemView *>(p_cvb->activeView()) : nullptr; 0166 if (!itemView) 0167 return; 0168 0169 bool itemsSelected = p_itemGroup && p_itemGroup->itemCount(); 0170 0171 itemView->actionByName("edit_raise")->setEnabled(itemsSelected); 0172 itemView->actionByName("edit_lower")->setEnabled(itemsSelected); 0173 0174 if (KTechlab::self()) { 0175 KTechlab::self()->actionByName("edit_cut")->setEnabled(itemsSelected); 0176 KTechlab::self()->actionByName("edit_copy")->setEnabled(itemsSelected); 0177 } 0178 0179 CNItemGroup *cnItemGroup = dynamic_cast<CNItemGroup *>(static_cast<ItemGroup *>(p_itemGroup)); 0180 CircuitView *circuitView = dynamic_cast<CircuitView *>(itemView); 0181 0182 if (cnItemGroup && circuitView) { 0183 bool canFlip = cnItemGroup->canFlip(); 0184 circuitView->actionByName("edit_flip_horizontally")->setEnabled(canFlip); 0185 circuitView->actionByName("edit_flip_vertically")->setEnabled(canFlip); 0186 0187 bool canRotate = cnItemGroup->canRotate(); 0188 circuitView->actionByName("edit_rotate_ccw")->setEnabled(canRotate); 0189 circuitView->actionByName("edit_rotate_cw")->setEnabled(canRotate); 0190 } 0191 } 0192 0193 void ItemInterface::setFlowPartOrientation(unsigned orientation) 0194 { 0195 CNItemGroup *cnItemGroup = dynamic_cast<CNItemGroup *>(static_cast<ItemGroup *>(p_itemGroup)); 0196 if (!cnItemGroup) 0197 return; 0198 0199 cnItemGroup->setFlowPartOrientation(orientation); 0200 } 0201 0202 void ItemInterface::setComponentOrientation(int angleDegrees, bool flipped) 0203 { 0204 CNItemGroup *cnItemGroup = dynamic_cast<CNItemGroup *>(static_cast<ItemGroup *>(p_itemGroup)); 0205 if (!cnItemGroup) 0206 return; 0207 0208 cnItemGroup->setComponentOrientation(angleDegrees, flipped); 0209 } 0210 0211 void ItemInterface::itemEditTBCleared() 0212 { 0213 m_stringLineEditMap.clear(); 0214 m_stringComboBoxMap.clear(); 0215 m_stringURLReqMap.clear(); 0216 m_intSpinBoxMap.clear(); 0217 m_doubleSpinBoxMap.clear(); 0218 m_colorComboMap.clear(); 0219 m_boolCheckMap.clear(); 0220 } 0221 0222 // The bool specifies whether advanced data should be shown 0223 QWidget *ItemInterface::configWidget() 0224 { 0225 if (!p_itemGroup || !p_itemGroup->activeItem() || !m_pActiveItemEditorToolBar) 0226 return nullptr; 0227 0228 VariantDataMap *variantMap = p_itemGroup->activeItem()->variantMap(); 0229 0230 QWidget *parent = m_pActiveItemEditorToolBar; 0231 0232 // Create new widget with the toolbar or dialog as the parent 0233 QWidget *configWidget = new QWidget(parent /*, "tbConfigWidget" */); 0234 configWidget->setObjectName("tbConfigWidget"); 0235 { 0236 // 2018.12.02 0237 // configWidget->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding, 1, 1 ) ); 0238 QSizePolicy p(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); 0239 p.setHorizontalStretch(1); 0240 p.setVerticalStretch(1); 0241 configWidget->setSizePolicy(p); 0242 } 0243 0244 QHBoxLayout *configLayout = new QHBoxLayout(configWidget); 0245 // configLayout->setAutoAdd( true ); 0246 configLayout->setSpacing(6); 0247 configLayout->setMargin(0); 0248 0249 // configLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed ) ); 0250 0251 const VariantDataMap::iterator vaEnd = variantMap->end(); 0252 for (VariantDataMap::iterator vait = variantMap->begin(); vait != vaEnd; ++vait) { 0253 if (vait.value()->isHidden() || vait.value()->isAdvanced()) 0254 continue; 0255 0256 const Variant::Type::Value type = vait.value()->type(); 0257 0258 // common to all types apart from bool 0259 QString toolbarCaption = vait.value()->toolbarCaption(); 0260 if (type != Variant::Type::Bool && !toolbarCaption.isEmpty()) 0261 configLayout->addWidget(new QLabel(toolbarCaption, configWidget)); 0262 0263 QWidget *editWidget = nullptr; // Should be set to the created widget 0264 0265 switch (type) { 0266 case Variant::Type::Port: 0267 case Variant::Type::Pin: 0268 case Variant::Type::VarName: 0269 case Variant::Type::Combo: 0270 case Variant::Type::Select: 0271 case Variant::Type::KeyPad: 0272 case Variant::Type::SevenSegment: { 0273 QString value = vait.value()->displayString(); 0274 if (!value.isEmpty() && !vait.value()->allowed().contains(value)) 0275 vait.value()->appendAllowed(value); 0276 0277 const QStringList allowed = vait.value()->allowed(); 0278 0279 KComboBox *box = new KComboBox(configWidget); 0280 0281 box->insertItems(box->count(), allowed); 0282 box->setCurrentItem(value); 0283 0284 if (type == Variant::Type::VarName || type == Variant::Type::Combo) 0285 box->setEditable(true); 0286 0287 m_stringComboBoxMap[vait.key()] = box; 0288 connectMapWidget(box, SIGNAL(editTextChanged(const QString &))); 0289 connectMapWidget(box, SIGNAL(activated(const QString &))); 0290 0291 connect(*vait, SIGNAL(valueChangedStrAndTrue(const QString &, bool)), box, SLOT(setCurrentItem(const QString &, bool))); 0292 0293 editWidget = box; 0294 break; 0295 } 0296 case Variant::Type::FileName: { 0297 qCDebug(KTL_LOG) << "create FileName"; 0298 QString value = vait.value()->value().toString(); 0299 if (!vait.value()->allowed().contains(value)) 0300 vait.value()->appendAllowed(value); 0301 0302 const QStringList allowed = vait.value()->allowed(); 0303 0304 KUrlComboRequester *urlreq = new KUrlComboRequester(configWidget); 0305 #if KIO_VERSION >= QT_VERSION_CHECK(5, 108, 0) 0306 urlreq->setNameFilters(vait.value()->fileFilters().toQtStyleStringList()); 0307 #else 0308 urlreq->setFilter(vait.value()->fileFilters().toKDEStyleString()); 0309 #endif 0310 connectMapWidget(urlreq, SIGNAL(urlSelected(QUrl))); 0311 m_stringURLReqMap[vait.key()] = urlreq; 0312 0313 KComboBox *box = urlreq->comboBox(); 0314 box->insertItems(box->count(), allowed); 0315 box->setEditable(true); 0316 0317 // Note this has to be called after inserting the allowed list 0318 urlreq->setUrl(QUrl::fromLocalFile(vait.value()->value().toString())); 0319 0320 // Generally we only want a file name once the user has finished typing out the full file name. 0321 connectMapWidget(box, SIGNAL(returnPressed(const QString &))); 0322 connectMapWidget(box, SIGNAL(activated(const QString &))); 0323 0324 connect(*vait, SIGNAL(valueChanged(const QString &)), box, SLOT(setEditText(const QString &))); 0325 0326 editWidget = urlreq; 0327 break; 0328 } 0329 case Variant::Type::String: { 0330 KLineEdit *edit = new KLineEdit(configWidget); 0331 0332 edit->setText(vait.value()->value().toString()); 0333 connectMapWidget(edit, SIGNAL(textChanged(const QString &))); 0334 m_stringLineEditMap[vait.key()] = edit; 0335 editWidget = edit; 0336 0337 connect(*vait, SIGNAL(valueChanged(const QString &)), edit, SLOT(setText(const QString &))); 0338 0339 break; 0340 } 0341 case Variant::Type::Int: { 0342 QSpinBox *spin = new QSpinBox(configWidget); 0343 spin->setMinimum(int(vait.value()->minValue())); 0344 spin->setMaximum(int(vait.value()->maxValue())); 0345 spin->setValue(vait.value()->value().toInt()); 0346 0347 connectMapWidget(spin, SIGNAL(valueChanged(int))); 0348 m_intSpinBoxMap[vait.key()] = spin; 0349 editWidget = spin; 0350 0351 connect(*vait, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int))); 0352 0353 break; 0354 } 0355 case Variant::Type::Double: { 0356 DoubleSpinBox *spin = new DoubleSpinBox(vait.value()->minValue(), vait.value()->maxValue(), vait.value()->minAbsValue(), vait.value()->value().toDouble(), vait.value()->unit(), configWidget); 0357 0358 connectMapWidget(spin, SIGNAL(valueChanged(double))); 0359 m_doubleSpinBoxMap[vait.key()] = spin; 0360 editWidget = spin; 0361 0362 connect(*vait, SIGNAL(valueChanged(double)), spin, SLOT(setValue(double))); 0363 0364 break; 0365 } 0366 case Variant::Type::Color: { 0367 QColor value = vait.value()->value().value<QColor>(); 0368 0369 KColorCombo *colorBox = ColorUtils::createColorCombo(static_cast<ColorUtils::ColorScheme>(vait.value()->colorScheme()), configWidget); 0370 0371 colorBox->setColor(value); 0372 connectMapWidget(colorBox, SIGNAL(activated(const QColor &))); 0373 m_colorComboMap[vait.key()] = colorBox; 0374 0375 connect(*vait, SIGNAL(valueChanged(const QColor &)), colorBox, SLOT(setColor(const QColor &))); 0376 0377 editWidget = colorBox; 0378 break; 0379 } 0380 case Variant::Type::Bool: { 0381 const bool value = vait.value()->value().toBool(); 0382 QCheckBox *box = new QCheckBox(vait.value()->toolbarCaption(), configWidget); 0383 0384 box->setChecked(value); 0385 connectMapWidget(box, SIGNAL(toggled(bool))); 0386 m_boolCheckMap[vait.key()] = box; 0387 0388 connect(*vait, SIGNAL(valueChanged(bool)), box, SLOT(setChecked(bool))); 0389 0390 editWidget = box; 0391 break; 0392 } 0393 case Variant::Type::Raw: 0394 case Variant::Type::PenStyle: 0395 case Variant::Type::PenCapStyle: 0396 case Variant::Type::Multiline: 0397 case Variant::Type::RichText: 0398 case Variant::Type::None: { 0399 // Do nothing, as these data types are not handled in the toolbar 0400 break; 0401 } 0402 } 0403 0404 if (!editWidget) 0405 continue; 0406 0407 const int widgetH = QFontMetrics(configWidget->font()).height() + 2; 0408 editWidget->setMinimumHeight(widgetH); // note: this is hack-ish; something is not ok with the layout 0409 editWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0410 0411 // In the case of the toolbar, we don't want it too high 0412 if (editWidget->height() > parent->height() - 2) 0413 editWidget->setMaximumHeight(parent->height() - 2); 0414 0415 switch (type) { 0416 case Variant::Type::VarName: 0417 case Variant::Type::Combo: 0418 case Variant::Type::String: { 0419 QSizePolicy p(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed /*, 1, 1 */); 0420 p.setHorizontalStretch(1); 0421 p.setVerticalStretch(1); 0422 0423 editWidget->setSizePolicy(p); 0424 editWidget->setMaximumWidth(250); 0425 break; 0426 } 0427 0428 case Variant::Type::FileName: 0429 case Variant::Type::Port: 0430 case Variant::Type::Pin: 0431 case Variant::Type::Select: 0432 case Variant::Type::KeyPad: 0433 case Variant::Type::SevenSegment: 0434 case Variant::Type::Int: 0435 case Variant::Type::Double: 0436 case Variant::Type::Color: 0437 case Variant::Type::Bool: 0438 case Variant::Type::Raw: 0439 case Variant::Type::PenStyle: 0440 case Variant::Type::PenCapStyle: 0441 case Variant::Type::Multiline: 0442 case Variant::Type::RichText: 0443 case Variant::Type::None: 0444 break; 0445 } 0446 0447 configLayout->addWidget(editWidget); 0448 } 0449 0450 configLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed)); 0451 0452 return configWidget; 0453 } 0454 0455 void ItemInterface::connectMapWidget(QWidget *widget, const char *_signal) 0456 { 0457 connect(widget, _signal, this, SLOT(tbDataChanged())); 0458 } 0459 0460 // TODO move to separate file 0461 struct BoolLock { 0462 bool *m_flagPtr; 0463 BoolLock(bool *flagPtr) 0464 : m_flagPtr(flagPtr) 0465 { 0466 if (m_flagPtr == nullptr) { 0467 qCCritical(KTL_LOG) << "nullptr flagPtr"; 0468 return; 0469 } 0470 if (*m_flagPtr == true) { 0471 qCWarning(KTL_LOG) << "flag expected to be false, addr=" << m_flagPtr << " Doing nothing"; 0472 m_flagPtr = nullptr; 0473 } else { 0474 *m_flagPtr = true; 0475 } 0476 } 0477 ~BoolLock() 0478 { 0479 if (m_flagPtr != nullptr) { 0480 *m_flagPtr = false; 0481 } 0482 } 0483 }; 0484 0485 void ItemInterface::tbDataChanged() 0486 { 0487 qCDebug(KTL_LOG) << "begin"; 0488 if (m_isInTbDataChanged) { 0489 qCDebug(KTL_LOG) << "avoiding recursion, returning"; 0490 return; 0491 } 0492 BoolLock inTbChangedLock(&m_isInTbDataChanged); 0493 // Manual string values 0494 const KLineEditMap::iterator m_stringLineEditMapEnd = m_stringLineEditMap.end(); 0495 for (KLineEditMap::iterator leit = m_stringLineEditMap.begin(); leit != m_stringLineEditMapEnd; ++leit) { 0496 slotSetData(leit.key(), leit.value()->text()); 0497 } 0498 0499 // String values from comboboxes 0500 const KComboBoxMap::iterator m_stringComboBoxMapEnd = m_stringComboBoxMap.end(); 0501 for (KComboBoxMap::iterator cmit = m_stringComboBoxMap.begin(); cmit != m_stringComboBoxMapEnd; ++cmit) { 0502 qCDebug(KTL_LOG) << "set KCombo data for " << cmit.key() << " to " << cmit.value()->currentText(); 0503 slotSetData(cmit.key(), cmit.value()->currentText()); 0504 } 0505 0506 // Colors values from colorcombos 0507 const KColorComboMap::iterator m_colorComboMapEnd = m_colorComboMap.end(); 0508 for (KColorComboMap::iterator ccit = m_colorComboMap.begin(); ccit != m_colorComboMapEnd; ++ccit) { 0509 slotSetData(ccit.key(), ccit.value()->color()); 0510 } 0511 0512 // Bool values from checkboxes 0513 const QCheckBoxMap::iterator m_boolCheckMapEnd = m_boolCheckMap.end(); 0514 for (QCheckBoxMap::iterator chit = m_boolCheckMap.begin(); chit != m_boolCheckMapEnd; ++chit) { 0515 slotSetData(chit.key(), chit.value()->isChecked()); 0516 } 0517 0518 const IntSpinBoxMap::iterator m_intSpinBoxMapEnd = m_intSpinBoxMap.end(); 0519 for (IntSpinBoxMap::iterator it = m_intSpinBoxMap.begin(); it != m_intSpinBoxMapEnd; ++it) { 0520 slotSetData(it.key(), it.value()->value()); 0521 } 0522 0523 // (?) Combined values from spin boxes and combo boxes 0524 // (?) Get values from all spin boxes 0525 0526 const DoubleSpinBoxMap::iterator m_doubleSpinBoxMapEnd = m_doubleSpinBoxMap.end(); 0527 for (DoubleSpinBoxMap::iterator sbit = m_doubleSpinBoxMap.begin(); sbit != m_doubleSpinBoxMapEnd; ++sbit) { 0528 // VariantDataMap::iterator vait = variantData.find(sbit.key()); 0529 slotSetData(sbit.key(), sbit.value()->value()); 0530 } 0531 0532 // Filenames from KUrlRequesters 0533 const KUrlReqMap::iterator m_stringURLReqMapEnd = m_stringURLReqMap.end(); 0534 for (KUrlReqMap::iterator urlit = m_stringURLReqMap.begin(); urlit != m_stringURLReqMapEnd; ++urlit) { 0535 qCDebug(KTL_LOG) << "set kurlrequester data for " << urlit.key() << " to " << urlit.value()->url(); 0536 QVariant urlVar(urlit.value()->url().path()); 0537 qCDebug(KTL_LOG) << "urlVar=" << urlVar << " urlVar.toUrl=" << urlVar.toUrl(); 0538 slotSetData(urlit.key(), urlVar); 0539 } 0540 0541 if (p_cvb) 0542 p_cvb->setModified(true); 0543 } 0544 0545 void ItemInterface::setProperty(Variant *v) 0546 { 0547 slotSetData(v->id(), v->value()); 0548 } 0549 0550 void ItemInterface::slotSetData(const QString &id, QVariant value) 0551 { 0552 if (!p_itemGroup || (p_itemGroup->itemCount() == 0)) { 0553 qCDebug(KTL_LOG) << "p_itemGroup not valid:" << p_itemGroup; 0554 return; 0555 } 0556 0557 if (!p_itemGroup->itemsAreSameType()) { 0558 qCDebug(KTL_LOG) << "Items are not the same type!"; 0559 return; 0560 } 0561 qCDebug(KTL_LOG) << "id=" << id << " value=" << value; 0562 0563 const ItemList itemList = p_itemGroup->items(true); 0564 const ItemList::const_iterator end = itemList.end(); 0565 for (ItemList::const_iterator it = itemList.begin(); it != end; ++it) { 0566 if (*it) 0567 (*it)->property(id)->setValue(value); 0568 } 0569 0570 if (p_cvb) 0571 p_cvb->setModified(true); 0572 0573 ItemEditor::self()->itemGroupUpdated(p_itemGroup); 0574 0575 if (p_cvb) 0576 p_cvb->requestStateSave(m_currentActionTicket); 0577 } 0578 0579 #include "moc_iteminterface.cpp"