diff options
Diffstat (limited to 'src')
26 files changed, 768 insertions, 412 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e0dd57679..63de09324 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -246,7 +246,6 @@ set(dolphinstatic_SRCS if(HAVE_BALOO) set(dolphinstatic_SRCS ${dolphinstatic_SRCS} - panels/information/filemetadataconfigurationdialog.cpp panels/information/informationpanel.cpp panels/information/informationpanelcontent.cpp panels/information/pixmapviewer.cpp @@ -384,6 +383,13 @@ install(TARGETS kcm_dolphingeneral DESTINATION ${KDE_INSTALL_PLUGINDIR} ) ########### install files ############### install( PROGRAMS org.kde.dolphin.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) + +install( DIRECTORY DESTINATION "${KDE_INSTALL_FULL_DATAROOTDIR}/kglobalaccel" ) + +install( + CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ${KDE_INSTALL_FULL_APPDIR}/org.kde.dolphin.desktop ${KDE_INSTALL_FULL_DATAROOTDIR}/kglobalaccel/org.kde.dolphin.desktop)" +) + install( FILES settings/dolphin_directoryviewpropertysettings.kcfg settings/dolphin_generalsettings.kcfg settings/dolphin_compactmodesettings.kcfg diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 7224c8ea9..2775d4285 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -928,9 +928,13 @@ void DolphinMainWindow::updateControlMenu() menu->addSeparator(); + // Overwrite Find action to Search action + QAction *searchAction = ac->action(KStandardAction::name(KStandardAction::Find)); + searchAction->setText(i18n("Search...")); + // Add "Edit" actions bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) | - addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Find)), menu) | + addActionToMenu(searchAction, menu) | addActionToMenu(ac->action(KStandardAction::name(KStandardAction::SelectAll)), menu) | addActionToMenu(ac->action(QStringLiteral("invert_selection")), menu); @@ -1140,7 +1144,8 @@ void DolphinMainWindow::setupActions() // due to the long text, the text "Paste" is used: paste->setIconText(i18nc("@action:inmenu Edit", "Paste")); - KStandardAction::find(this, &DolphinMainWindow::find, actionCollection()); + QAction *searchAction = KStandardAction::find(this, &DolphinMainWindow::find, actionCollection()); + searchAction->setText(i18n("Search...")); KStandardAction::selectAll(this, &DolphinMainWindow::selectAll, actionCollection()); diff --git a/src/filterbar/filterbar.cpp b/src/filterbar/filterbar.cpp index b4fef22a8..68da73a71 100644 --- a/src/filterbar/filterbar.cpp +++ b/src/filterbar/filterbar.cpp @@ -47,13 +47,12 @@ FilterBar::FilterBar(QWidget* parent) : m_lockButton->setToolTip(i18nc("@info:tooltip", "Keep Filter When Changing Folders")); connect(m_lockButton, &QToolButton::toggled, this, &FilterBar::slotToggleLockButton); - // Create label - QLabel* filterLabel = new QLabel(i18nc("@label:textbox", "Filter:"), this); // Create filter editor m_filterInput = new QLineEdit(this); m_filterInput->setLayoutDirection(Qt::LeftToRight); m_filterInput->setClearButtonEnabled(true); + m_filterInput->setPlaceholderText(i18n("Filter...")); connect(m_filterInput, &QLineEdit::textChanged, this, &FilterBar::filterChanged); setFocusProxy(m_filterInput); @@ -62,11 +61,8 @@ FilterBar::FilterBar(QWidget* parent) : QHBoxLayout* hLayout = new QHBoxLayout(this); hLayout->setContentsMargins(0, 0, 0, 0); hLayout->addWidget(closeButton); - hLayout->addWidget(filterLabel); hLayout->addWidget(m_filterInput); hLayout->addWidget(m_lockButton); - - filterLabel->setBuddy(m_filterInput); } FilterBar::~FilterBar() diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index fc14c79c1..ebd830f7a 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -2297,38 +2297,40 @@ void KFileItemModel::emitSortProgress(int resolvedCount) const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count) { static const RoleInfoMap rolesInfoMap[] = { - // | role | roleType | role translation | group translation | requires Baloo | requires indexer - { nullptr, NoRole, nullptr, nullptr, nullptr, nullptr, false, false }, - { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), nullptr, nullptr, false, false }, - { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), nullptr, nullptr, false, false }, - { "modificationtime", ModificationTimeRole, I18N_NOOP2_NOSTRIP("@label", "Modified"), nullptr, nullptr, false, false }, - { "creationtime", CreationTimeRole, I18N_NOOP2_NOSTRIP("@label", "Created"), nullptr, nullptr, false, false }, - { "accesstime", AccessTimeRole, I18N_NOOP2_NOSTRIP("@label", "Accessed"), nullptr, nullptr, false, false }, - { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), nullptr, nullptr, false, false }, - { "rating", RatingRole, I18N_NOOP2_NOSTRIP("@label", "Rating"), nullptr, nullptr, true, false }, - { "tags", TagsRole, I18N_NOOP2_NOSTRIP("@label", "Tags"), nullptr, nullptr, true, false }, - { "comment", CommentRole, I18N_NOOP2_NOSTRIP("@label", "Comment"), nullptr, nullptr, true, false }, - { "title", TitleRole, I18N_NOOP2_NOSTRIP("@label", "Title"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, - { "wordCount", WordCountRole, I18N_NOOP2_NOSTRIP("@label", "Word Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, - { "lineCount", LineCountRole, I18N_NOOP2_NOSTRIP("@label", "Line Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, - { "imageDateTime", ImageDateTimeRole, I18N_NOOP2_NOSTRIP("@label", "Date Photographed"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, - { "width", WidthRole, I18N_NOOP2_NOSTRIP("@label", "Width"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, - { "height", HeightRole, I18N_NOOP2_NOSTRIP("@label", "Height"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, - { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, - { "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "genre", GenreRole, I18N_NOOP2_NOSTRIP("@label", "Genre"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "album", AlbumRole, I18N_NOOP2_NOSTRIP("@label", "Album"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "duration", DurationRole, I18N_NOOP2_NOSTRIP("@label", "Duration"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "bitrate", BitrateRole, I18N_NOOP2_NOSTRIP("@label", "Bitrate"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "track", TrackRole, I18N_NOOP2_NOSTRIP("@label", "Track"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "releaseYear", ReleaseYearRole, I18N_NOOP2_NOSTRIP("@label", "Release Year"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, - { "path", PathRole, I18N_NOOP2_NOSTRIP("@label", "Path"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "deletiontime",DeletionTimeRole,I18N_NOOP2_NOSTRIP("@label", "Deletion Time"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "originUrl", OriginUrlRole, I18N_NOOP2_NOSTRIP("@label", "Downloaded From"), I18N_NOOP2_NOSTRIP("@label", "Other"), true, false }, - { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "owner", OwnerRole, I18N_NOOP2_NOSTRIP("@label", "Owner"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, - { "group", GroupRole, I18N_NOOP2_NOSTRIP("@label", "User Group"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + // | role | roleType | role translation | group translation | requires Baloo | requires indexer + { nullptr, NoRole, nullptr, nullptr, nullptr, nullptr, false, false }, + { "text", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), nullptr, nullptr, false, false }, + { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), nullptr, nullptr, false, false }, + { "modificationtime", ModificationTimeRole, I18N_NOOP2_NOSTRIP("@label", "Modified"), nullptr, nullptr, false, false }, + { "creationtime", CreationTimeRole, I18N_NOOP2_NOSTRIP("@label", "Created"), nullptr, nullptr, false, false }, + { "accesstime", AccessTimeRole, I18N_NOOP2_NOSTRIP("@label", "Accessed"), nullptr, nullptr, false, false }, + { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), nullptr, nullptr, false, false }, + { "rating", RatingRole, I18N_NOOP2_NOSTRIP("@label", "Rating"), nullptr, nullptr, true, false }, + { "tags", TagsRole, I18N_NOOP2_NOSTRIP("@label", "Tags"), nullptr, nullptr, true, false }, + { "comment", CommentRole, I18N_NOOP2_NOSTRIP("@label", "Comment"), nullptr, nullptr, true, false }, + { "title", TitleRole, I18N_NOOP2_NOSTRIP("@label", "Title"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, + { "wordCount", WordCountRole, I18N_NOOP2_NOSTRIP("@label", "Word Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, + { "lineCount", LineCountRole, I18N_NOOP2_NOSTRIP("@label", "Line Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, + { "imageDateTime", ImageDateTimeRole, I18N_NOOP2_NOSTRIP("@label", "Date Photographed"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "width", WidthRole, I18N_NOOP2_NOSTRIP("@label", "Width"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "height", HeightRole, I18N_NOOP2_NOSTRIP("@label", "Height"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "genre", GenreRole, I18N_NOOP2_NOSTRIP("@label", "Genre"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "album", AlbumRole, I18N_NOOP2_NOSTRIP("@label", "Album"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "duration", DurationRole, I18N_NOOP2_NOSTRIP("@label", "Duration"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "bitrate", BitrateRole, I18N_NOOP2_NOSTRIP("@label", "Bitrate"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "track", TrackRole, I18N_NOOP2_NOSTRIP("@label", "Track"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "releaseYear", ReleaseYearRole, I18N_NOOP2_NOSTRIP("@label", "Release Year"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, + { "aspectRatio", AspectRatioRole, I18N_NOOP2_NOSTRIP("@label", "Aspect Ratio"), I18N_NOOP2_NOSTRIP("@label", "Video"), true, true }, + { "frameRate", FrameRateRole, I18N_NOOP2_NOSTRIP("@label", "Frame Rate"), I18N_NOOP2_NOSTRIP("@label", "Video"), true, true }, + { "path", PathRole, I18N_NOOP2_NOSTRIP("@label", "Path"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "deletiontime", DeletionTimeRole, I18N_NOOP2_NOSTRIP("@label", "Deletion Time"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "originUrl", OriginUrlRole, I18N_NOOP2_NOSTRIP("@label", "Downloaded From"), I18N_NOOP2_NOSTRIP("@label", "Other"), true, false }, + { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "owner", OwnerRole, I18N_NOOP2_NOSTRIP("@label", "Owner"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "group", GroupRole, I18N_NOOP2_NOSTRIP("@label", "User Group"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, }; count = sizeof(rolesInfoMap) / sizeof(RoleInfoMap); diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index d15cfebc1..0f7926aae 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -288,7 +288,7 @@ private: // User visible roles available with Baloo: CommentRole, TagsRole, RatingRole, WidthRole, HeightRole, ImageDateTimeRole, OrientationRole, WordCountRole, TitleRole, LineCountRole, ArtistRole, GenreRole, AlbumRole, DurationRole, TrackRole, ReleaseYearRole, - BitrateRole, OriginUrlRole, + BitrateRole, OriginUrlRole, AspectRatioRole, FrameRateRole, // Non-visible roles: IsDirRole, IsLinkRole, IsHiddenRole, IsExpandedRole, IsExpandableRole, ExpandedParentsCountRole, // Mandatory last entry: diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index d3dbeb35c..6fb6a5132 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -231,6 +231,9 @@ bool KItemListController::keyPressEvent(QKeyEvent* event) const bool shiftPressed = event->modifiers() & Qt::ShiftModifier; const bool controlPressed = event->modifiers() & Qt::ControlModifier; const bool shiftOrControlPressed = shiftPressed || controlPressed; + const bool navigationPressed = key == Qt::Key_Home || key == Qt::Key_End || + key == Qt::Key_Up || key == Qt::Key_Down || + key == Qt::Key_Left || key == Qt::Key_Right; const int itemCount = m_model->count(); @@ -246,11 +249,8 @@ bool KItemListController::keyPressEvent(QKeyEvent* event) } } - const bool selectSingleItem = m_selectionBehavior != NoSelection && - itemCount == 1 && - (key == Qt::Key_Home || key == Qt::Key_End || - key == Qt::Key_Up || key == Qt::Key_Down || - key == Qt::Key_Left || key == Qt::Key_Right); + const bool selectSingleItem = m_selectionBehavior != NoSelection && itemCount == 1 && navigationPressed; + if (selectSingleItem) { const int current = m_selectionManager->currentItem(); m_selectionManager->setSelected(current); @@ -458,8 +458,12 @@ bool KItemListController::keyPressEvent(QKeyEvent* event) } break; } + } - m_view->scrollToItem(index); + if (navigationPressed) { + if (index < m_view->firstVisibleIndex() || index > m_view->lastVisibleIndex()) { + m_view->scrollToItem(index); + } } return true; } diff --git a/src/kitemviews/private/kbaloorolesprovider.cpp b/src/kitemviews/private/kbaloorolesprovider.cpp index 0eedf1806..469f07915 100644 --- a/src/kitemviews/private/kbaloorolesprovider.cpp +++ b/src/kitemviews/private/kbaloorolesprovider.cpp @@ -120,6 +120,8 @@ KBalooRolesProvider::KBalooRolesProvider() : { "album", "album" }, { "duration", "duration" }, { "bitRate", "bitrate" }, + { "aspectRatio", "aspectRatio" }, + { "frameRate", "frameRate" }, { "releaseYear", "releaseYear" }, { "trackNumber", "track" }, { "originUrl", "originUrl" } diff --git a/src/org.kde.dolphin.desktop b/src/org.kde.dolphin.desktop index e5bfed010..060dd526e 100755 --- a/src/org.kde.dolphin.desktop +++ b/src/org.kde.dolphin.desktop @@ -97,4 +97,5 @@ Terminal=false MimeType=inode/directory; InitialPreference=10 X-DBUS-ServiceName=org.kde.dolphin +X-KDE-Shortcuts=Meta+E StartupWMClass=dolphin diff --git a/src/panels/information/filemetadataconfigurationdialog.cpp b/src/panels/information/filemetadataconfigurationdialog.cpp deleted file mode 100644 index f3ca819b7..000000000 --- a/src/panels/information/filemetadataconfigurationdialog.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 by Peter Penz <[email protected]> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ - -#include "filemetadataconfigurationdialog.h" - -#include <Baloo/FileMetaDataConfigWidget> -#include <KConfigGroup> -#include <KLocalizedString> -#include <KSharedConfig> -#include <KWindowConfig> - -#include <QDialogButtonBox> -#include <QLabel> -#include <QPushButton> -#include <QVBoxLayout> - -FileMetaDataConfigurationDialog::FileMetaDataConfigurationDialog(QWidget* parent) : - QDialog(parent), - m_descriptionLabel(nullptr), - m_configWidget(nullptr) - -{ - setWindowTitle(i18nc("@title:window", "Configure Shown Data")); - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); - QVBoxLayout *mainLayout = new QVBoxLayout; - setLayout(mainLayout); - QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); - okButton->setDefault(true); - okButton->setShortcut(Qt::CTRL + Qt::Key_Return); - connect(buttonBox, &QDialogButtonBox::accepted, this, &FileMetaDataConfigurationDialog::slotAccepted); - connect(buttonBox, &QDialogButtonBox::rejected, this, &FileMetaDataConfigurationDialog::reject); - buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); - - m_descriptionLabel = new QLabel(i18nc("@label::textbox", - "Select which data should " - "be shown:"), this); - m_descriptionLabel->setWordWrap(true); - - m_configWidget = new Baloo::FileMetaDataConfigWidget(this); - - QWidget* mainWidget = new QWidget(this); - QVBoxLayout* topLayout = new QVBoxLayout(mainWidget); - topLayout->addWidget(m_descriptionLabel); - topLayout->addWidget(m_configWidget); - mainLayout->addWidget(mainWidget); - mainLayout->addWidget(buttonBox); - - - const KConfigGroup dialogConfig(KSharedConfig::openConfig(QStringLiteral("dolphinrc")), - "FileMetaDataConfigurationDialog"); - KWindowConfig::restoreWindowSize(windowHandle(), dialogConfig); -} - -FileMetaDataConfigurationDialog::~FileMetaDataConfigurationDialog() -{ - KConfigGroup dialogConfig(KSharedConfig::openConfig(QStringLiteral("dolphinrc")), - "FileMetaDataConfigurationDialog"); - KWindowConfig::saveWindowSize(windowHandle(), dialogConfig); -} - -void FileMetaDataConfigurationDialog::setItems(const KFileItemList& items) -{ - m_configWidget->setItems(items); -} - -KFileItemList FileMetaDataConfigurationDialog::items() const -{ - return m_configWidget->items(); -} - -void FileMetaDataConfigurationDialog::slotAccepted() -{ - m_configWidget->save(); - accept(); -} - -void FileMetaDataConfigurationDialog::setDescription(const QString& description) -{ - m_descriptionLabel->setText(description); -} - -QString FileMetaDataConfigurationDialog::description() const -{ - return m_descriptionLabel->text(); -} - diff --git a/src/panels/information/filemetadataconfigurationdialog.h b/src/panels/information/filemetadataconfigurationdialog.h deleted file mode 100644 index 04357783c..000000000 --- a/src/panels/information/filemetadataconfigurationdialog.h +++ /dev/null @@ -1,76 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 by Peter Penz <[email protected]> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ - -#ifndef FILEMETADATACONFIGURATIONDIALOG_H -#define FILEMETADATACONFIGURATIONDIALOG_H - -#include <QDialog> - -#include <KFileItem> -#include <config-baloo.h> -#ifndef HAVE_BALOO -class KFileMetaDataConfigurationWidget; -#else -namespace Baloo { - class FileMetaDataConfigWidget; -} -#endif - -class QLabel; - -/** - * @brief Dialog which allows to configure which meta data should be shown - * in the KFileMetaDataWidget. - */ -class FileMetaDataConfigurationDialog : public QDialog -{ - Q_OBJECT - -public: - explicit FileMetaDataConfigurationDialog(QWidget* parent = nullptr); - ~FileMetaDataConfigurationDialog() override; - - /** - * Sets the items, for which the visibility of the meta data should - * be configured. Note that the visibility of the meta data is not - * bound to the items itself, the items are only used to determine - * which meta data should be configurable. For example when a JPEG image - * is set as item, it will be configurable which EXIF data should be - * shown. If an audio file is set as item, it will be configurable - * whether the artist, album name, ... should be shown. - */ - void setItems(const KFileItemList& items); - KFileItemList items() const; - - /** - * Sets the description that is shown above the list - * of meta data. Per default the translated text for - * "Select which data should be shown." is set. - */ - void setDescription(const QString& description); - QString description() const; - -protected slots: - void slotAccepted(); -private: - QLabel* m_descriptionLabel; - Baloo::FileMetaDataConfigWidget* m_configWidget; -}; - -#endif diff --git a/src/panels/information/informationpanel.cpp b/src/panels/information/informationpanel.cpp index cd8b6b38d..9a0358df0 100644 --- a/src/panels/information/informationpanel.cpp +++ b/src/panels/information/informationpanel.cpp @@ -36,7 +36,6 @@ #include <QMenu> #include "dolphin_informationpanelsettings.h" -#include "filemetadataconfigurationdialog.h" InformationPanel::InformationPanel(QWidget* parent) : Panel(parent), @@ -168,7 +167,8 @@ void InformationPanel::contextMenuEvent(QContextMenuEvent* event) Panel::contextMenuEvent(event); } -void InformationPanel::showContextMenu(const QPoint &pos) { +void InformationPanel::showContextMenu(const QPoint &pos) +{ QMenu popup(this); QAction* previewAction = popup.addAction(i18nc("@action:inmenu", "Preview")); @@ -178,6 +178,9 @@ void InformationPanel::showContextMenu(const QPoint &pos) { QAction* configureAction = popup.addAction(i18nc("@action:inmenu", "Configure...")); configureAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); + if (m_inConfigurationMode) { + configureAction->setEnabled(false); + } QAction* dateformatAction = popup.addAction(i18nc("@action:inmenu", "Condensed Date")); dateformatAction->setIcon(QIcon::fromTheme(QStringLiteral("change-date-symbolic"))); @@ -185,7 +188,8 @@ void InformationPanel::showContextMenu(const QPoint &pos) { dateformatAction->setChecked(InformationPanelSettings::dateFormat() == static_cast<int>(Baloo::DateFormats::ShortFormat)); popup.addSeparator(); - foreach (QAction* action, customContextMenuActions()) { + const auto actions = customContextMenuActions(); + for (QAction *action : actions) { popup.addAction(action); } @@ -201,13 +205,8 @@ void InformationPanel::showContextMenu(const QPoint &pos) { InformationPanelSettings::setPreviewsShown(isChecked); m_content->refreshPreview(); } else if (action == configureAction) { - FileMetaDataConfigurationDialog* dialog = new FileMetaDataConfigurationDialog(this); - dialog->setDescription(i18nc("@label::textbox", - "Select which data should be shown in the information panel:")); - dialog->setItems(m_content->items()); - dialog->setAttribute(Qt::WA_DeleteOnClose); - dialog->show(); - connect(dialog, &FileMetaDataConfigurationDialog::destroyed, m_content, &InformationPanelContent::refreshMetaData); + m_inConfigurationMode = true; + m_content->configureShownProperties(); } if (action == dateformatAction) { int dateFormat = static_cast<int>(isChecked ? Baloo::DateFormats::ShortFormat : Baloo::DateFormats::LongFormat); @@ -311,7 +310,7 @@ void InformationPanel::slotFilesAdded(const QString& directory) void InformationPanel::slotFilesChanged(const QStringList& files) { - foreach (const QString& fileName, files) { + for (const QString& fileName : files) { if (m_shownUrl == QUrl::fromLocalFile(fileName)) { showItemInfo(); break; @@ -321,7 +320,7 @@ void InformationPanel::slotFilesChanged(const QStringList& files) void InformationPanel::slotFilesRemoved(const QStringList& files) { - foreach (const QString& fileName, files) { + for (const QString& fileName : files) { if (m_shownUrl == QUrl::fromLocalFile(fileName)) { // the currently shown item has been removed, show // the parent directory as fallback @@ -410,6 +409,7 @@ void InformationPanel::init() m_content = new InformationPanelContent(this); connect(m_content, &InformationPanelContent::urlActivated, this, &InformationPanel::urlActivated); + connect(m_content, &InformationPanelContent::configurationFinished, this, [this]() { m_inConfigurationMode = false; }); QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); diff --git a/src/panels/information/informationpanel.h b/src/panels/information/informationpanel.h index f63af1e53..321827c5b 100644 --- a/src/panels/information/informationpanel.h +++ b/src/panels/information/informationpanel.h @@ -161,6 +161,7 @@ private: KIO::Job* m_folderStatJob; InformationPanelContent* m_content; + bool m_inConfigurationMode = false; }; #endif // INFORMATIONPANEL_H diff --git a/src/panels/information/informationpanelcontent.cpp b/src/panels/information/informationpanelcontent.cpp index 0eaa125f1..5b7dbbfe9 100644 --- a/src/panels/information/informationpanelcontent.cpp +++ b/src/panels/information/informationpanelcontent.cpp @@ -40,6 +40,7 @@ #include <Phonon/MediaObject> #include <QLabel> +#include <QDialogButtonBox> #include <QScrollArea> #include <QTextLayout> #include <QTimer> @@ -107,18 +108,31 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) : m_metaDataWidget->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); m_metaDataWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); - // Encapsulate the MetaDataWidget inside a container that has a dummy widget - // at the bottom. This prevents that the meta data widget gets vertically stretched - // in the case where the height of m_metaDataArea > m_metaDataWidget. - QWidget* metaDataWidgetContainer = new QWidget(parent); - QVBoxLayout* containerLayout = new QVBoxLayout(metaDataWidgetContainer); - containerLayout->setContentsMargins(0, 0, 0, 0); - containerLayout->setSpacing(0); - containerLayout->addWidget(m_metaDataWidget); - containerLayout->addStretch(); + // Configuration + m_configureLabel = new QLabel(i18nc("@label::textbox", + "Select which data should be shown:"), this); + m_configureLabel->setWordWrap(true); + m_configureLabel->setVisible(false); + + m_configureButtons = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel); + m_configureButtons->setVisible(false); + connect(m_configureButtons, &QDialogButtonBox::accepted, this, [this]() { + m_metaDataWidget->setConfigurationMode(Baloo::ConfigurationMode::Accept); + m_configureButtons->setVisible(false); + m_configureLabel->setVisible(false); + emit configurationFinished(); + } + ); + connect(m_configureButtons, &QDialogButtonBox::rejected, this, [this]() { + m_metaDataWidget->setConfigurationMode(Baloo::ConfigurationMode::Cancel); + m_configureButtons->setVisible(false); + m_configureLabel->setVisible(false); + emit configurationFinished(); + } + ); m_metaDataArea = new QScrollArea(parent); - m_metaDataArea->setWidget(metaDataWidgetContainer); + m_metaDataArea->setWidget(m_metaDataWidget); m_metaDataArea->setWidgetResizable(true); m_metaDataArea->setFrameShape(QFrame::NoFrame); @@ -129,7 +143,9 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) : layout->addWidget(m_phononWidget); layout->addWidget(m_nameLabel); layout->addWidget(new KSeparator()); + layout->addWidget(m_configureLabel); layout->addWidget(m_metaDataArea); + layout->addWidget(m_configureButtons); m_placesItemModel = new PlacesItemModel(this); } @@ -147,7 +163,8 @@ void InformationPanelContent::showItem(const KFileItem& item) refreshMetaData(); } -void InformationPanelContent::refreshPreview() { +void InformationPanelContent::refreshPreview() +{ // If there is a preview job, kill it to prevent that we have jobs for // multiple items running, and thus a race condition (bug 250787). if (m_previewJob) { @@ -209,12 +226,18 @@ void InformationPanelContent::refreshPreview() { } } -void InformationPanelContent::refreshMetaData() { - if (m_metaDataWidget) { - m_metaDataWidget->setDateFormat(static_cast<Baloo::DateFormats>(InformationPanelSettings::dateFormat())); - m_metaDataWidget->show(); - m_metaDataWidget->setItems(KFileItemList() << m_item); - } +void InformationPanelContent::configureShownProperties() +{ + m_configureLabel->setVisible(true); + m_configureButtons->setVisible(true); + m_metaDataWidget->setConfigurationMode(Baloo::ConfigurationMode::ReStart); +} + +void InformationPanelContent::refreshMetaData() +{ + m_metaDataWidget->setDateFormat(static_cast<Baloo::DateFormats>(InformationPanelSettings::dateFormat())); + m_metaDataWidget->show(); + m_metaDataWidget->setItems(KFileItemList() << m_item); } void InformationPanelContent::showItems(const KFileItemList& items) @@ -230,9 +253,7 @@ void InformationPanelContent::showItems(const KFileItemList& items) ); setNameLabelText(i18ncp("@label", "%1 item selected", "%1 items selected", items.count())); - if (m_metaDataWidget) { - m_metaDataWidget->setItems(items); - } + m_metaDataWidget->setItems(items); m_phononWidget->hide(); @@ -297,7 +318,8 @@ void InformationPanelContent::markOutdatedPreview() m_preview->setPixmap(disabledPixmap); } -KFileItemList InformationPanelContent::items() { +KFileItemList InformationPanelContent::items() +{ return m_metaDataWidget->items(); } @@ -349,9 +371,7 @@ void InformationPanelContent::adjustWidgetSizes(int width) // The metadata widget also contains a text widget which may return // a large preferred width. - if (m_metaDataWidget) { - m_metaDataWidget->setMaximumWidth(maxWidth); - } + m_metaDataWidget->setMaximumWidth(maxWidth); // try to increase the preview as large as possible m_preview->setSizeHint(QSize(maxWidth, maxWidth)); diff --git a/src/panels/information/informationpanelcontent.h b/src/panels/information/informationpanelcontent.h index 83fb3d80b..43410ddfa 100644 --- a/src/panels/information/informationpanelcontent.h +++ b/src/panels/information/informationpanelcontent.h @@ -32,6 +32,7 @@ class PhononWidget; class PixmapViewer; class PlacesItemModel; class QPixmap; +class QDialogButtonBox; class QString; class QLabel; class QScrollArea; @@ -40,13 +41,9 @@ namespace KIO { class PreviewJob; } -#ifndef HAVE_BALOO -class KFileMetaDataWidget; -#else namespace Baloo { class FileMetaDataWidget; } -#endif /** * @brief Manages the widgets that display the meta information @@ -79,8 +76,14 @@ public: */ void refreshPreview(); + /** + * Switch the metadatawidget into configuration mode + */ + void configureShownProperties(); + signals: void urlActivated( const QUrl& url ); + void configurationFinished(); public slots: /** @@ -138,12 +141,10 @@ private: PixmapViewer* m_preview; PhononWidget* m_phononWidget; QLabel* m_nameLabel; -#ifndef HAVE_BALOO - KFileMetaDataWidget* m_metaDataWidget; -#else Baloo::FileMetaDataWidget* m_metaDataWidget; -#endif QScrollArea* m_metaDataArea; + QLabel* m_configureLabel; + QDialogButtonBox* m_configureButtons; PlacesItemModel* m_placesItemModel; }; diff --git a/src/search/dolphinsearchbox.cpp b/src/search/dolphinsearchbox.cpp index 9c41db9c5..d846e5b6c 100644 --- a/src/search/dolphinsearchbox.cpp +++ b/src/search/dolphinsearchbox.cpp @@ -49,7 +49,6 @@ DolphinSearchBox::DolphinSearchBox(QWidget* parent) : m_startedSearching(false), m_active(true), m_topLayout(nullptr), - m_searchLabel(nullptr), m_searchInput(nullptr), m_saveSearchAction(nullptr), m_optionsScrollArea(nullptr), @@ -87,22 +86,22 @@ void DolphinSearchBox::setSearchPath(const QUrl& url) QFontMetrics metrics(m_fromHereButton->font()); const int maxWidth = metrics.height() * 8; - QString location = url.fileName(); + const QUrl cleanedUrl = url.adjusted(QUrl::RemoveUserInfo | QUrl::StripTrailingSlash); + QString location = cleanedUrl.fileName(); if (location.isEmpty()) { - if (url.isLocalFile()) { - location = QStringLiteral("/"); - } else { - location = url.scheme() + QLatin1String(" - ") + url.host(); - } + location = cleanedUrl.toString(QUrl::PreferLocalFile); + } + if (m_fromHereButton->isChecked() && cleanedUrl.path() == QDir::homePath()) { + m_fromHereButton->setChecked(false); + m_everywhereButton->setChecked(true); + } else { + m_fromHereButton->setChecked(true); + m_everywhereButton->setChecked(false); } const QString elidedLocation = metrics.elidedText(location, Qt::ElideMiddle, maxWidth); m_fromHereButton->setText(i18nc("action:button", "From Here (%1)", elidedLocation)); - - const bool showSearchFromButtons = url.isLocalFile(); - m_separator->setVisible(showSearchFromButtons); - m_fromHereButton->setVisible(showSearchFromButtons); - m_everywhereButton->setVisible(showSearchFromButtons); + m_fromHereButton->setToolTip(i18nc("action:button", "Limit search to '%1' and its subfolders", cleanedUrl.toString(QUrl::PreferLocalFile))); bool hasFacetsSupport = false; #ifdef HAVE_BALOO @@ -138,9 +137,6 @@ QUrl DolphinSearchBox::urlForSearching() const QString encodedUrl; if (m_everywhereButton->isChecked()) { - // It is very unlikely, that the majority of Dolphins target users - // mean "the whole harddisk" instead of "my home folder" when - // selecting the "Everywhere" button. encodedUrl = QDir::homePath(); } else { encodedUrl = m_searchPath.url(); @@ -358,11 +354,9 @@ void DolphinSearchBox::init() closeButton->setToolTip(i18nc("@info:tooltip", "Quit searching")); connect(closeButton, &QToolButton::clicked, this, &DolphinSearchBox::emitCloseRequest); - // Create search label - m_searchLabel = new QLabel(this); - // Create search box m_searchInput = new QLineEdit(this); + m_searchInput->setPlaceholderText(i18n("Search...")); m_searchInput->installEventFilter(this); m_searchInput->setClearButtonEnabled(true); m_searchInput->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); @@ -384,7 +378,6 @@ void DolphinSearchBox::init() QHBoxLayout* searchInputLayout = new QHBoxLayout(); searchInputLayout->setContentsMargins(0, 0, 0, 0); searchInputLayout->addWidget(closeButton); - searchInputLayout->addWidget(m_searchLabel); searchInputLayout->addWidget(m_searchInput); // Create "Filename" and "Content" button @@ -402,13 +395,16 @@ void DolphinSearchBox::init() m_separator = new KSeparator(Qt::Vertical, this); - // Create "From Here" and "Everywhere"button + // Create "From Here" and "Your files" buttons m_fromHereButton = new QToolButton(this); m_fromHereButton->setText(i18nc("action:button", "From Here")); initButton(m_fromHereButton); m_everywhereButton = new QToolButton(this); - m_everywhereButton->setText(i18nc("action:button", "Everywhere")); + m_everywhereButton->setText(i18nc("action:button", "Your files")); + m_everywhereButton->setToolTip(i18nc("action:button", "Search in your home directory")); + m_everywhereButton->setIcon(QIcon::fromTheme(QStringLiteral("user-home"))); + m_everywhereButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); initButton(m_everywhereButton); QButtonGroup* searchLocationGroup = new QButtonGroup(this); diff --git a/src/search/dolphinsearchbox.h b/src/search/dolphinsearchbox.h index c138cfe7f..bb71049c7 100644 --- a/src/search/dolphinsearchbox.h +++ b/src/search/dolphinsearchbox.h @@ -166,7 +166,6 @@ private: QVBoxLayout* m_topLayout; - QLabel* m_searchLabel; QLineEdit* m_searchInput; QAction* m_saveSearchAction; QScrollArea* m_optionsScrollArea; diff --git a/src/settings/dolphinsettingsdialog.cpp b/src/settings/dolphinsettingsdialog.cpp index 6bddb861f..f4da53c9d 100644 --- a/src/settings/dolphinsettingsdialog.cpp +++ b/src/settings/dolphinsettingsdialog.cpp @@ -32,12 +32,14 @@ #include <KAuthorized> #include <KLocalizedString> #include <KWindowConfig> +#include <KMessageBox> #include <QPushButton> DolphinSettingsDialog::DolphinSettingsDialog(const QUrl& url, QWidget* parent) : KPageDialog(parent), - m_pages() + m_pages(), + m_unsavedChanges(false) { const QSize minSize = minimumSize(); @@ -121,6 +123,7 @@ DolphinSettingsDialog::~DolphinSettingsDialog() void DolphinSettingsDialog::enableApply() { buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(true); + m_unsavedChanges = true; } void DolphinSettingsDialog::applySettings() @@ -139,6 +142,7 @@ void DolphinSettingsDialog::applySettings() settings->save(); } buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(false); + m_unsavedChanges = false; } void DolphinSettingsDialog::restoreDefaults() @@ -148,6 +152,35 @@ void DolphinSettingsDialog::restoreDefaults() } } +void DolphinSettingsDialog::closeEvent(QCloseEvent* event) +{ + if (!m_unsavedChanges) { + event->accept(); + return; + } + + const auto response = KMessageBox::warningYesNoCancel(this, + i18n("You have unsaved changes. Do you want to apply the changes or discard them?"), + i18n("Warning"), + KStandardGuiItem::save(), + KStandardGuiItem::discard(), + KStandardGuiItem::cancel()); + switch (response) { + case KMessageBox::Yes: + applySettings(); + Q_FALLTHROUGH(); + case KMessageBox::No: + event->accept(); + break; + case KMessageBox::Cancel: + event->ignore(); + break; + default: + break; + } +} + + SettingsPageBase *DolphinSettingsDialog::createTrashSettingsPage(QWidget *parent) { if (!KAuthorized::authorizeControlModule(QStringLiteral("kcmtrash.desktop"))) { diff --git a/src/settings/dolphinsettingsdialog.h b/src/settings/dolphinsettingsdialog.h index 4c8768fde..85871b12d 100644 --- a/src/settings/dolphinsettingsdialog.h +++ b/src/settings/dolphinsettingsdialog.h @@ -48,10 +48,14 @@ private slots: void applySettings(); void restoreDefaults(); +protected: + void closeEvent(QCloseEvent* event) override; + private: static SettingsPageBase *createTrashSettingsPage(QWidget *parent); QList<SettingsPageBase*> m_pages; + bool m_unsavedChanges; }; #endif diff --git a/src/settings/services/servicemenudeinstallation b/src/settings/services/servicemenudeinstallation index 5e4234262..9e090e2cd 100755 --- a/src/settings/services/servicemenudeinstallation +++ b/src/settings/services/servicemenudeinstallation @@ -1,37 +1,72 @@ #!/usr/bin/env ruby + +# Copyright (C) 2009 Jonathan Schmidt-Dominé <[email protected]> +# Copyright (C) 2019 Harald Sitter <[email protected]> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + require 'fileutils' -archive = ARGV[0] -if archive[(archive.length - 8)..(archive.length)] == ".desktop" - FileUtils.rm(`qtpaths --writable-path GenericDataLocation`.strip! + "/kservices5/ServiceMenus/" + File.basename(archive)) - exit(0) + +ARCHIVE = ARGV[0] + +# @param log_msg [String] error that gets logged to CLI +def fail(log_msg: nil) + # FIXME: this is not translated... + msg = 'Dolphin service menu installation failed' + warn log_msg if log_msg + system('kdialog', '--passivepopup', msg, '15') + abort end -dir = archive + "-dir" -# try: deinstall.sh -# try: deinstall -# try: installKDE4.sh -# try: installKDE4 -# try: install.sh -# try: install -while true - dd = Dir.new(dir) - break if dd.count != 3 - odir = dir - for entry in dd - dir += "/" + entry if entry != "." && entry != ".." - end - if !File.directory? dir - dir = odir - break - end + +if ARCHIVE.end_with?('.desktop') + data_location = `qtpaths --writable-path GenericDataLocation`.strip + unless $?.success? + fail(log_msg: "Could not get GenericDataLocation #{data_location}") + end + FileUtils.rm("#{data_location}/kservices5/ServiceMenus/#{File.basename(ARCHIVE)}") + exit(0) end -Dir.chdir(dir) -def fail() - system("kdialog --passivepopup \"Deinstallation failed\" 15") - exit(-1) +dir = "#{ARCHIVE}-dir" + +deinstaller = nil +%w[deinstall.sh deinstall].find do |script| + deinstaller = Dir.glob("#{dir}/**/#{script}")[0] end -if !((File.exist?(file = "./deinstall.sh") || File.exist?(file = "./deinstall")) && system(file)) - fail() if !File.exist?(file = "./installKDE4.sh") && !File.exist?(file = "./installKDE4") && !File.exist?(file = "./install.sh") && !File.exist?(file = "./install") -File.new(file).chmod(0700) - fail() if !system(file + " --remove") && !system(file + " --delete") && !system(file + " --uninstall") && !system(file + " --deinstall") + +installer = nil +%w[install-it.sh install-it installKDE4.sh installKDE4 install.sh install].find do |script| + installer = Dir.glob("#{dir}/**/#{script}")[0] +end + +Dir.chdir(dir) do + deinstalled = false + + [deinstaller, installer].uniq.compact.each { |f| File.chmod(0o700, f) } + + if deinstaller + puts "[servicemenudeinstallation]: Trying to run deinstaller #{deinstaller}" + deinstalled = system(deinstaller) + elsif installer + puts "[servicemenudeinstallation]: Trying to run installer #{installer}" + %w[--remove --delete --uninstall --deinstall].any? do |arg| + deinstalled = system(installer, arg) + end + end + + fail unless deinstalled end + FileUtils.rm_r(dir) diff --git a/src/settings/services/servicemenuinstallation b/src/settings/services/servicemenuinstallation index 60b699bb6..e2d42bfbf 100755 --- a/src/settings/services/servicemenuinstallation +++ b/src/settings/services/servicemenuinstallation @@ -1,88 +1,136 @@ #!/usr/bin/env ruby -require 'pathname' + +# Copyright (C) 2009 Jonathan Schmidt-Dominé <[email protected]> +# Copyright (C) 2019 Harald Sitter <[email protected]> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + require 'fileutils' -archive = ARGV[0] -$servicedir = `qtpaths --writable-path GenericDataLocation`.strip! + "/kservices5/ServiceMenus/" -FileUtils.mkdir_p($servicedir) if !File.exist?($servicedir) -if archive[(archive.length - 8)..(archive.length - 1)] == ".desktop" - puts "Single-File Service-Menu" - puts archive - puts $servicedir - FileUtils.cp(archive, $servicedir); - exit(0) + +ARCHIVE_UNCOMPRESSORS = { + 'application/x-tar' => :"tar -xf %s -C %s", + 'application/tar' => :"tar -xf %s -C %s", + 'application/x-gzip' => :"tar -zxf %s -C %s", + 'application/gzip' => :"tar -zxf %s -C %s", + 'application/x-gzip-compressed-tar' => :"tar -zxf %s -C %s", + 'application/gzip-compressed-tar' => :"tar -zxf %s -C %s", + 'application/x-gzip-compressed' => :"tar -zxf %s -C %s", + 'application/gzip-compressed' => :"tar -zxf %s -C %s", + 'application/bzip' => :"tar -jxf %s -C %s", + 'application/bzip2' => :"tar -jxf %s -C %s", + 'application/x-bzip' => :"tar -jxf %s -C %s", + 'application/x-bzip2' => :"tar -jxf %s -C %s", + 'application/bzip-compressed' => :"tar -jxf %s -C %s", + 'application/bzip2-compressed' => :"tar -jxf %s -C %s", + 'application/x-bzip-compressed' => :"tar -jxf %s -C %s", + 'application/x-bzip2-compressed' => :"tar -jxf %s -C %s", + 'application/bzip-compressed-tar' => :"tar -jxf %s -C %s", + 'application/bzip2-compressed-tar' => :"tar -jxf %s -C %s", + 'application/x-bzip-compressed-tar' => :"tar -jxf %s -C %s", + 'application/x-bzip2-compressed-tar' => :"tar -jxf %s -C %s", + 'application/zip' => :"unzip %s -d %s", + 'application/x-zip' => :"unzip %s -d %s", + 'application/x-zip-compressed' => :"unzip %s -d %s", + 'multipart/x-zip' => :"unzip %s -d %s", + 'application/tgz' => :"tar -zxf %s -C %s", + 'application/x-compressed-gtar' => :"tar -zxf %s -C %s", + 'file/tgz' => :"tar -zxf %s -C %s", + 'multipart/x-tar-gz' => :"tar -zxf %s -C %s", + 'application/x-gunzip' => :"tar -zxf %s -C %s", + 'application/gzipped' => :"tar -zxf %s -C %s", + 'gzip/document' => :"tar -zxf %s -C %s", + 'application/x-bz2 ' => :"tar -jxf %s -C %s", + 'application/x-gtar' => :"tar -xf %s -C %s", + 'multipart/x-tar' => :"tar -xf %s -C %s" +} + +ARCHIVE = ARGV[0] + +# @param log_msg [String] error that gets logged to CLI +def fail(log_msg: nil) + # FIXME: this is not translated... + msg = 'Dolphin service menu installation failed' + warn log_msg if log_msg + system('kdialog', '--passivepopup', msg, '15') + abort end -def mimeType(filename) - IO.popen("file --mime-type -b " + filename).gets().strip!() + +def mime_type(filename) + ret = `xdg-mime query filetype #{filename}`.strip + return ret if $?.success? + + warn 'Failed to xdg-mime' + fail(log_msg: "Failed to xdg-mime #{filename}: #{ret}") end -$archivetypes = { "application/x-tar" => :"tar -xf %s -C %s", - "application/tar" => :"tar -xf %s -C %s", - "application/x-gzip" => :"tar -zxf %s -C %s", - "application/gzip" => :"tar -zxf %s -C %s", - "application/x-gzip-compressed-tar" => :"tar -zxf %s -C %s", - "application/gzip-compressed-tar" => :"tar -zxf %s -C %s", - "application/x-gzip-compressed" => :"tar -zxf %s -C %s", - "application/gzip-compressed" => :"tar -zxf %s -C %s", - "application/bzip" => :"tar -jxf %s -C %s", - "application/bzip2" => :"tar -jxf %s -C %s", - "application/x-bzip" => :"tar -jxf %s -C %s", - "application/x-bzip2" => :"tar -jxf %s -C %s", - "application/bzip-compressed" => :"tar -jxf %s -C %s", - "application/bzip2-compressed" => :"tar -jxf %s -C %s", - "application/x-bzip-compressed" => :"tar -jxf %s -C %s", - "application/x-bzip2-compressed" => :"tar -jxf %s -C %s", - "application/bzip-compressed-tar" => :"tar -jxf %s -C %s", - "application/bzip2-compressed-tar" => :"tar -jxf %s -C %s", - "application/x-bzip-compressed-tar" => :"tar -jxf %s -C %s", - "application/x-bzip2-compressed-tar" => :"tar -jxf %s -C %s", - "application/zip" => :"unzip %s -d %s", - "application/x-zip" => :"unzip %s -d %s", - "application/x-zip-compressed" => :"unzip %s -d %s", - "multipart/x-zip" => :"unzip %s -d %s", - "application/tgz" => :"tar -zxf %s -C %s", - "application/x-compressed-gtar" => :"tar -zxf %s -C %s", - "file/tgz" => :"tar -zxf %s -C %s", - "multipart/x-tar-gz" => :"tar -zxf %s -C %s", - "application/x-gunzip" => :"tar -zxf %s -C %s", - "application/gzipped" => :"tar -zxf %s -C %s", - "gzip/document" => :"tar -zxf %s -C %s", - "application/x-bz2 " => :"tar -jxf %s -C %s", - "application/x-gtar" => :"tar -xf %s -C %s", - "multipart/x-tar" => :"tar -xf %s -C %s" -} + def uncompress(filename, output) - system(sprintf($archivetypes[mimeType(filename)].to_s, filename, output)) + uncompressor = ARCHIVE_UNCOMPRESSORS.fetch(mime_type(filename)).to_s + system(format(uncompressor, filename, output)) +rescue KeyError => e + # If a mimetype doesn't have an uncompressor mapped we'll get a keyerror. + # we'll log the error but visually report the failure. + fail(log_msg: "Unmapped compression format #{filename}; #{e.message}") end -dir = archive + "-dir" -if File.exist?(dir) - FileUtils.rm_r(dir) + +data_location = `qtpaths --writable-path GenericDataLocation`.strip +unless $?.success? + fail(log_msg: "Could not get GenericDataLocation #{data_location}") end +servicedir = "#{data_location}/kservices5/ServiceMenus/" + +FileUtils.mkdir_p(servicedir) unless File.exist?(servicedir) +if ARCHIVE.end_with?('.desktop') + puts 'Single-File Service-Menu' + puts ARCHIVE + puts servicedir + FileUtils.cp(ARCHIVE, servicedir) + exit +end + +dir = "#{ARCHIVE}-dir" + +FileUtils.rm_r(dir) if File.exist?(dir) FileUtils.mkdir(dir) -exit(-1) if !uncompress(archive, dir) -# try: install-it.sh -# try: install-it -# try: installKDE4.sh -# try: installKDE4 -# try: install.sh -# try: install -while true - dd = Dir.new(dir) - break if dd.count != 3 - odir = dir - for entry in dd - dir += "/" + entry if entry != "." && entry != ".." - end - if !File.directory? dir - dir = odir - break - end + +fail(log_msg: 'uncompress failed') unless uncompress(ARCHIVE, dir) + +install_it = nil +%w[install-it.sh install-it].find do |script| + install_it = Dir.glob("#{dir}/**/#{script}")[0] +end + +installer = nil +%w[installKDE4.sh installKDE4 install.sh install].find do |script| + installer = Dir.glob("#{dir}/**/#{script}")[0] end -Dir.chdir(dir) -def fail() - system("kdialog --passivepopup \"Installation failed\" 15") - exit(-1) + +Dir.chdir(dir) do + installed = false + + [install_it, installer].uniq.compact.each { |f| File.chmod(0o700, f) } + + if install_it + puts "[servicemenuinstallation]: Trying to run install_it #{install_it}" + installed = system(install_it) + elsif installer + puts "[servicemenuinstallation]: Trying to run installer #{installer}" + %w[--local --local-install --install].any? do |arg| + installed = system(installer, arg) + end + end + + fail unless installed end -if !((File.exist?(file = "./install-it.sh") || File.exist?(file = "./install-it")) && system(file)) - fail() if !File.exist?(file = "./installKDE4.sh") && !File.exist?(file = "./installKDE4") && !File.exist?(file = "./install.sh") && !File.exist?(file = "./install") - File.new(file).chmod(0700) - fail() if !system(file + " --local") && !system(file + "--local-install") && !system(file + " --install") -end diff --git a/src/settings/services/test/service_menu_deinstallation_test.rb b/src/settings/services/test/service_menu_deinstallation_test.rb new file mode 100644 index 000000000..2b3514a65 --- /dev/null +++ b/src/settings/services/test/service_menu_deinstallation_test.rb @@ -0,0 +1,121 @@ +#!/usr/bin/env ruby + +# Copyright (C) 2019 Harald Sitter <[email protected]> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +require_relative 'test_helper' + +require 'tmpdir' + +class ServiceMenuDeinstallationTest < Test::Unit::TestCase + def setup + @tmpdir = Dir.mktmpdir("dolphintest-#{self.class.to_s.tr(':', '_')}") + @pwdir = Dir.pwd + Dir.chdir(@tmpdir) + + ENV['XDG_DATA_HOME'] = File.join(@tmpdir, 'data') + end + + def teardown + Dir.chdir(@pwdir) + FileUtils.rm_rf(@tmpdir) + + ENV.delete('XDG_DATA_HOME') + end + + def test_run_deinstall + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + archive_base = "#{service_dir}/foo.zip" + archive_dir = "#{archive_base}-dir/foo-1.1/" + FileUtils.mkpath(archive_dir) + File.write("#{archive_dir}/deinstall.sh", <<-DEINSTALL_SH) +#!/bin/sh +touch #{@tmpdir}/deinstall.sh-run + DEINSTALL_SH + File.write("#{archive_dir}/install.sh", <<-INSTALL_SH) +#!/bin/sh +touch #{@tmpdir}/install.sh-run + INSTALL_SH + + assert(covered_system('servicemenudeinstallation', archive_base)) + + # deinstaller should be run + # installer should not be run + # archive_dir should have been correctly removed + + assert_path_exist('deinstall.sh-run') + assert_path_not_exist('install.sh-run') + assert_path_not_exist(archive_dir) + end + + def test_run_install_with_arg + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + archive_base = "#{service_dir}/foo.zip" + archive_dir = "#{archive_base}-dir/foo-1.1/" + FileUtils.mkpath(archive_dir) + + File.write("#{archive_dir}/install.sh", <<-INSTALL_SH) +#!/bin/sh +if [ "$@" = "--uninstall" ]; then + touch #{@tmpdir}/install.sh-run + exit 0 +fi +exit 1 + INSTALL_SH + + assert(covered_system('servicemenudeinstallation', archive_base)) + + assert_path_not_exist('deinstall.sh-run') + assert_path_exist('install.sh-run') + assert_path_not_exist(archive_dir) + end + + # no scripts in sight + def test_run_fail + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + archive_base = "#{service_dir}/foo.zip" + archive_dir = "#{archive_base}-dir/foo-1.1/" + FileUtils.mkpath(archive_dir) + + refute(covered_system('servicemenudeinstallation', archive_base)) + + # I am unsure if deinstallation really should keep the files around. But + # that's how it behaved originally so it's supposedly intentional + # - sitter, 2019 + assert_path_exist(archive_dir) + end + + # For desktop files things are a bit special. There is one in .local/share/servicemenu-download + # and another in the actual ServiceMenus dir. The latter gets removed by the + # script, the former by KNS. + def test_run_desktop + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + downloaded_file = "#{service_dir}/foo.desktop" + FileUtils.mkpath(service_dir) + FileUtils.touch(downloaded_file) + + menu_dir = "#{ENV['XDG_DATA_HOME']}/kservices5/ServiceMenus/" + installed_file = "#{menu_dir}/foo.desktop" + FileUtils.mkpath(menu_dir) + FileUtils.touch(installed_file) + + assert(covered_system('servicemenudeinstallation', downloaded_file)) + + assert_path_exist(downloaded_file) + assert_path_not_exist(installed_file) + end +end diff --git a/src/settings/services/test/service_menu_installation_test.rb b/src/settings/services/test/service_menu_installation_test.rb new file mode 100644 index 000000000..3c9fc90ef --- /dev/null +++ b/src/settings/services/test/service_menu_installation_test.rb @@ -0,0 +1,122 @@ +#!/usr/bin/env ruby + +# Copyright (C) 2019 Harald Sitter <[email protected]> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +require_relative 'test_helper' + +require 'tmpdir' + +class ServiceMenuInstallationTest < Test::Unit::TestCase + def setup + @tmpdir = Dir.mktmpdir("dolphintest-#{self.class.to_s.tr(':', '_')}") + @pwdir = Dir.pwd + Dir.chdir(@tmpdir) + + ENV['XDG_DATA_HOME'] = File.join(@tmpdir, 'data') + end + + def teardown + Dir.chdir(@pwdir) + FileUtils.rm_rf(@tmpdir) + + ENV.delete('XDG_DATA_HOME') + end + + def test_run_install + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + FileUtils.mkpath(service_dir) + archive = "#{service_dir}/foo.tar" + + archive_dir = 'foo' # relative so tar cf is relative without fuzz + FileUtils.mkpath(archive_dir) + File.write("#{archive_dir}/install-it.sh", <<-INSTALL_IT_SH) +#!/bin/sh +touch #{@tmpdir}/install-it.sh-run +INSTALL_IT_SH + File.write("#{archive_dir}/install.sh", <<-INSTALL_SH) +#!/bin/sh +touch #{@tmpdir}/install.sh-run + INSTALL_SH + assert(system('tar', '-cf', archive, archive_dir)) + + assert(covered_system('servicemenuinstallation', archive)) + + tar_dir = "#{service_dir}/foo.tar-dir" + tar_extract_dir = "#{service_dir}/foo.tar-dir/foo" + assert_path_exist(tar_dir) + assert_path_exist(tar_extract_dir) + assert_path_exist("#{tar_extract_dir}/install-it.sh") + assert_path_exist("#{tar_extract_dir}/install.sh") + end + + def test_run_install_with_arg + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + FileUtils.mkpath(service_dir) + archive = "#{service_dir}/foo.tar" + + archive_dir = 'foo' # relative so tar cf is relative without fuzz + FileUtils.mkpath(archive_dir) + File.write("#{archive_dir}/install.sh", <<-INSTALL_SH) +#!/bin/sh +if [ "$@" = "--install" ]; then + touch #{@tmpdir}/install.sh-run + exit 0 +fi +exit 1 + INSTALL_SH + assert(system('tar', '-cf', archive, archive_dir)) + + assert(covered_system('servicemenuinstallation', archive)) + + tar_dir = "#{service_dir}/foo.tar-dir" + tar_extract_dir = "#{service_dir}/foo.tar-dir/foo" + assert_path_exist(tar_dir) + assert_path_exist(tar_extract_dir) + assert_path_not_exist("#{tar_extract_dir}/install-it.sh") + assert_path_exist("#{tar_extract_dir}/install.sh") + end + + def test_run_fail + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + FileUtils.mkpath(service_dir) + archive = "#{service_dir}/foo.tar" + + archive_dir = 'foo' # relative so tar cf is relative without fuzz + FileUtils.mkpath(archive_dir) + assert(system('tar', '-cf', archive, archive_dir)) + + refute(covered_system('servicemenuinstallation', archive)) + end + + def test_run_desktop + service_dir = File.join(Dir.pwd, 'share/servicemenu-download') + downloaded_file = "#{service_dir}/foo.desktop" + FileUtils.mkpath(service_dir) + FileUtils.touch(downloaded_file) + + menu_dir = "#{ENV['XDG_DATA_HOME']}/kservices5/ServiceMenus/" + installed_file = "#{menu_dir}/foo.desktop" + FileUtils.mkpath(menu_dir) + FileUtils.touch(installed_file) + + assert(covered_system('servicemenuinstallation', downloaded_file)) + + assert_path_exist(downloaded_file) + assert_path_exist(installed_file) + end +end diff --git a/src/settings/services/test/test_helper.rb b/src/settings/services/test/test_helper.rb new file mode 100644 index 000000000..9da5cf3c3 --- /dev/null +++ b/src/settings/services/test/test_helper.rb @@ -0,0 +1,100 @@ +# Copyright (C) 2019 Harald Sitter <[email protected]> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +GLOBAL_COVERAGE_ROOT = File.dirname(__dir__) # ../ + +# Simplecov is a bit meh and expects src and coverage to be under the +# same root. Since we get run through cmake that assumption absolutely +# doesn't hold true, so we'll need to figure out the coverage_dir relative +# to the root and the root must always be the source :/ +# The relativity only works because internally the path gets expanded, this +# isn't fully reliable, but oh well... +# https://github.com/colszowka/simplecov/issues/716 +GLOBAL_COVERAGE_DIR = begin + require 'pathname' + src_path = Pathname.new(GLOBAL_COVERAGE_ROOT) + coverage_path = Pathname.new(File.join(Dir.pwd, 'coverage')) + coverage_path.relative_path_from(src_path).to_s +end + +begin + require 'simplecov' + + SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( + [ + SimpleCov::Formatter::HTMLFormatter + ] + ) + + SimpleCov.start do + root GLOBAL_COVERAGE_ROOT + coverage_dir GLOBAL_COVERAGE_DIR + end +rescue LoadError + warn 'SimpleCov not loaded' +end + +# FIXME: add coverage report for jenkins? + +$LOAD_PATH.unshift(File.absolute_path('../', __dir__)) # ../ + +def __test_method_name__ + return @method_name if defined?(:@method_name) + index = 0 + caller = '' + until caller.start_with?('test_') + caller = caller_locations(index, 1)[0].label + index += 1 + end + caller +end + +# system() variant which sets up merge-coverage. simplecov supports merging +# of multiple coverage sets. we use this to get coverage metrics on the +# binaries without having to refactor the script into runnable classes. +def covered_system(cmd, *argv) + pid = fork do + Kernel.module_exec do + alias_method(:real_system, :system) + define_method(:system) do |*args| + return true if args.include?('kdialog') # disable kdialog call + real_system(*args) + end + end + + begin + require 'simplecov' + SimpleCov.start do + root GLOBAL_COVERAGE_ROOT + coverage_dir GLOBAL_COVERAGE_DIR + command_name "#{cmd}_#{__test_method_name__}" + end + rescue LoadError + warn 'SimpleCov not loaded' + end + + ARGV.replace(argv) + load "#{__dir__}/../#{cmd}" + puts 'all good, fork ending!' + exit 0 + end + waitedpid, status = Process.waitpid2(pid) + assert_equal(pid, waitedpid) + status.success? # behave like system and return the success only +end + +require 'test/unit' diff --git a/src/settings/services/test/test_run.rb b/src/settings/services/test/test_run.rb new file mode 100755 index 000000000..b2149bbe0 --- /dev/null +++ b/src/settings/services/test/test_run.rb @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby + +# Copyright (C) 2019 Harald Sitter <[email protected]> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# This is a fancy wrapper around test_helper to prevent the collector from +# loading the helper twice as it would occur if we ran the helper directly. + +require_relative 'test_helper' + +Test::Unit::AutoRunner.run(true, File.absolute_path(__dir__)) diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 07e4257a0..8ef20cb83 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -3,6 +3,18 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) find_package(Qt5Test CONFIG REQUIRED) include(ECMAddTests) +include(FindGem) + +find_gem(test-unit REQUIRED) +set_package_properties(Gem:test-unit PROPERTIES + DESCRIPTION "Ruby gem 'test-unit' required for testing of servicemenu helpers.") + +if(BUILD_COVERAGE) + find_gem(simplecov) + set_package_properties(Gem:simplecov PROPERTIES + DESCRIPTION "Ruby gem 'simplecov' used for coverage statistics.") +endif() + # KItemSetTest ecm_add_test(kitemsettest.cpp LINK_LIBRARIES dolphinprivate Qt5::Test) @@ -69,3 +81,5 @@ ecm_add_test(placesitemmodeltest.cpp TEST_NAME placesitemmodeltest LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test) +add_test(NAME servicemenutest + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../settings/services/test/test_run.rb) diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp index 2f258d17d..c53979970 100644 --- a/src/tests/kfileitemmodeltest.cpp +++ b/src/tests/kfileitemmodeltest.cpp @@ -1553,22 +1553,22 @@ void KFileItemModelTest::testChangeSortRoleWhileFiltering() KIO::UDSEntry entry[3]; - entry[0].insert(KIO::UDSEntry::UDS_NAME, "a.txt"); - entry[0].insert(KIO::UDSEntry::UDS_USER, "user-b"); + entry[0].fastInsert(KIO::UDSEntry::UDS_NAME, "a.txt"); + entry[0].fastInsert(KIO::UDSEntry::UDS_USER, "user-b"); - entry[1].insert(KIO::UDSEntry::UDS_NAME, "b.txt"); - entry[1].insert(KIO::UDSEntry::UDS_USER, "user-c"); + entry[1].fastInsert(KIO::UDSEntry::UDS_NAME, "b.txt"); + entry[1].fastInsert(KIO::UDSEntry::UDS_USER, "user-c"); - entry[2].insert(KIO::UDSEntry::UDS_NAME, "c.txt"); - entry[2].insert(KIO::UDSEntry::UDS_USER, "user-a"); + entry[2].fastInsert(KIO::UDSEntry::UDS_NAME, "c.txt"); + entry[2].fastInsert(KIO::UDSEntry::UDS_USER, "user-a"); for (int i = 0; i < 3; ++i) { - entry[i].insert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000); // S_IFREG might not be defined on non-Unix platforms. - entry[i].insert(KIO::UDSEntry::UDS_ACCESS, 07777); - entry[i].insert(KIO::UDSEntry::UDS_SIZE, 0); - entry[i].insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 0); - entry[i].insert(KIO::UDSEntry::UDS_GROUP, "group"); - entry[i].insert(KIO::UDSEntry::UDS_ACCESS_TIME, 0); + entry[i].fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000); // S_IFREG might not be defined on non-Unix platforms. + entry[i].fastInsert(KIO::UDSEntry::UDS_ACCESS, 07777); + entry[i].fastInsert(KIO::UDSEntry::UDS_SIZE, 0); + entry[i].fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 0); + entry[i].fastInsert(KIO::UDSEntry::UDS_GROUP, "group"); + entry[i].fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, 0); items.append(KFileItem(entry[i], m_testDir->url(), false, true)); } |
