/* This program 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mappingtreewidget.h" #include "mappingtreemodel.h" #include "smglobals.h" MappingTreeWidget::MappingTreeWidget(QWidget *parent) : QWidget(parent){ //setup model mModel = static_cast(SmGlobals::instance()->model("MappingTree")); mProxy = new MappingTreeProxy(this); mProxy->setSourceModel(mModel); mTypesModel = new QStringListModel(this); mTypesModel->setStringList(mModel->mappingTypeNames()); //setup gui mTree = new MappingTreeView; mTree->setModel(mProxy); mTree->setColumnHidden(1, true); mTree->setColumnHidden(2, true); mTree->setColumnHidden(3, true); mTree->setColumnHidden(4, true); mTree->setAlternatingRowColors(true); mTree->expandAll(); connect(mModel, SIGNAL(needExpansion()), mTree, SLOT(expandAll())); connect(mTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged())); /* Hide buttons, this doesn't really work, * but keep them for future use :) */ mTypeBox = new QComboBox; mTypeBox->setModel(mTypesModel); connect(mTypeBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(typeChanged(QString))); mTypeBox->setHidden(true); mAddType = new QPushButton(tr("Add &type")); connect(mAddType, SIGNAL(clicked()), this, SLOT(addType())); mAddType->setHidden(true); mDeleteType = new QPushButton(tr("Delete type")); connect(mDeleteType, SIGNAL(clicked()), this, SLOT(deleteType())); mDeleteType->setHidden(true); QHBoxLayout *typesButtonLayout = new QHBoxLayout; typesButtonLayout->addWidget(mDeleteType); typesButtonLayout->addWidget(mAddType); //select type and populate model QSettings s; QString lastType = s.value("ui/mappingtreetype").toString(); if(lastType.isEmpty()){ if(!mTypesModel->stringList().isEmpty()){ lastType = mTypesModel->stringList().at(0); } } int typeId = mModel->mappingTypeIdFromName(lastType); if(typeId != -1){ mModel->setType(typeId); mTypeBox->setCurrentIndex(mTypeBox->findText(lastType)); typeChanged(lastType); } // filter + refresh mFilter = new QLineEdit; connect(mFilter, SIGNAL(returnPressed()), this, SLOT(filter())); mDoFilter = new QPushButton(tr("Filter")); connect(mDoFilter, SIGNAL(clicked()), this, SLOT(filter())); mClearFilter = new QPushButton(tr("Clear")); connect(mClearFilter, SIGNAL(clicked()), this, SLOT(clearFilter())); mRefresh = new QPushButton; mRefresh->setIcon(QIcon(":/refresh.png")); connect(mRefresh, SIGNAL(clicked()), mModel, SLOT(populate())); QLabel *filterLabel = new QLabel(tr("Filter")); QHBoxLayout *filterLayout = new QHBoxLayout; filterLayout->addWidget(filterLabel); filterLayout->addWidget(mFilter); filterLayout->addWidget(mDoFilter); filterLayout->addWidget(mClearFilter); filterLayout->addWidget(mRefresh); //setup actions mAddChildA = new QAction(tr("Add child..."), this); connect(mAddChildA, SIGNAL(triggered()), this, SLOT(addChild())); mTree->addAction(mAddChildA); mAddActorA = new QAction(tr("Add actor..."), this); mAddActorA->setShortcut(Qt::CTRL + Qt::Key_A); connect(mAddActorA, SIGNAL(triggered()), this, SLOT(addActor())); mTree->addAction(mAddActorA); mDeleteChildA = new QAction(tr("Delete..."), this); connect(mDeleteChildA, SIGNAL(triggered()), this, SLOT(deleteChild())); mTree->addAction(mDeleteChildA); mEditChildA = new QAction(tr("Edit..."), this); connect(mEditChildA, SIGNAL(triggered()), this, SLOT(editChild())); mTree->addAction(mEditChildA); //widget layout and misc QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(mTypeBox); mainLayout->addLayout(filterLayout); mainLayout->addLayout(typesButtonLayout); mainLayout->addWidget(mTree); setLayout(mainLayout); } MappingData MappingTreeWidget::selectedItem() const { QModelIndex sel = selected(); if(!sel.isValid()){ return MappingData(); } QModelIndex real = mProxy->mapToSource(sel); return mModel->mappingDataFromIndex(real); } QList MappingTreeWidget::selectedTree() const { QModelIndex start = selected(); QList retval; retval << selectedTreeRecursive(start); return retval; } QList MappingTreeWidget::selectedTreeRecursive(const QModelIndex &start) const{ QList retval; QModelIndex curChild = start.child(0,0); if(!curChild.isValid()){ QModelIndex real = mProxy->mapToSource(start); retval << mModel->mappingDataFromIndex(real); return retval; } int row = 0; while(curChild.isValid()){ retval << selectedTreeRecursive(curChild); ++row; curChild = start.child(row, 0); } return retval; } void MappingTreeWidget::addChild(){ MappingInputDialog dlg(this); int retval = dlg.exec(); if(retval != QDialog::Accepted){ return; } QString value = dlg.mappingName(); if(value.isEmpty()){ return; } QModelIndex parent = mModel->rootIndex(); if(!dlg.createRoot()){ QModelIndex sel = selected(); parent = mProxy->mapToSource(sel); } int parentId = parent.data(MappingTreeModel::MappingIdRole).toInt(); if(!mModel->addChild(value, parent)){ QString err = QString(tr("

