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