File indexing completed on 2024-06-02 04:34:56
0001 /*************************************************************************** 0002 * * 0003 * copyright : (C) 2007 The University of Toronto * 0004 * netterfield@astro.utoronto.ca * 0005 * * 0006 * This program is free software; you can redistribute it and/or modify * 0007 * it under the terms of the GNU General Public License as published by * 0008 * the Free Software Foundation; either version 2 of the License, or * 0009 * (at your option) any later version. * 0010 * * 0011 ***************************************************************************/ 0012 0013 #include "changefiledialog.h" 0014 #include "dialogdefaults.h" 0015 0016 #include "datacollection.h" 0017 #include "datasourceconfiguredialog.h" 0018 #include "datavector.h" 0019 #include "datamatrix.h" 0020 #include "datascalar.h" 0021 #include "datastring.h" 0022 #include "vscalar.h" 0023 #include "primitive.h" 0024 0025 #include "plotitem.h" 0026 0027 #include "objectstore.h" 0028 #include "document.h" 0029 #include "mainwindow.h" 0030 #include "application.h" 0031 #include "updatemanager.h" 0032 #include "updateserver.h" 0033 #include "datasourcepluginmanager.h" 0034 0035 #include <QDir> 0036 #include <QMessageBox> 0037 #include <QThreadPool> 0038 0039 namespace Kst { 0040 0041 ChangeFileDialog::ChangeFileDialog(QWidget *parent) 0042 : QDialog(parent), _dataSource(0), _requestID(0) { 0043 setupUi(this); 0044 0045 MainWindow::setWidgetFlags(this); 0046 0047 if (MainWindow *mw = qobject_cast<MainWindow*>(parent)) { 0048 _store = mw->document()->objectStore(); 0049 } else { 0050 // FIXME: we need the object store 0051 qFatal("ERROR: can't construct a ChangeFileDialog without the object store"); 0052 } 0053 0054 connect(_add, SIGNAL(clicked()), this, SLOT(addButtonClicked())); 0055 connect(_remove, SIGNAL(clicked()), this, SLOT(removeButtonClicked())); 0056 connect(_removeAll, SIGNAL(clicked()), this, SLOT(removeAll())); 0057 connect(_addAll, SIGNAL(clicked()), this, SLOT(addAll())); 0058 0059 connect(_changeFilePrimitiveList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(availableDoubleClicked(QListWidgetItem*))); 0060 connect(_selectedFilePrimitiveList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(selectedDoubleClicked(QListWidgetItem*))); 0061 0062 connect(_allFromFile, SIGNAL(clicked()), this, SLOT(selectAllFromFile())); 0063 0064 connect(_changeFilePrimitiveList, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons())); 0065 connect(_selectedFilePrimitiveList, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons())); 0066 0067 connect(_duplicateSelected, SIGNAL(toggled(bool)), _duplicateDependents, SLOT(setEnabled(bool))); 0068 connect(_dataFile, SIGNAL(changed(QString)), this, SLOT(fileNameChanged(QString))); 0069 connect(_dataFile, SIGNAL(destroyed()), kstApp->mainWindow(), SLOT(cleanUpDataSourceList())); 0070 0071 connect(_buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(reject())); 0072 connect(_buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(OKClicked())); 0073 connect(_buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(apply())); 0074 0075 connect(_configure, SIGNAL(clicked()), this, SLOT(showConfigWidget())); 0076 connect(UpdateServer::self(), SIGNAL(objectListsChanged()), this, SLOT(updatePrimitiveList())); 0077 0078 _dataFile->setFile(dialogDefaults().value("changedatafile/newFileName",QDir::currentPath()).toString()); 0079 0080 updateButtons(); 0081 } 0082 0083 0084 ChangeFileDialog::~ChangeFileDialog() { 0085 } 0086 0087 0088 void ChangeFileDialog::show() { 0089 updatePrimitiveList(); 0090 QDialog::show(); 0091 } 0092 0093 void ChangeFileDialog::showConfigWidget() { 0094 QPointer<DataSourceConfigureDialog> dialog = new DataSourceConfigureDialog(DataDialog::New, _dataSource, this); 0095 if ( dialog->exec() == QDialog::Accepted ) { 0096 fileNameChanged(_dataSource->fileName()); 0097 } 0098 delete dialog; 0099 } 0100 0101 0102 void ChangeFileDialog::removeButtonClicked() { 0103 foreach (QListWidgetItem* item, _selectedFilePrimitiveList->selectedItems()) { 0104 _changeFilePrimitiveList->addItem(_selectedFilePrimitiveList->takeItem(_selectedFilePrimitiveList->row(item))); 0105 } 0106 0107 _changeFilePrimitiveList->clearSelection(); 0108 updateButtons(); 0109 } 0110 0111 0112 void ChangeFileDialog::selectedDoubleClicked(QListWidgetItem * item) { 0113 if (item) { 0114 _changeFilePrimitiveList->addItem(_selectedFilePrimitiveList->takeItem(_selectedFilePrimitiveList->row(item))); 0115 _changeFilePrimitiveList->clearSelection(); 0116 updateButtons(); 0117 } 0118 } 0119 0120 0121 void ChangeFileDialog::addButtonClicked() { 0122 foreach (QListWidgetItem* item, _changeFilePrimitiveList->selectedItems()) { 0123 _selectedFilePrimitiveList->addItem(_changeFilePrimitiveList->takeItem(_changeFilePrimitiveList->row(item))); 0124 } 0125 _selectedFilePrimitiveList->clearSelection(); 0126 updateButtons(); 0127 } 0128 0129 0130 void ChangeFileDialog::availableDoubleClicked(QListWidgetItem * item) { 0131 if (item) { 0132 _selectedFilePrimitiveList->addItem(_changeFilePrimitiveList->takeItem(_changeFilePrimitiveList->row(item))); 0133 _selectedFilePrimitiveList->clearSelection(); 0134 updateButtons(); 0135 } 0136 } 0137 0138 0139 void ChangeFileDialog::sourceValid(QString filename, int requestID) { 0140 if (_requestID != requestID) { 0141 return; 0142 } 0143 _dataSource = DataSourcePluginManager::findOrLoadSource(_store, filename); 0144 _fileType->setText(_dataSource->fileType()); 0145 updateButtons(); 0146 _configure->setEnabled(_dataSource->hasConfigWidget()); 0147 0148 _store->cleanUpDataSourceList(); 0149 0150 } 0151 0152 0153 void ChangeFileDialog::fileNameChanged(const QString &file) { 0154 _dataSource = 0; 0155 updateButtons(); 0156 _configure->setEnabled(false); 0157 0158 _requestID += 1; 0159 ValidateDataSourceThread *validateDSThread = new ValidateDataSourceThread(file, _requestID); 0160 connect(validateDSThread, SIGNAL(dataSourceValid(QString,int)), this, SLOT(sourceValid(QString,int))); 0161 QThreadPool::globalInstance()->start(validateDSThread); 0162 } 0163 0164 0165 void ChangeFileDialog::updatePrimitiveList() { 0166 PrimitiveList allPrimitives = _store->getObjects<Primitive>(); 0167 0168 QStringList fileNameList; 0169 0170 ObjectList<Primitive> primitives; 0171 0172 foreach (const PrimitivePtr& P, allPrimitives) { 0173 DataPrimitive* dp = qobject_cast<DataPrimitive*>(P); 0174 if (dp) { 0175 primitives.append(P); 0176 fileNameList.append(dp->filename()); 0177 } /*else { 0178 VScalar *vs = qobject_cast<VScalar*>(P); 0179 if (vs) { 0180 primitives.append(P); 0181 fileNameList.append(vs->filename()); 0182 } 0183 } */ 0184 } 0185 0186 // make sure all items in _changeFilePrimitiveList exist in the store; remove if they don't. 0187 for (int i_item = 0; i_item < _changeFilePrimitiveList->count(); i_item++) { 0188 bool exists=false; 0189 for (int i_primitive = 0; i_primitive<primitives.count(); i_primitive++) { 0190 if (primitives.at(i_primitive)->Name() == _changeFilePrimitiveList->item(i_item)->text()) { 0191 exists = true; 0192 break; 0193 } 0194 } 0195 if (!exists) { 0196 QListWidgetItem *item = _changeFilePrimitiveList->takeItem(i_item); 0197 delete item; 0198 i_item--; 0199 } 0200 } 0201 0202 // make sure all items in _selectedFilePrimitiveList exist in the store; remove if they don't. 0203 for (int i_item = 0; i_item < _selectedFilePrimitiveList->count(); i_item++) { 0204 bool exists=false; 0205 for (int i_primitive = 0; i_primitive<primitives.count(); i_primitive++) { 0206 if (primitives.at(i_primitive)->Name() == _selectedFilePrimitiveList->item(i_item)->text()) { 0207 exists = true; 0208 break; 0209 } 0210 } 0211 if (!exists) { 0212 QListWidgetItem *item = _selectedFilePrimitiveList->takeItem(i_item); 0213 delete item; 0214 i_item--; 0215 } 0216 } 0217 0218 // insert into _changeFilePrimitiveList all items in store not in one of the lists. 0219 for (int i_primitive = 0; i_primitive<primitives.count(); i_primitive++) { 0220 bool listed = false; 0221 for (int i_item = 0; i_item<_changeFilePrimitiveList->count(); i_item++) { 0222 if (primitives.at(i_primitive)->Name() == _changeFilePrimitiveList->item(i_item)->text()) { 0223 _changeFilePrimitiveList->item(i_item)->setToolTip(primitives.at(i_primitive)->descriptionTip()); 0224 listed = true; 0225 break; 0226 } 0227 } 0228 for (int i_item = 0; i_item<_selectedFilePrimitiveList->count(); i_item++) { 0229 if (primitives.at(i_primitive)->Name() == _selectedFilePrimitiveList->item(i_item)->text()) { 0230 _selectedFilePrimitiveList->item(i_item)->setToolTip(primitives.at(i_primitive)->descriptionTip()); 0231 listed = true; 0232 break; 0233 } 0234 } 0235 if (!listed) { 0236 QListWidgetItem *wi = new QListWidgetItem(primitives.at(i_primitive)->Name()); 0237 _changeFilePrimitiveList->addItem(wi); 0238 wi->setToolTip(primitives.at(i_primitive)->descriptionTip()); 0239 } 0240 } 0241 0242 // fill _files 0243 QString currentFile = _files->currentText(); 0244 _files->clear(); 0245 0246 for (QStringList::Iterator it = fileNameList.begin(); it != fileNameList.end(); ++it) { 0247 if (_files->findText(*it) == -1) { 0248 _files->addItem(*it); 0249 } 0250 } 0251 0252 _allFromFile->setEnabled(_files->count() > 0); 0253 _files->setEnabled(_files->count() > 0); 0254 updateButtons(); 0255 } 0256 0257 0258 void ChangeFileDialog::updateButtons() { 0259 bool valid = _selectedFilePrimitiveList->count() > 0 && _dataSource; 0260 _buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); 0261 _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(valid); 0262 _add->setEnabled(_changeFilePrimitiveList->selectedItems().count() > 0); 0263 _addAll->setEnabled(_changeFilePrimitiveList->count() > 0); 0264 _remove->setEnabled(_selectedFilePrimitiveList->selectedItems().count() > 0); 0265 _removeAll->setEnabled(_selectedFilePrimitiveList->count() > 0); 0266 } 0267 0268 0269 void ChangeFileDialog::addAll() { 0270 _changeFilePrimitiveList->selectAll(); 0271 addButtonClicked(); 0272 } 0273 0274 0275 void ChangeFileDialog::removeAll() { 0276 _selectedFilePrimitiveList->selectAll(); 0277 removeButtonClicked(); 0278 } 0279 0280 0281 void ChangeFileDialog::selectAllFromFile() { 0282 if (_files->count() <= 0) { 0283 return; 0284 } 0285 0286 _changeFilePrimitiveList->clearSelection(); 0287 0288 for (int i = 0; i < _changeFilePrimitiveList->count(); i++) { 0289 if (DataVectorPtr vector = kst_cast<DataVector>(_store->retrieveObject(_changeFilePrimitiveList->item(i)->text()))) { 0290 _changeFilePrimitiveList->item(i)->setSelected(vector->filename() == _files->currentText()); 0291 } else if (DataMatrixPtr matrix = kst_cast<DataMatrix>(_store->retrieveObject(_changeFilePrimitiveList->item(i)->text()))) { 0292 _changeFilePrimitiveList->item(i)->setSelected(matrix->filename() == _files->currentText()); 0293 } 0294 } 0295 addButtonClicked(); 0296 } 0297 0298 0299 void ChangeFileDialog::OKClicked() { 0300 apply(); 0301 accept(); 0302 } 0303 0304 0305 void ChangeFileDialog::apply() { 0306 Q_ASSERT(_store); 0307 if (!_dataSource->isValid() /*|| _dataSource->isEmpty()*/ ) { 0308 QMessageBox::critical(this, tr("Kst"), tr("The file could not be loaded or contains no data."), QMessageBox::Ok); 0309 return; 0310 } 0311 // Hook the new datasource to the status message area to receive progress status 0312 connect(_dataSource, SIGNAL(progress(int,QString)), kstApp->mainWindow(), SLOT(updateProgress(int,QString))); 0313 _dataSource->vector().prepareRead(_selectedFilePrimitiveList->count()); 0314 0315 // we need a list which preservs the order things are added, and a map to associate the duplicated 0316 // primitive with its duplicate. 0317 QMap<PrimitivePtr, PrimitivePtr> duplicatedPrimitiveMap; // associate duplicated with duplicates. 0318 PrimitiveList duplicatedPrimitiveList; // list of duplicated primitives in order they were found 0319 0320 DataSourceList oldSources; 0321 QString invalidSources; 0322 int invalid = 0; 0323 0324 _selectedFilePrimitiveList->selectAll(); 0325 QList<QListWidgetItem*> selectedItems = _selectedFilePrimitiveList->selectedItems(); 0326 int n_selectedItems = selectedItems.size(); 0327 for (int i = 0; i < n_selectedItems; ++i) { 0328 PrimitivePtr prim = kst_cast<Primitive>(_store->retrieveObject(selectedItems[i]->text())); 0329 if (prim) { 0330 DataPrimitive* dp = qobject_cast<DataPrimitive*>(prim); 0331 if (dp) { 0332 prim->readLock(); 0333 _dataSource->readLock(); 0334 bool valid = dp->checkValidity(_dataSource); 0335 _dataSource->unlock(); 0336 prim->unlock(); 0337 if (!valid) { 0338 if (invalid > 0) { 0339 invalidSources += ", "; 0340 } 0341 invalidSources += dp->field(); 0342 ++invalid; 0343 } else if (_duplicateSelected->isChecked()) { 0344 prim->readLock(); 0345 PrimitivePtr newPrim = dp->makeDuplicate(); 0346 DataPrimitive* newdp = qobject_cast<DataPrimitive*>(newPrim); 0347 prim->unlock(); 0348 newPrim->writeLock(); 0349 newdp->changeFile(_dataSource); 0350 newPrim->unlock(); 0351 duplicatedPrimitiveMap[prim] = newPrim; 0352 duplicatedPrimitiveList.append(prim); 0353 PrimitiveList output_prims = prim->outputPrimitives(); 0354 PrimitiveList dup_output_prims = newPrim->outputPrimitives(); 0355 // add output primitives to list of primitives that have been duplicated. 0356 int n = qMin(output_prims.count(), dup_output_prims.count()); 0357 for (int i_output=0; i_output<n; i_output++) { 0358 if (output_prims.at(i_output)->descriptiveName() == dup_output_prims.at(i_output)->descriptiveName()) { 0359 duplicatedPrimitiveList.append(output_prims.at(i_output)); 0360 duplicatedPrimitiveMap[output_prims.at(i_output)] = dup_output_prims.at(i_output); 0361 } 0362 } 0363 } else { 0364 prim->readLock(); 0365 if (!oldSources.contains(dp->dataSource())) { 0366 oldSources << dp->dataSource(); 0367 } 0368 prim->unlock(); 0369 prim->writeLock(); 0370 dp->changeFile(_dataSource); 0371 prim->unlock(); 0372 } 0373 } 0374 } 0375 prim = 0; 0376 } 0377 0378 // Duplicate data objects: 0379 // list of all data objects before we start duplication 0380 DataObjectList dataObjects = _store->getObjects<DataObject>(); 0381 0382 // map of data objects which have been duplicated. 0383 QMap<DataObjectPtr, DataObjectPtr> duplicatedDataObjects; 0384 0385 // Lookup list to record if the data object has already been duplicated 0386 // (We could have just checked if it was in the map, but that is slower) 0387 QVector<bool> dataObjectDuplicated(dataObjects.count()); 0388 int n_dataObjects = dataObjects.count(); 0389 for (int i_OB = 0; i_OB < n_dataObjects; i_OB++) { 0390 dataObjectDuplicated[i_OB] = false; 0391 } 0392 0393 0394 // go through whole list until nothing is duplicated 0395 bool new_duplicate_found; 0396 do { 0397 new_duplicate_found = false; 0398 for (int i_DP =0; i_DP<duplicatedPrimitiveList.count(); i_DP++) { // The list size will grow, so check count() each time. 0399 for (int i_OB = 0; i_OB < n_dataObjects; i_OB++) { 0400 PrimitiveList input_primitives = dataObjects.at(i_OB)->inputPrimitives(); 0401 if (input_primitives.contains(duplicatedPrimitiveList.at(i_DP))) { 0402 DataObjectPtr duplicate_object; 0403 if (!dataObjectDuplicated[i_OB]) { 0404 duplicate_object = dataObjects.at(i_OB)->makeDuplicate(); 0405 0406 // put the outputs of the new data object into the list of duplicated primitives. 0407 PrimitiveList dup_output_prims = duplicate_object->outputPrimitives(); 0408 PrimitiveList output_prims = dataObjects.at(i_OB)->outputPrimitives(); 0409 int n = output_prims.count(); 0410 for (int i_output=0; i_output<n; i_output++) { 0411 duplicatedPrimitiveList.append(output_prims.at(i_output)); 0412 duplicatedPrimitiveMap[output_prims.at(i_output)] = dup_output_prims.at(i_output); 0413 } 0414 0415 duplicatedDataObjects[dataObjects.at(i_OB)] = duplicate_object; 0416 dataObjectDuplicated[i_OB] = true; 0417 new_duplicate_found = true; 0418 } else { 0419 duplicate_object = duplicatedDataObjects[dataObjects.at(i_OB)]; 0420 } 0421 // replace the input primitive with its copy. 0422 PrimitivePtr old_input_primitive = duplicatedPrimitiveList.at(i_DP); 0423 PrimitivePtr new_input_primitive = duplicatedPrimitiveMap[old_input_primitive]; 0424 duplicate_object->replaceInput(old_input_primitive, new_input_primitive); 0425 } 0426 } 0427 } 0428 } while (new_duplicate_found); 0429 0430 0431 // Duplicate Relations 0432 RelationList relations = _store->getObjects<Relation>(); 0433 0434 // map of data objects which have been duplicated. 0435 QMap<RelationPtr, RelationPtr> duplicatedRelations; 0436 0437 // Lookup list to record if the relation has already been duplicated 0438 // (We could have just checked if it was in the map, but that is slower) 0439 int n_relations = relations.count(); 0440 QVector<bool> relationDuplicated(n_relations); 0441 for (int i_R = 0; i_R < n_relations; i_R++) { 0442 relationDuplicated[i_R] = false; 0443 } 0444 0445 for (int i_DP =0; i_DP<duplicatedPrimitiveList.count(); i_DP++) { 0446 for (int i_R = 0; i_R < n_relations; i_R++) { 0447 PrimitiveList input_primitives = relations.at(i_R)->inputPrimitives(); 0448 if (input_primitives.contains(duplicatedPrimitiveList.at(i_DP))) { 0449 RelationPtr duplicate_relation; 0450 if (!relationDuplicated[i_R]) { 0451 duplicate_relation = relations.at(i_R)->makeDuplicate(); 0452 duplicatedRelations[relations.at(i_R)] = duplicate_relation; 0453 relationDuplicated[i_R] = true; 0454 } else { 0455 duplicate_relation = duplicatedRelations[relations.at(i_R)]; 0456 } 0457 // replace the input primitive with its copy. 0458 PrimitivePtr old_input_primitive = duplicatedPrimitiveList.at(i_DP); 0459 PrimitivePtr new_input_primitive = duplicatedPrimitiveMap[old_input_primitive]; 0460 duplicate_relation->replaceInput(old_input_primitive, new_input_primitive); 0461 } 0462 } 0463 } 0464 0465 0466 // Plot the items. 0467 foreach (PlotItemInterface *plot, Data::self()->plotList()) { 0468 PlotItem* plotItem = static_cast<PlotItem*>(plot); 0469 foreach (PlotRenderItem* renderItem, plotItem->renderItems()) { 0470 for (QMap<RelationPtr, RelationPtr>::ConstIterator iter = duplicatedRelations.constBegin(); iter != duplicatedRelations.constEnd(); ++iter) { 0471 if (renderItem->relationList().contains(kst_cast<Relation>(iter.key())) && !renderItem->relationList().contains(kst_cast<Relation>(iter.value()))) { 0472 renderItem->addRelation(kst_cast<Relation>(iter.value())); 0473 } 0474 } 0475 } 0476 } 0477 0478 // clean up unused data sources 0479 for (DataSourceList::Iterator it = oldSources.begin(); it != oldSources.end(); ++it) { 0480 if ((*it)->getUsage() == 1) { 0481 _store->removeObject(*it); 0482 } 0483 } 0484 0485 if (!invalidSources.isEmpty()) { 0486 if (invalid == 1) { 0487 QMessageBox::warning(this, tr("Kst"), tr("The following field is not defined for the requested file:\n%1").arg(invalidSources), QMessageBox::Ok); 0488 } else { 0489 QMessageBox::warning(this, tr("Kst"), tr("The following fields are not defined for the requested file:\n%1").arg(invalidSources), QMessageBox::Ok); 0490 } 0491 } else { 0492 updatePrimitiveList(); 0493 } 0494 0495 UpdateManager::self()->doUpdates(true); 0496 UpdateServer::self()->requestUpdateSignal(); 0497 0498 kstApp->mainWindow()->document()->setChanged(true); 0499 0500 // store the newly used file as default for the next time the dialog is called 0501 dialogDefaults().setValue("changedatafile/newFileName",_dataFile->file()); 0502 0503 } 0504 0505 } 0506 // vim: ts=2 sw=2 et