File indexing completed on 2025-09-14 03:43:28
0001 /* 0002 File : FITSHeaderEditWidget.cpp 0003 Project : LabPlot 0004 Description : Widget for listing/editing FITS header keywords 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2016-2017 Fabian Kristof <fkristofszabolcs@gmail.com> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "FITSHeaderEditWidget.h" 0011 #include "FITSHeaderEditAddUnitDialog.h" 0012 #include "FITSHeaderEditNewKeywordDialog.h" 0013 #include "backend/core/Settings.h" 0014 #include "backend/datasources/filters/FITSFilter.h" 0015 #include "backend/lib/macros.h" 0016 #include "ui_fitsheadereditwidget.h" 0017 0018 #include <KConfigGroup> 0019 #include <KMessageBox> 0020 0021 #include <kcoreaddons_version.h> 0022 0023 #include <QContextMenuEvent> 0024 #include <QFileDialog> 0025 #include <QMenu> 0026 #include <QPushButton> 0027 #include <QTableWidget> 0028 0029 /*! \class FITSHeaderEditWidget 0030 * \brief Widget for listing/editing FITS header keywords 0031 * \since 2.4.0 0032 * \ingroup kdefrontend/widgets 0033 */ 0034 FITSHeaderEditWidget::FITSHeaderEditWidget(QWidget* parent) 0035 : QWidget(parent) 0036 , ui(new Ui::FITSHeaderEditWidget()) 0037 , m_fitsFilter(new FITSFilter()) { 0038 ui->setupUi(this); 0039 initActions(); 0040 connectActions(); 0041 initContextMenus(); 0042 0043 ui->bOpen->setIcon(QIcon::fromTheme(QStringLiteral("document-open"))); 0044 0045 ui->bAddKey->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0046 ui->bAddKey->setEnabled(false); 0047 ui->bAddKey->setToolTip(i18n("Add new keyword")); 0048 0049 ui->bRemoveKey->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); 0050 ui->bRemoveKey->setEnabled(false); 0051 ui->bRemoveKey->setToolTip(i18n("Remove selected keyword")); 0052 0053 ui->bAddUnit->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); 0054 ui->bAddUnit->setEnabled(false); 0055 ui->bAddUnit->setToolTip(i18n("Add unit to keyword")); 0056 0057 ui->bClose->setIcon(QIcon::fromTheme(QStringLiteral("document-close"))); 0058 ui->bClose->setEnabled(false); 0059 ui->bClose->setToolTip(i18n("Close file")); 0060 0061 ui->twKeywordsTable->setColumnCount(3); 0062 ui->twExtensions->setSelectionMode(QAbstractItemView::SingleSelection); 0063 ui->twExtensions->headerItem()->setText(0, i18n("Content")); 0064 ui->twKeywordsTable->setHorizontalHeaderItem(0, new QTableWidgetItem(i18n("Key"))); 0065 ui->twKeywordsTable->setHorizontalHeaderItem(1, new QTableWidgetItem(i18n("Value"))); 0066 ui->twKeywordsTable->setHorizontalHeaderItem(2, new QTableWidgetItem(i18n("Comment"))); 0067 ui->twKeywordsTable->setAlternatingRowColors(true); 0068 ui->twKeywordsTable->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); 0069 ui->twKeywordsTable->horizontalHeader()->setStretchLastSection(true); 0070 ui->twKeywordsTable->installEventFilter(this); 0071 ui->twExtensions->installEventFilter(this); 0072 0073 setAttribute(Qt::WA_DeleteOnClose); 0074 0075 connect(ui->bAddUnit, &QPushButton::clicked, m_actionAddmodifyUnit, &QAction::triggered); 0076 connect(ui->bClose, &QPushButton::clicked, this, &FITSHeaderEditWidget::closeFile); 0077 connect(ui->bOpen, &QPushButton::clicked, this, &FITSHeaderEditWidget::openFile); 0078 connect(ui->bAddKey, &QPushButton::clicked, this, &FITSHeaderEditWidget::addKeyword); 0079 connect(ui->bRemoveKey, &QPushButton::clicked, this, &FITSHeaderEditWidget::removeKeyword); 0080 connect(ui->twKeywordsTable, &QTableWidget::itemClicked, this, &FITSHeaderEditWidget::enableButtonAddUnit); 0081 connect(ui->twKeywordsTable, &QTableWidget::itemChanged, this, &FITSHeaderEditWidget::updateKeyword); 0082 connect(ui->twExtensions, &QTreeWidget::itemClicked, this, &FITSHeaderEditWidget::fillTableSlot); 0083 connect(ui->twExtensions, &QTreeWidget::itemClicked, this, &FITSHeaderEditWidget::enableButtonCloseFile); 0084 } 0085 0086 /*! 0087 * \brief Destructor 0088 */ 0089 FITSHeaderEditWidget::~FITSHeaderEditWidget() { 0090 delete m_fitsFilter; 0091 delete ui; 0092 } 0093 0094 /*! 0095 * \brief Fills the keywords tablewidget. 0096 * If the selected extension was not yet selected before, then the keywords are read from the file 0097 * and then the table is filled, otherwise the table is filled using the already existing keywords. 0098 */ 0099 void FITSHeaderEditWidget::fillTable() { 0100 m_initializingTable = true; 0101 if (!m_extensionData.contains(m_seletedExtension)) { 0102 m_extensionData[m_seletedExtension].keywords = m_fitsFilter->chduKeywords(m_seletedExtension); 0103 m_extensionData[m_seletedExtension].updates.updatedKeywords.reserve(m_extensionData[m_seletedExtension].keywords.size()); 0104 m_extensionData[m_seletedExtension].updates.updatedKeywords.resize(m_extensionData[m_seletedExtension].keywords.size()); 0105 0106 m_fitsFilter->parseHeader(m_seletedExtension, ui->twKeywordsTable); 0107 } else { 0108 QList<FITSFilter::Keyword> keywords = m_extensionData[m_seletedExtension].keywords; 0109 for (int i = 0; i < m_extensionData[m_seletedExtension].updates.updatedKeywords.size(); ++i) { 0110 FITSFilter::Keyword keyword = m_extensionData[m_seletedExtension].updates.updatedKeywords.at(i); 0111 if (!keyword.key.isEmpty()) 0112 keywords.operator[](i).key = keyword.key; 0113 if (!keyword.value.isEmpty()) 0114 keywords.operator[](i).value = keyword.value; 0115 if (!keyword.comment.isEmpty()) 0116 keywords.operator[](i).comment = keyword.comment; 0117 } 0118 for (const FITSFilter::Keyword& key : m_extensionData[m_seletedExtension].updates.newKeywords) 0119 keywords.append(key); 0120 m_fitsFilter->parseHeader(QString(), ui->twKeywordsTable, false, keywords); 0121 } 0122 m_initializingTable = false; 0123 } 0124 0125 /*! 0126 * \brief Fills the tablewidget with the keywords of extension \a item 0127 * \param item the extension selected 0128 * \param col the column of the selected item 0129 */ 0130 void FITSHeaderEditWidget::fillTableSlot(QTreeWidgetItem* item, int col) { 0131 WAIT_CURSOR; 0132 const QString& itemText = item->text(col); 0133 QString selectedExtension; 0134 int extType = 0; 0135 if (itemText.contains(QLatin1String("IMAGE #")) || itemText.contains(QLatin1String("ASCII_TBL #")) || itemText.contains(QLatin1String("BINARY_TBL #"))) 0136 extType = 1; 0137 else if (!itemText.compare(QLatin1String("Primary header"))) 0138 extType = 2; 0139 if (extType == 0) { 0140 if (item->parent() != nullptr) { 0141 if (item->parent()->parent() != nullptr) 0142 selectedExtension = item->parent()->parent()->text(0) + QStringLiteral("[") + item->text(col) + QStringLiteral("]"); 0143 } 0144 } else if (extType == 1) { 0145 if (item->parent() != nullptr) { 0146 if (item->parent()->parent() != nullptr) { 0147 bool ok; 0148 int hduNum = itemText.right(1).toInt(&ok); 0149 selectedExtension = item->parent()->parent()->text(0) + QStringLiteral("[") + QString::number(hduNum - 1) + QStringLiteral("]"); 0150 } 0151 } 0152 } else { 0153 if (item->parent()->parent() != nullptr) 0154 selectedExtension = item->parent()->parent()->text(col); 0155 } 0156 0157 if (!selectedExtension.isEmpty()) { 0158 if (!(m_seletedExtension == selectedExtension)) { 0159 m_seletedExtension = selectedExtension; 0160 fillTable(); 0161 } 0162 } 0163 RESET_CURSOR; 0164 } 0165 0166 /*! 0167 * \brief Shows a dialog for opening a FITS file 0168 * If the returned file name is not empty (so a FITS file was selected) and it's not opened yet 0169 * then the file is parsed, so the treeview for the extensions is built and the table is filled. 0170 */ 0171 void FITSHeaderEditWidget::openFile() { 0172 KConfigGroup conf = Settings::group(QStringLiteral("FITSHeaderEditWidget")); 0173 QString dir = conf.readEntry("LastDir", ""); 0174 QString fileName = QFileDialog::getOpenFileName(this, i18nc("@title:window", "Open FITS File"), dir, i18n("FITS files (*.fits *.fit *.fts)")); 0175 if (fileName.isEmpty()) 0176 return; 0177 0178 int pos = fileName.lastIndexOf(QLatin1String("/")); 0179 if (pos != -1) { 0180 QString newDir = fileName.left(pos); 0181 if (newDir != dir) 0182 conf.writeEntry("LastDir", newDir); 0183 } 0184 0185 WAIT_CURSOR; 0186 QTreeWidgetItem* root = ui->twExtensions->invisibleRootItem(); 0187 const int childCount = root->childCount(); 0188 bool opened = false; 0189 for (int i = 0; i < childCount; ++i) { 0190 if (root->child(i)->text(0) == fileName) { 0191 opened = true; 0192 break; 0193 } 0194 } 0195 if (!opened) { 0196 for (QTreeWidgetItem* item : ui->twExtensions->selectedItems()) 0197 item->setSelected(false); 0198 m_fitsFilter->parseExtensions(fileName, ui->twExtensions); 0199 ui->twExtensions->resizeColumnToContents(0); 0200 if (ui->twExtensions->selectedItems().size() > 0) 0201 fillTableSlot(ui->twExtensions->selectedItems().at(0), 0); 0202 0203 ui->bAddKey->setEnabled(true); 0204 ui->bRemoveKey->setEnabled(true); 0205 ui->bAddUnit->setEnabled(true); 0206 ui->bClose->setEnabled(false); 0207 0208 } else { 0209 KMessageBox::information(this, i18n("Cannot open file, file already opened."), i18n("File already opened")); 0210 } 0211 enableButtonAddUnit(); 0212 RESET_CURSOR; 0213 } 0214 0215 /*! 0216 * \brief Triggered when clicking the Save button 0217 * Saves the modifications (new keywords, new keyword units, keyword modifications, 0218 * deleted keywords, deleted extensions) to the FITS files. 0219 * \return \c true if there was something saved, otherwise false 0220 */ 0221 bool FITSHeaderEditWidget::save() { 0222 bool saved = false; 0223 0224 QMap<QString, ExtensionData>::const_iterator it = m_extensionData.constBegin(); 0225 while (it != m_extensionData.constEnd()) { 0226 const QString& fileName = it.key(); 0227 const auto& data = it.value(); 0228 if (data.updates.newKeywords.size() > 0) { 0229 m_fitsFilter->addNewKeyword(fileName, data.updates.newKeywords); 0230 if (!saved) 0231 saved = true; 0232 } 0233 if (data.updates.removedKeywords.size() > 0) { 0234 m_fitsFilter->deleteKeyword(fileName, data.updates.removedKeywords); 0235 if (!saved) 0236 saved = true; 0237 } 0238 if (!saved) { 0239 for (const FITSFilter::Keyword& key : data.updates.updatedKeywords) { 0240 if (!key.isEmpty()) { 0241 saved = true; 0242 break; 0243 } 0244 } 0245 } 0246 0247 m_fitsFilter->updateKeywords(fileName, data.keywords, data.updates.updatedKeywords); 0248 m_fitsFilter->addKeywordUnit(fileName, data.keywords); 0249 m_fitsFilter->addKeywordUnit(fileName, data.updates.newKeywords); 0250 0251 ++it; 0252 } 0253 0254 if (m_removedExtensions.size() > 0) { 0255 m_fitsFilter->removeExtensions(m_removedExtensions); 0256 if (!saved) 0257 saved = true; 0258 } 0259 if (saved) { 0260 // to reset the window title 0261 Q_EMIT changed(false); 0262 } 0263 0264 return saved; 0265 } 0266 0267 /*! 0268 * \brief Initializes the context menu's actions. 0269 */ 0270 void FITSHeaderEditWidget::initActions() { 0271 m_actionAddKeyword = new QAction(QIcon::fromTheme(QStringLiteral("list-add")), i18n("Add New Keyword"), this); 0272 m_actionRemoveKeyword = new QAction(QIcon::fromTheme(QStringLiteral("list-remove")), i18n("Remove Keyword"), this); 0273 m_actionRemoveExtension = new QAction(i18n("Delete"), this); 0274 m_actionAddmodifyUnit = new QAction(i18n("Add Unit"), this); 0275 } 0276 0277 /*! 0278 * \brief Connects signals of the actions to the appropriate slots. 0279 */ 0280 void FITSHeaderEditWidget::connectActions() { 0281 connect(m_actionAddKeyword, &QAction::triggered, this, [=]() { 0282 addKeyword(); 0283 }); 0284 connect(m_actionRemoveKeyword, &QAction::triggered, this, [=]() { 0285 removeKeyword(); 0286 }); 0287 connect(m_actionRemoveExtension, &QAction::triggered, this, [=]() { 0288 removeExtension(); 0289 }); 0290 connect(m_actionAddmodifyUnit, &QAction::triggered, this, [=]() { 0291 addModifyKeywordUnit(); 0292 }); 0293 } 0294 0295 /*! 0296 * \brief Initializes the context menus. 0297 */ 0298 void FITSHeaderEditWidget::initContextMenus() { 0299 m_keywordActionsMenu = new QMenu(this); 0300 m_keywordActionsMenu->addAction(m_actionAddKeyword); 0301 m_keywordActionsMenu->addAction(m_actionRemoveKeyword); 0302 m_keywordActionsMenu->addSeparator(); 0303 m_keywordActionsMenu->addAction(m_actionAddmodifyUnit); 0304 0305 m_extensionActionsMenu = new QMenu(this); 0306 m_extensionActionsMenu->addAction(m_actionRemoveExtension); 0307 } 0308 0309 /*! 0310 * \brief Shows a FITSHeaderEditNewKeywordDialog and decides whether the new keyword provided in the dialog 0311 * can be added to the new keywords or not. Updates the tablewidget if it's needed. 0312 */ 0313 void FITSHeaderEditWidget::addKeyword() { 0314 auto* newKeywordDialog = new FITSHeaderEditNewKeywordDialog; 0315 m_initializingTable = true; 0316 if (newKeywordDialog->exec() == QDialog::Accepted) { 0317 FITSFilter::Keyword newKeyWord = newKeywordDialog->newKeyword(); 0318 QList<FITSFilter::Keyword> currentKeywords = m_extensionData[m_seletedExtension].keywords; 0319 0320 for (const FITSFilter::Keyword& keyword : currentKeywords) { 0321 if (keyword.operator==(newKeyWord)) { 0322 KMessageBox::information(this, i18n("Cannot add keyword, keyword already added"), i18n("Cannot Add Keyword")); 0323 return; 0324 } 0325 } 0326 0327 for (const FITSFilter::Keyword& keyword : m_extensionData[m_seletedExtension].updates.newKeywords) { 0328 if (keyword.operator==(newKeyWord)) { 0329 KMessageBox::information(this, i18n("Cannot add keyword, keyword already added"), i18n("Cannot Add Keyword")); 0330 return; 0331 } 0332 } 0333 0334 for (const QString& keyword : mandatoryKeywords()) { 0335 if (!keyword.compare(newKeyWord.key)) { 0336 KMessageBox::information(this, i18n("Cannot add mandatory keyword, they are already present"), i18n("Cannot Add Keyword")); 0337 return; 0338 } 0339 } 0340 0341 /* 0342 - Column related keyword (TFIELDS, TTYPEn,TFORMn, etc.) in an image 0343 - SIMPLE, EXTEND, or BLOCKED keyword in any extension 0344 - BSCALE, BZERO, BUNIT, BLANK, DATAMAX, DATAMIN keywords in a table 0345 - Keyword name contains illegal character 0346 */ 0347 0348 m_extensionData[m_seletedExtension].updates.newKeywords.append(newKeyWord); 0349 0350 const int lastRow = ui->twKeywordsTable->rowCount(); 0351 ui->twKeywordsTable->setRowCount(lastRow + 1); 0352 auto* newKeyWordItem = new QTableWidgetItem(newKeyWord.key); 0353 newKeyWordItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); 0354 ui->twKeywordsTable->setItem(lastRow, 0, newKeyWordItem); 0355 0356 newKeyWordItem = new QTableWidgetItem(newKeyWord.value); 0357 newKeyWordItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); 0358 ui->twKeywordsTable->setItem(lastRow, 1, newKeyWordItem); 0359 0360 newKeyWordItem = new QTableWidgetItem(newKeyWord.comment); 0361 newKeyWordItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); 0362 ui->twKeywordsTable->setItem(lastRow, 2, newKeyWordItem); 0363 Q_EMIT changed(true); 0364 } 0365 m_initializingTable = false; 0366 delete newKeywordDialog; 0367 } 0368 0369 /*! 0370 * \brief Shows a messagebox whether we want to remove the keyword or not. 0371 * Mandatory keywords cannot be deleted. 0372 */ 0373 void FITSHeaderEditWidget::removeKeyword() { 0374 const int row = ui->twKeywordsTable->currentRow(); 0375 if (row == -1) 0376 return; 0377 0378 QString key = ui->twKeywordsTable->item(row, 0)->text(); 0379 #if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) 0380 auto status = KMessageBox::questionTwoActions(this, 0381 i18n("Are you sure you want to delete the keyword '%1'?", key), 0382 i18n("Confirm Deletion"), 0383 KStandardGuiItem::del(), 0384 KStandardGuiItem::cancel()); 0385 if (status == KMessageBox::PrimaryAction) { 0386 #else 0387 auto status = KMessageBox::questionYesNo(this, i18n("Are you sure you want to delete the keyword '%1'?", key), i18n("Confirm Deletion")); 0388 if (status == KMessageBox::Yes) { 0389 #endif 0390 bool remove = true; 0391 for (const QString& k : mandatoryKeywords()) { 0392 if (!k.compare(key)) { 0393 remove = false; 0394 break; 0395 } 0396 } 0397 0398 if (remove) { 0399 FITSFilter::Keyword toRemove = FITSFilter::Keyword(key, ui->twKeywordsTable->item(row, 1)->text(), ui->twKeywordsTable->item(row, 2)->text()); 0400 ui->twKeywordsTable->removeRow(row); 0401 0402 m_extensionData[m_seletedExtension].keywords.removeAt(row); 0403 m_extensionData[m_seletedExtension].updates.removedKeywords.append(toRemove); 0404 Q_EMIT changed(true); 0405 } else 0406 KMessageBox::information(this, i18n("Cannot remove mandatory keyword."), i18n("Removing Keyword")); 0407 } 0408 0409 enableButtonAddUnit(); 0410 } 0411 0412 /*! 0413 * \brief Trigggered when an item was updated by the user in the tablewidget 0414 * \param item the item which was updated 0415 */ 0416 void FITSHeaderEditWidget::updateKeyword(QTableWidgetItem* item) { 0417 if (!m_initializingTable) { 0418 const int row = item->row(); 0419 if (row < 0) 0420 return; 0421 0422 int idx; 0423 bool fromNewKeyword = false; 0424 if (row > m_extensionData[m_seletedExtension].keywords.size() - 1) { 0425 idx = row - m_extensionData[m_seletedExtension].keywords.size(); 0426 fromNewKeyword = true; 0427 } else 0428 idx = row; 0429 0430 if (item->column() == 0) { 0431 if (!fromNewKeyword) { 0432 m_extensionData[m_seletedExtension].updates.updatedKeywords.operator[](idx).key = item->text(); 0433 m_extensionData[m_seletedExtension].keywords.operator[](idx).updates.keyUpdated = true; 0434 } else { 0435 m_extensionData[m_seletedExtension].updates.newKeywords.operator[](idx).key = item->text(); 0436 m_extensionData[m_seletedExtension].updates.newKeywords.operator[](idx).updates.keyUpdated = true; 0437 } 0438 0439 } else if (item->column() == 1) { 0440 if (!fromNewKeyword) { 0441 m_extensionData[m_seletedExtension].updates.updatedKeywords.operator[](idx).value = item->text(); 0442 m_extensionData[m_seletedExtension].keywords.operator[](idx).updates.valueUpdated = true; 0443 } else { 0444 m_extensionData[m_seletedExtension].updates.newKeywords.operator[](idx).value = item->text(); 0445 m_extensionData[m_seletedExtension].updates.newKeywords.operator[](idx).updates.valueUpdated = true; 0446 } 0447 } else { 0448 if (!fromNewKeyword) { 0449 m_extensionData[m_seletedExtension].updates.updatedKeywords.operator[](idx).comment = item->text(); 0450 m_extensionData[m_seletedExtension].keywords.operator[](idx).updates.commentUpdated = true; 0451 } else { 0452 m_extensionData[m_seletedExtension].updates.newKeywords.operator[](idx).comment = item->text(); 0453 m_extensionData[m_seletedExtension].updates.newKeywords.operator[](idx).updates.commentUpdated = true; 0454 } 0455 } 0456 Q_EMIT changed(true); 0457 } 0458 } 0459 0460 /*! 0461 * \brief Shows a FITSHeaderEditAddUnitDialog on the selected keyword (provides the keyword's unit to the 0462 * dialog if it had one) and if the dialog was accepted then the new keyword unit is set and the tablewidget 0463 * is updated (filled with the modifications). 0464 */ 0465 0466 void FITSHeaderEditWidget::addModifyKeywordUnit() { 0467 FITSHeaderEditAddUnitDialog* addUnitDialog; 0468 0469 const int selectedRow = ui->twKeywordsTable->currentRow(); 0470 int idx; 0471 bool fromNewKeyword = false; 0472 if (selectedRow > m_extensionData[m_seletedExtension].keywords.size() - 1) { 0473 idx = selectedRow - m_extensionData[m_seletedExtension].keywords.size(); 0474 fromNewKeyword = true; 0475 } else 0476 idx = selectedRow; 0477 0478 QString unit; 0479 if (fromNewKeyword) { 0480 if (!m_extensionData[m_seletedExtension].updates.newKeywords.at(idx).unit.isEmpty()) 0481 unit = m_extensionData[m_seletedExtension].updates.newKeywords.at(idx).unit; 0482 } else { 0483 if (!m_extensionData[m_seletedExtension].keywords.at(idx).unit.isEmpty()) 0484 unit = m_extensionData[m_seletedExtension].keywords.at(idx).unit; 0485 } 0486 0487 addUnitDialog = new FITSHeaderEditAddUnitDialog(unit); 0488 if (addUnitDialog->exec() == QDialog::Accepted) { 0489 if (fromNewKeyword) { 0490 m_extensionData[m_seletedExtension].updates.newKeywords.operator[](idx).unit = addUnitDialog->unit(); 0491 if (!m_extensionData[m_seletedExtension].updates.newKeywords.at(idx).unit.isEmpty()) { 0492 m_extensionData[m_seletedExtension].updates.newKeywords.operator[](idx).updates.unitUpdated = true; 0493 } 0494 } else { 0495 m_extensionData[m_seletedExtension].keywords.operator[](idx).unit = addUnitDialog->unit(); 0496 if (!m_extensionData[m_seletedExtension].keywords.at(idx).unit.isEmpty()) 0497 m_extensionData[m_seletedExtension].keywords.operator[](idx).updates.unitUpdated = true; 0498 } 0499 Q_EMIT changed(true); 0500 fillTable(); 0501 } 0502 0503 delete addUnitDialog; 0504 } 0505 0506 /*! 0507 * \brief Removes the selected extension from the extensions treeview 0508 * If the last extension is removed from the tree, then the extension and the file will be removed too. 0509 */ 0510 void FITSHeaderEditWidget::removeExtension() { 0511 QTreeWidgetItem* current = ui->twExtensions->currentItem(); 0512 QTreeWidgetItem* newCurrent = ui->twExtensions->itemBelow(current); 0513 if (current->parent()) { 0514 if (current->parent()->childCount() < 2) 0515 delete current->parent(); 0516 else 0517 delete current; 0518 } 0519 const QStringList keys = m_extensionData.keys(); 0520 const int selectedidx = keys.indexOf(m_seletedExtension); 0521 0522 if (selectedidx > 0) { 0523 const QString& ext = m_seletedExtension; 0524 m_extensionData.remove(ext); 0525 m_removedExtensions.append(ext); 0526 m_seletedExtension = keys.at(selectedidx - 1); 0527 0528 fillTable(); 0529 } 0530 ui->twExtensions->setCurrentItem(newCurrent); 0531 Q_EMIT changed(true); 0532 } 0533 0534 /*! 0535 * \brief Returns a list of mandatory keywords according to the currently selected extension. 0536 * If the currently selected extension is an image then it returns the mandatory keywords of an image, 0537 * otherwise the mandatory keywords of a table 0538 * \return a list of mandatory keywords 0539 */ 0540 QList<QString> FITSHeaderEditWidget::mandatoryKeywords() const { 0541 QList<QString> mandatoryKeywords; 0542 const QTreeWidgetItem* currentItem = ui->twExtensions->currentItem(); 0543 if (currentItem->parent()->text(0).compare(QLatin1String("Images"))) 0544 mandatoryKeywords = FITSFilter::mandatoryImageExtensionKeywords(); 0545 else 0546 mandatoryKeywords = FITSFilter::mandatoryTableExtensionKeywords(); 0547 return mandatoryKeywords; 0548 } 0549 0550 /*! 0551 * \brief Manipulates the contextmenu event of the widget 0552 * \param watched the object on which the event occurred 0553 * \param event the event watched 0554 * \return 0555 */ 0556 bool FITSHeaderEditWidget::eventFilter(QObject* watched, QEvent* event) { 0557 if (event->type() == QEvent::ContextMenu) { 0558 auto* cm_event = static_cast<QContextMenuEvent*>(event); 0559 const QPoint& global_pos = cm_event->globalPos(); 0560 if (watched == ui->twKeywordsTable) { 0561 if (ui->twExtensions->selectedItems().size() != 0) 0562 m_keywordActionsMenu->exec(global_pos); 0563 } else if (watched == ui->twExtensions) { 0564 if (ui->twExtensions->selectedItems().size() != 0) { 0565 QTreeWidgetItem* current = ui->twExtensions->currentItem(); 0566 int col = ui->twExtensions->currentColumn(); 0567 if (current->parent()) { 0568 if ((current->text(col) != QLatin1String("Images")) && (current->text(col) != QLatin1String("Tables"))) 0569 m_extensionActionsMenu->exec(global_pos); 0570 } 0571 } 0572 } else 0573 return QWidget::eventFilter(watched, event); 0574 return true; 0575 } else 0576 return QWidget::eventFilter(watched, event); 0577 } 0578 0579 void FITSHeaderEditWidget::closeFile() { 0580 if (ui->twExtensions->currentItem()) { 0581 QTreeWidgetItem* current = ui->twExtensions->currentItem(); 0582 0583 int idxOfCurrentAsTopLevel = -1; 0584 for (int i = 0; i < ui->twExtensions->topLevelItemCount(); ++i) { 0585 if (current == ui->twExtensions->topLevelItem(i)) { 0586 idxOfCurrentAsTopLevel = i; 0587 break; 0588 } 0589 } 0590 0591 auto* newCurrent = (QTreeWidgetItem*)nullptr; 0592 if (idxOfCurrentAsTopLevel == 0) { 0593 if (ui->twExtensions->topLevelItemCount() == 1) { 0594 // last file closed, deactivate action buttons, clear keywords table 0595 ui->twKeywordsTable->setRowCount(0); 0596 ui->bClose->setEnabled(false); 0597 ui->bAddUnit->setEnabled(false); 0598 ui->bAddKey->setEnabled(false); 0599 ui->bRemoveKey->setEnabled(false); 0600 } else 0601 newCurrent = ui->twExtensions->topLevelItem(idxOfCurrentAsTopLevel + 1); 0602 } else 0603 newCurrent = ui->twExtensions->topLevelItem(idxOfCurrentAsTopLevel - 1); 0604 0605 if (newCurrent) { 0606 m_seletedExtension = newCurrent->text(0); 0607 fillTable(); 0608 } 0609 QMap<QString, ExtensionData>::const_iterator it = m_extensionData.constBegin(); 0610 while (it != m_extensionData.constEnd()) { 0611 const QString& key = it.key(); 0612 if (key.startsWith(current->text(0))) 0613 m_extensionData.remove(key); 0614 ++it; 0615 } 0616 0617 delete current; 0618 0619 enableButtonAddUnit(); 0620 Q_EMIT changed(false); 0621 } 0622 } 0623 void FITSHeaderEditWidget::enableButtonAddUnit() { 0624 if (ui->twKeywordsTable->currentItem() != nullptr) 0625 ui->bAddUnit->setEnabled(true); 0626 else 0627 ui->bAddUnit->setEnabled(false); 0628 } 0629 0630 void FITSHeaderEditWidget::enableButtonCloseFile(QTreeWidgetItem* item, int /*col*/) { 0631 ui->bClose->setEnabled(item->parent() ? false : true); 0632 }