/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2010-2012 University of Bonn. All rights reserved. * * * This file is part of MoleCuilder. * * MoleCuilder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * MoleCuilder is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MoleCuilder. If not, see . */ /* * QtMoleculeList.cpp * * Created on: Jan 21, 2010 * Author: crueger */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "QtMoleculeList.hpp" #include #include #include "UIElements/Views/Qt4/MoleculeList/QtMoleculeItem.hpp" #include "UIElements/Views/Qt4/MoleculeList/QtMoleculeItemFactory.hpp" #include "UIElements/Qt4/InstanceBoard/QtObservedInstanceBoard.hpp" #include #include #include //#include "CodePatterns/MemDebug.hpp" #include "CodePatterns/Log.hpp" #include "CodePatterns/Observer/Notification.hpp" #include "Atom/atom.hpp" #include "Actions/MoleculeAction/ChangeNameAction.hpp" #include "Actions/SelectionAction/Molecules/PopMoleculesAction.hpp" #include "Actions/SelectionAction/Molecules/PushMoleculesAction.hpp" #include "Actions/SelectionAction/Molecules/MoleculeByIdAction.hpp" #include "Actions/ActionQueue.hpp" #include "Actions/ActionSequence.hpp" #include "Actions/ActionTrait.hpp" #include "Actions/MakroAction.hpp" #include "Descriptors/MoleculeIdDescriptor.hpp" #include "Formula.hpp" #include "molecule.hpp" using namespace std; QtMoleculeList::QtMoleculeList( QtObservedInstanceBoard *_board, QObject *_parent) : QStandardItemModel(_parent), board(_board), observer(_board), nameIsChanged(false) { setColumnCount(QtMoleculeItemFactory::COLUMNCOUNT); connect(this,SIGNAL(itemChanged(QStandardItem*)), this,SLOT(moleculeNameChanged(QStandardItem*))); connect(this, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(checkForVisibilityChange(QStandardItem*))); connect(&observer, SIGNAL(MoleculeInserted(QtObservedMolecule::ptr)), this, SLOT(moleculeInserted(QtObservedMolecule::ptr))); connect(&observer, SIGNAL(MoleculeRemoved(ObservedValue_Index_t)), this, SLOT(moleculeRemoved(ObservedValue_Index_t))); connect(&observer, SIGNAL(FormulaChanged(const QtObservedMolecule::ptr)), this, SLOT(formulaChanged(const QtObservedMolecule::ptr))); connect(&observer, SIGNAL(NameChanged(const QtObservedMolecule::ptr)), this, SLOT(nameChanged(const QtObservedMolecule::ptr))); connect(&observer, SIGNAL(AtomCountChanged(const QtObservedMolecule::ptr)), this, SLOT(atomcountChanged(const QtObservedMolecule::ptr))); } QtMoleculeList::~QtMoleculeList() {} QVariant QtMoleculeList::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole) { if (orientation == Qt::Horizontal) { if (section < QtMoleculeItem::COLUMNTYPES_MAX) return QString(QtMoleculeItemFactory::COLUMNNAMES[section]); } } return QVariant(); } void QtMoleculeList::moleculeInserted(QtObservedMolecule::ptr _mol) { LOG(1, "Adding molecule " << _mol->getMolName()); // check that World knows the molecule still const std::string formula = addMolecule(_mol); LOG(1, "Adding " << formula << " to toBeSetOccurrence."); setOccurrence(FormulaToGroupItem(formula)); } void QtMoleculeList::moleculeRemoved(ObservedValue_Index_t _id) { LOG(1, "Removing molecule " << _id); if (!isMoleculeItemPresent(_id)) { ELOG(1, "QtMoleculeItem to id " << _id << " has disappeared before removal."); return; } QtMoleculeItem *item = MoleculeIdToItem(_id); if (item != NULL) { const std::string formula = item->parent()->text().toStdString(); LOG(1, "Adding " << formula << " to toBeSetOccurrence."); QStandardItem * const groupitem = FormulaToGroupItem(formula); // first remove molitem to reduce count removeMoleculeItem(item); const int removeindex = setOccurrence(groupitem); if (removeindex != -1) { LOG(1, "Removing row of group item to " << formula); removeRows(removeindex, 1, invisibleRootItem()->index()); } } } bool QtMoleculeList::isMoleculeItemPresent(ObservedValue_Index_t _molid) const { MoleculeItemBiMap_t::left_const_iterator iter = MoleculeItemBiMap.left.find(_molid); return ( iter != MoleculeItemBiMap.left.end()); } QtMoleculeItem * QtMoleculeList::MoleculeIdToItem(ObservedValue_Index_t _molid) const { MoleculeItemBiMap_t::left_const_iterator iter = MoleculeItemBiMap.left.find(_molid); ASSERT( iter != MoleculeItemBiMap.left.end(), "QtMoleculeList::MoleculeIdToItem() - could not find item to id " +toString(_molid)); return iter->second; } ObservedValue_Index_t QtMoleculeList::ItemToMoleculeId(const QtMoleculeItem * const _item) const { const MoleculeItemBiMap_t::right_const_iterator iter = MoleculeItemBiMap.right.find(const_cast(_item)); if (iter != MoleculeItemBiMap.right.end()) return iter->second; else return NULL; } QtMoleculeItem * QtMoleculeList::getSpecificMoleculeItem( const QtMoleculeItem * const _item, const enum QtMoleculeItem::COLUMNTYPES _type) const { QStandardItem *parent_item = _item->parent(); ASSERT( parent_item != NULL, "QtMoleculeList::getSpecificMoleculeItem() - parent of molecule item is NULL"); return static_cast(parent_item->child(_item->index().row(), _type)); } bool QtMoleculeList::isGroupItemPresent(const std::string &_formula) const { FormulaTreeItemBiMap_t::left_const_iterator iter = FormulaItemBiMap.left.find(_formula); return ( iter != FormulaItemBiMap.left.end()); } QStandardItem * QtMoleculeList::FormulaToGroupItem(const std::string &_formula) const { FormulaTreeItemBiMap_t::left_const_iterator iter = FormulaItemBiMap.left.find(_formula); ASSERT( iter != FormulaItemBiMap.left.end(), "QtMoleculeList::FormulaToGroupItem() - could not find item to formula " +toString(_formula)); return iter->second; } const std::string& QtMoleculeList::GroupItemToFormula(const QStandardItem * const _item) const { static std::string emptystring; const FormulaTreeItemBiMap_t::right_const_iterator iter = FormulaItemBiMap.right.find(const_cast(_item)); if (iter != FormulaItemBiMap.right.end()) return iter->second; else return emptystring; } QStandardItem * QtMoleculeList::getSpecificGroupItem( const QStandardItem * const _item, const enum QtMoleculeItem::COLUMNTYPES _type) const { return invisibleRootItem()->child(_item->index().row(), _type); } const QModelIndex QtMoleculeList::MoleculeIdToIndex(ObservedValue_Index_t _id) const { QtMoleculeItem * const item = MoleculeIdToItem(_id); ASSERT(item != NULL, "QtMoleculeList::MoleculeIdToIndex() - could not find item to " +toString(_id)); return indexFromItem(item); } ObservedValue_Index_t QtMoleculeList::IndexToMoleculeId(const QModelIndex &_index) const { QtMoleculeItem * const item = dynamic_cast(itemFromIndex(_index)); if (item == NULL) return NULL; else return ItemToMoleculeId(item); } void QtMoleculeList::addGroupItem( QStandardItem *&mainitem, const std::string &_molecule_formula) { QList groupItems = QtMoleculeItemFactory::createGroupItems(_molecule_formula); mainitem = groupItems.front(); FormulaItemBiMap.left.insert( std::make_pair(_molecule_formula, mainitem) ); if (FormulaVisibilityCountMap.count(_molecule_formula) == 0) FormulaVisibilityCountMap.insert( std::make_pair(_molecule_formula, 0)); invisibleRootItem()->appendRow(groupItems); } QList QtMoleculeList::createMoleculeItems( QtObservedMolecule::ptr &_ObservedMolecule, std::string &_molecule_formula) { QList molItems = QtMoleculeItemFactory::createMoleculeItems(_ObservedMolecule); QtMoleculeItem *mol_item = dynamic_cast(molItems.front()); ASSERT( mol_item != NULL, "QtMoleculeList::createMoleculeItems() - item from factory was not a QtMoleculeItem?"); MoleculeItemBiMap.left.insert( std::make_pair(_ObservedMolecule->getIndex(), mol_item) ); QStandardItem *formulaitem = molItems.at(QtMoleculeItem::FORMULA); ASSERT( formulaitem != NULL, "QtMoleculeList::createMoleculeItems() - Formula item not created by factory?"); _molecule_formula = formulaitem->text().toStdString(); LOG(1, "Adding " << _molecule_formula << " for " << _ObservedMolecule->getMolIndex() << " to MoleculeFormulaMap."); MoleculeFormulaMap.insert( std::make_pair( _ObservedMolecule->getIndex(), _molecule_formula) ); // LOG(1, "Inserting molecule " << _molid << ": " << _molecule_formula); return molItems; } std::string QtMoleculeList::addMolecule(QtObservedMolecule::ptr &_ObservedMolecule) { // find group if already in list QStandardItem *groupItem = NULL; // create molecule items and obtain the molecule's formula std::string molecule_formula; QList molItems = createMoleculeItems(_ObservedMolecule, molecule_formula); // new molecule type -> create new group if (!isGroupItemPresent(molecule_formula)){ // insert new formula entry into visibility #ifndef NDEBUG std::pair< FormulaVisibilityCountMap_t::iterator, bool> visibilityinserter = #endif FormulaVisibilityCountMap.insert( std::make_pair( molecule_formula, (unsigned int)0) ); ASSERT( visibilityinserter.second, "QtMoleculeList::refill() - molecule with formula " +molecule_formula+" already in FormulaVisibilityCountMap."); // create item and place into Map with formula as key addGroupItem(groupItem, molecule_formula); } else { groupItem = FormulaToGroupItem(molecule_formula); } ASSERT( groupItem != NULL, "QtMoleculeList::addMolecule() - item with id "+toString(_ObservedMolecule->getMolIndex()) +" has no parent?"); groupItem->appendRow(molItems); return molecule_formula; } void QtMoleculeList::removeMoleculeItem(QtMoleculeItem * const _item) { const QModelIndex mol_index = indexFromItem(_item); QStandardItem *groupitem = _item->parent(); const QModelIndex group_index = groupitem->index(); { MoleculeItemBiMap_t::right_iterator removeiter = MoleculeItemBiMap.right.find(_item); ASSERT( removeiter != MoleculeItemBiMap.right.end(), "QtMoleculeList::removeMoleculeItem() - could not find item in MoleculeBiMap."); // LOG(1, "Erasing molecule " << (removeiter->second)); { MoleculeFormulaMap_t::iterator removeformulaiter = MoleculeFormulaMap.find(removeiter->second); ASSERT( removeformulaiter != MoleculeFormulaMap.end(), "QtMoleculeList::removeMoleculeItem() - could not find id " +toString(removeiter->second)+" in MoleculeFormulaMap."); LOG(1, "Removing " << removeformulaiter->second << " for " << removeformulaiter->first << " from MoleculeFormulaMap."); MoleculeFormulaMap.erase( removeformulaiter ); } MoleculeItemBiMap.right.erase(removeiter); } removeRows(mol_index.row(), 1, group_index); } void QtMoleculeList::checkForVisibilityChange(QStandardItem* _item) { // qDebug() << "Item changed called."; if (_item->index().column() == QtMoleculeItem::VISIBILITY) { // qDebug() << "visibilityItem changed: " << (_item->checkState() ? "checked" : "unchecked"); if ((_item->parent() == NULL) || (_item->parent() == invisibleRootItem())) { LOG(1, "Updating visibility of group item " << _item); setVisibilityForGroupItem(_item); } else { LOG(1, "Updating visibility of item " << _item); setVisibilityForMoleculeItem( assert_cast(_item) ); } } } void QtMoleculeList::setVisibilityForMoleculeItem(QtMoleculeItem* _item) { const bool visible = _item->checkState(); const ObservedValue_Index_t molid = _item->getMoleculeIndex(); std::string molecule_formula("illegal"); { const MoleculeFormulaMap_t::const_iterator formulaiter = MoleculeFormulaMap.find(molid); ASSERT( formulaiter != MoleculeFormulaMap.end(), "QtMoleculeList::setVisibilityForMoleculeItem() - formula of molecule " +toString(molid)+" unknown."); molecule_formula = formulaiter->second; } ASSERT( FormulaVisibilityCountMap.count(molecule_formula) != 0, "QtMoleculeList::setVisibilityForMoleculeItem() - molecule with formula " +molecule_formula +" is not present in FormulaVisibilityCountMap."); // get parent QStandardItem *groupItem = _item->parent(); QStandardItem *visgroupItem = getSpecificGroupItem(groupItem, QtMoleculeItem::VISIBILITY); ASSERT( groupItem != NULL, "QtMoleculeList::setVisibilityForMoleculeItem() - item to " +toString(molid)+" has not parent?"); // check whether we have to set the group item if (visible) { ++(FormulaVisibilityCountMap[molecule_formula]); // compare with occurence/total number of molecules if (FormulaVisibilityCountMap[molecule_formula] == (unsigned int)(groupItem->rowCount())) visgroupItem->setCheckState(Qt::Checked); } else { --(FormulaVisibilityCountMap[molecule_formula]); // none selected anymore? if (FormulaVisibilityCountMap[molecule_formula] == 0) visgroupItem->setCheckState(Qt::Unchecked); } emit moleculesVisibilityChanged(molid, visible); } void QtMoleculeList::setVisibilityForGroupItem(QStandardItem* _item) { // go through all children, but don't enter for groupItem once more const bool visible = _item->checkState(); QStandardItem *groupitem = getSpecificGroupItem(_item, QtMoleculeItem::NAME); for (int i=0;irowCount();++i) { QtMoleculeItem * const molItem = dynamic_cast( groupitem->child(i, QtMoleculeItem::VISIBILITY)); if (molItem->checkState() != visible) { molItem->setCheckState(visible ? Qt::Checked : Qt::Unchecked); // emit signal emit moleculesVisibilityChanged(molItem->getMoleculeIndex(), visible); } } // set current number of visible children const std::string molecule_formula = GroupItemToFormula( getSpecificGroupItem(_item, QtMoleculeItem::NAME) ); FormulaVisibilityCountMap_t::iterator countiter = FormulaVisibilityCountMap.find(molecule_formula); ASSERT( countiter != FormulaVisibilityCountMap.end(), "QtMoleculeList::setVisibilityForGroupItem() - molecules "+molecule_formula +" have no entry in visibility count map?"); countiter->second = visible ? groupitem->rowCount() : 0; } static MoleCuilder::MakroAction *constructMakroRenameAction( MoleCuilder::ActionSequence &sequence, const std::string &_new_name, const moleculeId_t _molid ) { MoleCuilder::ActionQueue &AQ = MoleCuilder::ActionQueue::getInstance(); MoleCuilder::ActionTrait trait("change-single-molecule-name"); sequence.addAction(AQ.getActionByName("push-molecule-selection").clone(MoleCuilder::Action::NonInteractive)); MoleCuilder::Action * const selectaction = AQ.getActionByName("select-molecule-by-id").clone(MoleCuilder::Action::NonInteractive); { std::stringstream molid_string; molid_string << toString(_molid); selectaction->setOptionValue("select-molecule-by-id", molid_string.str()); } sequence.addAction(selectaction); MoleCuilder::Action * const changeaction = AQ.getActionByName("change-molname").clone(MoleCuilder::Action::NonInteractive); changeaction->setOptionValue("change-molname", _new_name); sequence.addAction(changeaction); sequence.addAction(AQ.getActionByName("pop-molecule-selection").clone(MoleCuilder::Action::NonInteractive)); MoleCuilder::MakroAction* makroaction = new MoleCuilder::MakroAction(trait, sequence); return makroaction; } void QtMoleculeList::moleculeNameChanged(QStandardItem* item) { // check whether name change came from outside if (nameIsChanged) return; // obtain molecule id if ( item->index().column() == QtMoleculeItem::NAME) { QtMoleculeItem *molitem = assert_cast(item); MoleculeItemBiMap_t::right_const_iterator iter = MoleculeItemBiMap.right.find(molitem); ASSERT( iter != MoleculeItemBiMap.right.end(), "QtMoleculeList::moleculeChanged() - name of unknown molecule changed."); const ObservedValue_Index_t index = iter->second; const moleculeId_t molid = board->getMoleculeIdToIndex(index); // change the name const std::string cellValue = item->text().toStdString(); const QtObservedMolecule::ptr mol = board->getObservedMolecule(index); if (cellValue != mol->getMolName()) { if (!cellValue.empty()) { // create actions such that we may undo static MoleCuilder::ActionSequence sequence; MoleCuilder::MakroAction *makroaction = constructMakroRenameAction(sequence, cellValue, molid); MoleCuilder::ActionQueue &AQ = MoleCuilder::ActionQueue::getInstance(); AQ.registerAction(makroaction); AQ.queueAction("change-single-molecule-name", MoleCuilder::Action::NonInteractive); } else { if (mol) { LOG(2, "WARNING: Not setting changing molecule " << mol->getMolName() << " to empty."); QtMoleculeItem * const molitem = assert_cast(item); molitem->updateState(mol); } } } } } int QtMoleculeList::setOccurrence(QStandardItem * const _groupitem) { ASSERT( _groupitem != NULL, "QtMoleculeList::setOccurrence() - group item at "+toString(_groupitem) +" is NULL"); QModelIndex modelindex = _groupitem->index(); ASSERT( modelindex.isValid(), "QtMoleculeList::setOccurrence() - groupitem not associated to model anymore."); const int index = modelindex.row(); QStandardItem *parent_item = _groupitem->parent() == NULL ? invisibleRootItem() : _groupitem->parent(); ASSERT( parent_item != NULL, "QtMoleculeList::setOccurrence() - group item at "+toString(index) +" does not have a parent?"); QStandardItem *occ_item = parent_item->child(index, QtMoleculeItem::OCCURRENCE); ASSERT( occ_item != NULL, "QtMoleculeList::setOccurrence() - group item at "+toString(index) +" does not have an occurrence?"); const int count = _groupitem->rowCount(); if (count == 0) { // we have to remove the group item completely const std::string molecule_formula = _groupitem->text().toStdString(); FormulaItemBiMap.left.erase(molecule_formula); FormulaVisibilityCountMap.erase(molecule_formula); return index; } else { occ_item->setText(QString::number(count)); return -1; } } void QtMoleculeList::moveItem( QtMoleculeItem *_molitem, const std::string &_new_formula) { QStandardItem *groupitem = NULL; // prohibit starting of selection actions emit MayNotStartSelections(); // use takeRows of molecule .. const QList mol_row = _molitem->parent()->takeRow(_molitem->index().row()); // .. and re-add where new formula fits if (!isGroupItemPresent(_new_formula)) { // add new group item and formula entry addGroupItem(groupitem, _new_formula); } else { groupitem = FormulaToGroupItem(_new_formula); } ASSERT( groupitem != NULL, "QtMoleculeList::readdItem() - failed to create a sensible new groupitem"); // finally add again groupitem->appendRow(mol_row); emit MayStartSelections(); } void QtMoleculeList::formulaChanged(const QtObservedMolecule::ptr _mol) { // we need the id as identifier to the item const ObservedValue_Index_t molid = _mol->getIndex(); LOG(3, "DEBUG: QtMoleculeList got formulaChanged for id " << molid); QtMoleculeItem *const molitem = MoleculeIdToItem(molid); // update item { QtMoleculeItem *const formulaitem = getSpecificMoleculeItem(molitem, QtMoleculeItem::FORMULA); ASSERT(formulaitem != NULL, "QtMoleculeList::formulaChanged() - could not item for FORMULA."); formulaitem->updateState(_mol); } LOG(3, "DEBUG: Moving item to id " << molid); const MoleculeFormulaMap_t::iterator formulaiter = MoleculeFormulaMap.find(molid); ASSERT( formulaiter != MoleculeFormulaMap.end(), "QtMoleculeList::updateItemStates() - formula of molecule " +toString(molid)+" unknown."); // we get old formula from stored map and new formula from the ObservedMolecule const std::string old_formula = formulaiter->second; const std::string new_formula = _mol->getMolFormula(); // then we move the item if necessary if (old_formula != new_formula) { LOG(3, "DEBUG: Moving item " << molitem); // remove from formula<->molecule bimap with old formula LOG(4, "DEBUG: Removing " << old_formula << " for " << formulaiter->first << " from MoleculeFormulaMap."); MoleculeFormulaMap.erase( formulaiter ); moveItem(molitem, new_formula); // changing occurrences for old_formula with possible removal const int removeindex = setOccurrence( FormulaToGroupItem(old_formula) ); if (removeindex != -1) { LOG(3, "DEBUG: Removing row of group item to " << old_formula); removeRows(removeindex, 1, invisibleRootItem()->index()); } // and add to formula<->molecule bimap with updated new_formula LOG(4, "DEBUG: Adding " << new_formula << " for " << molid << " to MoleculeFormulaMap."); MoleculeFormulaMap.insert( std::make_pair(molid, new_formula) ); const int addindex = setOccurrence( FormulaToGroupItem(new_formula) ); ASSERT( addindex == -1, "QtMoleculeList::formulaChanged() - add mol to new formula "+ toString(new_formula)+" made the row to be removed?"); } } void QtMoleculeList::atomcountChanged(const QtObservedMolecule::ptr _mol) { // we need the id as identifier to the items const ObservedValue_Index_t molid = _mol->getIndex(); LOG(3, "DEBUG: QtMoleculeList got atomcountChanged for id " << molid); QtMoleculeItem *const molitem = MoleculeIdToItem(molid); // update items QtMoleculeItem *const atomcountitem = getSpecificMoleculeItem(molitem, QtMoleculeItem::ATOMCOUNT); ASSERT(atomcountitem != NULL, "QtMoleculeList::atomcountChanged() - could not item for ATOMCOUNT."); atomcountitem->updateState(_mol); } void QtMoleculeList::nameChanged(const QtObservedMolecule::ptr _mol) { nameIsChanged = true; // we need the id as identifier to the items const ObservedValue_Index_t molid = _mol->getIndex(); LOG(3, "DEBUG: QtMoleculeList got nameChanged for id " << molid); QtMoleculeItem *const molitem = MoleculeIdToItem(molid); // update items QtMoleculeItem *const nameitem = getSpecificMoleculeItem(molitem, QtMoleculeItem::NAME); ASSERT(nameitem != NULL, "QtMoleculeList::nameChanged() - could not item for NAME."); nameitem->updateState(_mol); nameIsChanged = false; }