File indexing completed on 2024-04-21 05:43:40
0001 /*************************************************************************** 0002 * Copyright (C) 2003-2006 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 "itemselector.h" 0012 #include "circuitdocument.h" 0013 #include "docmanager.h" 0014 #include "flowcodedocument.h" 0015 #include "itemdocument.h" 0016 #include "itemlibrary.h" 0017 #include "katemdi.h" 0018 #include "libraryitem.h" 0019 #include "mechanicsdocument.h" 0020 #include <vector> // Temporay fix for pthread.h problem 0021 0022 #include <KConfigGroup> 0023 #include <KLocalizedString> 0024 #include <KSharedConfig> 0025 0026 // #include <q3dragobject.h> 0027 // #include <q3popupmenu.h> 0028 #include <QLayout> 0029 #include <QMenu> 0030 #include <QMimeData> 0031 0032 #include <cassert> 0033 0034 #include <ktechlab_debug.h> 0035 0036 ILVItem::ILVItem(QTreeWidget *parent, const QString &id) 0037 : QTreeWidgetItem(parent, 0 /* note: add item types */) 0038 { 0039 setData(0, DataRole_ID, QVariant(id)); 0040 // m_id = id; // 2018.08.12 - use value() 0041 b_isRemovable = false; 0042 m_pProjectItem = nullptr; 0043 } 0044 0045 ILVItem::ILVItem(QTreeWidgetItem *parent, const QString &id) 0046 : QTreeWidgetItem(parent, 0 /* note: add item types */) 0047 { 0048 // m_id = id; // 2018.08.12 - use value() 0049 setData(0, DataRole_ID, QVariant(id)); 0050 b_isRemovable = false; 0051 m_pProjectItem = nullptr; 0052 } 0053 0054 ItemSelector::ItemSelector(QWidget *parent) 0055 : QTreeWidget(parent) 0056 { 0057 qCDebug(KTL_LOG) << " this=" << this; 0058 0059 setDragDropMode(QAbstractItemView::DragOnly); 0060 setColumnCount(1); 0061 setHeaderLabel(i18n("Component")); 0062 // addColumn( i18n( "Component" ) ); // 2018.08.12 - use setHeaderLabel() 0063 // setFullWidth(true); // 2018.06.02 - need to be fixed 0064 setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); 0065 // setSorting( -1, false ); // 2018.08.12 - use setSortingEnabled 0066 setSortingEnabled(false); 0067 setRootIsDecorated(true); 0068 // setDragEnabled(true); // 2018.06.02 - needed? 0069 setFocusPolicy(Qt::NoFocus); 0070 0071 setSelectionMode(QAbstractItemView::SingleSelection); // 2015.12.10 - need to allow selection for removing items 0072 0073 if (parent->layout()) { 0074 parent->layout()->addWidget(this); 0075 qCDebug(KTL_LOG) << " added item selector to parent's layout " << parent; 0076 } else { 0077 qCWarning(KTL_LOG) << " unexpected null layout on parent " << parent; 0078 } 0079 0080 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // ? 0081 0082 // connect( this, SIGNAL(executed(K3ListViewItem*) ), this, SLOT(slotItemExecuted(K3ListViewItem*)) ); 0083 connect(this, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(slotItemClicked(QTreeWidgetItem *, int))); 0084 /*TODO Can't connect to itemClicked(QTreeWidgetItem *, int) 0085 connect(this, qOverload<QTreeWidgetItem*, int>(&ItemSelector::itemClicked), 0086 this, qOverload<QTreeWidgetItem*, int>(&ItemSelector::slotItemClicked));*/ 0087 connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(slotItemDoubleClicked(QTreeWidgetItem *, int))); 0088 /*TODO The same problem as above 0089 connect(this, qOverload<QTreeWidgetItem*, int>(&ItemSelector::itemClicked), 0090 this, qOverload<QTreeWidgetItem*, int>(&ItemSelector::slotItemDoubleClicked));*/ 0091 // connect( this, SIGNAL(contextMenuRequested(Q3ListViewItem*, const QPoint&, int )), this, 0092 // SLOT(slotContextMenuRequested(Q3ListViewItem*, const QPoint&, int )) ); // 2018.08.12 - use signal from below 0093 setContextMenuPolicy(Qt::CustomContextMenu); 0094 connect(this, &ItemSelector::customContextMenuRequested, this, &ItemSelector::slotContextMenuRequested); 0095 0096 connect(this, &ItemSelector::itemSelectionChanged, this, &ItemSelector::slotItemSelected); 0097 } 0098 0099 ItemSelector::~ItemSelector() 0100 { 0101 writeOpenStates(); 0102 } 0103 0104 void ItemSelector::clear() 0105 { 0106 m_categories.clear(); 0107 QTreeWidget::clear(); 0108 } 0109 0110 void ItemSelector::addItem(const QString &caption, const QString &id, const QString &_category, const QIcon &icon, bool removable) 0111 { 0112 qCDebug(KTL_LOG) << "id=" << id; 0113 ILVItem *parentItem = nullptr; 0114 0115 QString category = _category; 0116 if (!category.startsWith("/")) { 0117 category.prepend('/'); 0118 } 0119 0120 do { 0121 category.remove(0, 1); 0122 QString cat; 0123 category.replace("\\/", "|"); 0124 int pos = category.indexOf('/'); 0125 if (pos == -1) 0126 cat = category; 0127 else 0128 cat = category.left(pos); 0129 0130 cat.replace("|", "/"); 0131 0132 if (m_categories.indexOf(cat) == -1) { 0133 m_categories.append(cat); 0134 0135 if (parentItem) { 0136 parentItem = new ILVItem(parentItem, ""); 0137 } else { 0138 parentItem = new ILVItem(this, ""); 0139 } 0140 // parentItem->setExpandable(true); // 2018.08.12 - is it needed? 0141 0142 parentItem->setExpanded(readOpenState(cat)); 0143 0144 parentItem->setText(0, cat); 0145 } else { 0146 QList<QTreeWidgetItem *> foundList = findItems(cat, Qt::MatchExactly); 0147 if (foundList.size() > 1) { 0148 qCWarning(KTL_LOG) << "found multiple categories for '" << cat << "'"; 0149 } 0150 parentItem = dynamic_cast<ILVItem *>(foundList.front()); 0151 } 0152 0153 category.remove(0, pos); 0154 } while (category.contains('/')); 0155 0156 if (!parentItem) { 0157 qCCritical(KTL_LOG) << "Unexpected error in finding parent item for category list"; 0158 return; 0159 } 0160 0161 ILVItem *item = new ILVItem(parentItem, id); 0162 // item->setPixmap( 0, icon ); // 2018.08.12 - replaced with line below 0163 item->setIcon(0, icon); 0164 item->setText(0, caption); 0165 // item->setDragEnabled(true); // 2018.08.12 - replaced with line below 0166 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled); 0167 item->setRemovable(removable); 0168 } 0169 0170 void ItemSelector::writeOpenStates() 0171 { 0172 // KConfig *config = kapp->config(); 0173 KSharedConfigPtr configPtr = KSharedConfig::openConfig(); 0174 // config->setGroup( name() ); 0175 KConfigGroup configGroup = configPtr->group(objectName()); 0176 0177 const QStringList::iterator end = m_categories.end(); 0178 for (QStringList::iterator it = m_categories.begin(); it != end; ++it) { 0179 QList<QTreeWidgetItem *> itemsFound = findItems(*it, Qt::MatchExactly); 0180 if (itemsFound.size() > 1) { 0181 qCWarning(KTL_LOG) << " too many items " << itemsFound.size() << " for category '" << *it << "'"; 0182 } 0183 QTreeWidgetItem *item = itemsFound.first() /* findItem( *it, 0 ) */; 0184 if (item) { 0185 configGroup.writeEntry(*it + "IsOpen", item->isExpanded() /* isOpen() */); 0186 } 0187 } 0188 } 0189 0190 bool ItemSelector::readOpenState(const QString &id) 0191 { 0192 // KConfig *config = kapp->config(); 0193 KSharedConfigPtr configPtr = KSharedConfig::openConfig(); 0194 // config->setGroup( name() ); 0195 KConfigGroup configGroup = configPtr->group(objectName()); 0196 0197 return configGroup.readEntry<bool>(id + "IsOpen", true); 0198 } 0199 0200 QTreeWidgetItem *ItemSelector::selectedItem() const 0201 { 0202 QList<QTreeWidgetItem *> selectedList = selectedItems(); 0203 if (selectedList.empty()) { 0204 return nullptr; 0205 } 0206 if (selectedList.size() > 1) { 0207 qCWarning(KTL_LOG) << " expected 1 item in selection, got " << selectedList.size(); 0208 } 0209 return selectedList.first(); 0210 } 0211 0212 QMimeData *ItemSelector::mimeData(const QList<QTreeWidgetItem *> items) const 0213 { 0214 qCDebug(KTL_LOG) << " begin "; 0215 if (items.size() > 1) { 0216 qCWarning(KTL_LOG) << "expected 1 item, got " << items.size(); 0217 } 0218 QTreeWidgetItem *theItem = items.first(); 0219 if (!theItem) { 0220 qCWarning(KTL_LOG) << "unexpected null item"; 0221 return nullptr; 0222 } 0223 qCDebug(KTL_LOG) << " theItem = " << theItem; 0224 QVariant idAsVariant = theItem->data(0, ILVItem::DataRole_ID); 0225 qCDebug(KTL_LOG) << " idAsVariant = " << idAsVariant; 0226 const QString id = idAsVariant.toString(); 0227 qCDebug(KTL_LOG) << "id='" << id << "'"; 0228 0229 QMimeData *mime = new QMimeData(); 0230 0231 QByteArray data; 0232 QDataStream stream(&data, QIODevice::WriteOnly); 0233 stream << id; 0234 0235 if (id.startsWith("flow/")) { 0236 mime->setData("ktechlab/flowpart", data); 0237 } else if (id.startsWith("ec/")) { 0238 mime->setData("ktechlab/component", data); 0239 } else if (id.startsWith("sc/")) { 0240 mime->setData("ktechlab/subcircuit", data); 0241 } else if (id.startsWith("mech/")) { 0242 mime->setData("ktechlab/mechanical", data); 0243 } else { 0244 qCWarning(KTL_LOG) << "returning unset mime; unknown id '" << id << "'"; 0245 } 0246 0247 // A pixmap cursor is often hard to make out 0248 // QPixmap *pixmap = const_cast<QPixmap*>(currentItem()->pixmap(0)); 0249 // if (pixmap) d->setPixmap(*pixmap); 0250 0251 return mime; 0252 } 0253 0254 void ItemSelector::slotContextMenuRequested(const QPoint &pos) 0255 { 0256 QTreeWidgetItem *item = itemAt(pos); 0257 if (!item || !(static_cast<ILVItem *>(item))->isRemovable()) { 0258 return; 0259 } 0260 0261 QMenu *menu = new QMenu(this); 0262 /* menu->insertItem( 0263 //, Qt::Key_Delete // 2015.12.29 - do not specify shortcut key, because it does not work 0264 ); - 2018.12.01 */ 0265 menu->addAction(i18n("Remove %1", item->text(0)), this, SLOT(slotRemoveSelectedItem())); 0266 QPoint globalPos = mapToGlobal(pos); 0267 menu->popup(globalPos); 0268 } 0269 0270 void ItemSelector::slotRemoveSelectedItem() 0271 { 0272 qCDebug(KTL_LOG) << "removing selected item"; 0273 QList<QTreeWidgetItem *> selectedList = selectedItems(); 0274 if (selectedList.empty()) { 0275 qCDebug(KTL_LOG) << "selection is empty"; 0276 return; 0277 } 0278 QTreeWidgetItem *selectedItem = selectedList.first(); 0279 ILVItem *item = dynamic_cast<ILVItem *>(selectedItem); 0280 if (!item) { 0281 qCDebug(KTL_LOG) << "no selected item to remove"; 0282 return; 0283 } 0284 0285 emit itemRemoved(item->data(0, ILVItem::DataRole_ID).toString() /*key( 0, 0 ) */); 0286 ILVItem *parent = dynamic_cast<ILVItem *>(item->QTreeWidgetItem::parent()); 0287 delete item; 0288 // Get rid of the category as well if it has no children 0289 if (parent && !parent->childCount() /* firstChild() */) { 0290 m_categories.removeAll(parent->text(0)); 0291 delete parent; 0292 } 0293 } 0294 0295 void ItemSelector::setListCaption(const QString &caption) 0296 { 0297 // setColumnText( 0, caption ); // 2018.08.12 - see below 0298 setHeaderLabel(caption); 0299 } 0300 0301 void ItemSelector::slotItemSelected() 0302 { 0303 QTreeWidgetItem *item = selectedItem(); 0304 if (!item) { 0305 return; 0306 } 0307 0308 emit itemSelected(item->data(0, ILVItem::DataRole_ID).toString() /* item->key( 0, 0 ) */); 0309 } 0310 0311 void ItemSelector::slotItemClicked(QTreeWidgetItem *item, int) 0312 { 0313 if (!item) 0314 return; 0315 0316 if (ItemDocument *itemDocument = dynamic_cast<ItemDocument *>(DocManager::self()->getFocusedDocument())) 0317 itemDocument->slotUnsetRepeatedItemId(); 0318 0319 const QString &itemIdString = item->data(0, ILVItem::DataRole_ID).toString(); 0320 0321 emit itemClicked(itemIdString /* item->key( 0, 0 ) */); 0322 } 0323 0324 void ItemSelector::slotItemDoubleClicked(QTreeWidgetItem *item, int) 0325 { 0326 if (!item) 0327 return; 0328 0329 // QString id = item->key( 0, 0 ); 0330 const QString &id = item->data(0, ILVItem::DataRole_ID).toString(); 0331 0332 if (Document *doc = DocManager::self()->getFocusedDocument()) { 0333 if (doc->type() == Document::dt_flowcode && id.startsWith("flow/")) 0334 (static_cast<FlowCodeDocument *>(doc))->slotSetRepeatedItemId(id); 0335 0336 else if (doc->type() == Document::dt_circuit && (id.startsWith("ec/") || id.startsWith("sc/"))) 0337 (static_cast<CircuitDocument *>(doc))->slotSetRepeatedItemId(id); 0338 0339 else if (doc->type() == Document::dt_mechanics && id.startsWith("mech/")) 0340 (static_cast<MechanicsDocument *>(doc))->slotSetRepeatedItemId(id); 0341 } 0342 0343 emit itemDoubleClicked(id); 0344 } 0345 0346 #if 0 // 2018.08.12 - needed? 0347 // Q3DragObject* ItemSelector::dragObject() 0348 // { 0349 // const QString &id = currentItem()->data(0, ILVItem::DataRole_ID).toString() /* key(0,0) */; 0350 // 0351 // Q3StoredDrag * d = nullptr; 0352 // 0353 // if ( id.startsWith("flow/") ) 0354 // d = new Q3StoredDrag( "ktechlab/flowpart", this ); 0355 // 0356 // else if ( id.startsWith("ec/") ) 0357 // d = new Q3StoredDrag( "ktechlab/component", this ); 0358 // 0359 // else if ( id.startsWith("sc/") ) 0360 // d = new Q3StoredDrag( "ktechlab/subcircuit", this ); 0361 // 0362 // else if ( id.startsWith("mech/") ) 0363 // d = new Q3StoredDrag( "ktechlab/mechanical", this ); 0364 // 0365 // if (d) 0366 // { 0367 // QByteArray data; 0368 // QDataStream stream( &data, QIODevice::WriteOnly ); 0369 // stream << id; 0370 // d->setEncodedData(data); 0371 // } else { 0372 // qCWarning(KTL_LOG) << " null drag returned"; 0373 // } 0374 // 0375 // // A pixmap cursor is often hard to make out 0376 // // QPixmap *pixmap = const_cast<QPixmap*>(currentItem()->pixmap(0)); 0377 // // if (pixmap) d->setPixmap(*pixmap); 0378 // 0379 // return d; 0380 // } 0381 #endif 0382 0383 // BEGIN class ComponentSelector 0384 ComponentSelector *ComponentSelector::m_pSelf = nullptr; 0385 0386 ComponentSelector *ComponentSelector::self(KateMDI::ToolView *parent) 0387 { 0388 if (!m_pSelf) { 0389 assert(parent); 0390 m_pSelf = new ComponentSelector(parent); 0391 m_pSelf->setObjectName("Component Selector"); 0392 } 0393 return m_pSelf; 0394 } 0395 0396 ComponentSelector::ComponentSelector(KateMDI::ToolView *parent) 0397 : ItemSelector(parent) 0398 { 0399 qCDebug(KTL_LOG) << " creating " << this; 0400 0401 setWhatsThis( 0402 i18n("Add components to the circuit diagram by dragging them into the circuit.<br><br>" 0403 0404 "To add more than one component of the same type, doubleclick on a component, and click repeatedly in the circuit to place the component. Right click to stop placement.<br><br>" 0405 0406 "Some components (such as subcircuits) can be removed by right clicking on the item and selecting \"Remove\".")); 0407 0408 setListCaption(i18n("Component")); 0409 0410 LibraryItemList *items = itemLibrary()->items(); 0411 qCDebug(KTL_LOG) << " there are " << items->count() << " items"; 0412 const LibraryItemList::iterator end = items->end(); 0413 for (LibraryItemList::iterator it = items->begin(); it != end; ++it) { 0414 if ((*it)->type() == LibraryItem::lit_component) 0415 addItem((*it)->name(), (*it)->activeID(), (*it)->category(), (*it)->icon()); 0416 } 0417 } 0418 // END class ComponentSelector 0419 0420 // BEGIN class FlowPartSelector 0421 FlowPartSelector *FlowPartSelector::m_pSelf = nullptr; 0422 0423 FlowPartSelector *FlowPartSelector::self(KateMDI::ToolView *parent) 0424 { 0425 if (!m_pSelf) { 0426 assert(parent); 0427 m_pSelf = new FlowPartSelector(parent); 0428 m_pSelf->setObjectName("Part Selector"); 0429 } 0430 return m_pSelf; 0431 } 0432 0433 FlowPartSelector::FlowPartSelector(KateMDI::ToolView *parent) 0434 : ItemSelector(static_cast<QWidget *>(parent)) 0435 { 0436 setWhatsThis( 0437 i18n("Add FlowPart to the FlowCode document by dragging them there.<br><br>To add more than one FlowPart of the same type, doubleclick on a FlowPart, and click repeatedly in the FlowChart to place the component. Right click to " 0438 "stop placement.")); 0439 0440 setListCaption(i18n("Flow Part")); 0441 0442 LibraryItemList *items = itemLibrary()->items(); 0443 const LibraryItemList::iterator end = items->end(); 0444 for (LibraryItemList::iterator it = items->begin(); it != end; ++it) { 0445 if ((*it)->type() == LibraryItem::lit_flowpart) 0446 addItem((*it)->name(), (*it)->activeID(), (*it)->category(), (*it)->icon()); 0447 } 0448 } 0449 // END class FlowPartSelector 0450 0451 // BEGIN class MechanicsSelector 0452 MechanicsSelector *MechanicsSelector::m_pSelf = nullptr; 0453 0454 MechanicsSelector *MechanicsSelector::self(KateMDI::ToolView *parent) 0455 { 0456 if (!m_pSelf) { 0457 assert(parent); 0458 m_pSelf = new MechanicsSelector(static_cast<QWidget *>(parent)); 0459 m_pSelf->setObjectName("Mechanics Selector"); 0460 } 0461 return m_pSelf; 0462 } 0463 0464 MechanicsSelector::MechanicsSelector(QWidget *parent) 0465 : ItemSelector(static_cast<QWidget *>(parent)) 0466 { 0467 setWhatsThis(i18n("Add mechanical parts to the mechanics work area by dragging them there.")); 0468 0469 LibraryItemList *items = itemLibrary()->items(); 0470 const LibraryItemList::iterator end = items->end(); 0471 for (LibraryItemList::iterator it = items->begin(); it != end; ++it) { 0472 if ((*it)->type() == LibraryItem::lit_mechanical) { 0473 addItem((*it)->name(), (*it)->activeID(), (*it)->category(), (*it)->icon()); 0474 } 0475 } 0476 } 0477 // END class MechanicsSelector 0478 0479 #include "moc_itemselector.cpp"