File indexing completed on 2024-04-28 05:27:03

0001 /* This file is part of the KDE project
0002    SPDX-FileCopyrightText: 2000, 2007 David Faure <faure@kde.org>
0003 
0004    SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
0005 */
0006 
0007 // Own
0008 #include "filetypedetails.h"
0009 
0010 // Qt
0011 #include <QButtonGroup>
0012 #include <QCheckBox>
0013 #include <QInputDialog>
0014 #include <QLabel>
0015 #include <QListWidget>
0016 #include <QMimeDatabase>
0017 #include <QPushButton>
0018 #include <QRadioButton>
0019 #include <QVBoxLayout>
0020 
0021 // KDE
0022 #include <KConfig>
0023 #include <KConfigGroup>
0024 #include <KIconButton>
0025 #include <KIconDialog>
0026 #include <KLineEdit>
0027 #include <KLocalizedString>
0028 #include <KSharedConfig>
0029 
0030 // Local
0031 #include "kservicelistwidget.h"
0032 #include "typeslistitem.h"
0033 
0034 FileTypeDetails::FileTypeDetails(QWidget *parent)
0035     : QWidget(parent)
0036     , m_mimeTypeData(nullptr)
0037     , m_item(nullptr)
0038 {
0039     QVBoxLayout *topLayout = new QVBoxLayout(this);
0040     topLayout->setContentsMargins(0, 0, 0, 0);
0041 
0042     m_mimeTypeLabel = new QLabel(this);
0043     m_mimeTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
0044     topLayout->addWidget(m_mimeTypeLabel, 0, Qt::AlignCenter);
0045 
0046     m_tabWidget = new QTabWidget(this);
0047     topLayout->addWidget(m_tabWidget);
0048 
0049     QString wtstr;
0050     // First tab - General
0051     QWidget *firstWidget = new QWidget(m_tabWidget);
0052     QVBoxLayout *firstLayout = new QVBoxLayout(firstWidget);
0053 
0054     QHBoxLayout *hBox = new QHBoxLayout();
0055     firstLayout->addLayout(hBox);
0056 
0057     iconButton = new KIconButton(firstWidget);
0058     iconButton->setIconType(KIconLoader::Desktop, KIconLoader::MimeType);
0059     connect(iconButton, &KIconButton::iconChanged, this, &FileTypeDetails::updateIcon);
0060     iconButton->setWhatsThis(
0061         i18n("This button displays the icon associated"
0062              " with the selected file type. Click on it to choose a different icon."));
0063     iconButton->setFixedSize(70, 70);
0064     iconLabel = nullptr;
0065     hBox->addWidget(iconButton);
0066 
0067     QGroupBox *gb = new QGroupBox(i18n("Filename Patterns"), firstWidget);
0068     hBox->addWidget(gb);
0069 
0070     hBox = new QHBoxLayout(gb);
0071 
0072     extensionLB = new QListWidget(gb);
0073     connect(extensionLB, &QListWidget::itemSelectionChanged, this, &FileTypeDetails::enableExtButtons);
0074     hBox->addWidget(extensionLB);
0075 
0076     extensionLB->setFixedHeight(extensionLB->minimumSizeHint().height());
0077 
0078     extensionLB->setWhatsThis(
0079         i18n("This box contains a list of patterns that can be"
0080              " used to identify files of the selected type. For example, the pattern *.txt is"
0081              " associated with the file type 'text/plain'; all files ending in '.txt' are recognized"
0082              " as plain text files."));
0083 
0084     QVBoxLayout *vbox = new QVBoxLayout();
0085     hBox->addLayout(vbox);
0086 
0087     addExtButton = new QPushButton(i18n("Add..."), gb);
0088     addExtButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0089     addExtButton->setEnabled(false);
0090     connect(addExtButton, &QAbstractButton::clicked, this, &FileTypeDetails::addExtension);
0091     vbox->addWidget(addExtButton);
0092     addExtButton->setWhatsThis(i18n("Add a new pattern for the selected file type."));
0093 
0094     removeExtButton = new QPushButton(i18n("Remove"), gb);
0095     removeExtButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
0096     removeExtButton->setEnabled(false);
0097     connect(removeExtButton, &QAbstractButton::clicked, this, &FileTypeDetails::removeExtension);
0098     vbox->addWidget(removeExtButton);
0099     removeExtButton->setWhatsThis(i18n("Remove the selected filename pattern."));
0100 
0101     vbox->addStretch(1);
0102 
0103     gb->setFixedHeight(gb->minimumSizeHint().height());
0104 
0105     description = new KLineEdit(firstWidget);
0106     description->setClearButtonEnabled(true);
0107     connect(description, &QLineEdit::textChanged, this, &FileTypeDetails::updateDescription);
0108 
0109     QHBoxLayout *descriptionBox = new QHBoxLayout;
0110     descriptionBox->addWidget(new QLabel(i18n("Description:"), firstWidget));
0111     descriptionBox->addWidget(description);
0112     firstLayout->addLayout(descriptionBox);
0113 
0114     wtstr = i18n(
0115         "You can enter a short description for files of the selected"
0116         " file type (e.g. 'HTML Page'). This description will be used by applications"
0117         " like Konqueror to display directory content.");
0118     description->setWhatsThis(wtstr);
0119 
0120     serviceListWidget = new KServiceListWidget(KServiceListWidget::SERVICELIST_APPLICATIONS, firstWidget);
0121     connect(serviceListWidget, &KServiceListWidget::changed, this, &FileTypeDetails::changed);
0122     connect(serviceListWidget, &KServiceListWidget::multiApply, this, &FileTypeDetails::multiApply);
0123     firstLayout->addWidget(serviceListWidget, 5);
0124 
0125     // Second tab - Embedding
0126     QWidget *secondWidget = new QWidget(m_tabWidget);
0127     QVBoxLayout *secondLayout = new QVBoxLayout(secondWidget);
0128 
0129     m_autoEmbedBox = new QGroupBox(i18n("Left Click Action in Konqueror"), secondWidget);
0130     secondLayout->addWidget(m_autoEmbedBox);
0131 
0132     m_autoEmbedBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
0133 
0134     QRadioButton *embViewerRadio = new QRadioButton(i18n("Show file in embedded viewer"));
0135     QRadioButton *sepViewerRadio = new QRadioButton(i18n("Show file in separate viewer"));
0136     m_rbGroupSettings = new QRadioButton(QStringLiteral("Use settings for '%1' group"));
0137 
0138     m_chkAskSave = new QCheckBox(i18n("Ask whether to save to disk instead (only for Konqueror browser)"));
0139     connect(m_chkAskSave, &QAbstractButton::toggled, this, &FileTypeDetails::slotAskSaveToggled);
0140 
0141     m_autoEmbedGroup = new QButtonGroup(m_autoEmbedBox);
0142     m_autoEmbedGroup->addButton(embViewerRadio, 0);
0143     m_autoEmbedGroup->addButton(sepViewerRadio, 1);
0144     m_autoEmbedGroup->addButton(m_rbGroupSettings, 2);
0145     connect(m_autoEmbedGroup, &QButtonGroup::idClicked, this, &FileTypeDetails::slotAutoEmbedClicked);
0146 
0147     vbox = new QVBoxLayout(m_autoEmbedBox);
0148     vbox->addWidget(embViewerRadio);
0149     vbox->addWidget(sepViewerRadio);
0150     vbox->addWidget(m_rbGroupSettings);
0151     vbox->addWidget(m_chkAskSave);
0152 
0153     m_autoEmbedBox->setWhatsThis(
0154         i18n("Here you can configure what the Konqueror file manager"
0155              " will do when you click on a file of this type. Konqueror can either display the file in"
0156              " an embedded viewer, or start up a separate application. If set to 'Use settings for G group',"
0157              " the file manager will behave according to the settings of the group G to which this type belongs;"
0158              " for instance, 'image' if the current file type is image/png. Dolphin"
0159              " always shows files in a separate viewer."));
0160 
0161     embedServiceListWidget = new KServiceListWidget(KServiceListWidget::SERVICELIST_SERVICES, secondWidget);
0162     //  embedServiceListWidget->setMinimumHeight( serviceListWidget->sizeHint().height() );
0163     connect(embedServiceListWidget, &KServiceListWidget::changed, this, &FileTypeDetails::changed);
0164     connect(embedServiceListWidget, &KServiceListWidget::multiApply, this, &FileTypeDetails::multiApply);
0165     secondLayout->addWidget(embedServiceListWidget);
0166 
0167     m_tabWidget->addTab(firstWidget, i18n("&General"));
0168     m_tabWidget->addTab(secondWidget, i18n("&Embedding"));
0169 }
0170 
0171 void FileTypeDetails::updateRemoveButton()
0172 {
0173     removeExtButton->setEnabled(extensionLB->count() > 0);
0174 }
0175 
0176 void FileTypeDetails::updateIcon(const QString &icon)
0177 {
0178     if (!m_mimeTypeData) {
0179         return;
0180     }
0181 
0182     m_mimeTypeData->setUserSpecifiedIcon(icon);
0183 
0184     if (m_item) {
0185         m_item->setIcon(icon);
0186     }
0187 
0188     Q_EMIT changed(true);
0189 }
0190 
0191 void FileTypeDetails::updateDescription(const QString &desc)
0192 {
0193     if (!m_mimeTypeData) {
0194         return;
0195     }
0196 
0197     m_mimeTypeData->setComment(desc);
0198 
0199     Q_EMIT changed(true);
0200 }
0201 
0202 void FileTypeDetails::addExtension()
0203 {
0204     if (!m_mimeTypeData) {
0205         return;
0206     }
0207 
0208     bool ok;
0209     QString ext = QInputDialog::getText(this, i18n("Add New Extension"), i18n("Extension:"), QLineEdit::Normal, QStringLiteral("*."), &ok);
0210     if (ok) {
0211         extensionLB->addItem(ext);
0212         QStringList patt = m_mimeTypeData->patterns();
0213         patt += ext;
0214         m_mimeTypeData->setPatterns(patt);
0215         updateRemoveButton();
0216         Q_EMIT changed(true);
0217     }
0218 }
0219 
0220 void FileTypeDetails::removeExtension()
0221 {
0222     if (extensionLB->currentRow() == -1) {
0223         return;
0224     }
0225     if (!m_mimeTypeData) {
0226         return;
0227     }
0228     QStringList patt = m_mimeTypeData->patterns();
0229     patt.removeAll(extensionLB->currentItem()->text());
0230     m_mimeTypeData->setPatterns(patt);
0231     delete extensionLB->takeItem(extensionLB->currentRow());
0232     updateRemoveButton();
0233     Q_EMIT changed(true);
0234 }
0235 
0236 void FileTypeDetails::slotAutoEmbedClicked(int button)
0237 {
0238     if (!m_mimeTypeData || (button > 2)) {
0239         return;
0240     }
0241 
0242     m_mimeTypeData->setAutoEmbed((MimeTypeData::AutoEmbed)button);
0243 
0244     updateAskSave();
0245 
0246     Q_EMIT changed(true);
0247 }
0248 
0249 void FileTypeDetails::updateAskSave()
0250 {
0251     if (!m_mimeTypeData) {
0252         return;
0253     }
0254     QMimeDatabase db;
0255 
0256     MimeTypeData::AutoEmbed autoEmbed = m_mimeTypeData->autoEmbed();
0257     if (m_mimeTypeData->isMeta() && autoEmbed == MimeTypeData::UseGroupSetting) {
0258         // Resolve by looking at group (we could cache groups somewhere to avoid the re-parsing?)
0259         autoEmbed = MimeTypeData(m_mimeTypeData->majorType()).autoEmbed();
0260     }
0261 
0262     const QString mimeType = m_mimeTypeData->name();
0263 
0264     QString dontAskAgainName;
0265     if (autoEmbed == MimeTypeData::Yes) { // Embedded
0266         dontAskAgainName = QStringLiteral("askEmbedOrSave") + mimeType;
0267     } else {
0268         dontAskAgainName = QStringLiteral("askSave") + mimeType;
0269     }
0270 
0271     KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("filetypesrc"), KConfig::NoGlobals);
0272     // default value
0273     bool ask = config->group("Notification Messages").readEntry(dontAskAgainName, QString()).isEmpty();
0274     // per-mimetype override if there's one
0275     m_mimeTypeData->getAskSave(ask);
0276 
0277     bool neverAsk = false;
0278 
0279     if (autoEmbed == MimeTypeData::Yes) {
0280         const QMimeType mime = db.mimeTypeForName(mimeType);
0281         if (mime.isValid()) {
0282             // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC
0283             // NOTE: Keep this function in sync with
0284             // kparts/src/browseropenorsavequestion.cpp BrowserOpenOrSaveQuestionPrivate::autoEmbedMimeType
0285 
0286             // Don't ask for:
0287             // - html (even new tabs would ask, due to about:blank!)
0288             // - dirs obviously (though not common over HTTP :),
0289             // - images (reasoning: no need to save, most of the time, because fast to see)
0290             // e.g. postscript is different, because takes longer to read, so
0291             // it's more likely that the user might want to save it.
0292             // - multipart/* ("server push", see kmultipart)
0293             // clang-format off
0294             if (mime.inherits(QStringLiteral("text/html"))
0295                 || mime.inherits(QStringLiteral("application/xml"))
0296                 || mime.inherits(QStringLiteral("inode/directory"))
0297                 || mimeType.startsWith(QLatin1String("image"))
0298                 || mime.inherits(QStringLiteral("multipart/x-mixed-replace"))
0299                 || mime.inherits(QStringLiteral("multipart/replace"))) {
0300                 neverAsk = true;
0301             }
0302             // clang-format on
0303         }
0304     }
0305 
0306     m_chkAskSave->blockSignals(true);
0307     m_chkAskSave->setChecked(ask && !neverAsk);
0308     m_chkAskSave->setEnabled(!neverAsk);
0309     m_chkAskSave->blockSignals(false);
0310 }
0311 
0312 void FileTypeDetails::slotAskSaveToggled(bool askSave)
0313 {
0314     if (!m_mimeTypeData) {
0315         return;
0316     }
0317 
0318     m_mimeTypeData->setAskSave(askSave);
0319     Q_EMIT changed(true);
0320 }
0321 
0322 void FileTypeDetails::setMimeTypeData(MimeTypeData *mimeTypeData, TypesListItem *item)
0323 {
0324     m_mimeTypeData = mimeTypeData;
0325     m_item = item; // can be 0
0326     Q_ASSERT(mimeTypeData);
0327     m_mimeTypeLabel->setText(i18n("File type %1", mimeTypeData->name()));
0328     if (iconButton) {
0329         iconButton->setIcon(mimeTypeData->icon());
0330         iconButton->setToolTip(mimeTypeData->icon());
0331     } else {
0332         iconLabel->setPixmap(QIcon::fromTheme(mimeTypeData->icon()).pixmap(KIconLoader::SizeLarge));
0333     }
0334     description->setText(mimeTypeData->comment());
0335     m_rbGroupSettings->setText(i18n("Use settings for '%1' group", mimeTypeData->majorType()));
0336     extensionLB->clear();
0337     addExtButton->setEnabled(true);
0338     removeExtButton->setEnabled(false);
0339 
0340     serviceListWidget->setMimeTypeData(mimeTypeData);
0341     embedServiceListWidget->setMimeTypeData(mimeTypeData);
0342     m_autoEmbedGroup->button(mimeTypeData->autoEmbed())->setChecked(true);
0343     m_rbGroupSettings->setEnabled(mimeTypeData->canUseGroupSetting());
0344 
0345     extensionLB->addItems(mimeTypeData->patterns());
0346 
0347     updateAskSave();
0348 }
0349 
0350 void FileTypeDetails::enableExtButtons()
0351 {
0352     removeExtButton->setEnabled(true);
0353 }
0354 
0355 void FileTypeDetails::refresh()
0356 {
0357     if (!m_mimeTypeData) {
0358         return;
0359     }
0360 
0361     // Called when ksycoca has been updated -> refresh data, then widgets
0362     m_mimeTypeData->refresh();
0363     setMimeTypeData(m_mimeTypeData, m_item);
0364 }
0365 
0366 void FileTypeDetails::allowMultiApply(bool allow)
0367 {
0368     serviceListWidget->allowMultiApply(allow);
0369     embedServiceListWidget->allowMultiApply(allow);
0370 }
0371 
0372 #include "moc_filetypedetails.cpp"