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