File indexing completed on 2024-06-02 05:09:12
0001 /*************************************************************************** 0002 * SPDX-License-Identifier: GPL-2.0-or-later 0003 * * 0004 * SPDX-FileCopyrightText: 2004-2022 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 "associatedfilesui.h" 0021 0022 #include <QLayout> 0023 #include <QLabel> 0024 #include <QGroupBox> 0025 #include <QRadioButton> 0026 #include <QButtonGroup> 0027 #include <QDir> 0028 #include <QDialog> 0029 #include <QDialogButtonBox> 0030 #include <QPushButton> 0031 #include <QLineEdit> 0032 #include <QPointer> 0033 0034 #include <KLocalizedString> 0035 0036 class AssociatedFilesUI::Private 0037 { 0038 private: 0039 AssociatedFilesUI *p; 0040 0041 public: 0042 QLabel *labelGreeting; 0043 QLineEdit *lineEditSourceUrl; 0044 QRadioButton *radioNoCopyMove, *radioCopyFile, *radioMoveFile; 0045 QLabel *labelMoveCopyLocation; 0046 QLineEdit *lineMoveCopyLocation; 0047 QGroupBox *groupBoxRename; 0048 QRadioButton *radioKeepFilename, *radioRenameToEntryId, *radioUserDefinedName; 0049 QLineEdit *lineEditUserDefinedName; 0050 QGroupBox *groupBoxPathType; 0051 QRadioButton *radioRelativePath, *radioAbsolutePath; 0052 QLineEdit *linePreview; 0053 0054 QUrl sourceUrl; 0055 QSharedPointer<Entry> entry; 0056 QString entryId; 0057 const File *bibTeXfile; 0058 0059 Private(AssociatedFilesUI *parent) 0060 : p(parent), entry(QSharedPointer<Entry>()), bibTeXfile(nullptr) { 0061 setupGUI(); 0062 } 0063 0064 void setupGUI() { 0065 QBoxLayout *layout = new QVBoxLayout(p); 0066 0067 labelGreeting = new QLabel(p); 0068 layout->addWidget(labelGreeting); 0069 labelGreeting->setWordWrap(true); 0070 0071 lineEditSourceUrl = new QLineEdit(p); 0072 layout->addWidget(lineEditSourceUrl); 0073 lineEditSourceUrl->setReadOnly(true); 0074 0075 layout->addSpacing(8); 0076 0077 QLabel *label = new QLabel(i18n("The following operations can be performed when associating the document with the entry:"), p); 0078 layout->addWidget(label); 0079 label->setWordWrap(true); 0080 0081 QGroupBox *groupBox = new QGroupBox(i18n("File operation"), p); 0082 layout->addWidget(groupBox); 0083 QBoxLayout *groupBoxLayout = new QVBoxLayout(groupBox); 0084 QButtonGroup *buttonGroup = new QButtonGroup(groupBox); 0085 radioNoCopyMove = new QRadioButton(i18n("Do not copy or move document, only insert reference to it"), groupBox); 0086 groupBoxLayout->addWidget(radioNoCopyMove); 0087 buttonGroup->addButton(radioNoCopyMove); 0088 radioCopyFile = new QRadioButton(i18n("Copy document next to bibliography file"), groupBox); 0089 groupBoxLayout->addWidget(radioCopyFile); 0090 buttonGroup->addButton(radioCopyFile); 0091 radioMoveFile = new QRadioButton(i18n("Move document next to bibliography file"), groupBox); 0092 groupBoxLayout->addWidget(radioMoveFile); 0093 buttonGroup->addButton(radioMoveFile); 0094 #if QT_VERSION >= 0x050f00 0095 connect(buttonGroup, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::idClicked), p, &AssociatedFilesUI::updateUIandPreview); 0096 #else // QT_VERSION < 0x050f00 0097 connect(buttonGroup, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), p, &AssociatedFilesUI::updateUIandPreview); 0098 #endif // QT_VERSION >= 0x050f00 0099 radioNoCopyMove->setChecked(true); /// by default 0100 groupBoxLayout->addSpacing(4); 0101 labelMoveCopyLocation = new QLabel(i18n("Path and filename of bibliography file:"), groupBox); 0102 groupBoxLayout->addWidget(labelMoveCopyLocation, 1); 0103 lineMoveCopyLocation = new QLineEdit(groupBox); 0104 lineMoveCopyLocation->setReadOnly(true); 0105 groupBoxLayout->addWidget(lineMoveCopyLocation, 1); 0106 0107 groupBoxRename = new QGroupBox(i18n("Rename Document?"), p); 0108 layout->addWidget(groupBoxRename); 0109 QGridLayout *gridLayout = new QGridLayout(groupBoxRename); 0110 gridLayout->setColumnMinimumWidth(0, 16); 0111 gridLayout->setColumnStretch(0, 0); 0112 gridLayout->setColumnStretch(1, 1); 0113 buttonGroup = new QButtonGroup(groupBoxRename); 0114 radioKeepFilename = new QRadioButton(i18n("Keep document's original filename"), groupBoxRename); 0115 gridLayout->addWidget(radioKeepFilename, 0, 0, 1, 2); 0116 buttonGroup->addButton(radioKeepFilename); 0117 radioRenameToEntryId = new QRadioButton(groupBoxRename); 0118 gridLayout->addWidget(radioRenameToEntryId, 1, 0, 1, 2); 0119 buttonGroup->addButton(radioRenameToEntryId); 0120 radioUserDefinedName = new QRadioButton(i18n("User-defined name:"), groupBoxRename); 0121 gridLayout->addWidget(radioUserDefinedName, 2, 0, 1, 2); 0122 buttonGroup->addButton(radioUserDefinedName); 0123 lineEditUserDefinedName = new QLineEdit(groupBoxRename); 0124 gridLayout->addWidget(lineEditUserDefinedName, 3, 1, 1, 1); 0125 connect(lineEditUserDefinedName, &QLineEdit::textEdited, p, &AssociatedFilesUI::updateUIandPreview); 0126 #if QT_VERSION >= 0x050f00 0127 connect(buttonGroup, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::idClicked), p, &AssociatedFilesUI::updateUIandPreview); 0128 #else // QT_VERSION < 0x050f00 0129 connect(buttonGroup, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), p, &AssociatedFilesUI::updateUIandPreview); 0130 #endif // QT_VERSION >= 0x050f00 0131 radioRenameToEntryId->setChecked(true); /// by default 0132 0133 groupBoxPathType = new QGroupBox(i18n("Path as Inserted into Entry"), p); 0134 buttonGroup = new QButtonGroup(groupBoxPathType); 0135 layout->addWidget(groupBoxPathType); 0136 groupBoxLayout = new QVBoxLayout(groupBoxPathType); 0137 radioRelativePath = new QRadioButton(i18n("Relative Path"), groupBoxPathType); 0138 groupBoxLayout->addWidget(radioRelativePath); 0139 buttonGroup->addButton(radioRelativePath); 0140 radioAbsolutePath = new QRadioButton(i18n("Absolute Path"), groupBoxPathType); 0141 groupBoxLayout->addWidget(radioAbsolutePath); 0142 buttonGroup->addButton(radioAbsolutePath); 0143 #if QT_VERSION >= 0x050f00 0144 connect(buttonGroup, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::idClicked), p, &AssociatedFilesUI::updateUIandPreview); 0145 #else // QT_VERSION < 0x050f00 0146 connect(buttonGroup, static_cast<void(QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), p, &AssociatedFilesUI::updateUIandPreview); 0147 #endif // QT_VERSION >= 0x050f00 0148 radioRelativePath->setChecked(true); /// by default 0149 0150 layout->addSpacing(8); 0151 0152 label = new QLabel(i18n("Preview of reference to be inserted:"), p); 0153 layout->addWidget(label); 0154 0155 linePreview = new QLineEdit(p); 0156 layout->addWidget(linePreview); 0157 linePreview->setReadOnly(true); 0158 0159 layout->addStretch(10); 0160 } 0161 }; 0162 0163 QString AssociatedFilesUI::associateUrl(const QUrl &url, QSharedPointer<Entry> &entry, const File *bibTeXfile, const bool doInsertUrl, QWidget *parent) { 0164 QPointer<QDialog> dlg = new QDialog(parent); 0165 dlg->setWindowTitle(i18nc("@title:window", "Associate Document with Entry")); 0166 QBoxLayout *layout = new QVBoxLayout(dlg); 0167 QPointer<AssociatedFilesUI> ui = new AssociatedFilesUI(entry->id(), bibTeXfile, dlg); 0168 layout->addWidget(ui); 0169 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, dlg); 0170 buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); 0171 layout->addWidget(buttonBox); 0172 dlg->setLayout(layout); 0173 0174 connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, dlg.data(), &QDialog::accept); 0175 connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, dlg.data(), &QDialog::reject); 0176 0177 if (url.isLocalFile()) 0178 ui->setupForLocalFile(url, entry->id()); 0179 else 0180 ui->setupForRemoteUrl(url, entry->id()); 0181 0182 const bool accepted = dlg->exec() == QDialog::Accepted; 0183 bool success = false; 0184 QString referenceString; 0185 if (accepted) { 0186 const QUrl newUrl = AssociatedFiles::copyDocument(url, entry->id(), bibTeXfile, ui->renameOperation(), ui->moveCopyOperation(), dlg, ui->userDefinedFilename()); 0187 success = newUrl.isValid(); 0188 if (success) { 0189 referenceString = doInsertUrl ? AssociatedFiles::insertUrl(newUrl, entry, bibTeXfile, ui->pathType()) : AssociatedFiles::computeAssociateString(newUrl, bibTeXfile, ui->pathType()); 0190 success &= !referenceString.isEmpty(); 0191 } 0192 } 0193 0194 delete dlg; 0195 return success ? referenceString : QString(); 0196 } 0197 0198 AssociatedFilesUI::AssociatedFilesUI(const QString &entryId, const File *bibTeXfile, QWidget *parent) 0199 : QWidget(parent), d(new AssociatedFilesUI::Private(this)) { 0200 d->entryId = entryId; 0201 d->bibTeXfile = bibTeXfile; 0202 } 0203 0204 AssociatedFilesUI::~AssociatedFilesUI() 0205 { 0206 delete d; 0207 } 0208 0209 AssociatedFiles::RenameOperation AssociatedFilesUI::renameOperation() const { 0210 if (d->radioRenameToEntryId->isChecked()) 0211 return AssociatedFiles::RenameOperation::EntryId; 0212 else if (d->radioKeepFilename->isChecked() || d->lineEditUserDefinedName->text().isEmpty()) 0213 return AssociatedFiles::RenameOperation::KeepName; 0214 else 0215 return AssociatedFiles::RenameOperation::UserDefined; 0216 } 0217 0218 AssociatedFiles::MoveCopyOperation AssociatedFilesUI::moveCopyOperation() const { 0219 if (d->radioNoCopyMove->isChecked()) return AssociatedFiles::MoveCopyOperation::None; 0220 else if (d->radioMoveFile->isChecked()) return AssociatedFiles::MoveCopyOperation::Move; 0221 else return AssociatedFiles::MoveCopyOperation::Copy; 0222 } 0223 0224 AssociatedFiles::PathType AssociatedFilesUI::pathType() const { 0225 return d->radioAbsolutePath->isChecked() ? AssociatedFiles::PathType::Absolute : AssociatedFiles::PathType::Relative; 0226 } 0227 0228 QString AssociatedFilesUI::userDefinedFilename() const { 0229 QString text = d->lineEditUserDefinedName->text(); 0230 const int p = qMax(text.lastIndexOf(QLatin1Char('/')), text.lastIndexOf(QDir::separator())); 0231 if (p > 0) text = text.mid(p + 1); 0232 return text; 0233 } 0234 0235 void AssociatedFilesUI::updateUIandPreview() { 0236 QString preview = i18n("No preview available"); 0237 const QString entryId = d->entryId.isEmpty() && !d->entry.isNull() ? d->entry->id() : d->entryId; 0238 0239 const QUrl bibTeXfileUrl = d->bibTeXfile != nullptr && d->bibTeXfile->hasProperty(File::Url) ? d->bibTeXfile->property(File::Url).toUrl() : QUrl(); 0240 0241 if (entryId.isEmpty()) { 0242 /// If current entry has no identifier, then renaming after entry id is not possible 0243 d->radioRenameToEntryId->setEnabled(false); 0244 /// ... and the current filename should be kept 0245 d->radioKeepFilename->setChecked(true); 0246 } else 0247 /// But if the entry has an identifier, prefer to rename after it 0248 d->radioRenameToEntryId->setEnabled(true); 0249 if (!bibTeXfileUrl.isValid()) { 0250 /// If current file has no URL, e.g. because it hasn't been saved, 0251 /// no relative path can be assembled, i.e. only an absolute one is possible 0252 d->radioRelativePath->setEnabled(false); 0253 d->radioAbsolutePath->setChecked(true); 0254 } else if (bibTeXfileUrl.isValid() && !d->sourceUrl.isRelative() && (bibTeXfileUrl.scheme() != d->sourceUrl.scheme() || (bibTeXfileUrl.scheme() == d->sourceUrl.scheme() && bibTeXfileUrl.host() != d->sourceUrl.host()))) { 0255 /// If URL to be associated is not relative, i.e. has a scheme and this scheme is 0256 /// different from the current bibliography file's scheme or the scheme is the same 0257 /// but the host is different, then no relative path is possible 0258 d->radioRelativePath->setEnabled(false); 0259 d->radioAbsolutePath->setChecked(true); 0260 } else { 0261 /// If the file has a valid URL, prefer to use relative paths for association 0262 d->radioRelativePath->setEnabled(true); 0263 } 0264 if (bibTeXfileUrl.isValid()) { 0265 /// Show the URL of the current bibliography file 0266 d->lineMoveCopyLocation->setText(bibTeXfileUrl.path()); 0267 d->lineMoveCopyLocation->setToolTip(d->lineMoveCopyLocation->text()); 0268 d->labelMoveCopyLocation->show(); 0269 d->lineMoveCopyLocation->show(); 0270 } else { 0271 d->labelMoveCopyLocation->hide(); 0272 d->lineMoveCopyLocation->hide(); 0273 } 0274 /// Renaming is only possible if remote file is either copied or moved 0275 d->groupBoxRename->setEnabled(!d->radioNoCopyMove->isChecked()); 0276 if (d->radioNoCopyMove->isChecked()) 0277 /// Not moving/copying remote file implies that filename is kept 0278 d->radioKeepFilename->setChecked(true); 0279 0280 if (d->radioMoveFile->isChecked() || d->radioCopyFile->isChecked()) { 0281 /// Assuming that the remote URL is to be copied next to the bibliography file, 0282 /// compute the destination a.k.a. target URL of the copied file 0283 const QPair<QUrl, QUrl> newURLs = AssociatedFiles::computeSourceDestinationUrls(d->sourceUrl, entryId, d->bibTeXfile, renameOperation(), d->lineEditUserDefinedName->text()); 0284 if (newURLs.second.isValid()) 0285 preview = AssociatedFiles::computeAssociateString(newURLs.second, d->bibTeXfile, pathType()); 0286 } else if (d->radioNoCopyMove->isChecked()) { 0287 if (d->sourceUrl.isValid()) 0288 preview = AssociatedFiles::computeAssociateString(d->sourceUrl, d->bibTeXfile, pathType()); 0289 } 0290 d->linePreview->setText(preview); 0291 } 0292 0293 void AssociatedFilesUI::setupForRemoteUrl(const QUrl &url, const QString &entryId) { 0294 d->sourceUrl = url; 0295 d->lineEditSourceUrl->setText(url.toDisplayString()); 0296 if (entryId.isEmpty()) { 0297 d->labelGreeting->setText(i18n("The following remote document is about to be associated with the current entry:")); 0298 d->radioRenameToEntryId->setText(i18n("Rename after entry's id")); 0299 } else { 0300 d->labelGreeting->setText(i18n("The following remote document is about to be associated with entry '%1':", entryId)); 0301 d->radioRenameToEntryId->setText(i18n("Rename after entry's id: '%1'", entryId)); 0302 } 0303 updateUIandPreview(); 0304 } 0305 0306 void AssociatedFilesUI::setupForLocalFile(const QUrl &url, const QString &entryId) { 0307 d->sourceUrl = url; 0308 d->lineEditSourceUrl->setText(url.path()); 0309 if (entryId.isEmpty()) { 0310 d->labelGreeting->setText(i18n("The following local document is about to be associated with the current entry:")); 0311 d->radioRenameToEntryId->setText(i18n("Rename after entry's id")); 0312 } else { 0313 d->labelGreeting->setText(i18n("The following local document is about to be associated with entry '%1':", entryId)); 0314 d->radioRenameToEntryId->setText(i18n("Rename after entry's id: '%1'", entryId)); 0315 } 0316 updateUIandPreview(); 0317 }