CMakeLists.txt.user
.directory
*.kdev4
+/build*/
# KDE Application Version, managed by release script
set (KDE_APPLICATIONS_VERSION_MAJOR "19")
-set (KDE_APPLICATIONS_VERSION_MINOR "08")
-set (KDE_APPLICATIONS_VERSION_MICRO "1")
+set (KDE_APPLICATIONS_VERSION_MINOR "11")
+set (KDE_APPLICATIONS_VERSION_MICRO "70")
set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}")
project(Dolphin VERSION ${KDE_APPLICATIONS_VERSION})
set(QT_MIN_VERSION "5.8.0")
-set(KF5_MIN_VERSION "5.57.0")
+set(KF5_MIN_VERSION "5.61.0")
# ECM setup
find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED)
-/***************************************************************************
+ /***************************************************************************
* Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) and *
* Cvetoslav Ludmiloff *
* *
QAction* openParentAction = nullptr;
QAction* openParentInNewWindowAction = nullptr;
QAction* openParentInNewTabAction = nullptr;
- QAction* addToPlacesAction = nullptr;
const KFileItemListProperties& selectedItemsProps = selectedItemsProperties();
KFileItemActions fileItemActions;
// insert 'Add to Places' entry
if (!placeExists(m_fileInfo.url())) {
- addToPlacesAction = addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")),
- i18nc("@action:inmenu Add selected folder to places",
- "Add to Places"));
+ addAction(m_mainWindow->actionCollection()->action(QStringLiteral("add_to_places")));
}
addSeparator();
QAction* activatedAction = exec(m_pos);
if (activatedAction) {
- if (activatedAction == addToPlacesAction) {
- const QUrl selectedUrl(m_fileInfo.url());
- if (selectedUrl.isValid()) {
- PlacesItemModel model;
- const QString text = selectedUrl.fileName();
- model.createPlacesItem(text, selectedUrl, KIO::iconNameForUrl(selectedUrl));
- }
- } else if (activatedAction == openParentAction) {
+ if (activatedAction == openParentAction) {
m_command = OpenParentFolder;
} else if (activatedAction == openParentInNewWindowAction) {
m_command = OpenParentFolderInNewWindow;
addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new_tab")));
// Insert 'Add to Places' entry if exactly one item is selected
- QAction* addToPlacesAction = nullptr;
if (!placeExists(m_mainWindow->activeViewContainer()->url())) {
- addToPlacesAction = addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")),
- i18nc("@action:inmenu Add current folder to places", "Add to Places"));
+ addAction(m_mainWindow->actionCollection()->action(QStringLiteral("add_to_places")));
}
addSeparator();
addAction(propertiesAction);
addShowMenuBarAction();
-
- QAction* action = exec(m_pos);
- if (addToPlacesAction && (action == addToPlacesAction)) {
- const DolphinViewContainer* container = m_mainWindow->activeViewContainer();
- const QUrl url = container->url();
- if (url.isValid()) {
- PlacesItemModel model;
- QString icon;
- if (container->isSearchModeEnabled()) {
- icon = QStringLiteral("folder-saved-search-symbolic");
- } else {
- icon = KIO::iconNameForUrl(url);
- }
- model.createPlacesItem(container->placesText(), url, icon);
- }
- }
}
void DolphinContextMenu::insertDefaultItemActions(const KFileItemListProperties& properties)
#include "dolphintabpage.h"
#include "middleclickactioneventfilter.h"
#include "panels/folders/folderspanel.h"
+#include "panels/places/placesitemmodel.h"
#include "panels/places/placespanel.h"
#include "panels/information/informationpanel.h"
#include "panels/terminal/terminalpanel.h"
}
m_activeViewContainer->setUrl(url);
- updateEditActions();
+ updateFileAndEditActions();
updatePasteAction();
updateViewActions();
updateGoActions();
void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection)
{
- updateEditActions();
+ updateFileAndEditActions();
const int selectedUrlsCount = m_tabWidget->currentTabPage()->selectedItemsCount();
m_tabWidget->openNewActivatedTab();
}
+void DolphinMainWindow::addToPlaces()
+{
+ QUrl url;
+ QString name;
+
+ // If nothing is selected, act on the current dir
+ if (m_activeViewContainer->view()->selectedItems().count() == 0) {
+ url = m_activeViewContainer->url();
+ name = m_activeViewContainer->placesText();
+ } else {
+ const auto dirToAdd = m_activeViewContainer->view()->selectedItems().first();
+ url = dirToAdd.url();
+ name = dirToAdd.name();
+ }
+ if (url.isValid()) {
+ PlacesItemModel model;
+ QString icon;
+ if (m_activeViewContainer->isSearchModeEnabled()) {
+ icon = QStringLiteral("folder-saved-search-symbolic");
+ } else {
+ icon = KIO::iconNameForUrl(url);
+ }
+ model.createPlacesItem(name, url, icon);
+ }
+}
+
void DolphinMainWindow::openNewTab(const QUrl& url, DolphinTabWidget::TabPlacement tabPlacement)
{
m_tabWidget->openNewTab(url, QUrl(), tabPlacement);
m_activeViewContainer->setSearchModeEnabled(true);
}
+void DolphinMainWindow::updateSearchAction()
+{
+ QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
+ toggleSearchAction->setChecked(m_activeViewContainer->isSearchModeEnabled());
+}
+
void DolphinMainWindow::updatePasteAction()
{
QAction* pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste));
void DolphinMainWindow::slotTerminalPanelVisibilityChanged()
{
- if (m_terminalPanel->isHiddenInVisibleWindow()) {
+ if (m_terminalPanel->isHiddenInVisibleWindow() && m_activeViewContainer) {
m_activeViewContainer->view()->setFocus();
}
}
m_activeViewContainer = viewContainer;
if (oldViewContainer) {
+ const QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
+ toggleSearchAction->disconnect(oldViewContainer);
+
// Disconnect all signals between the old view container (container,
// view and url navigator) and main window.
oldViewContainer->disconnect(this);
m_actionHandler->setCurrentView(viewContainer->view());
updateHistory();
- updateEditActions();
+ updateFileAndEditActions();
updatePasteAction();
updateViewActions();
updateGoActions();
+ updateSearchAction();
const QUrl url = viewContainer->url();
emit urlChanged(url);
QAction* newWindow = KStandardAction::openNew(this, &DolphinMainWindow::openNewMainWindow, actionCollection());
newWindow->setText(i18nc("@action:inmenu File", "New &Window"));
+ newWindow->setToolTip(i18nc("@info", "Open a new Dolphin window"));
newWindow->setWhatsThis(xi18nc("@info:whatsthis", "This opens a new "
"window just like this one with the current location and view."
"<nl/>You can drag and drop items between windows."));
actionCollection()->setDefaultShortcuts(newTab, {Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::SHIFT + Qt::Key_N});
connect(newTab, &QAction::triggered, this, &DolphinMainWindow::openNewActivatedTab);
+ QAction* addToPlaces = actionCollection()->addAction(QStringLiteral("add_to_places"));
+ addToPlaces->setIcon(QIcon::fromTheme(QStringLiteral("bookmark-new")));
+ addToPlaces->setWhatsThis(xi18nc("@info:whatsthis", "This adds the selected folder "
+ "to the Places panel."));
+ connect(addToPlaces, &QAction::triggered, this, &DolphinMainWindow::addToPlaces);
+
QAction* closeTab = KStandardAction::close(m_tabWidget, QOverload<>::of(&DolphinTabWidget::closeTab), actionCollection());
closeTab->setText(i18nc("@action:inmenu File", "Close Tab"));
closeTab->setWhatsThis(i18nc("@info:whatsthis", "This closes the "
"the find bar so we can have a look at it while the settings are "
"explained.</para>"));
+ // toggle_search acts as a copy of the main searchAction to be used mainly
+ // in the toolbar, with no default shortcut attached, to avoid messing with
+ // existing workflows (search bar always open and Ctrl-F to focus)
+ QAction *toggleSearchAction = actionCollection()->addAction(QStringLiteral("toggle_search"));
+ toggleSearchAction->setText(i18nc("@action:inmenu", "Toggle Search Bar"));
+ toggleSearchAction->setIconText(i18nc("@action:intoolbar", "Search"));
+ toggleSearchAction->setIcon(searchAction->icon());
+ toggleSearchAction->setToolTip(searchAction->toolTip());
+ toggleSearchAction->setWhatsThis(searchAction->whatsThis());
+ toggleSearchAction->setCheckable(true);
+
QAction* selectAllAction = KStandardAction::selectAll(this, &DolphinMainWindow::selectAll, actionCollection());
selectAllAction->setWhatsThis(xi18nc("@info:whatsthis", "This selects all "
"files and folders in the current location."));
});
}
-void DolphinMainWindow::updateEditActions()
+
+void DolphinMainWindow::updateFileAndEditActions()
{
const KFileItemList list = m_activeViewContainer->view()->selectedItems();
+ const KActionCollection* col = actionCollection();
+ QAction* addToPlacesAction = col->action(QStringLiteral("add_to_places"));
+
if (list.isEmpty()) {
stateChanged(QStringLiteral("has_no_selection"));
+
+ addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add '%1' to Places", m_activeViewContainer->placesText()));
} else {
stateChanged(QStringLiteral("has_selection"));
- KActionCollection* col = actionCollection();
QAction* renameAction = col->action(KStandardAction::name(KStandardAction::RenameFile));
QAction* moveToTrashAction = col->action(KStandardAction::name(KStandardAction::MoveToTrash));
QAction* deleteAction = col->action(KStandardAction::name(KStandardAction::DeleteFile));
QAction* deleteWithTrashShortcut = col->action(QStringLiteral("delete_shortcut")); // see DolphinViewActionHandler
QAction* showTarget = col->action(QStringLiteral("show_target"));
+ if (list.length() == 1 && list.first().isDir()) {
+ addToPlacesAction->setEnabled(true);
+ addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add '%1' to Places", list.first().name()));
+ } else {
+ addToPlacesAction->setEnabled(false);
+ addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add to Places"));
+ }
+
KFileItemListProperties capabilities(list);
const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving();
m_controlButton = new QToolButton(this);
m_controlButton->setIcon(QIcon::fromTheme(QStringLiteral("application-menu")));
- m_controlButton->setText(i18nc("@action", "Control"));
+ m_controlButton->setToolTip(i18nc("@action", "Show menu"));
m_controlButton->setAttribute(Qt::WidgetAttribute::WA_CustomWhatsThis);
m_controlButton->setPopupMode(QToolButton::InstantPopup);
- m_controlButton->setToolButtonStyle(toolBar()->toolButtonStyle());
QMenu* controlMenu = new QMenu(m_controlButton);
connect(controlMenu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateControlMenu);
toolBar()->addWidget(m_controlButton);
connect(toolBar(), &KToolBar::iconSizeChanged,
m_controlButton, &QToolButton::setIconSize);
- connect(toolBar(), &KToolBar::toolButtonStyleChanged,
- m_controlButton, &QToolButton::setToolButtonStyle);
// The added widgets are owned by the toolbar and may get deleted when e.g. the toolbar
// gets edited. In this case we must add them again. The adding is done asynchronously by
this, &DolphinMainWindow::updateFilterBarAction);
connect(container, &DolphinViewContainer::writeStateChanged,
this, &DolphinMainWindow::slotWriteStateChanged);
+ connect(container, &DolphinViewContainer::searchModeEnabledChanged,
+ this, &DolphinMainWindow::updateSearchAction);
+
+ const QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search"));
+ connect(toggleSearchAction, &QAction::triggered, container, &DolphinViewContainer::setSearchModeEnabled);
const DolphinView* view = container->view();
connect(view, &DolphinView::selectionChanged,
/** Replaces the URL navigator by a search box to find files. */
void find();
+ /** Updates the state of the search action according to the view container. */
+ void updateSearchAction();
+
/**
* Updates the text of the paste action dependent on
* the number of items which are in the clipboard.
*/
void openNewActivatedTab();
+ /**
+ * Adds the current URL as an entry to the Places panel
+ */
+ void addToPlaces();
+
/**
* Opens a new tab in the background showing the URL \a url.
*/
*/
void setupDockWidgets();
- void updateEditActions();
+ void updateFileAndEditActions();
void updateViewActions();
void updateGoActions();
QWidget* focusWidget = QApplication::focusWidget();
DolphinTabPage* tabPage = new DolphinTabPage(primaryUrl, secondaryUrl, this);
+ tabPage->setActive(false);
tabPage->setPlacesSelectorVisible(m_placesSelectorVisible);
connect(tabPage, &DolphinTabPage::activeViewChanged,
this, &DolphinTabWidget::activeViewChanged);
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
-<kpartgui name="dolphin" version="22">
+<kpartgui name="dolphin" version="25">
<MenuBar>
<Menu name="file">
<Action name="new_menu" />
<Action name="file_close" />
<Action name="undo_close_tab" />
<Separator/>
+ <Action name="add_to_places" />
+ <Separator/>
<Action name="renamefile" />
<Action name="movetotrash" />
<Action name="deletefile" />
<Action name="compact" />
<Action name="details" />
<Separator name="separator_0" />
- <Action name="edit_find"/>
- <Action name="show_preview" />
+ <Action name="sort" />
+ <Spacer name="spacer_0" />
<Action name="split_view" />
<Action name="split_stash" />
+ <Action name="toggle_search" />
</ToolBar>
<ActionProperties scheme="Default">
<Action priority="0" name="go_back"/>
<Action priority="0" name="edit_cut"/>
<Action priority="0" name="edit_copy"/>
<Action priority="0" name="edit_paste"/>
+ <Action priority="0" name="toggle_search"/>
</ActionProperties>
</kpartgui>
}
m_searchModeEnabled = enabled;
+
+ emit searchModeEnabledChanged(enabled);
}
bool DolphinViewContainer::isSearchModeEnabled() const
/** Returns true, if the filter bar is visible. */
bool isFilterBarVisible() const;
- /**
- * Enables the search mode, if \p enabled is true. In the search mode the URL navigator
- * will be hidden and replaced by a line editor that allows to enter a search term.
- */
- void setSearchModeEnabled(bool enabled);
+
+ /** Returns true if the search mode is enabled. */
bool isSearchModeEnabled() const;
/**
*/
void setFilterBarVisible(bool visible);
+ /**
+ * Enables the search mode, if \p enabled is true. In the search mode the URL navigator
+ * will be hidden and replaced by a line editor that allows to enter a search term.
+ */
+ void setSearchModeEnabled(bool enabled);
+
signals:
/**
* Is emitted whenever the filter bar has changed its visibility state.
*/
void showFilterBarChanged(bool shown);
+ /**
+ * Is emitted whenever the search mode has changed its state.
+ */
+ void searchModeEnabledChanged(bool enabled);
/**
* Is emitted when the write state of the folder has been changed. The application
return false;
}
- const QStringList services = QDBusConnection::sessionBus().interface()->registeredServiceNames().value();
-
- // Don't match the service without trailing "-" (unique instance)
- const QString pattern = QStringLiteral("org.kde.dolphin-");
- // Don't match the pid without leading "-"
- const QString myPid = QStringLiteral("-") + QString::number(QCoreApplication::applicationPid());
QVector<QPair<QSharedPointer<QDBusInterface>, QStringList>> dolphinServices;
if (!preferredService.isEmpty()) {
QSharedPointer<QDBusInterface> preferred(
QStringLiteral("org.kde.dolphin.MainWindow"))
);
if (preferred->isValid() && !preferred->lastError().isValid()) {
- dolphinServices.append(qMakePair(preferred, QStringList() ));
+ dolphinServices.append(qMakePair(preferred, QStringList()));
}
}
// find all dolphin instances
+ const QStringList services = QDBusConnection::sessionBus().interface()->registeredServiceNames().value();
+ // Don't match the service without trailing "-" (unique instance)
+ const QString pattern = QStringLiteral("org.kde.dolphin-");
+ // Don't match the pid without leading "-"
+ const QString myPid = QStringLiteral("-") + QString::number(QCoreApplication::applicationPid());
for (const QString& service : services) {
if (service.startsWith(pattern) && !service.endsWith(myPid)) {
// Check if instance can handle our URLs
QStringLiteral("/dolphin/Dolphin_1"),
QStringLiteral("org.kde.dolphin.MainWindow"))
);
- if (!instance->isValid() || instance->lastError().isValid()) {
- continue;
+ if (instance->isValid() && !instance->lastError().isValid()) {
+ dolphinServices.append(qMakePair(instance, QStringList()));
}
- dolphinServices.append(qMakePair(instance, QStringList()));
}
}
for (auto& service: dolphinServices) {
QDBusReply<bool> isUrlOpen = service.first->call(QStringLiteral("isUrlOpen"), url);
if (isUrlOpen.isValid() && isUrlOpen.value()) {
- service.second.append(url);
- urlFound = true;
- break;
+ service.second.append(url);
+ urlFound = true;
+ break;
}
}
if (!urlFound) {
void KFileItemModel::slotCompleted()
{
+ m_maximumUpdateIntervalTimer->stop();
dispatchPendingItemsToInsert();
if (!m_urlsToExpand.isEmpty()) {
}
}
- if (useMaximumUpdateInterval() && !m_maximumUpdateIntervalTimer->isActive()) {
+ if (!m_maximumUpdateIntervalTimer->isActive()) {
// Assure that items get dispatched if no completed() or canceled() signal is
// emitted during the maximum update interval.
m_maximumUpdateIntervalTimer->start();
default: {
const QByteArray role = roleForType(m_sortRole);
- result = QString::compare(a->values.value(role).toString(),
- b->values.value(role).toString());
+ const QString roleValueA = a->values.value(role).toString();
+ const QString roleValueB = b->values.value(role).toString();
+ if (!roleValueA.isEmpty() && roleValueB.isEmpty()) {
+ result = -1;
+ } else if (roleValueA.isEmpty() && !roleValueB.isEmpty()) {
+ result = +1;
+ } else {
+ result = QString::compare(roleValueA, roleValueB);
+ }
break;
}
return QString::compare(a, b, Qt::CaseSensitive);
}
-bool KFileItemModel::useMaximumUpdateInterval() const
-{
- return !m_dirLister->url().isLocalFile();
-}
-
QList<QPair<int, QVariant> > KFileItemModel::nameRoleGroups() const
{
Q_ASSERT(!m_itemData.isEmpty());
if (firstChar != newFirstChar) {
QString newGroupValue;
if (newFirstChar.isLetter()) {
- // Try to find a matching group in the range 'A' to 'Z'.
- static std::vector<QChar> lettersAtoZ;
- lettersAtoZ.reserve('Z' - 'A' + 1);
- if (lettersAtoZ.empty()) {
- for (char c = 'A'; c <= 'Z'; ++c) {
- lettersAtoZ.push_back(QLatin1Char(c));
+
+ if (m_collator.compare(newFirstChar, QChar(QLatin1Char('A'))) >= 0 && m_collator.compare(newFirstChar, QChar(QLatin1Char('Z'))) <= 0) {
+ // WARNING! Symbols based on latin 'Z' like 'Z' with acute are treated wrong as non Latin and put in a new group.
+
+ // Try to find a matching group in the range 'A' to 'Z'.
+ static std::vector<QChar> lettersAtoZ;
+ lettersAtoZ.reserve('Z' - 'A' + 1);
+ if (lettersAtoZ.empty()) {
+ for (char c = 'A'; c <= 'Z'; ++c) {
+ lettersAtoZ.push_back(QLatin1Char(c));
+ }
}
- }
- auto localeAwareLessThan = [this](QChar c1, QChar c2) -> bool {
- return m_collator.compare(c1, c2) < 0;
- };
+ auto localeAwareLessThan = [this](QChar c1, QChar c2) -> bool {
+ return m_collator.compare(c1, c2) < 0;
+ };
- std::vector<QChar>::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan);
- if (it != lettersAtoZ.end()) {
- if (localeAwareLessThan(newFirstChar, *it) && it != lettersAtoZ.begin()) {
- // newFirstChar belongs to the group preceding *it.
- // Example: for an umlaut 'A' in the German locale, *it would be 'B' now.
- --it;
+ std::vector<QChar>::iterator it = std::lower_bound(lettersAtoZ.begin(), lettersAtoZ.end(), newFirstChar, localeAwareLessThan);
+ if (it != lettersAtoZ.end()) {
+ if (localeAwareLessThan(newFirstChar, *it)) {
+ // newFirstChar belongs to the group preceding *it.
+ // Example: for an umlaut 'A' in the German locale, *it would be 'B' now.
+ --it;
+ }
+ newGroupValue = *it;
}
- newGroupValue = *it;
+
} else {
+ // Symbols from non Latin-based scripts
newGroupValue = newFirstChar;
}
} else if (newFirstChar >= QLatin1Char('0') && newFirstChar <= QLatin1Char('9')) {
int stringCompare(const QString& a, const QString& b, const QCollator& collator) const;
- bool useMaximumUpdateInterval() const;
-
QList<QPair<int, QVariant> > nameRoleGroups() const;
QList<QPair<int, QVariant> > sizeRoleGroups() const;
QList<QPair<int, QVariant> > timeRoleGroups(const std::function<QDateTime(const ItemData *)> &fileTimeCb) const;
do {
QString lastTextLine = nameText.mid(line.textStart());
lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine,
- Qt::ElideRight,
+ Qt::ElideMiddle,
elidingWidth);
const QString elidedText = nameText.left(line.textStart()) + lastTextLine;
nameTextInfo->staticText.setText(elidedText);
textLine.setLineWidth(maxWidth);
requiredWidth = textLine.naturalTextWidth();
if (requiredWidth > maxWidth) {
- const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth);
+ const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth);
textInfo->staticText.setText(elidedText);
requiredWidth = m_customizedFontMetrics.width(elidedText);
} else if (role == "rating") {
qreal requiredWidth = m_customizedFontMetrics.width(text);
if (requiredWidth > maxWidth) {
requiredWidth = maxWidth;
- const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth);
+ const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, maxWidth);
textInfo->staticText.setText(elidedText);
}
}
if (requiredWidth > availableTextWidth) {
- text = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth);
+ text = m_customizedFontMetrics.elidedText(text, Qt::ElideMiddle, availableTextWidth);
requiredWidth = m_customizedFontMetrics.width(text);
}
#include <QTimer>
#include <QVBoxLayout>
#include <QStyle>
+#include <QPainter>
+#include <QBitmap>
+#include <QLinearGradient>
+#include <QPolygon>
#include "dolphin_informationpanelsettings.h"
#include "phononwidget.h"
#include "pixmapviewer.h"
+const int PLAY_ARROW_SIZE = 24;
+const int PLAY_ARROW_BORDER_SIZE = 2;
+
InformationPanelContent::InformationPanelContent(QWidget* parent) :
QWidget(parent),
m_item(),
m_nameLabel(nullptr),
m_metaDataWidget(nullptr),
m_metaDataArea(nullptr),
- m_placesItemModel(nullptr)
+ m_placesItemModel(nullptr),
+ m_isVideo(false)
{
parent->installEventFilter(this);
refreshPreview();
}
+void InformationPanelContent::refreshPixmapView()
+{
+ // 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) {
+ m_previewJob->kill();
+ }
+
+ // try to get a preview pixmap from the item...
+
+ // Mark the currently shown preview as outdated. This is done
+ // with a small delay to prevent a flickering when the next preview
+ // can be shown within a short timeframe. This timer is not started
+ // for directories, as directory previews might fail and return the
+ // same icon.
+ if (!m_item.isDir()) {
+ m_outdatedPreviewTimer->start();
+ }
+
+ QStringList plugins = KIO::PreviewJob::availablePlugins();
+ m_previewJob = new KIO::PreviewJob(KFileItemList() << m_item,
+ QSize(m_preview->width(), m_preview->height()),
+ &plugins);
+ m_previewJob->setScaleType(KIO::PreviewJob::Unscaled);
+ m_previewJob->setIgnoreMaximumSize(m_item.isLocalFile());
+ if (m_previewJob->uiDelegate()) {
+ KJobWidgets::setWindow(m_previewJob, this);
+ }
+
+ connect(m_previewJob.data(), &KIO::PreviewJob::gotPreview,
+ this, &InformationPanelContent::showPreview);
+ connect(m_previewJob.data(), &KIO::PreviewJob::failed,
+ this, &InformationPanelContent::showIcon);
+}
+
void InformationPanelContent::refreshPreview()
{
// If there is a preview job, kill it to prevent that we have jobs for
m_previewJob->kill();
}
+ m_preview->setCursor(Qt::ArrowCursor);
+ bool usePhonon = false;
setNameLabelText(m_item.text());
if (InformationPanelSettings::previewsShown()) {
QIcon::fromTheme(QStringLiteral("nepomuk")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous)
);
} else {
- // try to get a preview pixmap from the item...
-
- // Mark the currently shown preview as outdated. This is done
- // with a small delay to prevent a flickering when the next preview
- // can be shown within a short timeframe. This timer is not started
- // for directories, as directory previews might fail and return the
- // same icon.
- if (!m_item.isDir()) {
- m_outdatedPreviewTimer->start();
- }
- QStringList plugins = KIO::PreviewJob::availablePlugins();
- m_previewJob = new KIO::PreviewJob(KFileItemList() << m_item,
- QSize(m_preview->width(), m_preview->height()),
- &plugins);
- m_previewJob->setScaleType(KIO::PreviewJob::Unscaled);
- m_previewJob->setIgnoreMaximumSize(m_item.isLocalFile());
- if (m_previewJob->uiDelegate()) {
- KJobWidgets::setWindow(m_previewJob, this);
- }
-
- connect(m_previewJob.data(), &KIO::PreviewJob::gotPreview,
- this, &InformationPanelContent::showPreview);
- connect(m_previewJob.data(), &KIO::PreviewJob::failed,
- this, &InformationPanelContent::showIcon);
+ refreshPixmapView();
const QString mimeType = m_item.mimetype();
- const bool isVideo = mimeType.startsWith(QLatin1String("video/"));
- const bool usePhonon = mimeType.startsWith(QLatin1String("audio/")) || isVideo;
+ m_isVideo = mimeType.startsWith(QLatin1String("video/"));
+ usePhonon = m_isVideo || mimeType.startsWith(QLatin1String("audio/"));
if (usePhonon) {
-
- if (InformationPanelSettings::previewsAutoPlay() && isVideo) {
- // hides the preview now to avoid flickering when the autoplay video starts
- m_preview->hide();
- } else {
- // the video won't play before the preview is displayed
- m_preview->show();
+ // change the cursor of the preview
+ m_preview->setCursor(Qt::PointingHandCursor);
+ m_preview->installEventFilter(m_phononWidget);
+
+ // if the video is playing, has been paused or stopped
+ // we don't need to update the preview/phonon widget states
+ // unless the previewed file has changed,
+ // or the setting previewshown has changed
+ if ((m_phononWidget->state() != Phonon::State::PlayingState &&
+ m_phononWidget->state() != Phonon::State::PausedState &&
+ m_phononWidget->state() != Phonon::State::StoppedState) ||
+ m_item.targetUrl() != m_phononWidget->url() ||
+ (!m_preview->isVisible() &&! m_phononWidget->isVisible())) {
+
+ if (InformationPanelSettings::previewsAutoPlay() && m_isVideo) {
+ // hides the preview now to avoid flickering when the autoplay video starts
+ m_preview->hide();
+ } else {
+ // the video won't play before the preview is displayed
+ m_preview->show();
+ }
+
+ m_phononWidget->show();
+ m_phononWidget->setUrl(m_item.targetUrl(), m_isVideo ? PhononWidget::MediaKind::Video : PhononWidget::MediaKind::Audio);
+ adjustWidgetSizes(parentWidget()->width());
}
-
- m_phononWidget->show();
- m_phononWidget->setUrl(m_item.targetUrl(), isVideo ? PhononWidget::MediaKind::Video : PhononWidget::MediaKind::Audio);
- m_phononWidget->setVideoSize(m_preview->size());
} else {
// When we don't need it, hide the phonon widget first to avoid flickering
m_phononWidget->hide();
m_preview->show();
+ m_preview->removeEventFilter(m_phononWidget);
+ m_phononWidget->clearUrl();
}
}
} else {
const QPixmap& pixmap)
{
m_outdatedPreviewTimer->stop();
- Q_UNUSED(item);
+ Q_UNUSED(item)
QPixmap p = pixmap;
KIconLoader::global()->drawOverlays(item.overlays(), p, KIconLoader::Desktop);
+
+ if (m_isVideo) {
+ // adds a play arrow
+
+ // compute relative pixel positions
+ const int zeroX = static_cast<int>(p.width() / 2 - PLAY_ARROW_SIZE / 2 / devicePixelRatio());
+ const int zeroY = static_cast<int>(p.height() / 2 - PLAY_ARROW_SIZE / 2 / devicePixelRatio());
+
+ QPolygon arrow;
+ arrow << QPoint(zeroX, zeroY);
+ arrow << QPoint(zeroX, zeroY + PLAY_ARROW_SIZE);
+ arrow << QPoint(zeroX + PLAY_ARROW_SIZE, zeroY + PLAY_ARROW_SIZE / 2);
+
+ QPainterPath path;
+ path.addPolygon(arrow);
+
+ QLinearGradient gradient(QPointF(zeroX, zeroY),
+ QPointF(zeroX + PLAY_ARROW_SIZE,zeroY + PLAY_ARROW_SIZE));
+
+ QColor whiteColor = Qt::white;
+ QColor blackColor = Qt::black;
+ gradient.setColorAt(0, whiteColor);
+ gradient.setColorAt(1, blackColor);
+
+ QBrush brush(gradient);
+
+ QPainter painter(&p);
+
+ QPen pen(blackColor, PLAY_ARROW_BORDER_SIZE, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
+ painter.setPen(pen);
+
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.drawPolygon(arrow);
+ painter.fillPath(path, brush);
+ }
+
m_preview->setPixmap(p);
}
void InformationPanelContent::slotHasVideoChanged(bool hasVideo)
{
m_preview->setVisible(InformationPanelSettings::previewsShown() && !hasVideo);
+ if (m_preview->isVisible() && m_preview->size().width() != m_preview->pixmap().size().width()) {
+ // in case the information panel has been resized when the preview was not displayed
+ // we need to refresh its content
+ refreshPixmapView();
+ }
}
void InformationPanelContent::setPreviewAutoPlay(bool autoPlay) {
*/
void adjustWidgetSizes(int width);
+ /**
+ * Refreshes the image in the PixmapViewer
+ */
+ void refreshPixmapView();
+
private:
KFileItem m_item;
QDialogButtonBox* m_configureButtons;
PlacesItemModel* m_placesItemModel;
+ bool m_isVideo;
};
#endif // INFORMATIONPANELCONTENT_H
: QWidget(parent),
m_url(),
m_playButton(nullptr),
- m_stopButton(nullptr),
+ m_pauseButton(nullptr),
m_topLayout(nullptr),
m_media(nullptr),
m_seekSlider(nullptr),
return m_url;
}
+void PhononWidget::clearUrl()
+{
+ m_url.clear();
+}
+
+void PhononWidget::togglePlayback()
+{
+ if (m_media && m_media->state() == Phonon::State::PlayingState) {
+ m_media->pause();
+ } else {
+ play();
+ }
+}
+
+bool PhononWidget::eventFilter(QObject *object, QEvent *event)
+{
+ Q_UNUSED(object)
+ if (event->type() == QEvent::MouseButtonPress) {
+ const QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
+ if (mouseEvent->button() == Qt::LeftButton) {
+ // toggle playback
+ togglePlayback();
+ return true;
+ }
+ }
+ return false;
+}
+
void PhononWidget::setVideoSize(const QSize& size)
{
if (m_videoSize != size) {
controlsLayout->setSpacing(0);
m_playButton = new QToolButton(this);
- m_stopButton = new QToolButton(this);
+ m_pauseButton = new QToolButton(this);
m_seekSlider = new Phonon::SeekSlider(this);
controlsLayout->addWidget(m_playButton);
- controlsLayout->addWidget(m_stopButton);
+ controlsLayout->addWidget(m_pauseButton);
controlsLayout->addWidget(m_seekSlider);
m_topLayout->addLayout(controlsLayout);
m_playButton->setAutoRaise(true);
connect(m_playButton, &QToolButton::clicked, this, &PhononWidget::play);
- m_stopButton->setToolTip(i18n("stop"));
- m_stopButton->setIconSize(buttonSize);
- m_stopButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-stop")));
- m_stopButton->setAutoRaise(true);
- m_stopButton->hide();
- connect(m_stopButton, &QToolButton::clicked, this, &PhononWidget::stop);
+ m_pauseButton->setToolTip(i18n("pause"));
+ m_pauseButton->setIconSize(buttonSize);
+ m_pauseButton->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-pause")));
+ m_pauseButton->setAutoRaise(true);
+ m_pauseButton->hide();
+ connect(m_pauseButton, &QToolButton::clicked, this, &PhononWidget::togglePlayback);
m_seekSlider->setIconVisible(false);
switch (newstate) {
case Phonon::PlayingState:
case Phonon::BufferingState:
- m_stopButton->show();
m_playButton->hide();
+ m_pauseButton->show();
break;
default:
- m_stopButton->hide();
+ m_pauseButton->hide();
m_playButton->show();
break;
}
if (!m_videoPlayer) {
m_videoPlayer = new EmbeddedVideoPlayer(this);
+ m_videoPlayer->setCursor(Qt::PointingHandCursor);
m_videoPlayer->installEventFilter(this);
m_topLayout->insertWidget(0, m_videoPlayer);
Phonon::createPath(m_media, m_videoPlayer);
}
}
+Phonon::State PhononWidget::state() const
+{
+ return m_media == nullptr ? Phonon::State::StoppedState : m_media->state();
+}
+
void PhononWidget::stop()
{
if (m_media) {
void setUrl(const QUrl &url, MediaKind kind);
QUrl url() const;
+ void clearUrl();
void setVideoSize(const QSize& size);
QSize videoSize() const;
+ Phonon::State state() const;
void setAutoPlay(bool autoPlay);
+ bool eventFilter(QObject *object, QEvent *event) override;
signals:
/**
void applyVideoSize();
private:
+ void togglePlayback();
+
QUrl m_url;
QSize m_videoSize;
QToolButton *m_playButton;
- QToolButton *m_stopButton;
+ QToolButton *m_pauseButton;
QVBoxLayout *m_topLayout;
Phonon::MediaObject *m_media;
bool TerminalPanel::isHiddenInVisibleWindow() const
{
return parentWidget()
- && parentWidget()->isHidden()
- && m_terminal
- && !hasProgramRunning();
+ && parentWidget()->isHidden();
}
void TerminalPanel::dockVisibilityChanged()
{
// Only react when the DockWidget itself (not some parent) is hidden. This way we don't
// respond when e.g. Dolphin is minimized.
- if (isHiddenInVisibleWindow()) {
+ if (isHiddenInVisibleWindow() && m_terminal && !hasProgramRunning()) {
// Make sure that the following "cd /" command will not affect the view.
disconnect(m_konsolePart, SIGNAL(currentDirectoryChanged(QString)),
this, SLOT(slotKonsolePartCurrentDirectoryChanged(QString)));
// View properties
m_globalViewProps = new QRadioButton(i18nc("@option:radio", "Use common properties for all folders"));
m_localViewProps = new QRadioButton(i18nc("@option:radio", "Remember properties for each folder"));
- m_localViewProps->setToolTip(i18nc("@info", "Dolphin will create an hidden .directory file in each folder you change view properties for."));
+ m_localViewProps->setToolTip(i18nc("@info", "Dolphin will create a hidden .directory file in each folder you change view properties for."));
QButtonGroup* viewGroup = new QButtonGroup(this);
viewGroup->addButton(m_globalViewProps);
#include <QDir>
#include <QDirIterator>
#include <QCommandLineParser>
+#include <QMimeDatabase>
#include <KLocalizedString>
exit(1);
}
-bool evaluateShell(const QString &program, const QStringList &arguments, QString &output, QString &errorText)
-{
- QProcess process;
- process.start(program, arguments, QIODevice::ReadOnly);
- if (!process.waitForStarted()) {
- fail(i18n("Failed to run process: %1 %2", program, arguments.join(" ")));
- }
-
- if (!process.waitForFinished()) {
- fail(i18n("Process did not finish in reasonable time: %1 %2", program, arguments.join(" ")));
- }
-
- const auto stdoutResult = QString::fromUtf8(process.readAllStandardOutput()).trimmed();
- const auto stderrResult = QString::fromUtf8(process.readAllStandardError()).trimmed();
-
- if (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0) {
- output = stdoutResult;
- return true;
- } else {
- errorText = stderrResult + stdoutResult;
- return false;
- }
-}
-
-QString mimeType(const QString &path)
-{
- QString result;
- QString errorText;
- if (evaluateShell("xdg-mime", QStringList{"query", "filetype", path}, result, errorText)) {
- return result;
- } else {
- fail(i18n("Failed to run xdg-mime %1: %2", path, errorText));
- }
-}
-
QString getServiceMenusDir()
{
const QString dataLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
"multipart/x-zip"},
UncompressCommand{"unzip", QStringList{}, QStringList{"-d"}}});
- const auto mime = mimeType(inputPath);
+ const auto mime = QMimeDatabase().mimeTypeForFile(inputPath).name();
UncompressCommand command{};
for (const auto &pair : mimeTypeToCommand) {
m_container = new KItemListContainer(controller, this);
m_container->installEventFilter(this);
setFocusProxy(m_container);
- connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, &DolphinView::hideToolTip);
- connect(m_container->verticalScrollBar(), &QScrollBar::valueChanged, this, &DolphinView::hideToolTip);
+ connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); });
+ connect(m_container->verticalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); });
controller->setSelectionBehavior(KItemListController::MultiSelection);
connect(controller, &KItemListController::itemActivated, this, &DolphinView::slotItemActivated);
break;
case QEvent::KeyPress:
+ hideToolTip(ToolTipManager::HideBehavior::Instantly);
if (GeneralSettings::useTabForSwitchingSplitView()) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier) {
}
}
-void DolphinView::hideToolTip()
+void DolphinView::hideToolTip(const ToolTipManager::HideBehavior behavior)
{
#ifdef HAVE_BALOO
if (GeneralSettings::showToolTips()) {
- m_toolTipManager->hideToolTip();
+ m_toolTipManager->hideToolTip(behavior);
}
#endif
}
#include "dolphintabwidget.h"
#include "dolphin_export.h"
+#include "tooltips/tooltipmanager.h"
#include <KFileItem>
#include <KIO/Job>
*/
void updateViewState();
- void hideToolTip();
-
/**
* Calculates the number of currently shown files into
* \a fileCount and the number of folders into \a folderCount.
*/
void applyModeToView();
+ /**
+ * Hides tooltip displayed over element.
+ */
+ void hideToolTip(const ToolTipManager::HideBehavior behavior = ToolTipManager::HideBehavior::Later);
+
/**
* Helper method for DolphinView::paste() and DolphinView::pasteIntoFolder().
* Pastes the clipboard data into the URL \a url.
"</interface> option is enabled.</para>"));
compactAction->setWhatsThis(xi18nc("@info:whatsthis Compact view mode",
"<para>This switches to a compact view mode that lists the folders "
- "and files in columns with the names beside the icons. <para></para>"
+ "and files in columns with the names beside the icons.</para><para>"
"This helps to keep the overview in folders with many items.</para>"));
detailsAction->setWhatsThis(xi18nc("@info:whatsthis Details view mode",
"<para>This switches to a list view mode that focuses on folder "
QAction* zoomOutAction = KStandardAction::zoomOut(this,
&DolphinViewActionHandler::zoomOut,
m_actionCollection);
- zoomOutAction->setWhatsThis(i18nc("@info:whatsthis zoom in", "This reduces the icon size."));
+ zoomOutAction->setWhatsThis(i18nc("@info:whatsthis zoom out", "This reduces the icon size."));
KToggleAction* showPreview = m_actionCollection->add<KToggleAction>(QStringLiteral("show_preview"));
showPreview->setText(i18nc("@action:intoolbar", "Preview"));
QActionGroup* DolphinViewActionHandler::createFileItemRolesActionGroup(const QString& groupPrefix)
{
const bool isSortGroup = (groupPrefix == QLatin1String("sort_by_"));
- Q_ASSERT(isSortGroup || (!isSortGroup && groupPrefix == QLatin1String("show_")));
+ Q_ASSERT(isSortGroup || groupPrefix == QLatin1String("show_"));
QActionGroup* rolesActionGroup = new QActionGroup(m_actionCollection);
rolesActionGroup->setExclusive(isSortGroup);
Q_ASSERT(!m_metaDataRequested);
}
-void ToolTipManager::hideToolTip()
+void ToolTipManager::hideToolTip(const HideBehavior behavior)
{
if (m_appliedWaitCursor) {
QApplication::restoreOverrideCursor();
m_showToolTipTimer->stop();
m_contentRetrievalTimer->stop();
if (m_tooltipWidget) {
- m_tooltipWidget->hideLater();
+ switch (behavior) {
+ case HideBehavior::Instantly:
+ m_tooltipWidget->hide();
+ break;
+ case HideBehavior::Later:
+ m_tooltipWidget->hideLater();
+ break;
+ }
}
}
Q_OBJECT
public:
+ enum class HideBehavior {
+ Instantly,
+ Later
+ };
+
explicit ToolTipManager(QWidget* parent);
~ToolTipManager() override;
/**
* Hides the currently shown tooltip.
*/
- void hideToolTip();
+ void hideToolTip(const HideBehavior behavior = HideBehavior::Later);
signals:
/**
#include <QCryptographicHash>
+#include <KFileItem>
+
namespace {
const int AdditionalInfoViewPropertiesVersion = 1;
const int NameRolePropertiesVersion = 2;
m_filePath = url.toLocalFile();
bool useDestinationDir = !isPartOfHome(m_filePath);
+ if (!useDestinationDir) {
+ const KFileItem fileItem(url);
+ useDestinationDir = fileItem.isSlow();
+ }
+
if (!useDestinationDir) {
const QFileInfo dirInfo(m_filePath);
const QFileInfo fileInfo(m_filePath + QDir::separator() + ViewPropertiesFileName);