File indexing completed on 2025-01-05 05:14:49
0001 /* 0002 SPDX-FileCopyrightText: 2021 Hamed Masafi <hamed.masfi@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "fileviewerdialog.h" 0008 #include "gitmanager.h" 0009 0010 #include <KActionCollection> 0011 #include <KLocalizedString> 0012 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0013 #include <KMimeTypeTrader> 0014 #endif 0015 #include <KStandardAction> 0016 #include <KXMLGUIFactory> 0017 0018 #include "libkommitwidgets_appdebug.h" 0019 #include <QFile> 0020 #include <QMimeData> 0021 #include <QMimeDatabase> 0022 #include <QProgressDialog> 0023 #include <QSettings> 0024 #include <QStandardPaths> 0025 #include <QStyle> 0026 0027 // TODO: This file need to be refactored 0028 FileViewerDialog::FileViewerDialog(Git::Manager *git, const QString &place, const QString &fileName, QWidget *parent) 0029 : FileViewerDialog(git, Git::File(git, place, fileName), parent) 0030 { 0031 } 0032 0033 FileViewerDialog::FileViewerDialog(Git::Manager *git, const Git::File &file, QWidget *parent) 0034 : KParts::MainWindow(parent) 0035 , mGit(git) 0036 { 0037 setupUi(this); 0038 showFile(Git::File(file)); 0039 QSettings s; 0040 restoreGeometry(s.value(QStringLiteral("FileViewerDialog_Geometry")).toByteArray()); 0041 KStandardAction::close(this, &QMainWindow::close, actionCollection()); 0042 0043 setupGUI(ToolBar, QStringLiteral("kommitfileviewerui.rc")); 0044 } 0045 0046 FileViewerDialog::~FileViewerDialog() 0047 { 0048 QSettings s; 0049 s.setValue(QStringLiteral("FileViewerDialog_Geometry"), saveGeometry()); 0050 0051 if (!mFilePath.isEmpty() && QFile::exists(mFilePath)) 0052 QFile::remove(mFilePath); 0053 0054 if (m_part) { 0055 QProgressDialog progressDialog(this); 0056 progressDialog.setWindowTitle(i18nc("@title:window", "Closing preview")); 0057 progressDialog.setLabelText(i18n("Please wait while the preview is being closed...")); 0058 0059 progressDialog.setMinimumDuration(500); 0060 progressDialog.setModal(true); 0061 progressDialog.setCancelButton(nullptr); 0062 progressDialog.setRange(0, 0); 0063 0064 // #261785: this preview dialog is not modal, so we need to delete 0065 // the previewed file ourselves when the dialog is closed; 0066 0067 m_part.data()->closeUrl(); 0068 0069 // if (!m_fileName.isEmpty()) { 0070 // QFile::remove(m_fileName); 0071 // } 0072 } 0073 0074 guiFactory()->removeClient(m_part); 0075 delete m_part; 0076 } 0077 0078 void FileViewerDialog::showFile(const Git::File &file) 0079 { 0080 QMimeDatabase mimeDatabase; 0081 const auto fn = file.fileName().mid(file.fileName().lastIndexOf(QLatin1Char('/')) + 1); 0082 const auto mime = mimeDatabase.mimeTypeForFile(fn, QMimeDatabase::MatchExtension); 0083 mFilePath = file.fileName(); 0084 mFilePath = mFilePath.mid(mFilePath.lastIndexOf(QLatin1Char('/')) + 1); 0085 mFilePath.prepend(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1Char('/')); 0086 0087 lineEditBranchName->setText(file.place()); 0088 lineEditFileName->setText(file.fileName()); 0089 plainTextEdit->setReadOnly(true); 0090 setWindowTitle(i18nc("@title:window", "View file: %1", file.fileName())); 0091 setWindowFilePath(file.fileName()); 0092 labelFileIcon->setPixmap(QIcon::fromTheme(mime.iconName()).pixmap(style()->pixelMetric(QStyle::PixelMetric::PM_SmallIconSize))); 0093 0094 auto ptr = getInternalViewer(mime.name()); 0095 if (ptr && ptr->isValid()) { 0096 file.save(mFilePath); 0097 if (viewInInternalViewer(ptr, mFilePath, mime)) 0098 return; 0099 } 0100 0101 if (mime.name().startsWith(QStringLiteral("text/"))) 0102 showInEditor(file); 0103 else if (mime.name().startsWith(QStringLiteral("image/"))) 0104 showAsImage(file); 0105 else { 0106 if (!ptr || !ptr->isValid()) { 0107 showInEditor(file); 0108 qCDebug(KOMMIT_WIDGETS_LOG()) << "fallback to text mode"; 0109 } else { 0110 file.save(mFilePath); 0111 if (!viewInInternalViewer(ptr, mFilePath, mime)) 0112 showInEditor(file); 0113 } 0114 } 0115 qCDebug(KOMMIT_WIDGETS_LOG()) << "mime is" << mime.name() << fn << mimeDatabase.suffixForFileName(fn) << stackedWidget->currentIndex(); 0116 } 0117 0118 void FileViewerDialog::showInEditor(const Git::File &file) 0119 { 0120 stackedWidget->setCurrentIndex(0); 0121 plainTextEdit->setPlainText(file.content()); 0122 plainTextEdit->setHighlighting(file.fileName()); 0123 } 0124 0125 void FileViewerDialog::showAsImage(const Git::File &file) 0126 { 0127 stackedWidget->setCurrentIndex(1); 0128 const auto p = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QStringLiteral("/klient_img"); 0129 file.save(p); 0130 QImage img{p}; 0131 labelImage->setPixmap(QPixmap::fromImage(img)); 0132 } 0133 0134 KService::Ptr FileViewerDialog::getInternalViewer(const QString &mimeType) 0135 { 0136 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0137 // No point in even trying to find anything for application/octet-stream 0138 if (mimeType == QLatin1String("application/octet-stream")) { 0139 return {}; 0140 } 0141 0142 // Try to get a read-only kpart for the internal viewer 0143 KService::List offers = KMimeTypeTrader::self()->query(mimeType, QStringLiteral("KParts/ReadOnlyPart")); 0144 0145 qCDebug(KOMMIT_WIDGETS_LOG()) << offers.size() << "offer(s) found for" << mimeType; 0146 for (const auto &offer : std::as_const(offers)) 0147 qCDebug(KOMMIT_WIDGETS_LOG()) << " *" << offer->name() << offer->genericName(); 0148 /*auto arkPartIt = std::find_if(offers.begin(), offers.end(), [](KService::Ptr service) { 0149 return service->storageId() == QLatin1String("ark_part.desktop"); 0150 }); 0151 0152 // Use the Ark part only when the mime type matches an archive type directly. 0153 // Many file types (e.g. Open Document) are technically just archives 0154 // but browsing their internals is typically not what the user wants. 0155 if (arkPartIt != offers.end()) { 0156 // Not using hasMimeType() as we're explicitly not interested in inheritance. 0157 if (!(*arkPartIt)->mimeTypes().contains(mimeType)) { 0158 offers.erase(arkPartIt); 0159 } 0160 }*/ 0161 0162 // Skip the KHTML part 0163 auto khtmlPart = std::find_if(offers.begin(), offers.end(), [](KService::Ptr service) { 0164 return service->desktopEntryName() == QLatin1String("khtml"); 0165 }); 0166 0167 if (khtmlPart != offers.end()) { 0168 offers.erase(khtmlPart); 0169 } 0170 0171 if (!offers.isEmpty()) { 0172 return offers.first(); 0173 } else { 0174 return {}; 0175 } 0176 #else 0177 qWarning() << "FileViewerDialog::getInternalViewer need to be port to QT6 "; 0178 return {}; 0179 0180 #endif 0181 } 0182 0183 void FileViewerDialog::keyPressEvent(QKeyEvent *event) 0184 { 0185 if (event->key() == Qt::Key_Escape) 0186 close(); 0187 0188 KParts::MainWindow::keyPressEvent(event); 0189 } 0190 0191 KService::Ptr FileViewerDialog::getExternalViewer(const QString &mimeType) 0192 { 0193 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0194 qCDebug(KOMMIT_WIDGETS_LOG()) << mimeType; 0195 const KService::List offers = KMimeTypeTrader::self()->query(mimeType); 0196 0197 if (!offers.isEmpty()) { 0198 return offers.first(); 0199 } else { 0200 return {}; 0201 } 0202 #else 0203 qWarning() << "FileViewerDialog::getExternalViewer need to be port to QT6 "; 0204 return {}; 0205 #endif 0206 } 0207 0208 bool FileViewerDialog::viewInInternalViewer(const KService::Ptr &viewer, const QString &fileName, const QMimeType &mimeType) 0209 { 0210 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0211 Q_UNUSED(mimeType) 0212 // Set icon and comment for the mimetype. 0213 // m_iconLabel->setPixmap(QIcon::fromTheme(mimeType.iconName()).pixmap(style()->pixelMetric(QStyle::PixelMetric::PM_SmallIconSize))); 0214 // m_commentLabel->setText(mimeType.comment()); 0215 0216 // Create the ReadOnlyPart instance. 0217 QString error; 0218 m_part = viewer->createInstance<KParts::ReadOnlyPart>(widgetContainer, widgetContainer, QVariantList(), &error); 0219 0220 if (!m_part.data()) { 0221 qCDebug(KOMMIT_WIDGETS_LOG()) << "m_part is null" << error; 0222 return false; 0223 } 0224 0225 // Insert the KPart into its placeholder. 0226 kPartWidgetLayout->addWidget(m_part.data()->widget()); 0227 stackedWidget->setCurrentIndex(2); 0228 // layout()->replaceWidget(plainTextEdit, m_part.data()->widget()); 0229 /* 0230 QAction* action = actionCollection()->addAction(QStringLiteral("help_about_kpart")); 0231 const KPluginMetaData partMetaData = m_part->metaData(); 0232 const QString iconName = partMetaData.iconName(); 0233 if (!iconName.isEmpty()) { 0234 action->setIcon(QIcon::fromTheme(iconName)); 0235 } 0236 action->setText(i18nc("@action", "About Viewer Component")); 0237 connect(action, &QAction::triggered, this, &ArkViewer::aboutKPart); 0238 */ 0239 createGUI(m_part.data()); 0240 // setAutoSaveSettings(QStringLiteral("Viewer"), true); 0241 0242 m_part.data()->openUrl(QUrl::fromLocalFile(fileName)); 0243 m_part.data()->widget()->setFocus(); 0244 // m_fileName = fileName; 0245 #else 0246 qWarning() << "FileViewerDialog::viewInInternalViewer need to be port to QT6 "; 0247 return {}; 0248 #endif 0249 return true; 0250 } 0251 0252 #include "moc_fileviewerdialog.cpp"