Failed to create child! Database said:

%1

")).arg(mModel->lastError().text()); QMessageBox::critical(this, tr("Error"), err); }else{ // we repopulated the model, so all indexes are invalid! QModelIndex newPIdx = mModel->findRecursive(parentId, MappingTreeModel::MappingId, mModel->rootIndex()); if(newPIdx.isValid()){ QModelIndex newSIdx = mModel->find(value, MappingTreeModel::Name, newPIdx); if(newSIdx.isValid()){ QModelIndex newSIdxP = mProxy->mapFromSource(newSIdx); mTree->scrollTo(newSIdxP, QAbstractItemView::EnsureVisible); mTree->selectionModel()->setCurrentIndex(newSIdxP, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current | QItemSelectionModel::Rows); } } } } void MappingTreeWidget::addActor(){ QModelIndex actorIndex = mModel->find("actors", MappingTreeModel::Name); if(!actorIndex.isValid()){ QMessageBox::critical(this, tr("Error"), tr("Could not find actors!")); return; } QString newActorName = QInputDialog::getText(this, tr("New actor"), tr("Actor")); if(!newActorName.isEmpty()){ QModelIndex newActorIdx = mModel->find(newActorName, MappingTreeModel::Name, actorIndex); if(newActorIdx.isValid()){ QModelIndex newIdxProxy = mProxy->mapFromSource(newActorIdx); mTree->scrollTo(newIdxProxy, QAbstractItemView::EnsureVisible); mTree->selectionModel()->setCurrentIndex(newIdxProxy, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current | QItemSelectionModel::Rows); }else{ mModel->addChild(newActorName, actorIndex); actorIndex = mModel->find("actors", MappingTreeModel::Name); QModelIndex newIdx = mModel->find(newActorName, MappingTreeModel::Name, actorIndex); if(newIdx.isValid()){ QModelIndex newIdxProxy = mProxy->mapFromSource(newIdx); mTree->scrollTo(newIdxProxy, QAbstractItemView::EnsureVisible); mTree->selectionModel()->setCurrentIndex(newIdxProxy, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current | QItemSelectionModel::Rows); } } } } void MappingTreeWidget::addType(){ QString typeName = QInputDialog::getText(this, tr("Enter type name"), tr("Name")).toLower().trimmed(); if(typeName.isEmpty()){ return; } if(mModel->addMappingType(typeName)){ mTypesModel->setStringList(mModel->mappingTypeNames()); int idxOf = mTypeBox->findText(typeName); if(idxOf != -1){ mTypeBox->setCurrentIndex(idxOf); } }else{ QMessageBox::critical(this, tr("Error"), tr("Failed to add type. Most likely it already exists!")); } } void MappingTreeWidget::deleteChild(){ QModelIndex sel = selected(); QModelIndex real = mProxy->mapToSource(sel); if(mModel->rowCount(real) > 0){ QString msg = QString(tr("Cannot delete item %1, because it has %2 children!")).arg(real.data().toString()).arg(QString::number(mModel->rowCount(real))); QMessageBox::critical(this, tr("Error"), msg); return; } QString question = QString(tr("Really delete %1?")).arg(real.data(MappingTreeModel::NameRole).toString()); int retval = QMessageBox::question(this, tr("Question"), question, QMessageBox::Yes | QMessageBox::No); if(retval == QMessageBox::Yes){ if(!mModel->deleteChild(real)){ QSqlError last = mModel->lastError(); QString msg = QString(tr("Failed to delete %1. Database said: %2")).arg(real.data().toString()).arg(last.text()); QMessageBox::critical(this, tr("Error"), msg); } } } void MappingTreeWidget::deleteType(){ QString curText = mTypeBox->currentText(); if(curText.isEmpty()){ return; } QString msg = QString(tr("Really delete %1?")).arg(curText); int retval = QMessageBox::question(this, tr("Question"), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if(retval == QMessageBox::Yes){ int typeId = mModel->mappingTypeIdFromName(curText); if(typeId != -1){ if(mModel->deleteMappingType(typeId)){ mTypesModel->setStringList(mModel->mappingTypeNames()); if(mTypesModel->stringList().count() > 0){ mTypeBox->setCurrentIndex(0); } } } } } void MappingTreeWidget::hideEvent(QHideEvent *event){ QString type = mTypeBox->currentText(); if(!type.isEmpty()){ QSettings s; s.setValue("ui/mappingtreetype", type); } event->accept(); } void MappingTreeWidget::typeChanged(QString type){ int typeId = mModel->mappingTypeIdFromName(type); if(typeId != -1){ mModel->setType(typeId); mModel->populate(); mTree->expandAll(); } } void MappingTreeWidget::editChild(){ QModelIndex sel = selected(); QString question = QString("Rename %1").arg(sel.data(MappingTreeModel::NameRole).toString()); bool ok = false; QString newName = QInputDialog::getText(mTree, tr("Rename"), question, QLineEdit::Normal, sel.data(MappingTreeModel::NameRole).toString(), &ok); if(ok){ QModelIndex real = mProxy->mapToSource(sel); if(real.isValid()){ mModel->renameChild(real, newName); } } } void MappingTreeWidget::selectionChanged(){ QModelIndex sel = selected(); QModelIndex real = mProxy->mapToSource(sel); emit mappingChanged(real.data(MappingTreeModel::MappingIdRole).toInt()); } void MappingTreeWidget::filter(){ QString filter = mFilter->text(); mProxy->setFilter(filter); mTree->expandAll(); } void MappingTreeWidget::clearFilter(){ mFilter->clear(); mProxy->setFilter(QString()); mTree->expandAll(); } const QModelIndex MappingTreeWidget::selected() const{ QModelIndexList sel = mTree->selectionModel()->selectedRows(); if(sel.isEmpty()){ return QModelIndex(); } return sel.first(); } MappingTreeView::MappingTreeView(QWidget *parent) : SmTreeView(parent) {} void MappingTreeView::contextMenuEvent(QContextMenuEvent *e){ QMenu ctxMenu(this); ctxMenu.addActions(actions()); ctxMenu.exec(e->globalPos()); } void MappingTreeView::keyPressEvent(QKeyEvent *e){ if(e->key() == Qt::Key_Right && (e->modifiers() & Qt::ControlModifier)){ clearFocus(); emit shiftFocus(); return; } if(e->key() == Qt::Key_Return && (e->modifiers() & Qt::ControlModifier)){ emit acceptPics(); } if(e->key() == Qt::Key_Right){ emit addMapping(); } if(e->key() == Qt::Key_Delete){ emit clearMappings(); } SmTreeView::keyPressEvent(e); } MappingTreeResultView::MappingTreeResultView(QWidget *parent) : SmTreeView(parent) { setAlternatingRowColors(true); setPalette(qApp->palette()); } void MappingTreeResultView::keyPressEvent(QKeyEvent *e){ if(e->key() == Qt::Key_Left && (e->modifiers() & Qt::ControlModifier)){ clearFocus(); emit shiftFocus(); return; } if(e->key() == Qt::Key_Left){ emit removeMapping(); } SmTreeView::keyPressEvent(e); } MappingEditWidget::MappingEditWidget(QWidget *parent) : QWidget(parent){ //the views mMappingTree = new MappingTreeWidget; mMappingResult = new MappingTreeResultView; mResultModel = new MappingTreeResultModel(QStringList() << tr("Name") << tr("MappingId") << tr("ParentId") << tr("MyId"), this); mMappingResult->setModel(mResultModel); mMappingResult->setAlternatingRowColors(true); mMappingResult->setColumnHidden(1, true); mMappingResult->setColumnHidden(2, true); mMappingResult->setColumnHidden(3, true); connect(mMappingTree->mappingTreeView(), SIGNAL(addMapping()), this, SLOT(addMapping())); connect(mMappingTree->mappingTreeView(), SIGNAL(clearMappings()), this, SLOT(clearMapping())); connect(mMappingTree->mappingTreeView(), SIGNAL(shiftFocus()), this, SLOT(shiftFocusResult())); connect(mMappingResult, SIGNAL(shiftFocus()), this, SLOT(shiftFocusMappings())); connect(mMappingResult, SIGNAL(removeMapping()), this, SLOT(removeMapping())); //buttons mAddMapping = new QPushButton(tr(">>")); connect(mAddMapping, SIGNAL(clicked()), this, SLOT(addMapping())); mRemoveMapping = new QPushButton(tr("<<")); connect(mRemoveMapping, SIGNAL(clicked()), this, SLOT(removeMapping())); mClearMapping = new QPushButton(tr("&Clear")); connect(mClearMapping, SIGNAL(clicked()), this, SLOT(clearMapping())); mAddTree = new QPushButton(tr(">>>>")); connect(mAddTree, SIGNAL(clicked()), this, SLOT(addTree())); //layout QHBoxLayout *mainLayout = new QHBoxLayout; mainLayout->addWidget(mMappingTree); QVBoxLayout *buttonLayout = new QVBoxLayout; buttonLayout->addStretch(); buttonLayout->addWidget(mAddMapping); buttonLayout->addWidget(mRemoveMapping); buttonLayout->addWidget(mClearMapping); buttonLayout->addWidget(mAddTree); buttonLayout->addStretch(); mainLayout->addLayout(buttonLayout); mainLayout->addWidget(mMappingResult); setLayout(mainLayout); mMappingTree->mappingTreeView()->setFocus(); } void MappingEditWidget::addMapping(){ MappingData selected = mMappingTree->selectedItem(); mResultModel->addItem(selected); mMappingResult->expandAll(); mMappingTree->mappingTreeView()->setFocus(); } void MappingEditWidget::addTree(){ QList retval = mMappingTree->selectedTree(); foreach(MappingData md, retval){ mResultModel->addItem(md); } mMappingResult->expandAll(); mMappingTree->mappingTreeView()->setFocus(); } void MappingEditWidget::removeMapping(){ QModelIndexList sel = mMappingResult->selectionModel()->selectedRows(); if(sel.isEmpty()){ return; } QModelIndex firstIdx = sel.first(); if(firstIdx.isValid()){ mResultModel->removeItem(firstIdx); } mMappingResult->expandAll(); mMappingTree->mappingTreeView()->setFocus(); } void MappingEditWidget::clearMapping(){ mResultModel->clearData(); mMappingTree->mappingTreeView()->setFocus(); } void MappingEditWidget::setMappings(const QList &mappings){ if(mappings.isEmpty()){ return; } mResultModel->clearData(); foreach(MappingData d, mappings){ mResultModel->addItem(d); } mMappingResult->expandAll(); } void MappingEditWidget::expandAllResults(){ mMappingResult->expandAll(); } void MappingEditWidget::shiftFocusResult(){ mMappingResult->setFocus(); } void MappingEditWidget::shiftFocusMappings(){ mMappingTree->mappingTreeView()->setFocus(); } void MappingEditWidget::saveMappings(QString where){ const QList mappingData = model()->mappingData(); QByteArray saveVal; QDataStream in(&saveVal, QIODevice::WriteOnly); foreach(MappingData md, mappingData){ in << md; } QSettings s; s.setValue(where, saveVal); } void MappingEditWidget::loadMappings(QString from){ QSettings s; QByteArray val = s.value(from).toByteArray(); QDataStream out(&val, QIODevice::ReadOnly); MappingData md; while(!out.atEnd()){ out >> md; model()->addItem(md); } expandAllResults(); } MappingInputDialog::MappingInputDialog(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f){ mOk = new QPushButton(tr("Ok")); connect(mOk, SIGNAL(clicked()), this, SLOT(accept())); mCancel = new QPushButton(tr("Cancel")); connect(mCancel, SIGNAL(clicked()), this, SLOT(reject())); mIsRoot = new QCheckBox(tr("Create root item")); mEditor = new QLineEdit; QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); buttonLayout->addWidget(mOk); buttonLayout->addWidget(mCancel); QVBoxLayout* inputLayout = new QVBoxLayout; QLabel *caption = new QLabel(tr("Enter mapping name")); inputLayout->addWidget(caption); inputLayout->addWidget(mEditor); QHBoxLayout *cbLayout = new QHBoxLayout; cbLayout->addStretch(); cbLayout->addWidget(mIsRoot); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addLayout(inputLayout); mainLayout->addLayout(cbLayout); mainLayout->addLayout(buttonLayout); setLayout(mainLayout); } QString MappingInputDialog::mappingName() const{ return mEditor->text(); } bool MappingInputDialog::createRoot() const { return mIsRoot->checkState() == Qt::Checked; } MappingEditDialog::MappingEditDialog(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f){ mEditWidget = new MappingEditWidget; mOk = new QPushButton(tr("Ok")); connect(mOk, SIGNAL(clicked()), this, SLOT(accept())); mCancel = new QPushButton(tr("Cancel")); connect(mCancel, SIGNAL(clicked()), this, SLOT(reject())); QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); buttonLayout->addWidget(mOk); buttonLayout->addWidget(mCancel); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(mEditWidget); mainLayout->addLayout(buttonLayout); setLayout(mainLayout); } MappingTreeProxy::MappingTreeProxy(QObject *parent) : QSortFilterProxyModel(parent) {} void MappingTreeProxy::setFilter(const QString &filter){ mFilter = QRegExp(filter); invalidateFilter(); } bool MappingTreeProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const{ if(mFilter.isEmpty()){ return true; } QModelIndex nameIdx = sourceModel()->index(sourceRow, MappingTreeModel::Name, sourceParent); MappingTreeModel *model = qobject_cast(sourceModel()); return model->matchRecursive(nameIdx, mFilter); }