Warning, file /utilities/krusader/app/KViewer/panelviewer.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2002 Shie Erlich <erlich@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2002 Rafi Yanai <yanai@users.sourceforge.net> 0004 SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "panelviewer.h" 0010 0011 // QtCore 0012 #include <QDebug> 0013 #include <QFile> 0014 // QtWidgets 0015 #include <QApplication> 0016 0017 #include <kservice_version.h> 0018 0019 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 82, 0) 0020 #define CREATE_KPART_5_82 1 0021 #else 0022 #define CREATE_KPART_5_82 0 0023 #endif 0024 0025 #include <KConfigCore/KSharedConfig> 0026 #include <KI18n/KLocalizedString> 0027 #include <KParts/BrowserExtension> 0028 #include <KParts/ReadWritePart> 0029 0030 #if CREATE_KPART_5_82 0031 #include <KParts/PartLoader> 0032 #endif 0033 0034 #include <KIOCore/KFileItem> 0035 #include <KService/KMimeTypeTrader> 0036 #include <KService/KServiceTypeProfile> 0037 #include <KWidgetsAddons/KMessageBox> 0038 0039 #include "../defaults.h" 0040 #include "lister.h" 0041 0042 #define DICTSIZE 211 0043 0044 PanelViewerBase::PanelViewerBase(QWidget *parent, KrViewer::Mode mode) 0045 : QStackedWidget(parent) 0046 , mimes(nullptr) 0047 , cpart(nullptr) 0048 , mode(mode) 0049 { 0050 setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored)); 0051 0052 mimes = new QHash<QString, QPointer<KParts::ReadOnlyPart>>(); 0053 cpart = nullptr; 0054 // NOTE: the fallback should be never visible. The viewer is not opened without a file and the 0055 // tab is closed if a file cannot be opened. 0056 fallback = new QLabel(i18n("No file selected or selected file cannot be displayed."), this); 0057 fallback->setAlignment(Qt::Alignment(QFlag(Qt::AlignCenter | Qt::TextExpandTabs))); 0058 fallback->setWordWrap(true); 0059 addWidget(fallback); 0060 setCurrentWidget(fallback); 0061 } 0062 0063 PanelViewerBase::~PanelViewerBase() 0064 { 0065 // cpart->queryClose(); 0066 closeUrl(); 0067 QHashIterator<QString, QPointer<KParts::ReadOnlyPart>> lit(*mimes); 0068 while (lit.hasNext()) { 0069 QPointer<KParts::ReadOnlyPart> p = lit.next().value(); 0070 if (p) 0071 delete p; 0072 } 0073 mimes->clear(); 0074 delete mimes; 0075 delete fallback; 0076 } 0077 0078 void PanelViewerBase::slotStatResult(KJob *job) 0079 { 0080 if (job->error()) { 0081 KMessageBox::error(this, job->errorString()); 0082 emit openUrlFinished(this, false); 0083 } else { 0084 KIO::UDSEntry entry = dynamic_cast<KIO::StatJob *>(job)->statResult(); 0085 openFile(KFileItem(entry, curl)); 0086 } 0087 } 0088 0089 KParts::ReadOnlyPart *PanelViewerBase::getPart(const QString &mimetype) 0090 { 0091 KParts::ReadOnlyPart *part = nullptr; 0092 0093 if (mimes->find(mimetype) == mimes->end()) { 0094 part = createPart(mimetype); 0095 if (part) 0096 mimes->insert(mimetype, part); 0097 } else 0098 part = (*mimes)[mimetype]; 0099 0100 return part; 0101 } 0102 0103 void PanelViewerBase::openUrl(const QUrl &url) 0104 { 0105 closeUrl(); 0106 curl = url; 0107 emit urlChanged(this, url); 0108 0109 if (url.isLocalFile()) { 0110 if (!QFile::exists(url.path())) { 0111 KMessageBox::error(krMainWindow, i18n("Error at opening %1.", url.path())); 0112 emit openUrlFinished(this, false); 0113 return; 0114 } 0115 openFile(KFileItem(url)); 0116 } else { 0117 KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); 0118 connect(statJob, &KIO::StatJob::result, this, &PanelViewerBase::slotStatResult); 0119 } 0120 } 0121 0122 /* ----==={ PanelViewer }===---- */ 0123 0124 PanelViewer::PanelViewer(QWidget *parent, KrViewer::Mode mode) 0125 : PanelViewerBase(parent, mode) 0126 { 0127 } 0128 0129 PanelViewer::~PanelViewer() = default; 0130 0131 KParts::ReadOnlyPart *PanelViewer::getListerPart(bool hexMode) 0132 { 0133 KParts::ReadOnlyPart *part = nullptr; 0134 0135 if (mimes->find(QLatin1String("krusader_lister")) == mimes->end()) { 0136 part = new Lister(this); 0137 mimes->insert(QLatin1String("krusader_lister"), part); 0138 } else 0139 part = (*mimes)[QLatin1String("krusader_lister")]; 0140 0141 if (part) { 0142 auto *lister = qobject_cast<Lister *>((KParts::ReadOnlyPart *)part); 0143 if (lister) 0144 lister->setHexMode(hexMode); 0145 } 0146 return part; 0147 } 0148 0149 KParts::ReadOnlyPart *PanelViewer::getHexPart() 0150 { 0151 KParts::ReadOnlyPart *part = nullptr; 0152 0153 if (KConfigGroup(krConfig, "General").readEntry("UseOktetaViewer", _UseOktetaViewer)) { 0154 if (mimes->find("oktetapart") == mimes->end()) { 0155 KPluginFactory *factory = nullptr; 0156 // Okteta >= 0.26 provides a desktop file, prefer that as the binary changes name 0157 KService::Ptr service = KService::serviceByDesktopName("oktetapart"); 0158 if (service) { 0159 factory = KPluginLoader(*service.data()).factory(); 0160 } else { 0161 // fallback to search for desktopfile-less old variant 0162 factory = KPluginLoader("oktetapart").factory(); 0163 } 0164 if (factory) { 0165 if ((part = factory->create<KParts::ReadOnlyPart>(this, this))) 0166 mimes->insert("oktetapart", part); 0167 } 0168 } else 0169 part = (*mimes)["oktetapart"]; 0170 } 0171 0172 return part ? part : getListerPart(true); 0173 } 0174 0175 KParts::ReadOnlyPart *PanelViewer::getTextPart() 0176 { 0177 KParts::ReadOnlyPart *part = getPart("text/plain"); 0178 if (!part) 0179 part = getPart("all/allfiles"); 0180 return part ? part : getListerPart(); 0181 } 0182 0183 KParts::ReadOnlyPart *PanelViewer::getDefaultPart(const KFileItem &fi) 0184 { 0185 KConfigGroup group(krConfig, "General"); 0186 QString modeString = group.readEntry("Default Viewer Mode", QString("generic")); 0187 0188 KrViewer::Mode mode = KrViewer::Generic; 0189 0190 if (modeString == "generic") 0191 mode = KrViewer::Generic; 0192 else if (modeString == "text") 0193 mode = KrViewer::Text; 0194 else if (modeString == "hex") 0195 mode = KrViewer::Hex; 0196 else if (modeString == "lister") 0197 mode = KrViewer::Lister; 0198 0199 QMimeType mimeType = fi.determineMimeType(); 0200 bool isBinary = false; 0201 if (mode == KrViewer::Generic || mode == KrViewer::Lister) { 0202 isBinary = !mimeType.inherits(QStringLiteral("text/plain")); 0203 } 0204 0205 KIO::filesize_t fileSize = fi.size(); 0206 KIO::filesize_t limit = (KIO::filesize_t)group.readEntry("Lister Limit", _ListerLimit) * 0x100000; 0207 0208 QString mimetype = fi.mimetype(); 0209 0210 KParts::ReadOnlyPart *part = nullptr; 0211 0212 switch (mode) { 0213 case KrViewer::Generic: 0214 if ((mimetype.startsWith(QLatin1String("text/")) || mimetype.startsWith(QLatin1String("all/"))) && fileSize > limit) { 0215 part = getListerPart(isBinary); 0216 break; 0217 } else if ((part = getPart(mimetype))) { 0218 break; 0219 } 0220 #if __GNUC__ >= 7 0221 [[gnu::fallthrough]]; 0222 #endif 0223 case KrViewer::Text: 0224 part = fileSize > limit ? getListerPart(false) : getTextPart(); 0225 break; 0226 case KrViewer::Lister: 0227 part = getListerPart(isBinary); 0228 break; 0229 case KrViewer::Hex: 0230 part = fileSize > limit ? getListerPart(true) : getHexPart(); 0231 break; 0232 default: 0233 abort(); 0234 } 0235 0236 return part; 0237 } 0238 0239 void PanelViewer::openFile(KFileItem fi) 0240 { 0241 switch (mode) { 0242 case KrViewer::Generic: 0243 cpart = getPart(fi.mimetype()); 0244 break; 0245 case KrViewer::Text: 0246 cpart = getTextPart(); 0247 break; 0248 case KrViewer::Lister: 0249 cpart = getListerPart(); 0250 break; 0251 case KrViewer::Hex: 0252 cpart = getHexPart(); 0253 break; 0254 case KrViewer::Default: 0255 cpart = getDefaultPart(fi); 0256 break; 0257 default: 0258 abort(); 0259 } 0260 0261 if (cpart) { 0262 addWidget(cpart->widget()); 0263 setCurrentWidget(cpart->widget()); 0264 0265 if (cpart->inherits("KParts::ReadWritePart")) 0266 dynamic_cast<KParts::ReadWritePart *>(cpart.data())->setReadWrite(false); 0267 KParts::OpenUrlArguments args; 0268 args.setReload(true); 0269 cpart->setArguments(args); 0270 0271 connect(cpart.data(), &KParts::ReadOnlyPart::canceled, this, [=]() { 0272 qDebug() << "openFile canceled: '" << curl << "'"; 0273 }); 0274 0275 auto cPartCompleted = [=]() { 0276 connect(cpart.data(), &KParts::ReadOnlyPart::destroyed, this, &PanelViewer::slotCPartDestroyed); 0277 emit openUrlFinished(this, true); 0278 qDebug() << "openFile completed: '" << curl << "'"; 0279 }; 0280 connect(cpart.data(), QOverload<>::of(&KParts::ReadOnlyPart::completed), this, cPartCompleted); 0281 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 81, 0) 0282 connect(cpart.data(), &KParts::ReadOnlyPart::completedWithPendingAction, this, cPartCompleted); 0283 #else 0284 connect(cpart.data(), QOverload<bool>::of(&KParts::ReadOnlyPart::completed), this, cPartCompleted); 0285 #endif 0286 0287 // Note: Don't rely on return value of openUrl as the call is async in general 0288 cpart->openUrl(curl); 0289 0290 return; 0291 } 0292 0293 setCurrentWidget(fallback); 0294 emit openUrlFinished(this, false); 0295 } 0296 0297 bool PanelViewer::closeUrl() 0298 { 0299 setCurrentWidget(fallback); 0300 if (cpart && cpart->closeUrl()) { 0301 cpart = nullptr; 0302 return true; 0303 } 0304 return false; 0305 } 0306 0307 #if CREATE_KPART_5_82 0308 0309 template<typename T> 0310 static T *createKPartForMimeType(QString mimetype, QWidget *parentWidget) 0311 { 0312 auto metaDataList = KParts::PartLoader::partsForMimeType(mimetype); 0313 if (metaDataList.isEmpty()) 0314 return nullptr; 0315 0316 KPluginLoader pluginLoader(metaDataList.first().fileName()); 0317 KPluginFactory *pluginFactory = pluginLoader.factory(); 0318 if (!pluginFactory) 0319 return nullptr; 0320 0321 return pluginFactory->create<T>(parentWidget, parentWidget); 0322 } 0323 0324 #endif // CREATE_KPART_5_82 0325 0326 KParts::ReadOnlyPart *PanelViewer::createPart(QString mimetype) 0327 { 0328 KParts::ReadOnlyPart *part = nullptr; 0329 0330 #if CREATE_KPART_5_82 0331 part = createKPartForMimeType<KParts::ReadOnlyPart>(mimetype, this); 0332 #else 0333 KService::Ptr ptr = KMimeTypeTrader::self()->preferredService(mimetype, "KParts/ReadOnlyPart"); 0334 if (ptr) { 0335 QVariantList args; 0336 QVariant argsProp = ptr->property("X-KDE-BrowserView-Args"); 0337 if (argsProp.isValid()) 0338 args << argsProp; 0339 QVariant prop = ptr->property("X-KDE-BrowserView-AllowAsDefault"); 0340 if (!prop.isValid() || prop.toBool()) // defaults to true 0341 part = ptr->createInstance<KParts::ReadOnlyPart>(this, this, args); 0342 } 0343 #endif 0344 0345 if (part) { 0346 KParts::BrowserExtension *ext = KParts::BrowserExtension::childObject(part); 0347 if (ext) { 0348 connect(ext, &KParts::BrowserExtension::openUrlRequestDelayed, this, &PanelViewer::openUrl); 0349 connect(ext, &KParts::BrowserExtension::openUrlRequestDelayed, this, &PanelViewer::openUrlRequest); 0350 } 0351 } 0352 return part; 0353 } 0354 0355 /* ----==={ PanelEditor }===---- */ 0356 0357 PanelEditor::PanelEditor(QWidget *parent, KrViewer::Mode mode) 0358 : PanelViewerBase(parent, mode) 0359 { 0360 } 0361 0362 PanelEditor::~PanelEditor() = default; 0363 0364 void PanelEditor::configureDeps() 0365 { 0366 bool foundPlugin = false; 0367 0368 #if CREATE_KPART_5_82 0369 foundPlugin = !KParts::PartLoader::partsForMimeType("text/plain").isEmpty(); 0370 #else 0371 KService::Ptr ptr = KMimeTypeTrader::self()->preferredService("text/plain", "KParts/ReadWritePart"); 0372 if (!ptr) 0373 ptr = KMimeTypeTrader::self()->preferredService("all/allfiles", "KParts/ReadWritePart"); 0374 foundPlugin = (ptr != nullptr); 0375 #endif 0376 0377 if (!foundPlugin) 0378 KMessageBox::error(nullptr, missingKPartMsg(), i18n("Missing Plugin"), KMessageBox::AllowLink); 0379 } 0380 0381 QString PanelEditor::missingKPartMsg() 0382 { 0383 return i18nc("missing kpart - arg1 is a URL", 0384 "<b>No text editor plugin available.</b><br/>" 0385 "Internal editor will not work without this.<br/>" 0386 "You can fix this by installing Kate:<br/>%1", 0387 QString("<a href='%1'>%1</a>").arg("https://kde.org/applications/utilities/org.kde.kate")); 0388 } 0389 0390 void PanelEditor::openFile(KFileItem fi) 0391 { 0392 KIO::filesize_t fileSize = fi.size(); 0393 KConfigGroup group(krConfig, "General"); 0394 KIO::filesize_t limitMB = (KIO::filesize_t)group.readEntry("Lister Limit", _ListerLimit); 0395 QString mimetype = fi.mimetype(); 0396 0397 if (mode == KrViewer::Generic) 0398 cpart = getPart(mimetype); 0399 0400 if (fileSize > limitMB * 0x100000) { 0401 if (!cpart || mimetype.startsWith(QLatin1String("text/")) || mimetype.startsWith(QLatin1String("all/"))) { 0402 if (KMessageBox::Cancel 0403 == KMessageBox::warningContinueCancel(this, i18n("%1 is bigger than %2 MB", curl.toDisplayString(QUrl::PreferLocalFile), limitMB))) { 0404 setCurrentWidget(fallback); 0405 emit openUrlFinished(this, false); 0406 return; 0407 } 0408 } 0409 } 0410 0411 if (!cpart) 0412 cpart = getPart("text/plain"); 0413 if (!cpart) 0414 cpart = getPart("all/allfiles"); 0415 0416 if (cpart) { 0417 addWidget(cpart->widget()); 0418 setCurrentWidget(cpart->widget()); 0419 0420 KParts::OpenUrlArguments args; 0421 args.setReload(true); 0422 cpart->setArguments(args); 0423 if (cpart->openUrl(curl)) { 0424 connect(cpart.data(), &KParts::ReadOnlyPart::destroyed, this, &PanelEditor::slotCPartDestroyed); 0425 emit openUrlFinished(this, true); 0426 return; 0427 } // else: don't show error message - assume this has been done by the editor part 0428 } else 0429 KMessageBox::error(this, missingKPartMsg(), i18n("Cannot edit %1", curl.toDisplayString(QUrl::PreferLocalFile)), KMessageBox::AllowLink); 0430 0431 setCurrentWidget(fallback); 0432 emit openUrlFinished(this, false); 0433 } 0434 0435 bool PanelEditor::queryClose() 0436 { 0437 if (!cpart) 0438 return true; 0439 return dynamic_cast<KParts::ReadWritePart *>((KParts::ReadOnlyPart *)cpart)->queryClose(); 0440 } 0441 0442 bool PanelEditor::closeUrl() 0443 { 0444 if (!cpart) 0445 return false; 0446 0447 dynamic_cast<KParts::ReadWritePart *>((KParts::ReadOnlyPart *)cpart)->closeUrl(false); 0448 0449 setCurrentWidget(fallback); 0450 cpart = nullptr; 0451 return true; 0452 } 0453 0454 KParts::ReadOnlyPart *PanelEditor::createPart(QString mimetype) 0455 { 0456 KParts::ReadWritePart *part = nullptr; 0457 0458 #if CREATE_KPART_5_82 0459 part = createKPartForMimeType<KParts::ReadWritePart>(mimetype, this); 0460 #else 0461 KService::Ptr ptr = KMimeTypeTrader::self()->preferredService(mimetype, "KParts/ReadWritePart"); 0462 if (ptr) { 0463 QVariantList args; 0464 QVariant argsProp = ptr->property("X-KDE-BrowserView-Args"); 0465 if (argsProp.isValid()) 0466 args << argsProp; 0467 QVariant prop = ptr->property("X-KDE-BrowserView-AllowAsDefault"); 0468 if (!prop.isValid() || prop.toBool()) // defaults to true 0469 part = ptr->createInstance<KParts::ReadWritePart>(this, this, args); 0470 } 0471 #endif 0472 0473 if (part) { 0474 KParts::BrowserExtension *ext = KParts::BrowserExtension::childObject(part); 0475 if (ext) { 0476 connect(ext, &KParts::BrowserExtension::openUrlRequestDelayed, this, &PanelEditor::openUrl); 0477 connect(ext, &KParts::BrowserExtension::openUrlRequestDelayed, this, &PanelEditor::openUrlRequest); 0478 } 0479 } 0480 return part; 0481 } 0482 0483 bool PanelEditor::isModified() 0484 { 0485 if (cpart) 0486 return dynamic_cast<KParts::ReadWritePart *>((KParts::ReadOnlyPart *)cpart)->isModified(); 0487 else 0488 return false; 0489 }