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 }