File indexing completed on 2025-01-12 04:35:49
0001 /*************************************************************************** 0002 * SPDX-License-Identifier: GPL-2.0-or-later 0003 * * 0004 * SPDX-FileCopyrightText: 2004-2019 Thomas Fischer <fischer@unix-ag.uni-kl.de> 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 * This program is distributed in the hope that it will be useful, * 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0014 * GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License * 0017 * along with this program; if not, see <https://www.gnu.org/licenses/>. * 0018 ***************************************************************************/ 0019 0020 #include "searchresults.h" 0021 0022 #include <QGridLayout> 0023 #include <QLabel> 0024 #include <QPushButton> 0025 #include <QAction> 0026 0027 #include <KLocalizedString> 0028 #include <KIconLoader> 0029 0030 #include <File> 0031 #include <file/Clipboard> 0032 #include <file/FileView> 0033 #include <file/FileDelegate> 0034 #include <models/FileModel> 0035 #include <IdSuggestions> 0036 #include "logging_program.h" 0037 0038 class SearchResults::SearchResultsPrivate 0039 { 0040 private: 0041 // UNUSED SearchResults *p; 0042 Clipboard *clipboard; 0043 0044 public: 0045 MDIWidget *m; 0046 File *file; 0047 QWidget *widgetCannotImport; 0048 QLabel *labelCannotImportMsg; 0049 QPushButton *buttonImport; 0050 FileView *resultList, *mainEditor; 0051 QAction *actionViewCurrent, *actionImportSelected, *actionCopySelected; 0052 0053 SearchResultsPrivate(MDIWidget *mdiWidget, SearchResults *parent) 0054 : /* UNUSED p(parent),*/ m(mdiWidget), file(new File()), mainEditor(nullptr) { 0055 QGridLayout *layout = new QGridLayout(parent); 0056 layout->setContentsMargins(0, 0, 0, 0); 0057 layout->setColumnStretch(0, 1); 0058 layout->setColumnStretch(1, 0); 0059 0060 resultList = new FileView(QStringLiteral("SearchResults"), parent); 0061 resultList->setItemDelegate(new FileDelegate(resultList)); 0062 resultList->setReadOnly(true); 0063 resultList->setFrameShadow(QFrame::Sunken); 0064 resultList->setFrameShape(QFrame::StyledPanel); 0065 resultList->setContextMenuPolicy(Qt::ActionsContextMenu); 0066 layout->addWidget(resultList, 0, 0, 1, 2); 0067 0068 clipboard = new Clipboard(resultList); 0069 0070 /// Create a special widget that shows a small icon and a text 0071 /// stating that there are no search results. It will only be 0072 /// shown until the first search results arrive. 0073 // TODO nearly identical code as in ElementFormPrivate constructor, create common class 0074 widgetCannotImport = new QWidget(parent); 0075 layout->addWidget(widgetCannotImport, 1, 0, 1, 1); 0076 QBoxLayout *layoutCannotImport = new QHBoxLayout(widgetCannotImport); 0077 layoutCannotImport->addStretch(10); 0078 QLabel *label = new QLabel(widgetCannotImport); 0079 label->setAlignment(Qt::AlignCenter | Qt::AlignVCenter); 0080 label->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-warning")).pixmap(KIconLoader::SizeSmall)); 0081 layoutCannotImport->addWidget(label); 0082 labelCannotImportMsg = new QLabel(widgetCannotImport); 0083 labelCannotImportMsg->setAlignment(Qt::AlignRight | Qt::AlignVCenter); 0084 layoutCannotImport->addWidget(labelCannotImportMsg); 0085 /// see also updateCannotImportMessage() 0086 0087 buttonImport = new QPushButton(QIcon::fromTheme(QStringLiteral("svn-update")), i18n("Import"), parent); 0088 layout->addWidget(buttonImport, 1, 1, 1, 1); 0089 buttonImport->setEnabled(false); 0090 0091 SortFilterFileModel *model = new SortFilterFileModel(parent); 0092 FileModel *fileModel = new FileModel(parent); 0093 fileModel->setBibliographyFile(file); 0094 model->setSourceModel(fileModel); 0095 resultList->setModel(model); 0096 0097 actionViewCurrent = new QAction(QIcon::fromTheme(QStringLiteral("document-preview")), i18n("View Element"), parent); 0098 resultList->addAction(actionViewCurrent); 0099 actionViewCurrent->setEnabled(false); 0100 connect(actionViewCurrent, &QAction::triggered, resultList, &FileView::viewCurrentElement); 0101 0102 actionImportSelected = new QAction(QIcon::fromTheme(QStringLiteral("svn-update")), i18n("Import"), parent); 0103 resultList->addAction(actionImportSelected); 0104 actionImportSelected->setEnabled(false); 0105 connect(actionImportSelected, &QAction::triggered, parent, &SearchResults::importSelected); 0106 0107 actionCopySelected = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("Copy"), parent); 0108 resultList->addAction(actionCopySelected); 0109 actionCopySelected->setEnabled(false); 0110 connect(actionCopySelected, &QAction::triggered, clipboard, &Clipboard::copy); 0111 0112 connect(resultList, &FileView::doubleClicked, resultList, &FileView::viewCurrentElement); 0113 connect(resultList, &FileView::selectedElementsChanged, parent, &SearchResults::updateGUI); 0114 connect(buttonImport, &QPushButton::clicked, parent, &SearchResults::importSelected); 0115 0116 updateCannotImportMessage(); 0117 } 0118 0119 ~SearchResultsPrivate() { 0120 delete file; 0121 } 0122 0123 void clear() { 0124 FileModel *model = resultList->fileModel(); 0125 if (model != nullptr) 0126 model->clear(); 0127 } 0128 0129 bool insertElement(QSharedPointer<Element> element) { 0130 static IdSuggestions idSuggestions; 0131 FileModel *model = resultList->fileModel(); 0132 0133 /// If the user had configured a default formatting string 0134 /// for entry ids, apply this formatting strings here 0135 QSharedPointer<Entry> entry = element.dynamicCast<Entry>(); 0136 if (!entry.isNull()) 0137 idSuggestions.applyDefaultFormatId(*entry.data()); 0138 0139 bool result = model != nullptr ? model->insertRow(element, model->rowCount()) : false; 0140 if (result) 0141 resultList->sortFilterProxyModel()->invalidate(); 0142 0143 return result; 0144 } 0145 0146 void updateCannotImportMessage() { 0147 const bool isEmpty = resultList->model()->rowCount() == 0; 0148 if (mainEditor == nullptr && isEmpty) 0149 labelCannotImportMsg->setText(i18n("No results to import and no bibliography open to import to.")); 0150 else if (mainEditor == nullptr) 0151 labelCannotImportMsg->setText(i18n("No bibliography open to import to.")); 0152 else if (isEmpty) 0153 labelCannotImportMsg->setText(i18n("No results to import.")); 0154 widgetCannotImport->setVisible(mainEditor == nullptr || isEmpty); 0155 } 0156 }; 0157 0158 SearchResults::SearchResults(MDIWidget *mdiWidget, QWidget *parent) 0159 : QWidget(parent), d(new SearchResultsPrivate(mdiWidget, this)) 0160 { 0161 /// nothing 0162 } 0163 0164 SearchResults::~SearchResults() 0165 { 0166 delete d; 0167 } 0168 0169 void SearchResults::clear() 0170 { 0171 d->clear(); 0172 } 0173 0174 bool SearchResults::insertElement(QSharedPointer<Element> element) 0175 { 0176 const bool success = d->insertElement(element); 0177 if (success) 0178 d->updateCannotImportMessage(); 0179 return success; 0180 } 0181 0182 void SearchResults::documentSwitched(FileView *oldEditor, FileView *newEditor) 0183 { 0184 Q_UNUSED(oldEditor) 0185 d->mainEditor = newEditor; 0186 updateGUI(); 0187 } 0188 0189 void SearchResults::updateGUI() 0190 { 0191 d->updateCannotImportMessage(); 0192 d->buttonImport->setEnabled(d->mainEditor != nullptr && !d->resultList->selectedElements().isEmpty()); 0193 d->actionImportSelected->setEnabled(d->buttonImport->isEnabled()); 0194 d->actionCopySelected->setEnabled(!d->resultList->selectedElements().isEmpty()); 0195 d->actionViewCurrent->setEnabled(d->resultList->currentElement() != nullptr); 0196 } 0197 0198 void SearchResults::importSelected() 0199 { 0200 Q_ASSERT_X(d->mainEditor != nullptr, "SearchResults::importSelected", "d->mainEditor is NULL"); 0201 0202 FileModel *targetModel = d->mainEditor->fileModel(); 0203 FileModel *sourceModel = d->resultList->fileModel(); 0204 if (targetModel == nullptr || sourceModel == nullptr) return; ///< either source or target model is invalid 0205 0206 bool atLeastOneSuccessfullInsertion = false; 0207 const QModelIndexList selList = d->resultList->selectionModel()->selectedRows(); 0208 for (const QModelIndex &modelIndex : selList) { 0209 /// Map from visible row to 'real' row 0210 /// that may be hidden through sorting 0211 int row = d->resultList->sortFilterProxyModel()->mapToSource(modelIndex).row(); 0212 /// Should only be an Entry, 0213 /// everthing else is unexpected 0214 QSharedPointer<Entry> entry = sourceModel->element(row).dynamicCast<Entry>(); 0215 if (!entry.isNull()) { 0216 /// Important: make clone of entry before inserting 0217 /// in main list, otherwise data would be shared 0218 QSharedPointer<Entry> clone(new Entry(*entry)); 0219 atLeastOneSuccessfullInsertion |= targetModel->insertRow(clone, targetModel->rowCount()); 0220 } else 0221 qCWarning(LOG_KBIBTEX_PROGRAM) << "Trying to import something that isn't an Entry"; 0222 } 0223 0224 if (atLeastOneSuccessfullInsertion) 0225 d->mainEditor->externalModification(); 0226 }