File indexing completed on 2024-05-12 16:40:57
0001 /* This file is part of the KDE project 0002 Copyright (C) 2003-2014 Jarosław Staniek <staniek@kde.org> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "KexiStartupFileHandler.h" 0021 #include <kexi_global.h> 0022 #include <core/kexi.h> 0023 #include <KexiFileFilters.h> 0024 #include <kexiutils/utils.h> 0025 #include <kexiutils/KexiContextMessage.h> 0026 0027 #include <KDbDriver> 0028 #include <KDbUtils> 0029 0030 #include <KMessageBox> 0031 #include <KFileWidget> 0032 #include <KFile> 0033 #include <KUrlComboBox> 0034 #include <KActionCollection> 0035 //removed in KEXI3 #include <KFileDialog> 0036 #define KIOWIDGETS_NO_DEPRECATED 0037 #include <KUrlRequester> 0038 #include <KLocalizedString> 0039 0040 #include <QDebug> 0041 #include <QEvent> 0042 #include <QAction> 0043 #include <QEventLoop> 0044 #include <QMimeDatabase> 0045 #include <QMimeType> 0046 #include <QStandardPaths> 0047 #include <QUrl> 0048 0049 //! @internal 0050 class Q_DECL_HIDDEN KexiStartupFileHandler::Private 0051 { 0052 public: 0053 Private() 0054 : confirmOverwrites(true) 0055 //, filtersUpdated(false) 0056 { 0057 } 0058 ~Private() { 0059 if (messageWidgetLoop) { 0060 messageWidgetLoop->exit(0); 0061 messageWidgetLoop->processEvents(); // for safe exit 0062 messageWidgetLoop->exit(0); 0063 delete messageWidgetLoop; 0064 } 0065 } 0066 0067 void setUrl(const QUrl &url) 0068 { 0069 if (requester) { 0070 requester->setUrl(url); 0071 } 0072 /*removed in KEXI3 0073 else { 0074 dialog->setUrl(url); 0075 }*/ 0076 } 0077 0078 // removed in KEXI3 QPointer<KFileDialog> dialog; 0079 QPointer<KUrlRequester> requester; 0080 QString lastFileName; 0081 KexiFileFilters::Mode mode; 0082 QSet<QString> additionalMimeTypes, excludedMimeTypes; 0083 QString defaultExtension; 0084 bool confirmOverwrites; 0085 QString recentDirClass; 0086 0087 QPointer<QEventLoop> messageWidgetLoop; 0088 //! Used in KexiStartupFileHandler::askForOverwriting() to remember path that 0089 //! was recently accepted for overwrite by the user. 0090 QString recentFilePathConfirmed; 0091 }; 0092 0093 //------------------ 0094 0095 /* removed in KEXI3 0096 KexiStartupFileHandler::KexiStartupFileHandler( 0097 const QUrl &startDirOrVariable, Mode mode, KFileDialog *dialog) 0098 : QObject(dialog->parent()) 0099 , d(new Private) 0100 { 0101 d->dialog = dialog; 0102 init(startDirOrVariable, mode); 0103 }*/ 0104 0105 KexiStartupFileHandler::KexiStartupFileHandler( 0106 const QUrl &startDirOrVariable, KexiFileFilters::Mode mode, KUrlRequester *requester) 0107 : QObject(requester->parent()) 0108 , d(new Private) 0109 { 0110 d->requester = requester; 0111 //removed in KEXI3 d->dialog = d->requester->fileDialog(); 0112 init(startDirOrVariable, mode); 0113 } 0114 0115 void KexiStartupFileHandler::init(const QUrl &startDirOrVariable, KexiFileFilters::Mode mode) 0116 { 0117 //removed in KEXI3 connect(d->dialog, SIGNAL(accepted()), this, SLOT(slotAccepted())); 0118 QUrl url; 0119 if (startDirOrVariable.scheme() == "kfiledialog") { 0120 url = KFileWidget::getStartUrl(startDirOrVariable, d->recentDirClass); 0121 } 0122 else { 0123 url = startDirOrVariable; 0124 } 0125 if (url.toLocalFile().isEmpty() || !QDir(url.toLocalFile()).exists()) { 0126 url = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); 0127 QDir docDir(url.toLocalFile()); 0128 if (!docDir.exists()) { // create if missing 0129 (void)docDir.mkpath(QString()); 0130 } 0131 } 0132 d->setUrl(url); 0133 setMode(mode); 0134 /*removed in KEXI3 0135 QAction *previewAction = d->dialog->actionCollection()->action("preview"); 0136 if (previewAction) 0137 previewAction->setChecked(false);*/ 0138 } 0139 0140 KexiStartupFileHandler::~KexiStartupFileHandler() 0141 { 0142 saveRecentDir(); 0143 delete d; 0144 } 0145 0146 void KexiStartupFileHandler::saveRecentDir() 0147 { 0148 if (!d->recentDirClass.isEmpty()) { 0149 qDebug() << d->recentDirClass; 0150 0151 QUrl dirUrl; 0152 if (d->requester) 0153 dirUrl = d->requester->url(); 0154 //removed in KEXI3 else if (d->dialog) 0155 //removed in KEXI3 dirUrl = d->dialog->selectedUrl(); 0156 qDebug() << dirUrl; 0157 if (dirUrl.isValid() && dirUrl.isLocalFile()) { 0158 dirUrl = dirUrl.adjusted(QUrl::RemoveFilename); 0159 dirUrl.setPath(dirUrl.path() + QString()); 0160 qDebug() << "Added" << dirUrl.path() << "to recent dirs class" << d->recentDirClass; 0161 KexiUtils::addRecentDir(d->recentDirClass, dirUrl.path()); 0162 } 0163 } 0164 } 0165 0166 KexiFileFilters::Mode KexiStartupFileHandler::mode() const 0167 { 0168 return d->mode; 0169 } 0170 0171 void KexiStartupFileHandler::setMode(KexiFileFilters::Mode mode) 0172 { 0173 //delayed 0174 d->mode = mode; 0175 updateFilters(); 0176 } 0177 0178 QStringList KexiStartupFileHandler::additionalMimeTypes() const 0179 { 0180 return d->additionalMimeTypes.toList(); 0181 } 0182 0183 void KexiStartupFileHandler::setAdditionalMimeTypes(const QStringList &mimeTypes) 0184 { 0185 //delayed 0186 d->additionalMimeTypes = mimeTypes.toSet(); 0187 updateFilters(); 0188 } 0189 0190 QStringList KexiStartupFileHandler::excludedMimeTypes() const 0191 { 0192 return d->excludedMimeTypes.toList(); 0193 } 0194 0195 void KexiStartupFileHandler::setExcludedMimeTypes(const QStringList &mimeTypes) 0196 { 0197 //delayed 0198 d->excludedMimeTypes.clear(); 0199 //convert to lowercase 0200 for(const QString& mimeType : mimeTypes) { 0201 d->excludedMimeTypes.insert(mimeType.toLower()); 0202 } 0203 updateFilters(); 0204 } 0205 0206 void KexiStartupFileHandler::updateFilters() 0207 { 0208 d->lastFileName.clear(); 0209 //removed in KEXI3 d->dialog->clearFilter(); 0210 0211 QString filter; 0212 QMimeDatabase db; 0213 QMimeType mime; 0214 QStringList allfilters; 0215 0216 KexiFileFiltersFormat format; 0217 format.type = KexiFileFiltersFormat::Type::KDE; 0218 format.addAllFiles = true; 0219 const QString separator(KexiFileFilters::separator(format)); 0220 if (d->mode == KexiFileFilters::Opening || d->mode == KexiFileFilters::SavingFileBasedDB) { 0221 mime = db.mimeTypeForName(KDb::defaultFileBasedDriverMimeType()); 0222 if (mime.isValid() && !d->excludedMimeTypes.contains(mime.name().toLower())) { 0223 if (!filter.isEmpty()) { 0224 filter += separator; 0225 } 0226 filter += KexiFileFilters::toString(mime, format); 0227 allfilters += mime.globPatterns(); 0228 } 0229 } 0230 if (d->mode == KexiFileFilters::Opening || d->mode == KexiFileFilters::SavingServerBasedDB) { 0231 mime = db.mimeTypeForName("application/x-kexiproject-shortcut"); 0232 if (mime.isValid() && !d->excludedMimeTypes.contains(mime.name().toLower())) { 0233 if (!filter.isEmpty()) { 0234 filter += separator; 0235 } 0236 filter += KexiFileFilters::toString(mime, format); 0237 allfilters += mime.globPatterns(); 0238 } 0239 } 0240 if (d->mode == KexiFileFilters::Opening || d->mode == KexiFileFilters::SavingServerBasedDB) { 0241 mime = db.mimeTypeForName("application/x-kexi-connectiondata"); 0242 if (mime.isValid() && !d->excludedMimeTypes.contains(mime.name().toLower())) { 0243 if (!filter.isEmpty()) { 0244 filter += separator; 0245 } 0246 filter += KexiFileFilters::toString(mime, format); 0247 allfilters += mime.globPatterns(); 0248 } 0249 } 0250 0251 //! @todo hardcoded for MSA: 0252 if (d->mode == KexiFileFilters::Opening || d->mode == KexiFileFilters::CustomOpening) { 0253 mime = db.mimeTypeForName("application/vnd.ms-access"); 0254 if (mime.isValid() && !d->excludedMimeTypes.contains(mime.name().toLower())) { 0255 if (!filter.isEmpty()) { 0256 filter += separator; 0257 } 0258 filter += KexiFileFilters::toString(mime, format); 0259 allfilters += mime.globPatterns(); 0260 } 0261 } 0262 0263 foreach(const QString& mimeName, d->additionalMimeTypes) { 0264 if (mimeName == "all/allfiles") { 0265 continue; 0266 } 0267 if (d->excludedMimeTypes.contains(mimeName.toLower())) { 0268 continue; 0269 } 0270 if (!filter.isEmpty()) { 0271 filter += separator; 0272 } 0273 filter += KexiFileFilters::toString(mimeName, format); 0274 mime = db.mimeTypeForName(mimeName); 0275 allfilters += mime.globPatterns(); 0276 } 0277 0278 //remove duplicates made because upper- and lower-case extenstions are used: 0279 QStringList allfiltersUnique = allfilters.toSet().toList(); 0280 allfiltersUnique.sort(); 0281 0282 if (allfiltersUnique.count() > 1) {//prepend "all supoported files" entry 0283 if (!filter.isEmpty()) { 0284 filter += separator; 0285 } 0286 filter.prepend( 0287 KexiFileFilters::toString(allfiltersUnique, xi18n("All Supported Files"), format)); 0288 } 0289 0290 d->requester->setFilter(filter); 0291 0292 if (d->mode == KexiFileFilters::Opening || d->mode == KexiFileFilters::CustomOpening) { 0293 d->requester->setMode(KFile::ExistingOnly | KFile::LocalOnly | KFile::File); 0294 //removed in KEXI3 d->dialog->setOperationMode(KFileDialog::Opening); 0295 } else { 0296 d->requester->setMode(KFile::LocalOnly | KFile::File); 0297 //removed in KEXI3 d->dialog->setOperationMode(KFileDialog::Saving); 0298 } 0299 } 0300 0301 //! @todo 0302 /*TODO 0303 QString KexiStartupFileDialog::selectedFile() const 0304 { 0305 #ifdef Q_OS_WIN 0306 // QString path = selectedFile(); 0307 //js @todo 0308 // qDebug() << "selectedFile() == " << path << " '" << url().fileName() << "' " << m_lineEdit->text(); 0309 QString path = dir()->absolutePath(); 0310 if (!path.endsWith('/') && !path.endsWith("\\")) 0311 path.append("/"); 0312 path += m_lineEdit->text(); 0313 // QString path = QFileInfo(selectedFile()).dirPath(true) + "/" + m_lineEdit->text(); 0314 #else 0315 // QString path = locationEdit->currentText().trimmed(); //url.path().trimmed(); that does not work, if the full path is not in the location edit !!!!! 0316 QString path( KFileWidget::selectedFile() ); 0317 qDebug() << "prev selectedFile() == " << path; 0318 qDebug() << "locationEdit == " << locationEdit()->currentText().trimmed(); 0319 //make sure user-entered path is acceped: 0320 //! @todo KEXI3 setSelection( locationEdit()->currentText().trimmed() ); 0321 // path = KFileWidget::selectedFile(); 0322 path = locationEdit()->currentText().trimmed(); 0323 qDebug() << "selectedFile() == " << path; 0324 0325 #endif 0326 0327 if (!currentFilter().isEmpty()) { 0328 if (d->mode & SavingFileBasedDB) { 0329 const QStringList filters( currentFilter().split(' ') ); 0330 qDebug()<< " filter == " << filters; 0331 QString ext( QFileInfo(path).suffix() ); 0332 bool hasExtension = false; 0333 foreach (const QString& filter, filters) { 0334 const QString f( filter.trimmed() ); 0335 hasExtension = !f.mid(2).isEmpty() && ext==f.mid(2); 0336 if (hasExtension) 0337 break; 0338 } 0339 if (!hasExtension) { 0340 //no extension: add one 0341 QString defaultExtension( d->defaultExtension ); 0342 if (defaultExtension.isEmpty()) 0343 defaultExtension = filters.first().trimmed().mid(2); //first one 0344 path += (QString(".")+defaultExtension); 0345 qDebug() << "KexiStartupFileDialog::checkURL(): append extension, " << path; 0346 } 0347 } 0348 } 0349 qDebug() << "KexiStartupFileDialog::currentFileName() == " << path; 0350 return path; 0351 } 0352 */ 0353 0354 bool KexiStartupFileHandler::checkSelectedUrl() 0355 { 0356 //qDebug() << "d->highlightedUrl: " << d->highlightedUrl; 0357 0358 QUrl url; 0359 if (d->requester) 0360 url = d->requester->url(); 0361 //removed in KEXI3 else 0362 //removed in KEXI3 url = d->dialog->selectedUrl(); 0363 qDebug() << url; 0364 #if 0 0365 if (/*d->highlightedUrl.isEmpty() &&*/ !locationEdit()->lineEdit()->text().isEmpty()) { 0366 qDebug() << locationEdit()->lineEdit()->text(); 0367 //qDebug() << locationEdit()->urls(); 0368 qDebug() << baseUrl(); 0369 0370 d->highlightedUrl = baseUrl(); 0371 const QString firstUrl(locationEdit()->lineEdit()->text()); // FIXME: find first... 0372 if (QDir::isAbsolutePath(firstUrl)) 0373 d->highlightedUrl = QUrl::fromLocalFile(firstUrl); 0374 else 0375 d->highlightedUrl.addPath(firstUrl); 0376 } 0377 #endif 0378 //qDebug() << "d->highlightedUrl: " << d->highlightedUrl; 0379 if (!url.isValid() || QFileInfo(url.path()).isDir()) { 0380 KMessageBox::error(d->requester->parentWidget(), xi18n("Enter a filename.")); 0381 return false; 0382 } 0383 0384 if (!d->requester->filter().isEmpty()) { 0385 if (d->mode == KexiFileFilters::SavingFileBasedDB) { 0386 const QStringList filters( d->requester->filter().split('\n') ); 0387 QString path = url.toLocalFile(); 0388 qDebug()<< "filters:" << filters << "path:" << path; 0389 QString ext( QFileInfo(path).suffix() ); 0390 bool hasExtension = false; 0391 for (const QString &filter : filters) { 0392 QStringList filterPatterns = filter.split('|').first().split(' '); 0393 for (const QString &filterPattern : filterPatterns) { 0394 const QString f( filterPattern.trimmed() ); 0395 if (!f.midRef(2).isEmpty() && ext == f.midRef(2)) { 0396 hasExtension = true; 0397 break; 0398 } 0399 } 0400 if (hasExtension) { 0401 break; 0402 } 0403 } 0404 if (!hasExtension) { 0405 //no extension: add one 0406 QString defaultExtension( d->defaultExtension ); 0407 if (defaultExtension.isEmpty()) { 0408 defaultExtension = filters.first().trimmed().mid(2); //first one 0409 } 0410 path += (QLatin1String(".") + defaultExtension); 0411 qDebug() << "appended extension, result:" << path; 0412 url = QUrl(path); 0413 d->setUrl(url); 0414 } 0415 } 0416 } 0417 0418 // qDebug() << "KexiStartupFileDialog::checkURL() path: " << d->highlightedUrl; 0419 // qDebug() << "KexiStartupFileDialog::checkURL() fname: " << url.fileName(); 0420 //! @todo if ( url.isLocalFile() ) { 0421 QFileInfo fi(url.toLocalFile()); 0422 if (d->mode & KFile::ExistingOnly) { 0423 if (!fi.exists()) { 0424 KMessageBox::error(d->requester->parentWidget(), 0425 xi18nc("@info", "The file <filename>%1</filename> does not exist.", 0426 QDir::toNativeSeparators(url.toLocalFile()))); 0427 return false; 0428 } else if (mode() & KFile::File) { 0429 if (!fi.isFile()) { 0430 KMessageBox::error(d->requester->parentWidget(), 0431 xi18nc("@info", "Enter a filename.")); 0432 return false; 0433 } else if (!fi.isReadable()) { 0434 KMessageBox::error(d->requester->parentWidget(), 0435 xi18nc("@info", "The file <filename>%1</filename> is not readable.", 0436 QDir::toNativeSeparators(url.toLocalFile()))); 0437 return false; 0438 } 0439 } 0440 } 0441 else if (d->confirmOverwrites && !askForOverwriting(url.toLocalFile())) 0442 { 0443 return false; 0444 } 0445 return true; 0446 } 0447 0448 void KexiStartupFileHandler::messageWidgetActionYesTriggered() 0449 { 0450 d->messageWidgetLoop->exit(1); 0451 } 0452 0453 void KexiStartupFileHandler::messageWidgetActionNoTriggered() 0454 { 0455 d->messageWidgetLoop->exit(0); 0456 } 0457 0458 void KexiStartupFileHandler::updateUrl(const QString &name) 0459 { 0460 QUrl url = d->requester->url(); 0461 QString path = url.toLocalFile(); 0462 if (!QFileInfo(path).isDir() && !path.endsWith('/')) { 0463 url = url.adjusted(QUrl::RemoveFilename); 0464 path = url.toLocalFile(); 0465 } 0466 QString fn = KDbUtils::stringToFileName(name); 0467 if (!fn.isEmpty() && !fn.endsWith(".kexi")) 0468 fn += ".kexi"; 0469 url.setPath(QDir(path).absoluteFilePath(fn)); 0470 d->requester->setUrl(url); 0471 } 0472 0473 bool KexiStartupFileHandler::askForOverwriting(const QString& filePath) 0474 { 0475 QFileInfo fi(filePath); 0476 if (d->recentFilePathConfirmed == filePath) { 0477 return true; 0478 } 0479 d->recentFilePathConfirmed.clear(); 0480 if (!fi.exists()) 0481 return true; 0482 KexiContextMessage message( 0483 xi18n("This file already exists. Do you want to overwrite it?")); 0484 QScopedPointer<QAction> messageWidgetActionYes(new QAction(xi18n("Overwrite"), 0)); 0485 connect(messageWidgetActionYes.data(), SIGNAL(triggered()), 0486 this, SLOT(messageWidgetActionYesTriggered())); 0487 message.addAction(messageWidgetActionYes.data()); 0488 QScopedPointer<QAction> messageWidgetActionNo(new QAction(KStandardGuiItem::no().text(), 0)); 0489 connect(messageWidgetActionNo.data(), SIGNAL(triggered()), 0490 this, SLOT(messageWidgetActionNoTriggered())); 0491 message.addAction(messageWidgetActionNo.data()); 0492 message.setDefaultAction(messageWidgetActionNo.data()); 0493 emit askForOverwriting(message); 0494 if (!d->messageWidgetLoop) { 0495 d->messageWidgetLoop = new QEventLoop; 0496 } 0497 bool ok = d->messageWidgetLoop->exec(); 0498 if (ok) { 0499 d->recentFilePathConfirmed = filePath; 0500 } 0501 return ok; 0502 } 0503 0504 /*removed in KEXI3 0505 void KexiStartupFileHandler::setLocationText(const QString& fn) 0506 { 0507 d->dialog->locationEdit()->setUrl(QUrl(fn)); 0508 }*/ 0509 0510 void KexiStartupFileHandler::setDefaultExtension(const QString& ext) 0511 { 0512 d->defaultExtension = ext; 0513 } 0514 0515 void KexiStartupFileHandler::setConfirmOverwrites(bool set) 0516 { 0517 d->confirmOverwrites = set; 0518 } 0519 0520 void KexiStartupFileHandler::slotAccepted() 0521 { 0522 checkSelectedUrl(); 0523 } 0524