File indexing completed on 2024-04-28 16:44:25

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.h>
0023 #include <kconfiggroup.h>
0024 #include <kiconbutton.h>
0025 #include <kicondialog.h>
0026 #include <klineedit.h>
0027 #include <klocalizedstring.h>
0028 #include <ksharedconfig.h>
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     firstLayout->addWidget(serviceListWidget, 5);
0123 
0124     // Second tab - Embedding
0125     QWidget *secondWidget = new QWidget(m_tabWidget);
0126     QVBoxLayout *secondLayout = new QVBoxLayout(secondWidget);
0127 
0128     m_autoEmbedBox = new QGroupBox(i18n("Left Click Action in Konqueror"), secondWidget);
0129     secondLayout->addWidget(m_autoEmbedBox);
0130 
0131     m_autoEmbedBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
0132 
0133     QRadioButton *embViewerRadio = new QRadioButton(i18n("Show file in embedded viewer"));
0134     QRadioButton *sepViewerRadio = new QRadioButton(i18n("Show file in separate viewer"));
0135     m_rbGroupSettings = new QRadioButton(QStringLiteral("Use settings for '%1' group"));
0136 
0137     m_chkAskSave = new QCheckBox(i18n("Ask whether to save to disk instead (only for Konqueror browser)"));
0138     connect(m_chkAskSave, &QAbstractButton::toggled, this, &FileTypeDetails::slotAskSaveToggled);
0139 
0140     m_autoEmbedGroup = new QButtonGroup(m_autoEmbedBox);
0141     m_autoEmbedGroup->addButton(embViewerRadio, 0);
0142     m_autoEmbedGroup->addButton(sepViewerRadio, 1);
0143     m_autoEmbedGroup->addButton(m_rbGroupSettings, 2);
0144     connect(m_autoEmbedGroup, SIGNAL(buttonClicked(int)), SLOT(slotAutoEmbedClicked(int)));
0145 
0146     vbox = new QVBoxLayout(m_autoEmbedBox);
0147     vbox->addWidget(embViewerRadio);
0148     vbox->addWidget(sepViewerRadio);
0149     vbox->addWidget(m_rbGroupSettings);
0150     vbox->addWidget(m_chkAskSave);
0151 
0152     m_autoEmbedBox->setWhatsThis(
0153         i18n("Here you can configure what the Konqueror file manager"
0154              " will do when you click on a file of this type. Konqueror can either display the file in"
0155              " an embedded viewer, or start up a separate application. If set to 'Use settings for G group',"
0156              " the file manager will behave according to the settings of the group G to which this type belongs;"
0157              " for instance, 'image' if the current file type is image/png. Dolphin"
0158              " always shows files in a separate viewer."));
0159 
0160     embedServiceListWidget = new KServiceListWidget(KServiceListWidget::SERVICELIST_SERVICES, secondWidget);
0161     //  embedServiceListWidget->setMinimumHeight( serviceListWidget->sizeHint().height() );
0162     connect(embedServiceListWidget, &KServiceListWidget::changed, this, &FileTypeDetails::changed);
0163     secondLayout->addWidget(embedServiceListWidget);
0164 
0165     m_tabWidget->addTab(firstWidget, i18n("&General"));
0166     m_tabWidget->addTab(secondWidget, i18n("&Embedding"));
0167 }
0168 
0169 void FileTypeDetails::updateRemoveButton()
0170 {
0171     removeExtButton->setEnabled(extensionLB->count() > 0);
0172 }
0173 
0174 void FileTypeDetails::updateIcon(const QString &icon)
0175 {
0176     if (!m_mimeTypeData) {
0177         return;
0178     }
0179 
0180     m_mimeTypeData->setUserSpecifiedIcon(icon);
0181 
0182     if (m_item) {
0183         m_item->setIcon(icon);
0184     }
0185 
0186     Q_EMIT changed(true);
0187 }
0188 
0189 void FileTypeDetails::updateDescription(const QString &desc)
0190 {
0191     if (!m_mimeTypeData) {
0192         return;
0193     }
0194 
0195     m_mimeTypeData->setComment(desc);
0196 
0197     Q_EMIT changed(true);
0198 }
0199 
0200 void FileTypeDetails::addExtension()
0201 {
0202     if (!m_mimeTypeData) {
0203         return;
0204     }
0205 
0206     bool ok;
0207     QString ext = QInputDialog::getText(this, i18n("Add New Extension"), i18n("Extension:"), QLineEdit::Normal, QStringLiteral("*."), &ok);
0208     if (ok) {
0209         extensionLB->addItem(ext);
0210         QStringList patt = m_mimeTypeData->patterns();
0211         patt += ext;
0212         m_mimeTypeData->setPatterns(patt);
0213         updateRemoveButton();
0214         Q_EMIT changed(true);
0215     }
0216 }
0217 
0218 void FileTypeDetails::removeExtension()
0219 {
0220     if (extensionLB->currentRow() == -1) {
0221         return;
0222     }
0223     if (!m_mimeTypeData) {
0224         return;
0225     }
0226     QStringList patt = m_mimeTypeData->patterns();
0227     patt.removeAll(extensionLB->currentItem()->text());
0228     m_mimeTypeData->setPatterns(patt);
0229     delete extensionLB->takeItem(extensionLB->currentRow());
0230     updateRemoveButton();
0231     Q_EMIT changed(true);
0232 }
0233 
0234 void FileTypeDetails::slotAutoEmbedClicked(int button)
0235 {
0236     if (!m_mimeTypeData || (button > 2)) {
0237         return;
0238     }
0239 
0240     m_mimeTypeData->setAutoEmbed((MimeTypeData::AutoEmbed)button);
0241 
0242     updateAskSave();
0243 
0244     Q_EMIT changed(true);
0245 }
0246 
0247 void FileTypeDetails::updateAskSave()
0248 {
0249     if (!m_mimeTypeData) {
0250         return;
0251     }
0252     QMimeDatabase db;
0253 
0254     MimeTypeData::AutoEmbed autoEmbed = m_mimeTypeData->autoEmbed();
0255     if (m_mimeTypeData->isMeta() && autoEmbed == MimeTypeData::UseGroupSetting) {
0256         // Resolve by looking at group (we could cache groups somewhere to avoid the re-parsing?)
0257         autoEmbed = MimeTypeData(m_mimeTypeData->majorType()).autoEmbed();
0258     }
0259 
0260     const QString mimeType = m_mimeTypeData->name();
0261 
0262     QString dontAskAgainName;
0263     if (autoEmbed == MimeTypeData::Yes) { // Embedded
0264         dontAskAgainName = QStringLiteral("askEmbedOrSave") + mimeType;
0265     } else {
0266         dontAskAgainName = QStringLiteral("askSave") + mimeType;
0267     }
0268 
0269     KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("filetypesrc"), KConfig::NoGlobals);
0270     // default value
0271     bool ask = config->group("Notification Messages").readEntry(dontAskAgainName, QString()).isEmpty();
0272     // per-mimetype override if there's one
0273     m_mimeTypeData->getAskSave(ask);
0274 
0275     bool neverAsk = false;
0276 
0277     if (autoEmbed == MimeTypeData::Yes) {
0278         const QMimeType mime = db.mimeTypeForName(mimeType);
0279         if (mime.isValid()) {
0280             // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC
0281             // NOTE: Keep this function in sync with
0282             // kparts/src/browseropenorsavequestion.cpp BrowserOpenOrSaveQuestionPrivate::autoEmbedMimeType
0283 
0284             // Don't ask for:
0285             // - html (even new tabs would ask, due to about:blank!)
0286             // - dirs obviously (though not common over HTTP :),
0287             // - images (reasoning: no need to save, most of the time, because fast to see)
0288             // e.g. postscript is different, because takes longer to read, so
0289             // it's more likely that the user might want to save it.
0290             // - multipart/* ("server push", see kmultipart)
0291             // clang-format off
0292             if (mime.inherits(QStringLiteral("text/html"))
0293                 || mime.inherits(QStringLiteral("application/xml"))
0294                 || mime.inherits(QStringLiteral("inode/directory"))
0295                 || mimeType.startsWith(QLatin1String("image"))
0296                 || mime.inherits(QStringLiteral("multipart/x-mixed-replace"))
0297                 || mime.inherits(QStringLiteral("multipart/replace"))) {
0298                 neverAsk = true;
0299             }
0300             // clang-format on
0301         }
0302     }
0303 
0304     m_chkAskSave->blockSignals(true);
0305     m_chkAskSave->setChecked(ask && !neverAsk);
0306     m_chkAskSave->setEnabled(!neverAsk);
0307     m_chkAskSave->blockSignals(false);
0308 }
0309 
0310 void FileTypeDetails::slotAskSaveToggled(bool askSave)
0311 {
0312     if (!m_mimeTypeData) {
0313         return;
0314     }
0315 
0316     m_mimeTypeData->setAskSave(askSave);
0317     Q_EMIT changed(true);
0318 }
0319 
0320 void FileTypeDetails::setMimeTypeData(MimeTypeData *mimeTypeData, TypesListItem *item)
0321 {
0322     m_mimeTypeData = mimeTypeData;
0323     m_item = item; // can be 0
0324     Q_ASSERT(mimeTypeData);
0325     m_mimeTypeLabel->setText(i18n("File type %1", mimeTypeData->name()));
0326     if (iconButton) {
0327         iconButton->setIcon(mimeTypeData->icon());
0328         iconButton->setToolTip(mimeTypeData->icon());
0329     } else {
0330         iconLabel->setPixmap(QIcon::fromTheme(mimeTypeData->icon()).pixmap(KIconLoader::SizeLarge));
0331     }
0332     description->setText(mimeTypeData->comment());
0333     m_rbGroupSettings->setText(i18n("Use settings for '%1' group", mimeTypeData->majorType()));
0334     extensionLB->clear();
0335     addExtButton->setEnabled(true);
0336     removeExtButton->setEnabled(false);
0337 
0338     serviceListWidget->setMimeTypeData(mimeTypeData);
0339     embedServiceListWidget->setMimeTypeData(mimeTypeData);
0340     m_autoEmbedGroup->button(mimeTypeData->autoEmbed())->setChecked(true);
0341     m_rbGroupSettings->setEnabled(mimeTypeData->canUseGroupSetting());
0342 
0343     extensionLB->addItems(mimeTypeData->patterns());
0344 
0345     updateAskSave();
0346 }
0347 
0348 void FileTypeDetails::enableExtButtons()
0349 {
0350     removeExtButton->setEnabled(true);
0351 }
0352 
0353 void FileTypeDetails::refresh()
0354 {
0355     if (!m_mimeTypeData) {
0356         return;
0357     }
0358 
0359     // Called when ksycoca has been updated -> refresh data, then widgets
0360     m_mimeTypeData->refresh();
0361     setMimeTypeData(m_mimeTypeData, m_item);
0362 }