Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
find_package(KF6Bookmarks ${KF_DEP_VERSION} REQUIRED)
|
||||
|
||||
configure_file(config-kiofilewidgets.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kiofilewidgets.h)
|
||||
|
||||
add_library(KF6KIOFileWidgets)
|
||||
add_library(KF6::KIOFileWidgets ALIAS KF6KIOFileWidgets)
|
||||
|
||||
set_target_properties(KF6KIOFileWidgets PROPERTIES
|
||||
VERSION ${KIO_VERSION}
|
||||
SOVERSION ${KIO_SOVERSION}
|
||||
EXPORT_NAME KIOFileWidgets
|
||||
)
|
||||
|
||||
target_sources(KF6KIOFileWidgets PRIVATE
|
||||
kfilemetapreview.cpp
|
||||
kimagefilepreview.cpp
|
||||
kpreviewwidgetbase.cpp
|
||||
defaultviewadapter.cpp
|
||||
|
||||
kdiroperator.cpp
|
||||
kdiroperatordetailview.cpp
|
||||
kdiroperatoriconview.cpp
|
||||
kdirsortfilterproxymodel.cpp #used in combination with kdirmodel.cpp
|
||||
kencodingfiledialog.cpp
|
||||
kfilebookmarkhandler.cpp
|
||||
kfilecopytomenu.cpp
|
||||
kfilecustomdialog.cpp
|
||||
kfilefiltercombo.cpp
|
||||
kfilewidget.cpp
|
||||
kfilewidgetdocktitlebar.cpp
|
||||
kfileplacesitem.cpp
|
||||
kfileplacesmodel.cpp
|
||||
kfileplacesview.cpp
|
||||
kfileplaceeditdialog.cpp
|
||||
kfilepreviewgenerator.cpp
|
||||
knameandurlinputdialog.cpp
|
||||
knewfilemenu.cpp
|
||||
kurlnavigatordropdownbutton.cpp
|
||||
kurlnavigatorbuttonbase.cpp
|
||||
kurlnavigatorbutton.cpp
|
||||
kurlnavigatorplacesselector.cpp
|
||||
kurlnavigatorschemecombo.cpp
|
||||
kurlnavigatortogglebutton.cpp
|
||||
kurlnavigator.cpp
|
||||
kurlnavigatormenu.cpp
|
||||
kurlnavigatorpathselectoreventfilter.cpp
|
||||
|
||||
../new_file_templates/templates.qrc
|
||||
)
|
||||
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER KFILEWIDGETS_LOG
|
||||
CATEGORY_NAME kf.kio.filewidgets
|
||||
OLD_CATEGORY_NAMES kf5.kio.filewidgets
|
||||
DESCRIPTION "KFileWidgets (KIO)"
|
||||
EXPORT KIO
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER KIO_KFILEWIDGETS_FW
|
||||
CATEGORY_NAME kf.kio.filewidgets.kfilewidget
|
||||
OLD_CATEGORY_NAMES kf5.kio.filewidgets.kfilewidget
|
||||
DEFAULT_SEVERITY Info
|
||||
DESCRIPTION "KFileWidgets (KIO)"
|
||||
EXPORT KIO
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(KF6KIOFileWidgets
|
||||
HEADER kfilefiltercombo_debug.h
|
||||
IDENTIFIER KIO_KFILEWIDGETS_KFILEFILTERCOMBO
|
||||
CATEGORY_NAME kf.kio.filewidgets.kfilefiltercombo
|
||||
OLD_CATEGORY_NAMES kf5.kio.filewidgets.kfilefiltercombo
|
||||
DEFAULT_SEVERITY Warning
|
||||
DESCRIPTION "KFileFilterCombo (KIO)"
|
||||
EXPORT KIO
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(KF6KIOFileWidgets
|
||||
HEADER kfilewidgets_debug.h
|
||||
IDENTIFIER KFILEWIDGETS_LOG
|
||||
CATEGORY_NAME kf.kio.filewidgets.kfilewidgets
|
||||
OLD_CATEGORY_NAMES kf5.kio.filewidgets.kfilewidgets
|
||||
DEFAULT_SEVERITY Warning
|
||||
DESCRIPTION "KFileWidgets (KIO)"
|
||||
EXPORT KIO
|
||||
)
|
||||
|
||||
ecm_generate_export_header(KF6KIOFileWidgets
|
||||
BASE_NAME KIOFileWidgets
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
USE_VERSION_HEADER
|
||||
VERSION_BASE_NAME KIO
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
DEPRECATION_VERSIONS 6.3
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_include_directories(KF6KIOFileWidgets INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KIOFileWidgets>")
|
||||
|
||||
target_link_libraries(KF6KIOFileWidgets
|
||||
PUBLIC
|
||||
KF6::KIOWidgets
|
||||
KF6::Bookmarks # in KFilePlacesModel's API
|
||||
KF6::ItemViews # kdirsortfilterproxymodel
|
||||
KF6::Solid # KFilePlacesModel/KFilePlacesView
|
||||
PRIVATE
|
||||
KF6::GuiAddons # KIconUtils
|
||||
KF6::IconThemes # KIconLoader
|
||||
KF6::IconWidgets # KIconButton
|
||||
KF6::I18n
|
||||
KF6::ConfigGui
|
||||
KF6::BookmarksWidgets
|
||||
KF6::ColorScheme
|
||||
)
|
||||
if (Qt6Core_VERSION VERSION_LESS 6.7.0)
|
||||
target_link_libraries(KF6KIOFileWidgets PRIVATE Qt6::Core5Compat)
|
||||
endif()
|
||||
|
||||
ecm_generate_headers(KIOFileWidgets_HEADERS
|
||||
HEADER_NAMES
|
||||
KAbstractViewAdapter
|
||||
KImageFilePreview
|
||||
KPreviewWidgetBase
|
||||
KDirOperator
|
||||
KDirSortFilterProxyModel
|
||||
KFileCopyToMenu
|
||||
KFileCustomDialog
|
||||
KFileFilterCombo
|
||||
KFilePlaceEditDialog
|
||||
KFilePlacesModel
|
||||
KFilePlacesView
|
||||
KFilePreviewGenerator
|
||||
KFileWidget
|
||||
KUrlNavigator
|
||||
KNewFileMenu
|
||||
KNameAndUrlInputDialog
|
||||
KEncodingFileDialog
|
||||
|
||||
REQUIRED_HEADERS KIOFileWidgets_HEADERS
|
||||
)
|
||||
|
||||
install(TARGETS KF6KIOFileWidgets EXPORT KF6KIOTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
install(FILES
|
||||
${KIOFileWidgets_HEADERS}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/kiofilewidgets_export.h
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KIOFileWidgets COMPONENT Devel)
|
||||
|
||||
# make available to ecm_add_qch in parent folder
|
||||
set(KIOFileWidgets_QCH_SOURCES ${KIOFileWidgets_HEADERS} PARENT_SCOPE)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/** @mainpage File Selection Widgets
|
||||
|
||||
@section purpose Purpose
|
||||
|
||||
The KIOFileWidgets library provides the file selection dialog and its components.
|
||||
|
||||
@section desc Description
|
||||
|
||||
The file dialog is available to all kde applications by using the QFileDialog class.
|
||||
The KDE QPA theme plugin uses a KFileDialog automatically.
|
||||
|
||||
The KIOFileWidgets library provides KFileWidget, which is the main widget in the file dialog, as well as
|
||||
the KDirOperator widget which contains the file views, and other components of the file dialog.
|
||||
Applications only need to link to KIOFileWidgets if they need direct access to KFileWidget
|
||||
or KDirOperator or to the other widgets used by the file dialog, either in order to
|
||||
modify the behavior of the file dialog, or to reuse its components into the application itself.
|
||||
|
||||
@authors
|
||||
Stephan Kulow<br>
|
||||
Carsten Pfeiffer<br>
|
||||
Peter Penz<br>
|
||||
Aaron J. Seigo<br>
|
||||
and others
|
||||
|
||||
@maintainers
|
||||
David Faure
|
||||
|
||||
@licenses
|
||||
@lgpl
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// DOXYGEN_REFERENCES = KIOCore KIOWidgets
|
||||
// DOXYGEN_SET_PROJECT_NAME = KIOFileWidgets
|
||||
// vim:ts=4:sw=4:expandtab:filetype=doxygen
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Stephan Kulow <coolo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef DEFAULTS_KFILE_H
|
||||
#define DEFAULTS_KFILE_H
|
||||
|
||||
#include <QLatin1String>
|
||||
|
||||
static const bool DefaultShowHidden = false;
|
||||
static const bool DefaultDirsFirst = true;
|
||||
static const bool DefaultHiddenFilesLast = false;
|
||||
static const bool DefaultSortReversed = false;
|
||||
static constexpr int DefaultRecentURLsNumber = 15;
|
||||
static const bool DefaultAutoSelectExtChecked = true;
|
||||
static const QLatin1String ConfigGroup("KFileDialog Settings");
|
||||
static const QLatin1String RecentURLs("Recent URLs");
|
||||
static const QLatin1String RecentFiles("Recent Files");
|
||||
static const QLatin1String RecentURLsNumber("Maximum of recent URLs");
|
||||
static const QLatin1String RecentFilesNumber("Maximum of recent files");
|
||||
static const QLatin1String PathComboCompletionMode("PathCombo Completionmode");
|
||||
static const QLatin1String LocationComboCompletionMode("LocationCombo Completionmode");
|
||||
static const QLatin1String ShowSpeedbar("Show Speedbar");
|
||||
static const QLatin1String SpeedbarWidth("Speedbar Width");
|
||||
static const QLatin1String ShowBookmarks("Show Bookmarks");
|
||||
static const QLatin1String AutoSelectExtChecked("Automatically select filename extension");
|
||||
static const QLatin1String BreadcrumbNavigation("Breadcrumb Navigation");
|
||||
static const QLatin1String ShowFullPath("Show Full Path");
|
||||
static const QLatin1String PlacesIconsAutoresize("Places Icons Auto-resize");
|
||||
static const QLatin1String PlacesIconsStaticSize("Places Icons Static Size");
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Peter Penz <peter.penz@gmx.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "defaultviewadapter_p.h"
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QScrollBar>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
DefaultViewAdapter::DefaultViewAdapter(QAbstractItemView *view, QObject *parent)
|
||||
: KAbstractViewAdapter(parent)
|
||||
, m_view(view)
|
||||
{
|
||||
}
|
||||
|
||||
QAbstractItemModel *DefaultViewAdapter::model() const
|
||||
{
|
||||
return m_view->model();
|
||||
}
|
||||
|
||||
QSize DefaultViewAdapter::iconSize() const
|
||||
{
|
||||
return m_view->iconSize();
|
||||
}
|
||||
|
||||
QPalette DefaultViewAdapter::palette() const
|
||||
{
|
||||
return m_view->palette();
|
||||
}
|
||||
|
||||
QRect DefaultViewAdapter::visibleArea() const
|
||||
{
|
||||
return m_view->viewport()->rect();
|
||||
}
|
||||
|
||||
QRect DefaultViewAdapter::visualRect(const QModelIndex &index) const
|
||||
{
|
||||
return m_view->visualRect(index);
|
||||
}
|
||||
|
||||
void DefaultViewAdapter::connect(Signal signal, QObject *receiver, const char *slot)
|
||||
{
|
||||
if (signal == ScrollBarValueChanged) {
|
||||
QObject::connect(m_view->horizontalScrollBar(), SIGNAL(valueChanged(int)), receiver, slot);
|
||||
QObject::connect(m_view->verticalScrollBar(), SIGNAL(valueChanged(int)), receiver, slot);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_defaultviewadapter_p.cpp"
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Peter Penz <peter.penz@gmx.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef DEFAULTVIEWADAPTER_H
|
||||
#define DEFAULTVIEWADAPTER_H
|
||||
|
||||
#include "kabstractviewadapter.h"
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
class QAbstractItemView;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
/**
|
||||
* Implementation of the view adapter for the default case when
|
||||
* an instance of QAbstractItemView is used as view.
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT DefaultViewAdapter : public KAbstractViewAdapter
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DefaultViewAdapter(QAbstractItemView *view, QObject *parent);
|
||||
QAbstractItemModel *model() const override;
|
||||
QSize iconSize() const override;
|
||||
QPalette palette() const override;
|
||||
QRect visibleArea() const override;
|
||||
QRect visualRect(const QModelIndex &index) const override;
|
||||
void connect(Signal signal, QObject *receiver, const char *slot) override;
|
||||
|
||||
private:
|
||||
QAbstractItemView *m_view;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Fredrik Höglund <fredrik@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KABSTRACTVIEWADAPTER_H
|
||||
#define KABSTRACTVIEWADAPTER_H
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
#include <QObject>
|
||||
|
||||
class QAbstractItemModel;
|
||||
class QModelIndex;
|
||||
class QPalette;
|
||||
class QRect;
|
||||
class QSize;
|
||||
|
||||
/*
|
||||
* TODO KF6 Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize NOTIFY iconSizeChanged)
|
||||
* TODO KF6 virtual void setIconSize(const QSize &size);
|
||||
* TODO KF6 iconSizeChanged();
|
||||
*
|
||||
* TODO KF6:
|
||||
* KAbstractViewAdapter exists to allow KFilePreviewGenerator to be
|
||||
* reused with new kinds of views. Unfortunately it doesn't cover
|
||||
* all use cases that would be useful right now, in particular there
|
||||
* are no change notifications for the properties it has getters for.
|
||||
* This requires view implementations to e.g. call updateIcons() on
|
||||
* the generator when the icon size changes, which means updating two
|
||||
* entities (the generator and the adapter) instead of only one.
|
||||
* In KF6 we should make iconSize a Q_PROPERTY with a virtual setter
|
||||
* and a change notification signal, and make KFilePreviewGenerator
|
||||
* listen to that signal.
|
||||
* A related problem is that while the adapter is supposed to inter-
|
||||
* face a view to the generator, it is sometimes the generator that
|
||||
* is responsible for instantiating the adapter: KDirOperator in this
|
||||
* framework uses the KFilePreviewGenerator constructor that doesn't
|
||||
* take an adapter instance, which makes the generator instantiate a
|
||||
* KIO::DefaultViewAdapter internally, which it doesn't expose to the
|
||||
* outside. That means even when a setIconSize() is added,
|
||||
* KDirOperator won't be able to call it on the adapter. This mis-
|
||||
* design needs to be addressed as well so all change notifications
|
||||
* can run through the adapter, also for the DefaultViewAdapter
|
||||
* implementation (though for this specific example, perhaps Qt will
|
||||
* one day give us a NOTIFY for QAbstractItemView::iconSize that the
|
||||
* DefaultViewAdapter can use, obviating the need for KDirOperator
|
||||
* to do anything except call setIconSize on its QAbstractItemView).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class KAbstractViewAdapter kabstractviewadapter.h <KAbstractViewAdapter>
|
||||
*
|
||||
* Interface used by KFilePreviewGenerator to generate previews
|
||||
* for files. The interface allows KFilePreviewGenerator to be
|
||||
* independent from the view implementation.
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KAbstractViewAdapter : public QObject
|
||||
{
|
||||
public:
|
||||
enum Signal {
|
||||
ScrollBarValueChanged,
|
||||
IconSizeChanged
|
||||
};
|
||||
|
||||
KAbstractViewAdapter(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
~KAbstractViewAdapter() override
|
||||
{
|
||||
}
|
||||
virtual QAbstractItemModel *model() const = 0;
|
||||
virtual QSize iconSize() const = 0;
|
||||
virtual QPalette palette() const = 0;
|
||||
virtual QRect visibleArea() const = 0;
|
||||
virtual QRect visualRect(const QModelIndex &index) const = 0;
|
||||
|
||||
// TODO KF6 make this connect work with a PointerToMemberFunction/Functor
|
||||
virtual void connect(Signal signal, QObject *receiver, const char *slot) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007 Peter Penz <peter.penz@gmx.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kdiroperatordetailview_p.h"
|
||||
|
||||
#include <kdirlister.h>
|
||||
#include <kdirmodel.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QEvent>
|
||||
#include <QHeaderView>
|
||||
#include <QListView>
|
||||
#include <QMimeData>
|
||||
#include <QScrollBar>
|
||||
|
||||
KDirOperatorDetailView::KDirOperatorDetailView(QWidget *parent)
|
||||
: QTreeView(parent)
|
||||
, m_hideDetailColumns(false)
|
||||
{
|
||||
setRootIsDecorated(false);
|
||||
setSortingEnabled(true);
|
||||
setUniformRowHeights(true);
|
||||
setDragDropMode(QListView::DragOnly);
|
||||
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
setVerticalScrollMode(QListView::ScrollPerPixel);
|
||||
setHorizontalScrollMode(QListView::ScrollPerPixel);
|
||||
|
||||
const QFontMetrics metrics(viewport()->font());
|
||||
const int singleStep = metrics.height() * QApplication::wheelScrollLines();
|
||||
|
||||
verticalScrollBar()->setSingleStep(singleStep);
|
||||
horizontalScrollBar()->setSingleStep(singleStep);
|
||||
}
|
||||
|
||||
KDirOperatorDetailView::~KDirOperatorDetailView()
|
||||
{
|
||||
}
|
||||
|
||||
bool KDirOperatorDetailView::setViewMode(KFile::FileView viewMode)
|
||||
{
|
||||
bool tree = false;
|
||||
|
||||
if (KFile::isDetailView(viewMode)) {
|
||||
m_hideDetailColumns = false;
|
||||
setAlternatingRowColors(true);
|
||||
} else if (KFile::isTreeView(viewMode)) {
|
||||
m_hideDetailColumns = true;
|
||||
setAlternatingRowColors(false);
|
||||
tree = true;
|
||||
} else if (KFile::isDetailTreeView(viewMode)) {
|
||||
m_hideDetailColumns = false;
|
||||
setAlternatingRowColors(true);
|
||||
tree = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
setRootIsDecorated(tree);
|
||||
setItemsExpandable(tree);
|
||||
// This allows to have a horizontal scrollbar in case this view is used as
|
||||
// a plain treeview instead of cutting off filenames, especially useful when
|
||||
// using KDirOperator in horizontally limited parts of an app.
|
||||
if (tree && m_hideDetailColumns) {
|
||||
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
} else {
|
||||
header()->setSectionResizeMode(QHeaderView::Interactive);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KDirOperatorDetailView::initViewItemOption(QStyleOptionViewItem *option) const
|
||||
{
|
||||
QTreeView::initViewItemOption(option);
|
||||
option->textElideMode = Qt::ElideMiddle;
|
||||
}
|
||||
|
||||
bool KDirOperatorDetailView::event(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::Polish) {
|
||||
QHeaderView *headerView = header();
|
||||
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
headerView->setSectionResizeMode(1, QHeaderView::ResizeToContents);
|
||||
headerView->setSectionResizeMode(2, QHeaderView::ResizeToContents);
|
||||
headerView->setStretchLastSection(false);
|
||||
headerView->setSectionsMovable(false);
|
||||
|
||||
setColumnHidden(KDirModel::Size, m_hideDetailColumns);
|
||||
setColumnHidden(KDirModel::ModifiedTime, m_hideDetailColumns);
|
||||
hideColumn(KDirModel::Type);
|
||||
hideColumn(KDirModel::Permissions);
|
||||
hideColumn(KDirModel::Owner);
|
||||
hideColumn(KDirModel::Group);
|
||||
} else if (event->type() == QEvent::UpdateRequest) {
|
||||
// A wheel movement will scroll 4 items
|
||||
if (model()->rowCount()) {
|
||||
verticalScrollBar()->setSingleStep((sizeHintForRow(0) / 3) * 4);
|
||||
}
|
||||
}
|
||||
|
||||
return QTreeView::event(event);
|
||||
}
|
||||
|
||||
void KDirOperatorDetailView::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if (event->mimeData()->hasUrls()) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
||||
void KDirOperatorDetailView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
QTreeView::mousePressEvent(event);
|
||||
|
||||
const QModelIndex index = indexAt(event->pos());
|
||||
if (!index.isValid() || (index.column() != KDirModel::Name)) {
|
||||
const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
|
||||
if (!(modifiers & Qt::ShiftModifier) && !(modifiers & Qt::ControlModifier)) {
|
||||
clearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KDirOperatorDetailView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
||||
{
|
||||
QTreeView::currentChanged(current, previous);
|
||||
}
|
||||
|
||||
#include "moc_kdiroperatordetailview_p.cpp"
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007 Peter Penz <peter.penz@gmx.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KDIROPERATORDETAILVIEW_P_H
|
||||
#define KDIROPERATORDETAILVIEW_P_H
|
||||
|
||||
#include <QTreeView>
|
||||
|
||||
#include <kfile.h>
|
||||
|
||||
class QAbstractItemModel;
|
||||
|
||||
/**
|
||||
* Default detail view for KDirOperator using
|
||||
* custom resizing options and columns.
|
||||
*/
|
||||
class KDirOperatorDetailView : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KDirOperatorDetailView(QWidget *parent = nullptr);
|
||||
~KDirOperatorDetailView() override;
|
||||
|
||||
/**
|
||||
* Displays either Detail, Tree or DetailTree modes.
|
||||
*/
|
||||
virtual bool setViewMode(KFile::FileView viewMode);
|
||||
|
||||
protected:
|
||||
void initViewItemOption(QStyleOptionViewItem *option) const override;
|
||||
|
||||
bool event(QEvent *event) override;
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override;
|
||||
|
||||
private:
|
||||
bool m_hideDetailColumns;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2019 Méven Car <meven.car@kdemail.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kdiroperatoriconview_p.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QMimeData>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include <KFileItemDelegate>
|
||||
#include <KIconLoader>
|
||||
|
||||
KDirOperatorIconView::KDirOperatorIconView(QWidget *parent, QStyleOptionViewItem::Position aDecorationPosition)
|
||||
: QListView(parent)
|
||||
{
|
||||
setViewMode(QListView::IconMode);
|
||||
setResizeMode(QListView::Adjust);
|
||||
setSpacing(0);
|
||||
setMovement(QListView::Static);
|
||||
setDragDropMode(QListView::DragOnly);
|
||||
setVerticalScrollMode(QListView::ScrollPerPixel);
|
||||
setHorizontalScrollMode(QListView::ScrollPerPixel);
|
||||
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
setWordWrap(true);
|
||||
setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall));
|
||||
|
||||
decorationPosition = aDecorationPosition;
|
||||
|
||||
const QFontMetrics metrics(viewport()->font());
|
||||
const int singleStep = metrics.height() * QApplication::wheelScrollLines();
|
||||
|
||||
verticalScrollBar()->setSingleStep(singleStep);
|
||||
horizontalScrollBar()->setSingleStep(singleStep);
|
||||
|
||||
updateLayout();
|
||||
connect(this, &QListView::iconSizeChanged, this, &KDirOperatorIconView::updateLayout);
|
||||
}
|
||||
|
||||
KDirOperatorIconView::~KDirOperatorIconView()
|
||||
{
|
||||
}
|
||||
|
||||
void KDirOperatorIconView::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
void KDirOperatorIconView::initViewItemOption(QStyleOptionViewItem *option) const
|
||||
{
|
||||
QListView::initViewItemOption(option);
|
||||
option->showDecorationSelected = true;
|
||||
option->textElideMode = Qt::ElideMiddle;
|
||||
option->decorationPosition = decorationPosition;
|
||||
if (option->decorationPosition == QStyleOptionViewItem::Left) {
|
||||
option->displayAlignment = Qt::AlignLeft | Qt::AlignVCenter;
|
||||
} else {
|
||||
option->displayAlignment = Qt::AlignCenter;
|
||||
}
|
||||
}
|
||||
|
||||
void KDirOperatorIconView::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if (event->mimeData()->hasUrls()) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
||||
void KDirOperatorIconView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!indexAt(event->pos()).isValid()) {
|
||||
const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers();
|
||||
if (!(modifiers & Qt::ShiftModifier) && !(modifiers & Qt::ControlModifier)) {
|
||||
clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
QListView::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void KDirOperatorIconView::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
QListView::wheelEvent(event);
|
||||
|
||||
// apply the vertical wheel event to the horizontal scrollbar, as
|
||||
// the items are aligned from left to right
|
||||
if (event->angleDelta().y() != 0) {
|
||||
QWheelEvent horizEvent(event->position(),
|
||||
event->globalPosition(),
|
||||
QPoint(event->pixelDelta().y(), 0),
|
||||
QPoint(event->angleDelta().y(), 0),
|
||||
event->buttons(),
|
||||
event->modifiers(),
|
||||
event->phase(),
|
||||
event->inverted(),
|
||||
event->source());
|
||||
QApplication::sendEvent(horizontalScrollBar(), &horizEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void KDirOperatorIconView::updateLayout()
|
||||
{
|
||||
if (decorationPosition == QStyleOptionViewItem::Position::Top) {
|
||||
// Icons view
|
||||
setFlow(QListView::LeftToRight);
|
||||
const QFontMetrics metrics(viewport()->font());
|
||||
|
||||
const int height = iconSize().height() + metrics.height() * 2.5;
|
||||
const int minWidth = qMax(height, metrics.height() * 5);
|
||||
|
||||
const int scrollBarWidth = verticalScrollBar()->sizeHint().width();
|
||||
|
||||
// Subtract 1 px to prevent flickering when resizing the window
|
||||
// For Oxygen a column is missing after showing the dialog without resizing it,
|
||||
// therefore subtract 4 more (scaled) pixels
|
||||
const int viewPortWidth = contentsRect().width() - scrollBarWidth - 1 - 4 * devicePixelRatioF();
|
||||
const int itemsInRow = qMax(1, viewPortWidth / minWidth);
|
||||
const int remainingWidth = viewPortWidth - (minWidth * itemsInRow);
|
||||
const int width = minWidth + (remainingWidth / itemsInRow);
|
||||
|
||||
const QSize itemSize(width, height);
|
||||
|
||||
setGridSize(itemSize);
|
||||
KFileItemDelegate *delegate = qobject_cast<KFileItemDelegate *>(itemDelegate());
|
||||
if (delegate) {
|
||||
delegate->setMaximumSize(itemSize);
|
||||
}
|
||||
} else {
|
||||
// compact view
|
||||
setFlow(QListView::TopToBottom);
|
||||
setGridSize(QSize());
|
||||
KFileItemDelegate *delegate = qobject_cast<KFileItemDelegate *>(itemDelegate());
|
||||
if (delegate) {
|
||||
delegate->setMaximumSize(QSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
void KDirOperatorIconView::setDecorationPosition(QStyleOptionViewItem::Position newDecorationPosition)
|
||||
{
|
||||
decorationPosition = newDecorationPosition;
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
#include "moc_kdiroperatoriconview_p.cpp"
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2019 Méven Car <meven.car@kdemail.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KDIROPERATORICONVIEW_P_H
|
||||
#define KDIROPERATORICONVIEW_P_H
|
||||
|
||||
#include <QListView>
|
||||
|
||||
/**
|
||||
* Default icon view for KDirOperator using
|
||||
* custom view options.
|
||||
*/
|
||||
class KDirOperatorIconView : public QListView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KDirOperatorIconView(QWidget *parent = nullptr, QStyleOptionViewItem::Position decorationPosition = QStyleOptionViewItem::Position::Top);
|
||||
~KDirOperatorIconView() override;
|
||||
void setDecorationPosition(QStyleOptionViewItem::Position decorationPosition);
|
||||
|
||||
protected:
|
||||
void initViewItemOption(QStyleOptionViewItem *option) const override;
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
protected
|
||||
Q_SLOT : void updateLayout();
|
||||
|
||||
private:
|
||||
QStyleOptionViewItem::Position decorationPosition;
|
||||
};
|
||||
|
||||
#endif // KDIROPERATORICONVIEW_P_H
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2006 Dominic Battre <dominic@battre.de>
|
||||
SPDX-FileCopyrightText: 2006 Martin Pool <mbp@canonical.com>
|
||||
|
||||
Separated from Dolphin by Nick Shaforostoff <shafff@ukr.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kdirsortfilterproxymodel.h"
|
||||
|
||||
#include "defaults-kfile.h"
|
||||
|
||||
#include <KConfigGroup>
|
||||
#include <KLocalizedString>
|
||||
#include <KSharedConfig>
|
||||
#include <kdirmodel.h>
|
||||
#include <kfileitem.h>
|
||||
|
||||
#include <QCollator>
|
||||
|
||||
class Q_DECL_HIDDEN KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate
|
||||
{
|
||||
public:
|
||||
KDirSortFilterProxyModelPrivate();
|
||||
|
||||
int compare(const QString &, const QString &, Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive);
|
||||
void slotNaturalSortingChanged();
|
||||
|
||||
bool m_sortFoldersFirst;
|
||||
bool m_sortHiddenFilesLast;
|
||||
bool m_naturalSorting;
|
||||
QCollator m_collator;
|
||||
};
|
||||
|
||||
KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::KDirSortFilterProxyModelPrivate()
|
||||
: m_sortFoldersFirst(true)
|
||||
, m_sortHiddenFilesLast(DefaultHiddenFilesLast)
|
||||
{
|
||||
slotNaturalSortingChanged();
|
||||
}
|
||||
|
||||
int KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::compare(const QString &a, const QString &b, Qt::CaseSensitivity caseSensitivity)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (m_naturalSorting) {
|
||||
m_collator.setCaseSensitivity(caseSensitivity);
|
||||
result = m_collator.compare(a, b);
|
||||
} else {
|
||||
result = QString::compare(a, b, caseSensitivity);
|
||||
}
|
||||
|
||||
if (caseSensitivity == Qt::CaseSensitive || result != 0) {
|
||||
// Only return the result, if the strings are not equal. If they are equal by a case insensitive
|
||||
// comparison, still a deterministic sort order is required. A case sensitive
|
||||
// comparison is done as fallback.
|
||||
return result;
|
||||
}
|
||||
|
||||
return QString::compare(a, b, Qt::CaseSensitive);
|
||||
}
|
||||
|
||||
void KDirSortFilterProxyModel::KDirSortFilterProxyModelPrivate::slotNaturalSortingChanged()
|
||||
{
|
||||
KConfigGroup g(KSharedConfig::openConfig(), QStringLiteral("KDE"));
|
||||
m_naturalSorting = g.readEntry("NaturalSorting", true);
|
||||
m_collator.setNumericMode(m_naturalSorting);
|
||||
}
|
||||
|
||||
KDirSortFilterProxyModel::KDirSortFilterProxyModel(QObject *parent)
|
||||
: KCategorizedSortFilterProxyModel(parent)
|
||||
, d(new KDirSortFilterProxyModelPrivate)
|
||||
{
|
||||
setDynamicSortFilter(true);
|
||||
|
||||
// sort by the user visible string for now
|
||||
setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
sort(KDirModel::Name, Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
Qt::DropActions KDirSortFilterProxyModel::supportedDragOptions() const
|
||||
{
|
||||
return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction | Qt::IgnoreAction;
|
||||
}
|
||||
|
||||
KDirSortFilterProxyModel::~KDirSortFilterProxyModel() = default;
|
||||
|
||||
bool KDirSortFilterProxyModel::hasChildren(const QModelIndex &parent) const
|
||||
{
|
||||
const QModelIndex sourceParent = mapToSource(parent);
|
||||
return sourceModel()->hasChildren(sourceParent);
|
||||
}
|
||||
|
||||
bool KDirSortFilterProxyModel::canFetchMore(const QModelIndex &parent) const
|
||||
{
|
||||
const QModelIndex sourceParent = mapToSource(parent);
|
||||
return sourceModel()->canFetchMore(sourceParent);
|
||||
}
|
||||
|
||||
int KDirSortFilterProxyModel::pointsForPermissions(const QFileInfo &info)
|
||||
{
|
||||
int points = 0;
|
||||
|
||||
const QFile::Permission permissionsCheck[] = {QFile::ReadUser,
|
||||
QFile::WriteUser,
|
||||
QFile::ExeUser,
|
||||
QFile::ReadGroup,
|
||||
QFile::WriteGroup,
|
||||
QFile::ExeGroup,
|
||||
QFile::ReadOther,
|
||||
QFile::WriteOther,
|
||||
QFile::ExeOther};
|
||||
|
||||
for (QFile::Permission perm : permissionsCheck) {
|
||||
points += info.permission(perm) ? 1 : 0;
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
void KDirSortFilterProxyModel::setSortFoldersFirst(bool foldersFirst)
|
||||
{
|
||||
if (d->m_sortFoldersFirst == foldersFirst) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->m_sortFoldersFirst = foldersFirst;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
bool KDirSortFilterProxyModel::sortFoldersFirst() const
|
||||
{
|
||||
return d->m_sortFoldersFirst;
|
||||
}
|
||||
|
||||
void KDirSortFilterProxyModel::setSortHiddenFilesLast(bool hiddenFilesLast)
|
||||
{
|
||||
if (d->m_sortHiddenFilesLast == hiddenFilesLast) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->m_sortHiddenFilesLast = hiddenFilesLast;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
bool KDirSortFilterProxyModel::sortHiddenFilesLast() const
|
||||
{
|
||||
return d->m_sortHiddenFilesLast;
|
||||
}
|
||||
|
||||
bool KDirSortFilterProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
KDirModel *dirModel = static_cast<KDirModel *>(sourceModel());
|
||||
|
||||
const KFileItem leftFileItem = dirModel->itemForIndex(left);
|
||||
const KFileItem rightFileItem = dirModel->itemForIndex(right);
|
||||
|
||||
const bool isLessThan = (sortOrder() == Qt::AscendingOrder);
|
||||
|
||||
// Show hidden files and folders last
|
||||
if (d->m_sortHiddenFilesLast) {
|
||||
const bool leftItemIsHidden = leftFileItem.isHidden();
|
||||
const bool rightItemIsHidden = rightFileItem.isHidden();
|
||||
if (leftItemIsHidden && !rightItemIsHidden) {
|
||||
return !isLessThan;
|
||||
} else if (!leftItemIsHidden && rightItemIsHidden) {
|
||||
return isLessThan;
|
||||
}
|
||||
}
|
||||
|
||||
// Folders go before files if the corresponding setting is set.
|
||||
if (d->m_sortFoldersFirst) {
|
||||
const bool leftItemIsDir = leftFileItem.isDir();
|
||||
const bool rightItemIsDir = rightFileItem.isDir();
|
||||
if (leftItemIsDir && !rightItemIsDir) {
|
||||
return isLessThan;
|
||||
} else if (!leftItemIsDir && rightItemIsDir) {
|
||||
return !isLessThan;
|
||||
}
|
||||
}
|
||||
|
||||
switch (left.column()) {
|
||||
case KDirModel::Name: {
|
||||
int result = d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity());
|
||||
if (result == 0) {
|
||||
// KFileItem::text() may not be unique in case UDS_DISPLAY_NAME is used
|
||||
result = d->compare(leftFileItem.name(sortCaseSensitivity() == Qt::CaseInsensitive),
|
||||
rightFileItem.name(sortCaseSensitivity() == Qt::CaseInsensitive),
|
||||
sortCaseSensitivity());
|
||||
if (result == 0) {
|
||||
// If KFileItem::text() is also not unique most probably a search protocol is used
|
||||
// that allows showing the same file names from different directories
|
||||
result = d->compare(leftFileItem.url().toString(), rightFileItem.url().toString(), sortCaseSensitivity());
|
||||
}
|
||||
}
|
||||
|
||||
return result < 0;
|
||||
}
|
||||
|
||||
case KDirModel::Size: {
|
||||
// If we have two folders, what we have to measure is the number of
|
||||
// items that contains each other
|
||||
if (leftFileItem.isDir() && rightFileItem.isDir()) {
|
||||
QVariant leftValue = dirModel->data(left, KDirModel::ChildCountRole);
|
||||
int leftCount = (leftValue.typeId() == QMetaType::Int) ? leftValue.toInt() : KDirModel::ChildCountUnknown;
|
||||
|
||||
QVariant rightValue = dirModel->data(right, KDirModel::ChildCountRole);
|
||||
int rightCount = (rightValue.typeId() == QMetaType::Int) ? rightValue.toInt() : KDirModel::ChildCountUnknown;
|
||||
|
||||
// In the case they two have the same child items, we sort them by
|
||||
// their names. So we have always everything ordered. We also check
|
||||
// if we are taking in count their cases.
|
||||
if (leftCount == rightCount) {
|
||||
return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
|
||||
}
|
||||
|
||||
// If one of them has unknown child items, place them on the end. If we
|
||||
// were comparing two unknown childed items, the previous comparison
|
||||
// sorted them by QCollator between them. This case is when we
|
||||
// have an unknown childed item, and another known.
|
||||
if (leftCount == KDirModel::ChildCountUnknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rightCount == KDirModel::ChildCountUnknown) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If they had different number of items, we sort them depending
|
||||
// on how many items had each other.
|
||||
return leftCount < rightCount;
|
||||
}
|
||||
|
||||
// If what we are measuring is two files and they have the same size,
|
||||
// sort them by their file names.
|
||||
if (leftFileItem.size() == rightFileItem.size()) {
|
||||
return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
|
||||
}
|
||||
|
||||
// If their sizes are different, sort them by their sizes, as expected.
|
||||
return leftFileItem.size() < rightFileItem.size();
|
||||
}
|
||||
|
||||
case KDirModel::ModifiedTime: {
|
||||
QDateTime leftModifiedTime = leftFileItem.time(KFileItem::ModificationTime).toLocalTime();
|
||||
QDateTime rightModifiedTime = rightFileItem.time(KFileItem::ModificationTime).toLocalTime();
|
||||
|
||||
if (leftModifiedTime == rightModifiedTime) {
|
||||
return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
|
||||
}
|
||||
|
||||
return leftModifiedTime < rightModifiedTime;
|
||||
}
|
||||
|
||||
case KDirModel::Permissions: {
|
||||
const int leftPermissions = leftFileItem.permissions();
|
||||
const int rightPermissions = rightFileItem.permissions();
|
||||
|
||||
if (leftPermissions == rightPermissions) {
|
||||
return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
|
||||
}
|
||||
|
||||
return leftPermissions > rightPermissions;
|
||||
}
|
||||
|
||||
case KDirModel::Owner: {
|
||||
if (leftFileItem.user() == rightFileItem.user()) {
|
||||
return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
|
||||
}
|
||||
|
||||
return d->compare(leftFileItem.user(), rightFileItem.user()) < 0;
|
||||
}
|
||||
|
||||
case KDirModel::Group: {
|
||||
if (leftFileItem.group() == rightFileItem.group()) {
|
||||
return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
|
||||
}
|
||||
|
||||
return d->compare(leftFileItem.group(), rightFileItem.group()) < 0;
|
||||
}
|
||||
|
||||
case KDirModel::Type: {
|
||||
if (leftFileItem.mimetype() == rightFileItem.mimetype()) {
|
||||
return d->compare(leftFileItem.text(), rightFileItem.text(), sortCaseSensitivity()) < 0;
|
||||
}
|
||||
|
||||
return d->compare(leftFileItem.mimeComment(), rightFileItem.mimeComment()) < 0;
|
||||
}
|
||||
}
|
||||
|
||||
// We have set a SortRole and trust the ProxyModel to do
|
||||
// the right thing for now.
|
||||
return KCategorizedSortFilterProxyModel::subSortLessThan(left, right);
|
||||
}
|
||||
|
||||
#include "moc_kdirsortfilterproxymodel.cpp"
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2006 Dominic Battre <dominic@battre.de>
|
||||
SPDX-FileCopyrightText: 2006 Martin Pool <mbp@canonical.com>
|
||||
|
||||
Separated from Dolphin by Nick Shaforostoff <shafff@ukr.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KDIRSORTFILTERPROXYMODEL_H
|
||||
#define KDIRSORTFILTERPROXYMODEL_H
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <KCategorizedSortFilterProxyModel>
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
/**
|
||||
* @class KDirSortFilterProxyModel kdirsortfilterproxymodel.h <KDirSortFilterProxyModel>
|
||||
*
|
||||
* @brief Acts as proxy model for KDirModel to sort and filter
|
||||
* KFileItems.
|
||||
*
|
||||
* A natural sorting is done. This means that items like:
|
||||
* - item_10.png
|
||||
* - item_1.png
|
||||
* - item_2.png
|
||||
*
|
||||
* are sorted like
|
||||
* - item_1.png
|
||||
* - item_2.png
|
||||
* - item_10.png
|
||||
*
|
||||
* Don't use it with non-KDirModel derivatives.
|
||||
*
|
||||
* @author Dominic Battre, Martin Pool and Peter Penz
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KDirSortFilterProxyModel : public KCategorizedSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KDirSortFilterProxyModel(QObject *parent = nullptr);
|
||||
~KDirSortFilterProxyModel() override;
|
||||
|
||||
/** Reimplemented from QAbstractItemModel. Returns true for directories. */
|
||||
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemModel.
|
||||
* Returns true for 'empty' directories so they can be populated later.
|
||||
*/
|
||||
bool canFetchMore(const QModelIndex &parent) const override;
|
||||
|
||||
/**
|
||||
* Returns the permissions in "points". This is useful for sorting by
|
||||
* permissions.
|
||||
*/
|
||||
static int pointsForPermissions(const QFileInfo &info);
|
||||
|
||||
/**
|
||||
* Choose if files and folders are sorted separately (with folders first) or not.
|
||||
*/
|
||||
void setSortFoldersFirst(bool foldersFirst);
|
||||
|
||||
/**
|
||||
* Returns if files and folders are sorted separately (with folders first) or not.
|
||||
*/
|
||||
bool sortFoldersFirst() const;
|
||||
|
||||
/**
|
||||
* Sets a separate sorting with hidden files and folders last (true) or not (false).
|
||||
* @since 5.95
|
||||
*/
|
||||
void setSortHiddenFilesLast(bool hiddenFilesLast);
|
||||
bool sortHiddenFilesLast() const;
|
||||
|
||||
Qt::DropActions supportedDragOptions() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Reimplemented from KCategorizedSortFilterProxyModel.
|
||||
*/
|
||||
bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const override;
|
||||
|
||||
private:
|
||||
class KDirSortFilterProxyModelPrivate;
|
||||
std::unique_ptr<KDirSortFilterProxyModelPrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,267 @@
|
||||
// -*- c++ -*-
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2003 Joseph Wenninger <jowenn@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Andras Mantia <amantia@freemail.hu>
|
||||
SPDX-FileCopyrightText: 2013 Teo Mrnjavac <teo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kencodingfiledialog.h"
|
||||
|
||||
#include "kfilewidget.h"
|
||||
|
||||
#include <defaults-kfile.h>
|
||||
|
||||
#include <KConfigGroup>
|
||||
#include <KLocalizedString>
|
||||
#include <KSharedConfig>
|
||||
#include <KWindowConfig>
|
||||
#include <krecentdocument.h>
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QStringDecoder>
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
|
||||
#include <QTextCodec>
|
||||
#endif
|
||||
|
||||
struct KEncodingFileDialogPrivate {
|
||||
KEncodingFileDialogPrivate()
|
||||
: cfgGroup(KSharedConfig::openConfig(), ConfigGroup)
|
||||
{
|
||||
}
|
||||
|
||||
QComboBox *encoding;
|
||||
KFileWidget *w;
|
||||
KConfigGroup cfgGroup;
|
||||
};
|
||||
|
||||
KEncodingFileDialog::KEncodingFileDialog(const QUrl &startDir,
|
||||
const QString &encoding,
|
||||
const QString &filter,
|
||||
const QString &title,
|
||||
QFileDialog::AcceptMode type,
|
||||
QWidget *parent)
|
||||
: QDialog(parent, Qt::Dialog)
|
||||
, d(new KEncodingFileDialogPrivate)
|
||||
{
|
||||
d->w = new KFileWidget(startDir, this);
|
||||
d->w->setFilters(KFileFilter::fromFilterString(filter));
|
||||
if (type == QFileDialog::AcceptOpen) {
|
||||
d->w->setOperationMode(KFileWidget::Opening);
|
||||
} else {
|
||||
d->w->setOperationMode(KFileWidget::Saving);
|
||||
}
|
||||
|
||||
setWindowTitle(title);
|
||||
// ops->clearHistory();
|
||||
|
||||
KWindowConfig::restoreWindowSize(windowHandle(), d->cfgGroup);
|
||||
|
||||
QBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->addWidget(d->w);
|
||||
|
||||
d->w->okButton()->show();
|
||||
connect(d->w->okButton(), &QAbstractButton::clicked, this, &KEncodingFileDialog::slotOk);
|
||||
d->w->cancelButton()->show();
|
||||
connect(d->w->cancelButton(), &QAbstractButton::clicked, this, &KEncodingFileDialog::slotCancel);
|
||||
connect(d->w, &KFileWidget::accepted, this, &KEncodingFileDialog::accept);
|
||||
|
||||
d->encoding = new QComboBox(this);
|
||||
d->w->setCustomWidget(i18n("Encoding:"), d->encoding);
|
||||
|
||||
d->encoding->clear();
|
||||
QByteArray sEncoding = encoding.toUtf8();
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
|
||||
const auto systemEncoding = QTextCodec::codecForLocale()->name();
|
||||
#else
|
||||
const auto systemEncoding = QStringDecoder(QStringDecoder::System).name();
|
||||
#endif
|
||||
if (sEncoding.isEmpty() || sEncoding == "System") {
|
||||
sEncoding = systemEncoding;
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
|
||||
auto encodings = QTextCodec::availableCodecs();
|
||||
#else
|
||||
auto encodings = QStringDecoder::availableCodecs();
|
||||
#endif
|
||||
std::sort(encodings.begin(), encodings.end(), [](auto &a, auto &b) {
|
||||
return (a.compare(b, Qt::CaseInsensitive) < 0);
|
||||
});
|
||||
|
||||
int insert = 0;
|
||||
int system = 0;
|
||||
bool foundRequested = false;
|
||||
for (const auto &encoding : encodings) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
|
||||
QTextCodec *codecForEnc = QTextCodec::codecForName(encoding);
|
||||
|
||||
if (codecForEnc) {
|
||||
d->encoding->addItem(QString::fromUtf8(encoding));
|
||||
auto codecName = codecForEnc->name();
|
||||
if ((codecName == sEncoding) || (encoding == sEncoding)) {
|
||||
d->encoding->setCurrentIndex(insert);
|
||||
foundRequested = true;
|
||||
}
|
||||
|
||||
if ((codecName == systemEncoding) || (encoding == systemEncoding)) {
|
||||
system = insert;
|
||||
}
|
||||
insert++;
|
||||
}
|
||||
#else
|
||||
d->encoding->addItem(encoding);
|
||||
const auto codecName = QStringDecoder(encoding.toUtf8().constData()).name();
|
||||
if ((codecName == sEncoding) || (encoding.toUtf8() == sEncoding)) {
|
||||
d->encoding->setCurrentIndex(insert);
|
||||
foundRequested = true;
|
||||
}
|
||||
|
||||
if ((codecName == systemEncoding) || (encoding.toUtf8() == systemEncoding)) {
|
||||
system = insert;
|
||||
}
|
||||
insert++;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!foundRequested) {
|
||||
d->encoding->setCurrentIndex(system);
|
||||
}
|
||||
}
|
||||
|
||||
KEncodingFileDialog::~KEncodingFileDialog() = default;
|
||||
|
||||
QString KEncodingFileDialog::selectedEncoding() const
|
||||
{
|
||||
if (d->encoding) {
|
||||
return d->encoding->currentText();
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
KEncodingFileDialog::Result
|
||||
KEncodingFileDialog::getOpenFileNameAndEncoding(const QString &encoding, const QUrl &startDir, const QString &filter, QWidget *parent, const QString &title)
|
||||
{
|
||||
KEncodingFileDialog dlg(startDir, encoding, filter, title.isNull() ? i18n("Open") : title, QFileDialog::AcceptOpen, parent);
|
||||
|
||||
dlg.d->w->setMode(KFile::File | KFile::LocalOnly);
|
||||
dlg.exec();
|
||||
|
||||
Result res;
|
||||
res.fileNames << dlg.d->w->selectedFile();
|
||||
res.encoding = dlg.selectedEncoding();
|
||||
return res;
|
||||
}
|
||||
|
||||
KEncodingFileDialog::Result
|
||||
KEncodingFileDialog::getOpenFileNamesAndEncoding(const QString &encoding, const QUrl &startDir, const QString &filter, QWidget *parent, const QString &title)
|
||||
{
|
||||
KEncodingFileDialog dlg(startDir, encoding, filter, title.isNull() ? i18n("Open") : title, QFileDialog::AcceptOpen, parent);
|
||||
dlg.d->w->setMode(KFile::Files | KFile::LocalOnly);
|
||||
dlg.exec();
|
||||
|
||||
Result res;
|
||||
res.fileNames = dlg.d->w->selectedFiles();
|
||||
res.encoding = dlg.selectedEncoding();
|
||||
return res;
|
||||
}
|
||||
|
||||
KEncodingFileDialog::Result
|
||||
KEncodingFileDialog::getOpenUrlAndEncoding(const QString &encoding, const QUrl &startDir, const QString &filter, QWidget *parent, const QString &title)
|
||||
{
|
||||
KEncodingFileDialog dlg(startDir, encoding, filter, title.isNull() ? i18n("Open") : title, QFileDialog::AcceptOpen, parent);
|
||||
|
||||
dlg.d->w->setMode(KFile::File);
|
||||
dlg.exec();
|
||||
|
||||
Result res;
|
||||
res.URLs << dlg.d->w->selectedUrl();
|
||||
res.encoding = dlg.selectedEncoding();
|
||||
return res;
|
||||
}
|
||||
|
||||
KEncodingFileDialog::Result
|
||||
KEncodingFileDialog::getOpenUrlsAndEncoding(const QString &encoding, const QUrl &startDir, const QString &filter, QWidget *parent, const QString &title)
|
||||
{
|
||||
KEncodingFileDialog dlg(startDir, encoding, filter, title.isNull() ? i18n("Open") : title, QFileDialog::AcceptOpen, parent);
|
||||
|
||||
dlg.d->w->setMode(KFile::Files);
|
||||
dlg.exec();
|
||||
|
||||
Result res;
|
||||
res.URLs = dlg.d->w->selectedUrls();
|
||||
res.encoding = dlg.selectedEncoding();
|
||||
return res;
|
||||
}
|
||||
|
||||
KEncodingFileDialog::Result
|
||||
KEncodingFileDialog::getSaveFileNameAndEncoding(const QString &encoding, const QUrl &dir, const QString &filter, QWidget *parent, const QString &title)
|
||||
{
|
||||
KEncodingFileDialog dlg(dir, encoding, filter, title.isNull() ? i18n("Save As") : title, QFileDialog::AcceptSave, parent);
|
||||
dlg.d->w->setMode(KFile::File);
|
||||
dlg.exec();
|
||||
|
||||
QString filename = dlg.d->w->selectedFile();
|
||||
if (!filename.isEmpty()) {
|
||||
KRecentDocument::add(QUrl::fromLocalFile(filename));
|
||||
}
|
||||
|
||||
Result res;
|
||||
res.fileNames << filename;
|
||||
res.encoding = dlg.selectedEncoding();
|
||||
return res;
|
||||
}
|
||||
|
||||
KEncodingFileDialog::Result
|
||||
KEncodingFileDialog::getSaveUrlAndEncoding(const QString &encoding, const QUrl &dir, const QString &filter, QWidget *parent, const QString &title)
|
||||
{
|
||||
KEncodingFileDialog dlg(dir, encoding, filter, title.isNull() ? i18n("Save As") : title, QFileDialog::AcceptSave, parent);
|
||||
dlg.d->w->setMode(KFile::File);
|
||||
|
||||
Result res;
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
QUrl url = dlg.d->w->selectedUrl();
|
||||
if (url.isValid()) {
|
||||
KRecentDocument::add(url);
|
||||
}
|
||||
res.URLs << url;
|
||||
res.encoding = dlg.selectedEncoding();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QSize KEncodingFileDialog::sizeHint() const
|
||||
{
|
||||
return d->w->dialogSizeHint();
|
||||
}
|
||||
|
||||
void KEncodingFileDialog::hideEvent(QHideEvent *e)
|
||||
{
|
||||
KWindowConfig::saveWindowSize(windowHandle(), d->cfgGroup, KConfigBase::Persistent);
|
||||
|
||||
QDialog::hideEvent(e);
|
||||
}
|
||||
|
||||
void KEncodingFileDialog::accept()
|
||||
{
|
||||
d->w->accept();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void KEncodingFileDialog::slotOk()
|
||||
{
|
||||
d->w->slotOk();
|
||||
}
|
||||
|
||||
void KEncodingFileDialog::slotCancel()
|
||||
{
|
||||
d->w->slotCancel();
|
||||
reject();
|
||||
}
|
||||
|
||||
#include "moc_kencodingfiledialog.cpp"
|
||||
@@ -0,0 +1,295 @@
|
||||
// -*- c++ -*-
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2003 Joseph Wenninger <jowenn@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Andras Mantia <amantia@freemail.hu>
|
||||
SPDX-FileCopyrightText: 2013 Teo Mrnjavac <teo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __KENCODINGFILEDIALOG_H__
|
||||
#define __KENCODINGFILEDIALOG_H__
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct KEncodingFileDialogPrivate;
|
||||
|
||||
/**
|
||||
* @class KEncodingFileDialog kencodingfiledialog.h <KEncodingFileDialog>
|
||||
*
|
||||
* Provides a user (and developer) friendly way to select files with support for
|
||||
* choosing encoding.
|
||||
* This class comes with a private constructor, the only way to show a file dialog
|
||||
* is through its static methods.
|
||||
*/
|
||||
class KEncodingFileDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
class KIOFILEWIDGETS_EXPORT Result
|
||||
{
|
||||
public:
|
||||
QStringList fileNames;
|
||||
QList<QUrl> URLs;
|
||||
QString encoding;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a modal file dialog and return the selected
|
||||
* filename or an empty string if none was chosen additionally a chosen
|
||||
* encoding value is returned.
|
||||
*
|
||||
* Note that with
|
||||
* this method the user must select an existing filename.
|
||||
*
|
||||
* @param encoding The encoding shown in the encoding combo.
|
||||
* @param startDir This can either be
|
||||
* @li The URL of the directory to start in.
|
||||
* @li QString() to start in the current working
|
||||
* directory, or the last directory where a file has been
|
||||
* selected.
|
||||
* @li ':<keyword>' to start in the directory last used
|
||||
* by a filedialog in the same application that specified
|
||||
* the same keyword.
|
||||
* @li '::<keyword>' to start in the directory last used
|
||||
* by a filedialog in any application that specified the
|
||||
* same keyword.
|
||||
* @param filter A shell glob or a MIME type filter that specifies which files to display.
|
||||
* see KFileFilter::KFileFilter(const QString &label, const QStringList &filePatterns, const QStringList &mimePatterns)
|
||||
* for examples of patterns.
|
||||
* @param parent The widget the dialog will be centered on initially.
|
||||
* @param title The name of the dialog widget.
|
||||
*/
|
||||
static KIOFILEWIDGETS_EXPORT Result getOpenFileNameAndEncoding(const QString &encoding = QString(),
|
||||
const QUrl &startDir = QUrl(),
|
||||
const QString &filter = QString(),
|
||||
QWidget *parent = nullptr,
|
||||
const QString &title = QString());
|
||||
|
||||
/**
|
||||
* Creates a modal file dialog and returns the selected encoding and the selected
|
||||
* filenames or an empty list if none was chosen.
|
||||
*
|
||||
* Note that with
|
||||
* this method the user must select an existing filename.
|
||||
*
|
||||
* @param encoding The encoding shown in the encoding combo.
|
||||
* @param startDir This can either be
|
||||
* @li The URL of the directory to start in.
|
||||
* @li QString() to start in the current working
|
||||
* directory, or the last directory where a file has been
|
||||
* selected.
|
||||
* @li ':<keyword>' to start in the directory last used
|
||||
* by a filedialog in the same application that specified
|
||||
* the same keyword.
|
||||
* @li '::<keyword>' to start in the directory last used
|
||||
* by a filedialog in any application that specified the
|
||||
* same keyword.
|
||||
* @param filter A shell glob or a MIME type filter that specifies which files to display.
|
||||
* see KFileFilter::KFileFilter(const QString &label, const QStringList &filePatterns, const QStringList &mimePatterns)
|
||||
* for examples of patterns.
|
||||
* @param parent The widget the dialog will be centered on initially.
|
||||
* @param title The name of the dialog widget.
|
||||
*/
|
||||
static KIOFILEWIDGETS_EXPORT Result getOpenFileNamesAndEncoding(const QString &encoding = QString(),
|
||||
const QUrl &startDir = QUrl(),
|
||||
const QString &filter = QString(),
|
||||
QWidget *parent = nullptr,
|
||||
const QString &title = QString());
|
||||
|
||||
/**
|
||||
* Creates a modal file dialog and returns the selected encoding and
|
||||
* URL or an empty string if none was chosen.
|
||||
*
|
||||
* Note that with
|
||||
* this method the user must select an existing URL.
|
||||
*
|
||||
* @param encoding The encoding shown in the encoding combo.
|
||||
* @param startDir This can either be
|
||||
* @li The URL of the directory to start in.
|
||||
* @li QString() to start in the current working
|
||||
* directory, or the last directory where a file has been
|
||||
* selected.
|
||||
* @li ':<keyword>' to start in the directory last used
|
||||
* by a filedialog in the same application that specified
|
||||
* the same keyword.
|
||||
* @li '::<keyword>' to start in the directory last used
|
||||
* by a filedialog in any application that specified the
|
||||
* same keyword.
|
||||
* @param filter A shell glob or a MIME type filter that specifies which files to display.
|
||||
* see KFileFilter::KFileFilter(const QString &label, const QStringList &filePatterns, const QStringList &mimePatterns)
|
||||
* for examples of patterns.
|
||||
* @param parent The widget the dialog will be centered on initially.
|
||||
* @param title The name of the dialog widget.
|
||||
*/
|
||||
static KIOFILEWIDGETS_EXPORT Result getOpenUrlAndEncoding(const QString &encoding = QString(),
|
||||
const QUrl &startDir = QUrl(),
|
||||
const QString &filter = QString(),
|
||||
QWidget *parent = nullptr,
|
||||
const QString &title = QString());
|
||||
|
||||
/**
|
||||
* Creates a modal file dialog and returns the selected encoding
|
||||
* URLs or an empty list if none was chosen.
|
||||
*
|
||||
* Note that with
|
||||
* this method the user must select an existing filename.
|
||||
*
|
||||
* @param encoding The encoding shown in the encoding combo.
|
||||
* @param startDir This can either be
|
||||
* @li The URL of the directory to start in.
|
||||
* @li QString() to start in the current working
|
||||
* directory, or the last directory where a file has been
|
||||
* selected.
|
||||
* @li ':<keyword>' to start in the directory last used
|
||||
* by a filedialog in the same application that specified
|
||||
* the same keyword.
|
||||
* @li '::<keyword>' to start in the directory last used
|
||||
* by a filedialog in any application that specified the
|
||||
* same keyword.
|
||||
* @param filter A shell glob or a MIME type filter that specifies which files to display.
|
||||
* see KFileFilter::KFileFilter(const QString &label, const QStringList &filePatterns, const QStringList &mimePatterns)
|
||||
* for examples of patterns.
|
||||
* @param parent The widget the dialog will be centered on initially.
|
||||
* @param title The name of the dialog widget.
|
||||
*/
|
||||
static KIOFILEWIDGETS_EXPORT Result getOpenUrlsAndEncoding(const QString &encoding = QString(),
|
||||
const QUrl &startDir = QUrl(),
|
||||
const QString &filter = QString(),
|
||||
QWidget *parent = nullptr,
|
||||
const QString &title = QString());
|
||||
|
||||
/**
|
||||
* Creates a modal file dialog and returns the selected encoding and
|
||||
* filename or an empty string if none was chosen.
|
||||
*
|
||||
* Note that with this
|
||||
* method the user need not select an existing filename.
|
||||
*
|
||||
* @param encoding The encoding shown in the encoding combo.
|
||||
* @param startDir This can either be
|
||||
* @li The URL of the directory to start in.
|
||||
* @li a relative path or a filename determining the
|
||||
* directory to start in and the file to be selected.
|
||||
* @li QString() to start in the current working
|
||||
* directory, or the last directory where a file has been
|
||||
* selected.
|
||||
* @li ':<keyword>' to start in the directory last used
|
||||
* by a filedialog in the same application that specified
|
||||
* the same keyword.
|
||||
* @li '::<keyword>' to start in the directory last used
|
||||
* by a filedialog in any application that specified the
|
||||
* same keyword.
|
||||
* @param filter A shell glob or a MIME type filter that specifies which files to display.
|
||||
* see KFileFilter::KFileFilter(const QString &label, const QStringList &filePatterns, const QStringList &mimePatterns)
|
||||
* for examples of patterns.
|
||||
* @param parent The widget the dialog will be centered on initially.
|
||||
* @param title The name of the dialog widget.
|
||||
*/
|
||||
static KIOFILEWIDGETS_EXPORT Result getSaveFileNameAndEncoding(const QString &encoding = QString(),
|
||||
const QUrl &startDir = QUrl(),
|
||||
const QString &filter = QString(),
|
||||
QWidget *parent = nullptr,
|
||||
const QString &title = QString());
|
||||
|
||||
/**
|
||||
* Creates a modal file dialog and returns the selected encoding and
|
||||
* filename or an empty string if none was chosen.
|
||||
*
|
||||
* Note that with this
|
||||
* method the user need not select an existing filename.
|
||||
*
|
||||
* @param encoding The encoding shown in the encoding combo.
|
||||
* @param startDir This can either be
|
||||
* @li The URL of the directory to start in.
|
||||
* @li a relative path or a filename determining the
|
||||
* directory to start in and the file to be selected.
|
||||
* @li QString() to start in the current working
|
||||
* directory, or the last directory where a file has been
|
||||
* selected.
|
||||
* @li ':<keyword>' to start in the directory last used
|
||||
* by a filedialog in the same application that specified
|
||||
* the same keyword.
|
||||
* @li '::<keyword>' to start in the directory last used
|
||||
* by a filedialog in any application that specified the
|
||||
* same keyword.
|
||||
* @param filter A shell glob or a MIME type filter that specifies which files to display.
|
||||
* see KFileFilter::KFileFilter(const QString &label, const QStringList &filePatterns, const QStringList &mimePatterns)
|
||||
* for examples of patterns.
|
||||
* @param parent The widget the dialog will be centered on initially.
|
||||
* @param title The name of the dialog widget.
|
||||
*/
|
||||
static KIOFILEWIDGETS_EXPORT Result getSaveUrlAndEncoding(const QString &encoding = QString(),
|
||||
const QUrl &startDir = QUrl(),
|
||||
const QString &filter = QString(),
|
||||
QWidget *parent = nullptr,
|
||||
const QString &title = QString());
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent *e) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void accept() override;
|
||||
|
||||
void slotOk();
|
||||
void slotCancel();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Constructs a file dialog for text files with encoding selection possibility.
|
||||
*
|
||||
* @param startDir This can either be
|
||||
* @li The URL of the directory to start in.
|
||||
* @li QString() to start in the current working
|
||||
* directory, or the last directory where a file has been
|
||||
* selected.
|
||||
* @li ':<keyword>' to start in the directory last used
|
||||
* by a filedialog in the same application that specified
|
||||
* the same keyword.
|
||||
* @li '::<keyword>' to start in the directory last used
|
||||
* by a filedialog in any application that specified the
|
||||
* same keyword.
|
||||
*
|
||||
* @param encoding The encoding shown in the encoding combo. If it's
|
||||
* QString(), the global default encoding will be shown.
|
||||
*
|
||||
* @param filter A shell glob or a MIME type filter that specifies which files to display.
|
||||
* The preferred option is to set a list of MIME type names, see setMimeFilter() for details.
|
||||
* Otherwise you can set the text to be displayed for the each glob, and
|
||||
* provide multiple globs, see setFilters() for details.
|
||||
*
|
||||
* @param title The title of the dialog
|
||||
*
|
||||
* @param type This can either be
|
||||
* @li QFileDialog::AcceptOpen (open dialog, the default setting)
|
||||
* @li QFileDialog::AcceptSave
|
||||
* @param parent The parent widget of this dialog
|
||||
*/
|
||||
KEncodingFileDialog(const QUrl &startDir = QUrl(),
|
||||
const QString &encoding = QString(),
|
||||
const QString &filter = QString(),
|
||||
const QString &title = QString(),
|
||||
QFileDialog::AcceptMode type = QFileDialog::AcceptOpen,
|
||||
QWidget *parent = nullptr);
|
||||
/**
|
||||
* Destructs the file dialog.
|
||||
*/
|
||||
~KEncodingFileDialog() override;
|
||||
|
||||
/**
|
||||
* @returns The selected encoding if the constructor with the encoding parameter was used, otherwise QString().
|
||||
*/
|
||||
QString selectedEncoding() const;
|
||||
|
||||
std::unique_ptr<KEncodingFileDialogPrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kfilebookmarkhandler_p.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <kio/global.h>
|
||||
|
||||
#include "kfilewidget.h"
|
||||
|
||||
KFileBookmarkHandler::KFileBookmarkHandler(KFileWidget *widget)
|
||||
: QObject(widget)
|
||||
, KBookmarkOwner()
|
||||
, m_widget(widget)
|
||||
{
|
||||
setObjectName(QStringLiteral("KFileBookmarkHandler"));
|
||||
m_menu = new QMenu(widget);
|
||||
m_menu->setObjectName(QStringLiteral("bookmark menu"));
|
||||
|
||||
QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kfile/bookmarks.xml"));
|
||||
if (file.isEmpty()) {
|
||||
file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kfile/bookmarks.xml");
|
||||
}
|
||||
|
||||
m_bookmarkManager = new KBookmarkManager(file, this);
|
||||
|
||||
m_bookmarkMenu = new KBookmarkMenu(m_bookmarkManager, this, m_menu);
|
||||
}
|
||||
|
||||
KFileBookmarkHandler::~KFileBookmarkHandler()
|
||||
{
|
||||
delete m_bookmarkMenu;
|
||||
}
|
||||
|
||||
void KFileBookmarkHandler::openBookmark(const KBookmark &bm, Qt::MouseButtons, Qt::KeyboardModifiers)
|
||||
{
|
||||
Q_EMIT openUrl(bm.url().toString());
|
||||
}
|
||||
|
||||
QUrl KFileBookmarkHandler::currentUrl() const
|
||||
{
|
||||
return m_widget->baseUrl();
|
||||
}
|
||||
|
||||
QString KFileBookmarkHandler::currentTitle() const
|
||||
{
|
||||
return m_widget->baseUrl().toDisplayString();
|
||||
}
|
||||
|
||||
QString KFileBookmarkHandler::currentIcon() const
|
||||
{
|
||||
return KIO::iconNameForUrl(currentUrl());
|
||||
}
|
||||
|
||||
#include "moc_kfilebookmarkhandler_p.cpp"
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KFILEBOOKMARKHANDLER_H
|
||||
#define KFILEBOOKMARKHANDLER_H
|
||||
|
||||
#include <KBookmarkManager>
|
||||
#include <KBookmarkMenu>
|
||||
#include <KBookmarkOwner>
|
||||
|
||||
class QMenu;
|
||||
class KFileWidget;
|
||||
|
||||
class KFileBookmarkHandler : public QObject, public KBookmarkOwner
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KFileBookmarkHandler(KFileWidget *widget);
|
||||
~KFileBookmarkHandler() override;
|
||||
|
||||
QMenu *popupMenu();
|
||||
|
||||
// KBookmarkOwner interface:
|
||||
QString currentTitle() const override;
|
||||
QUrl currentUrl() const override;
|
||||
QString currentIcon() const override;
|
||||
|
||||
QMenu *menu() const
|
||||
{
|
||||
return m_menu;
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void openBookmark(const KBookmark &bm, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void openUrl(const QString &url);
|
||||
|
||||
private:
|
||||
void importOldBookmarks(const QString &path, KBookmarkManager *manager);
|
||||
|
||||
KFileWidget *m_widget;
|
||||
QMenu *m_menu;
|
||||
KBookmarkMenu *m_bookmarkMenu;
|
||||
KBookmarkManager *m_bookmarkManager;
|
||||
};
|
||||
|
||||
#endif // KFILEBOOKMARKHANDLER_H
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008, 2009, 2015 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "kfilecopytomenu.h"
|
||||
#include "kfilecopytomenu_p.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QIcon>
|
||||
#include <QMimeDatabase>
|
||||
#include <QMimeType>
|
||||
|
||||
#include "../utils_p.h"
|
||||
|
||||
#include <KIO/CopyJob>
|
||||
#include <KIO/FileUndoManager>
|
||||
#include <KIO/JobUiDelegate>
|
||||
|
||||
#include <KJobWidgets>
|
||||
#include <KLocalizedString>
|
||||
#include <KSharedConfig>
|
||||
#include <KStringHandler>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
static constexpr int s_maxRecentDirs = 10; // Hardcoded max size
|
||||
|
||||
KFileCopyToMenuPrivate::KFileCopyToMenuPrivate(KFileCopyToMenu *qq, QWidget *parentWidget)
|
||||
: q(qq)
|
||||
, m_urls()
|
||||
, m_parentWidget(parentWidget)
|
||||
, m_readOnly(false)
|
||||
, m_autoErrorHandling(false)
|
||||
{
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
KFileCopyToMenu::KFileCopyToMenu(QWidget *parentWidget)
|
||||
: QObject(parentWidget)
|
||||
, d(new KFileCopyToMenuPrivate(this, parentWidget))
|
||||
{
|
||||
}
|
||||
|
||||
KFileCopyToMenu::~KFileCopyToMenu() = default;
|
||||
|
||||
void KFileCopyToMenu::setUrls(const QList<QUrl> &urls)
|
||||
{
|
||||
d->m_urls = urls;
|
||||
}
|
||||
|
||||
void KFileCopyToMenu::setReadOnly(bool ro)
|
||||
{
|
||||
d->m_readOnly = ro;
|
||||
}
|
||||
|
||||
void KFileCopyToMenu::setAutoErrorHandlingEnabled(bool b)
|
||||
{
|
||||
d->m_autoErrorHandling = b;
|
||||
}
|
||||
|
||||
void KFileCopyToMenu::addActionsTo(QMenu *menu) const
|
||||
{
|
||||
QMenu *mainCopyMenu = new KFileCopyToMainMenu(menu, d.get(), Copy);
|
||||
mainCopyMenu->setTitle(i18nc("@title:menu", "Copy To"));
|
||||
mainCopyMenu->menuAction()->setObjectName(QStringLiteral("copyTo_submenu")); // for the unittest
|
||||
menu->addMenu(mainCopyMenu);
|
||||
|
||||
if (!d->m_readOnly) {
|
||||
QMenu *mainMoveMenu = new KFileCopyToMainMenu(menu, d.get(), Move);
|
||||
mainMoveMenu->setTitle(i18nc("@title:menu", "Move To"));
|
||||
mainMoveMenu->menuAction()->setObjectName(QStringLiteral("moveTo_submenu")); // for the unittest
|
||||
menu->addMenu(mainMoveMenu);
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
KFileCopyToMainMenu::KFileCopyToMainMenu(QMenu *parent, KFileCopyToMenuPrivate *_d, MenuType menuType)
|
||||
: QMenu(parent)
|
||||
, m_menuType(menuType)
|
||||
, m_actionGroup(static_cast<QWidget *>(nullptr))
|
||||
, d(_d)
|
||||
, m_recentDirsGroup(KSharedConfig::openConfig(), m_menuType == Copy ? QStringLiteral("kuick-copy") : QStringLiteral("kuick-move"))
|
||||
{
|
||||
connect(this, &KFileCopyToMainMenu::aboutToShow, this, &KFileCopyToMainMenu::slotAboutToShow);
|
||||
connect(&m_actionGroup, &QActionGroup::triggered, this, &KFileCopyToMainMenu::slotTriggered);
|
||||
}
|
||||
|
||||
void KFileCopyToMainMenu::slotAboutToShow()
|
||||
{
|
||||
clear();
|
||||
KFileCopyToDirectoryMenu *subMenu;
|
||||
// Home Folder
|
||||
subMenu = new KFileCopyToDirectoryMenu(this, this, QDir::homePath());
|
||||
subMenu->setTitle(i18nc("@title:menu", "Home Folder"));
|
||||
subMenu->setIcon(QIcon::fromTheme(QStringLiteral("go-home")));
|
||||
QAction *act = addMenu(subMenu);
|
||||
act->setObjectName(QStringLiteral("home"));
|
||||
|
||||
// Root Folder
|
||||
#ifndef Q_OS_WIN
|
||||
subMenu = new KFileCopyToDirectoryMenu(this, this, QDir::rootPath());
|
||||
subMenu->setTitle(i18nc("@title:menu", "Root Folder"));
|
||||
subMenu->setIcon(QIcon::fromTheme(QStringLiteral("folder-red")));
|
||||
act = addMenu(subMenu);
|
||||
act->setObjectName(QStringLiteral("root"));
|
||||
#else
|
||||
const QFileInfoList drives = QDir::drives();
|
||||
for (const QFileInfo &info : drives) {
|
||||
QString driveIcon = QStringLiteral("drive-harddisk");
|
||||
const uint type = GetDriveTypeW((wchar_t *)info.absoluteFilePath().utf16());
|
||||
switch (type) {
|
||||
case DRIVE_REMOVABLE:
|
||||
driveIcon = QStringLiteral("drive-removable-media");
|
||||
break;
|
||||
case DRIVE_FIXED:
|
||||
driveIcon = QStringLiteral("drive-harddisk");
|
||||
break;
|
||||
case DRIVE_REMOTE:
|
||||
driveIcon = QStringLiteral("network-server");
|
||||
break;
|
||||
case DRIVE_CDROM:
|
||||
driveIcon = QStringLiteral("drive-optical");
|
||||
break;
|
||||
case DRIVE_RAMDISK:
|
||||
case DRIVE_UNKNOWN:
|
||||
case DRIVE_NO_ROOT_DIR:
|
||||
default:
|
||||
driveIcon = QStringLiteral("drive-harddisk");
|
||||
}
|
||||
subMenu = new KFileCopyToDirectoryMenu(this, this, info.absoluteFilePath());
|
||||
subMenu->setTitle(info.absoluteFilePath());
|
||||
subMenu->setIcon(QIcon::fromTheme(driveIcon));
|
||||
addMenu(subMenu);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Browse... action, shows a file dialog
|
||||
auto *browseAction = new QAction(i18nc("@action:inmenu in Copy To or Move To submenu", "Browse…"), this);
|
||||
browseAction->setObjectName(QStringLiteral("browse"));
|
||||
connect(browseAction, &QAction::triggered, this, &KFileCopyToMainMenu::slotBrowse);
|
||||
addAction(browseAction);
|
||||
|
||||
addSeparator(); // Qt handles removing it automatically if it's last in the menu, nice.
|
||||
|
||||
// Recent Destinations
|
||||
const QStringList recentDirs = m_recentDirsGroup.readPathEntry("Paths", QStringList());
|
||||
for (const QString &recentDir : recentDirs) {
|
||||
const QUrl url = QUrl::fromLocalFile(recentDir);
|
||||
const QString text = KStringHandler::csqueeze(url.toDisplayString(QUrl::PreferLocalFile), 60); // shorten very long paths (#61386)
|
||||
QAction *act = new QAction(text, this);
|
||||
act->setObjectName(recentDir);
|
||||
act->setData(url);
|
||||
m_actionGroup.addAction(act);
|
||||
addAction(act);
|
||||
}
|
||||
}
|
||||
|
||||
void KFileCopyToMainMenu::slotBrowse()
|
||||
{
|
||||
const QUrl dest = QFileDialog::getExistingDirectoryUrl(d->m_parentWidget ? d->m_parentWidget : this);
|
||||
if (!dest.isEmpty()) {
|
||||
copyOrMoveTo(dest);
|
||||
}
|
||||
}
|
||||
|
||||
void KFileCopyToMainMenu::slotTriggered(QAction *action)
|
||||
{
|
||||
const QUrl url = action->data().toUrl();
|
||||
Q_ASSERT(!url.isEmpty());
|
||||
copyOrMoveTo(url);
|
||||
}
|
||||
|
||||
void KFileCopyToMainMenu::copyOrMoveTo(const QUrl &dest)
|
||||
{
|
||||
// Insert into the recent destinations list
|
||||
QStringList recentDirs = m_recentDirsGroup.readPathEntry("Paths", QStringList());
|
||||
const QString niceDest = dest.toDisplayString(QUrl::PreferLocalFile);
|
||||
if (!recentDirs.contains(niceDest)) { // don't change position if already there, moving stuff is bad usability
|
||||
recentDirs.prepend(niceDest);
|
||||
if (recentDirs.size() > s_maxRecentDirs) {
|
||||
recentDirs.erase(recentDirs.begin() + s_maxRecentDirs, recentDirs.end());
|
||||
}
|
||||
m_recentDirsGroup.writePathEntry("Paths", recentDirs);
|
||||
}
|
||||
|
||||
// #199549: add a trailing slash to avoid unexpected results when the
|
||||
// dest doesn't exist anymore: it was creating a file with the name of
|
||||
// the now non-existing dest.
|
||||
QUrl dirDest = dest;
|
||||
Utils::appendSlashToPath(dirDest);
|
||||
|
||||
// And now let's do the copy or move -- with undo/redo support.
|
||||
KIO::CopyJob *job = m_menuType == Copy ? KIO::copy(d->m_urls, dirDest) : KIO::move(d->m_urls, dirDest);
|
||||
KIO::FileUndoManager::self()->recordCopyJob(job);
|
||||
KJobWidgets::setWindow(job, d->m_parentWidget ? d->m_parentWidget : this);
|
||||
if (job->uiDelegate()) {
|
||||
job->uiDelegate()->setAutoErrorHandlingEnabled(d->m_autoErrorHandling);
|
||||
}
|
||||
connect(job, &KIO::CopyJob::result, this, [this](KJob *job) {
|
||||
Q_EMIT d->q->error(job->error(), job->errorString());
|
||||
});
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
KFileCopyToDirectoryMenu::KFileCopyToDirectoryMenu(QMenu *parent, KFileCopyToMainMenu *mainMenu, const QString &path)
|
||||
: QMenu(parent)
|
||||
, m_mainMenu(mainMenu)
|
||||
, m_path(Utils::slashAppended(path))
|
||||
{
|
||||
connect(this, &KFileCopyToDirectoryMenu::aboutToShow, this, &KFileCopyToDirectoryMenu::slotAboutToShow);
|
||||
}
|
||||
|
||||
void KFileCopyToDirectoryMenu::slotAboutToShow()
|
||||
{
|
||||
clear();
|
||||
QAction *act = new QAction(m_mainMenu->menuType() == Copy ? i18nc("@title:menu", "Copy Here") : i18nc("@title:menu", "Move Here"), this);
|
||||
act->setData(QUrl::fromLocalFile(m_path));
|
||||
act->setEnabled(QFileInfo(m_path).isWritable());
|
||||
m_mainMenu->actionGroup().addAction(act);
|
||||
addAction(act);
|
||||
|
||||
addSeparator(); // Qt handles removing it automatically if it's last in the menu, nice.
|
||||
|
||||
// List directory
|
||||
// All we need is sub folder names, their permissions, their icon.
|
||||
// KDirLister or KIO::listDir would fetch much more info, and would be async,
|
||||
// and we only care about local directories so we use QDir directly.
|
||||
QDir dir(m_path);
|
||||
const QStringList entries = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::LocaleAware);
|
||||
const QMimeDatabase db;
|
||||
const QMimeType dirMime = db.mimeTypeForName(QStringLiteral("inode/directory"));
|
||||
for (const QString &subDir : entries) {
|
||||
QString subPath = m_path + subDir;
|
||||
KFileCopyToDirectoryMenu *subMenu = new KFileCopyToDirectoryMenu(this, m_mainMenu, subPath);
|
||||
QString menuTitle(subDir);
|
||||
// Replace '&' by "&&" to make sure that '&' inside the directory name is displayed
|
||||
// correctly and not misinterpreted as an indicator for a keyboard shortcut
|
||||
subMenu->setTitle(menuTitle.replace(QLatin1Char('&'), QLatin1String("&&")));
|
||||
const QString iconName = dirMime.iconName();
|
||||
subMenu->setIcon(QIcon::fromTheme(iconName));
|
||||
if (QFileInfo(subPath).isSymLink()) {
|
||||
QFont font = subMenu->menuAction()->font();
|
||||
font.setItalic(true);
|
||||
subMenu->menuAction()->setFont(font);
|
||||
}
|
||||
addMenu(subMenu);
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_kfilecopytomenu.cpp"
|
||||
#include "moc_kfilecopytomenu_p.cpp"
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008, 2015 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KFILECOPYTOMENU_H
|
||||
#define KFILECOPYTOMENU_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
#include <kiofilewidgets_export.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QMenu;
|
||||
class KFileCopyToMenuPrivate;
|
||||
|
||||
/**
|
||||
* @class KFileCopyToMenu kfilecopytomenu.h <KFileCopyToMenu>
|
||||
*
|
||||
* This class adds "Copy To" and "Move To" submenus to a popupmenu.
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KFileCopyToMenu : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Creates a KFileCopyToMenu instance
|
||||
* Note that this instance (and the widget) must stay alive for at least as
|
||||
* long as the popupmenu; it has the slots for the actions created by addActionsTo.
|
||||
*
|
||||
* @param parentWidget parent widget for the file dialog and message boxes.
|
||||
* The parentWidget also serves as a parent for this object.
|
||||
*/
|
||||
explicit KFileCopyToMenu(QWidget *parentWidget);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~KFileCopyToMenu() override;
|
||||
|
||||
/**
|
||||
* Sets the URLs which the actions apply to.
|
||||
*/
|
||||
void setUrls(const QList<QUrl> &urls);
|
||||
|
||||
/**
|
||||
* If setReadOnly(true) is called, the "Move To" submenu will not appear.
|
||||
*/
|
||||
void setReadOnly(bool ro);
|
||||
|
||||
/**
|
||||
* Generate the actions and submenus, and adds them to the @p menu.
|
||||
* All actions are created as children of the menu.
|
||||
*/
|
||||
void addActionsTo(QMenu *menu) const;
|
||||
|
||||
/**
|
||||
* Enables or disables automatic error handling with message boxes.
|
||||
* When called with true, a messagebox is shown in case of an error during a copy or move.
|
||||
* When called with false, the application should connect to the error signal instead.
|
||||
* Auto error handling is disabled by default.
|
||||
*/
|
||||
void setAutoErrorHandlingEnabled(bool b);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the copy or move job fails.
|
||||
* @param errorCode the KIO job error code
|
||||
* @param message the error message to show the user
|
||||
*/
|
||||
void error(int errorCode, const QString &message);
|
||||
|
||||
private:
|
||||
std::unique_ptr<KFileCopyToMenuPrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008, 2015 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KFILECOPYTOMENU_P_H
|
||||
#define KFILECOPYTOMENU_P_H
|
||||
|
||||
#include <KConfigGroup>
|
||||
#include <QActionGroup>
|
||||
#include <QMenu>
|
||||
#include <QUrl>
|
||||
|
||||
class KFileCopyToMenuPrivate
|
||||
{
|
||||
public:
|
||||
KFileCopyToMenuPrivate(KFileCopyToMenu *qq, QWidget *parentWidget);
|
||||
|
||||
KFileCopyToMenu *const q;
|
||||
QList<QUrl> m_urls;
|
||||
QWidget *m_parentWidget;
|
||||
bool m_readOnly;
|
||||
bool m_autoErrorHandling;
|
||||
};
|
||||
|
||||
enum MenuType {
|
||||
Copy,
|
||||
Move
|
||||
};
|
||||
|
||||
// The main menu, shown when opening "Copy To" or "Move To"
|
||||
// It contains Home Folder, Root Folder, Browse, and recent destinations
|
||||
class KFileCopyToMainMenu : public QMenu
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KFileCopyToMainMenu(QMenu *parent, KFileCopyToMenuPrivate *d, MenuType menuType);
|
||||
|
||||
QActionGroup &actionGroup()
|
||||
{
|
||||
return m_actionGroup; // used by submenus
|
||||
}
|
||||
MenuType menuType() const
|
||||
{
|
||||
return m_menuType; // used by submenus
|
||||
}
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotAboutToShow();
|
||||
void slotBrowse();
|
||||
void slotTriggered(QAction *action);
|
||||
|
||||
private:
|
||||
void copyOrMoveTo(const QUrl &dest);
|
||||
|
||||
private:
|
||||
MenuType m_menuType;
|
||||
QActionGroup m_actionGroup;
|
||||
KFileCopyToMenuPrivate *d; // this isn't our own d pointer, it's the one for the public class
|
||||
KConfigGroup m_recentDirsGroup;
|
||||
};
|
||||
|
||||
// The menu that lists a directory
|
||||
class KFileCopyToDirectoryMenu : public QMenu
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KFileCopyToDirectoryMenu(QMenu *parent, KFileCopyToMainMenu *mainMenu, const QString &path);
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotAboutToShow();
|
||||
|
||||
private:
|
||||
KFileCopyToMainMenu *m_mainMenu;
|
||||
QString m_path;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
||||
Work sponsored by the LiMux project of the city of Munich
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kfilecustomdialog.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QUrl>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
class KFileCustomDialogPrivate
|
||||
{
|
||||
public:
|
||||
explicit KFileCustomDialogPrivate(KFileCustomDialog *qq)
|
||||
: q(qq)
|
||||
{
|
||||
}
|
||||
void init(const QUrl &startDir);
|
||||
|
||||
KFileWidget *mFileWidget = nullptr;
|
||||
KFileCustomDialog *const q;
|
||||
};
|
||||
|
||||
void KFileCustomDialogPrivate::init(const QUrl &startDir)
|
||||
{
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(q);
|
||||
mainLayout->setObjectName(QStringLiteral("mainlayout"));
|
||||
|
||||
mFileWidget = new KFileWidget(startDir, q);
|
||||
mFileWidget->setObjectName(QStringLiteral("filewidget"));
|
||||
mainLayout->addWidget(mFileWidget);
|
||||
|
||||
mFileWidget->okButton()->show();
|
||||
q->connect(mFileWidget->okButton(), &QPushButton::clicked, q, [this]() {
|
||||
mFileWidget->slotOk();
|
||||
});
|
||||
mFileWidget->cancelButton()->show();
|
||||
q->connect(mFileWidget->cancelButton(), &QPushButton::clicked, q, [this]() {
|
||||
mFileWidget->slotCancel();
|
||||
q->reject();
|
||||
});
|
||||
q->connect(mFileWidget, &KFileWidget::accepted, q, [this] {
|
||||
q->accept();
|
||||
});
|
||||
}
|
||||
|
||||
KFileCustomDialog::KFileCustomDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, d(new KFileCustomDialogPrivate(this))
|
||||
{
|
||||
d->init(QUrl());
|
||||
}
|
||||
|
||||
KFileCustomDialog::KFileCustomDialog(const QUrl &startDir, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, d(new KFileCustomDialogPrivate(this))
|
||||
{
|
||||
d->init(startDir);
|
||||
}
|
||||
|
||||
KFileCustomDialog::~KFileCustomDialog() = default;
|
||||
|
||||
void KFileCustomDialog::setUrl(const QUrl &url)
|
||||
{
|
||||
d->mFileWidget->setUrl(url);
|
||||
}
|
||||
|
||||
void KFileCustomDialog::setCustomWidget(QWidget *widget)
|
||||
{
|
||||
d->mFileWidget->setCustomWidget(QString(), widget);
|
||||
}
|
||||
|
||||
KFileWidget *KFileCustomDialog::fileWidget() const
|
||||
{
|
||||
return d->mFileWidget;
|
||||
}
|
||||
|
||||
void KFileCustomDialog::setOperationMode(KFileWidget::OperationMode op)
|
||||
{
|
||||
d->mFileWidget->setOperationMode(op);
|
||||
}
|
||||
|
||||
void KFileCustomDialog::accept()
|
||||
{
|
||||
d->mFileWidget->accept();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
#include "moc_kfilecustomdialog.cpp"
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
|
||||
Work sponsored by the LiMux project of the city of Munich
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KFILECUSTOMDIALOG_H
|
||||
#define KFILECUSTOMDIALOG_H
|
||||
|
||||
#include "kfilewidget.h"
|
||||
#include "kiofilewidgets_export.h"
|
||||
#include <QDialog>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class KFileWidget;
|
||||
class KFileCustomDialogPrivate;
|
||||
|
||||
/**
|
||||
* This class implement a custom file dialog.
|
||||
* It uses a KFileWidget and allows the application to provide a custom widget.
|
||||
* @since 5.42
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KFileCustomDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Constructs a custom file dialog
|
||||
*/
|
||||
explicit KFileCustomDialog(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Constructs a custom file dialog
|
||||
* @param startDir see the KFileWidget constructor for documentation
|
||||
* @since 5.67
|
||||
*/
|
||||
explicit KFileCustomDialog(const QUrl &startDir, QWidget *parent = nullptr);
|
||||
|
||||
~KFileCustomDialog() override;
|
||||
|
||||
/**
|
||||
* Sets the directory to view.
|
||||
*
|
||||
* @param url URL to show.
|
||||
*/
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Set a custom widget that should be added to the file dialog.
|
||||
* @param widget A widget, or a widget of widgets, for displaying custom
|
||||
* data in the file widget. This can be used, for example, to
|
||||
* display a check box with the title "Open as read-only".
|
||||
* When creating this widget, you don't need to specify a parent,
|
||||
* since the widget's parent will be set automatically by KFileWidget.
|
||||
*/
|
||||
void setCustomWidget(QWidget *widget);
|
||||
|
||||
/**
|
||||
* @brief fileWidget
|
||||
* @return the filewidget used inside this dialog
|
||||
*/
|
||||
KFileWidget *fileWidget() const;
|
||||
|
||||
/**
|
||||
* Sets the operational mode of the filedialog to @p Saving, @p Opening
|
||||
* or @p Other. This will set some flags that are specific to loading
|
||||
* or saving files. E.g. setKeepLocation() makes mostly sense for
|
||||
* a save-as dialog. So setOperationMode( KFileWidget::Saving ); sets
|
||||
* setKeepLocation for example.
|
||||
*
|
||||
* The mode @p Saving, together with a default filter set via
|
||||
* setMimeFilter() will make the filter combobox read-only.
|
||||
*
|
||||
* The default mode is @p Opening.
|
||||
*
|
||||
* Call this method right after instantiating KFileWidget.
|
||||
*
|
||||
* @see operationMode
|
||||
* @see KFileWidget::OperationMode
|
||||
*/
|
||||
void setOperationMode(KFileWidget::OperationMode op);
|
||||
|
||||
public Q_SLOTS:
|
||||
void accept() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<KFileCustomDialogPrivate> const d;
|
||||
};
|
||||
|
||||
#endif // KFILECUSTOMDIALOG_H
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: Stephan Kulow <coolo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kfilefiltercombo.h"
|
||||
#include "kfilefilter.h"
|
||||
#include "kfilefiltercombo_debug.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <QDebug>
|
||||
#include <QEvent>
|
||||
#include <QLineEdit>
|
||||
#include <QMimeDatabase>
|
||||
|
||||
#include <config-kiofilewidgets.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
class KFileFilterComboPrivate
|
||||
{
|
||||
public:
|
||||
explicit KFileFilterComboPrivate(KFileFilterCombo *qq)
|
||||
: q(qq)
|
||||
{
|
||||
}
|
||||
|
||||
void slotFilterChanged();
|
||||
|
||||
KFileFilterCombo *const q;
|
||||
// when we have more than 3 mimefilters and no default-filter,
|
||||
// we don't show the comments of all mimefilters in one line,
|
||||
// instead we show "All supported files". We have to translate
|
||||
// that back to the list of mimefilters in currentFilter() tho.
|
||||
bool m_hasAllSupportedFiles = false;
|
||||
// true when setMimeFilter was called
|
||||
bool m_isMimeFilter = false;
|
||||
QString m_lastFilter;
|
||||
KFileFilter m_defaultFilter = KFileFilter::fromFilterString(i18nc("Default mime type filter that shows all file types", "*|All Files")).first();
|
||||
|
||||
QList<KFileFilter> m_filters;
|
||||
bool m_allTypes;
|
||||
};
|
||||
|
||||
KFileFilterCombo::KFileFilterCombo(QWidget *parent)
|
||||
: KComboBox(true, parent)
|
||||
, d(new KFileFilterComboPrivate(this))
|
||||
{
|
||||
setTrapReturnKey(true);
|
||||
setInsertPolicy(QComboBox::NoInsert);
|
||||
connect(this, &QComboBox::activated, this, &KFileFilterCombo::filterChanged);
|
||||
connect(this, &KComboBox::returnPressed, this, &KFileFilterCombo::filterChanged);
|
||||
connect(this, &KFileFilterCombo::filterChanged, this, [this]() {
|
||||
d->slotFilterChanged();
|
||||
});
|
||||
d->m_allTypes = false;
|
||||
}
|
||||
|
||||
KFileFilterCombo::~KFileFilterCombo() = default;
|
||||
|
||||
void KFileFilterCombo::setFilters(const QList<KFileFilter> &types, const KFileFilter &defaultFilter)
|
||||
{
|
||||
clear();
|
||||
d->m_filters.clear();
|
||||
QString delim = QStringLiteral(", ");
|
||||
d->m_hasAllSupportedFiles = false;
|
||||
bool hasAllFilesFilter = false;
|
||||
QMimeDatabase db;
|
||||
|
||||
if (types.isEmpty()) {
|
||||
d->m_filters = {d->m_defaultFilter};
|
||||
addItem(d->m_defaultFilter.label());
|
||||
|
||||
d->m_lastFilter = currentText();
|
||||
return;
|
||||
}
|
||||
|
||||
d->m_allTypes = defaultFilter.isEmpty() && (types.count() > 1);
|
||||
|
||||
if (!types.isEmpty() && types.first().mimePatterns().isEmpty()) {
|
||||
d->m_allTypes = false;
|
||||
}
|
||||
|
||||
// If there's MIME types that have the same comment, we will show the extension
|
||||
// in addition to the MIME type comment
|
||||
QHash<QString, int> allTypeComments;
|
||||
for (const KFileFilter &filter : types) {
|
||||
allTypeComments[filter.label()] += 1;
|
||||
}
|
||||
|
||||
for (const KFileFilter &filter : types) {
|
||||
if (!filter.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QStringList mimeTypes = filter.mimePatterns();
|
||||
|
||||
const bool isAllFileFilters = std::any_of(mimeTypes.cbegin(), mimeTypes.cend(), [&db](const QString &mimeTypeName) {
|
||||
const QMimeType type = db.mimeTypeForName(mimeTypeName);
|
||||
|
||||
if (!type.isValid()) {
|
||||
qCWarning(KIO_KFILEWIDGETS_KFILEFILTERCOMBO) << mimeTypeName << "is not a valid MIME type";
|
||||
return false;
|
||||
}
|
||||
|
||||
return type.name().startsWith(QLatin1String("all/")) || type.isDefault();
|
||||
});
|
||||
|
||||
if (isAllFileFilters) {
|
||||
hasAllFilesFilter = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (allTypeComments.value(filter.label()) > 1) {
|
||||
QStringList mimeSuffixes;
|
||||
|
||||
for (const QString &mimeTypeName : filter.mimePatterns()) {
|
||||
const QMimeType type = db.mimeTypeForName(mimeTypeName);
|
||||
mimeSuffixes << type.suffixes();
|
||||
}
|
||||
|
||||
const QString label = i18nc("%1 is the mimetype name, %2 is the extensions", "%1 (%2)", filter.label(), mimeSuffixes.join(QLatin1String(", ")));
|
||||
KFileFilter newFilter(label, filter.filePatterns(), filter.mimePatterns());
|
||||
|
||||
d->m_filters.append(newFilter);
|
||||
addItem(newFilter.label());
|
||||
} else {
|
||||
d->m_filters.append(filter);
|
||||
addItem(filter.label());
|
||||
}
|
||||
|
||||
if (filter == defaultFilter) {
|
||||
setCurrentIndex(count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (count() == 1) {
|
||||
d->m_allTypes = false;
|
||||
}
|
||||
|
||||
if (d->m_allTypes) {
|
||||
QStringList allMimePatterns;
|
||||
QStringList allFilePatterns;
|
||||
for (const KFileFilter &filter : std::as_const(d->m_filters)) {
|
||||
allMimePatterns << filter.mimePatterns();
|
||||
allFilePatterns << filter.filePatterns();
|
||||
}
|
||||
|
||||
KFileFilter allSupportedFilesFilter;
|
||||
|
||||
if (count() <= 3) { // show the MIME type comments of at max 3 types
|
||||
QStringList allComments;
|
||||
for (const KFileFilter &filter : std::as_const(d->m_filters)) {
|
||||
allComments << filter.label();
|
||||
}
|
||||
|
||||
allSupportedFilesFilter = KFileFilter(allComments.join(delim), allFilePatterns, allMimePatterns);
|
||||
} else {
|
||||
allSupportedFilesFilter = KFileFilter(i18n("All Supported Files"), allFilePatterns, allMimePatterns);
|
||||
d->m_hasAllSupportedFiles = true;
|
||||
}
|
||||
|
||||
insertItem(0, allSupportedFilesFilter.label());
|
||||
d->m_filters.prepend(allSupportedFilesFilter);
|
||||
setCurrentIndex(0);
|
||||
}
|
||||
|
||||
if (hasAllFilesFilter) {
|
||||
addItem(i18n("All Files"));
|
||||
|
||||
KFileFilter allFilter(i18n("All Files"), {}, {QStringLiteral("application/octet-stream")});
|
||||
|
||||
d->m_filters.append(allFilter);
|
||||
|
||||
if (defaultFilter == allFilter) {
|
||||
setCurrentIndex(count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
d->m_lastFilter = currentText();
|
||||
}
|
||||
|
||||
KFileFilter KFileFilterCombo::currentFilter() const
|
||||
{
|
||||
if (currentText() != itemText(currentIndex())) {
|
||||
// The user edited the text
|
||||
|
||||
const QList<KFileFilter> filter = KFileFilter::fromFilterString(currentText());
|
||||
|
||||
if (!filter.isEmpty()) {
|
||||
return filter.first();
|
||||
} else {
|
||||
return KFileFilter();
|
||||
}
|
||||
} else {
|
||||
if (currentIndex() == -1) {
|
||||
return KFileFilter();
|
||||
}
|
||||
|
||||
return d->m_filters[currentIndex()];
|
||||
}
|
||||
}
|
||||
|
||||
bool KFileFilterCombo::showsAllTypes() const
|
||||
{
|
||||
return d->m_allTypes;
|
||||
}
|
||||
|
||||
QList<KFileFilter> KFileFilterCombo::filters() const
|
||||
{
|
||||
return d->m_filters;
|
||||
}
|
||||
|
||||
void KFileFilterCombo::setCurrentFilter(const KFileFilter &filter)
|
||||
{
|
||||
auto it = std::find(d->m_filters.cbegin(), d->m_filters.cend(), filter);
|
||||
|
||||
if (it == d->m_filters.cend()) {
|
||||
qCWarning(KIO_KFILEWIDGETS_KFILEFILTERCOMBO) << "KFileFilterCombo::setCurrentFilter: Could not find file filter" << filter;
|
||||
setCurrentIndex(-1);
|
||||
Q_EMIT filterChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentIndex(std::distance(d->m_filters.cbegin(), it));
|
||||
Q_EMIT filterChanged();
|
||||
}
|
||||
|
||||
void KFileFilterComboPrivate::slotFilterChanged()
|
||||
{
|
||||
m_lastFilter = q->currentText();
|
||||
}
|
||||
|
||||
bool KFileFilterCombo::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
if (o == lineEdit() && e->type() == QEvent::FocusOut) {
|
||||
if (currentText() != d->m_lastFilter) {
|
||||
Q_EMIT filterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
return KComboBox::eventFilter(o, e);
|
||||
}
|
||||
|
||||
void KFileFilterCombo::setDefaultFilter(const KFileFilter &filter)
|
||||
{
|
||||
d->m_defaultFilter = filter;
|
||||
}
|
||||
|
||||
KFileFilter KFileFilterCombo::defaultFilter() const
|
||||
{
|
||||
return d->m_defaultFilter;
|
||||
}
|
||||
|
||||
#include "moc_kfilefiltercombo.cpp"
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: Stephan Kulow <coolo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KFILEFILTERCOMBO_H
|
||||
#define KFILEFILTERCOMBO_H
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include <KComboBox>
|
||||
#include <KFileFilter>
|
||||
|
||||
class KFileFilterComboPrivate;
|
||||
|
||||
/**
|
||||
* @class KFileFilterCombo kfilefiltercombo.h <KFileFilterCombo>
|
||||
*
|
||||
* File filter combo box.
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KFileFilterCombo : public KComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a new filter combo box.
|
||||
*
|
||||
* @param parent The parent widget.
|
||||
*/
|
||||
explicit KFileFilterCombo(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destroys the filter combo box.
|
||||
*/
|
||||
~KFileFilterCombo() override;
|
||||
|
||||
/**
|
||||
* Sets the filters to be used.
|
||||
*
|
||||
* @param filters each item in the list corresponds to one item in the combobox.
|
||||
* Entries for "All files" and "All supported files" are added automatically as needed.
|
||||
*
|
||||
* @param defaultFilter if not empty this will be the by default active filter
|
||||
*
|
||||
* @since 6.0
|
||||
*
|
||||
*/
|
||||
void setFilters(const QList<KFileFilter> &filters, const KFileFilter &defaultFilter = KFileFilter());
|
||||
|
||||
/**
|
||||
* The currently selected/active filter.
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
KFileFilter currentFilter() const;
|
||||
|
||||
/**
|
||||
* The current filters.
|
||||
*
|
||||
* This is not necessarily the same as the list set by setFileFilters() since
|
||||
* entries for "All files" and "All supported files" are added automatically as needed.
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
QList<KFileFilter> filters() const;
|
||||
|
||||
/**
|
||||
* This method allows to set a default-filter, that is used when an
|
||||
* empty filter is set. Make sure you call this before calling
|
||||
* setFileFilter().
|
||||
*
|
||||
* By default, this is set to match all files.
|
||||
* @see defaultFileFilter
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
void setDefaultFilter(const KFileFilter &filter);
|
||||
|
||||
/**
|
||||
* @return the default filter, used when an empty filter is set.
|
||||
* @see setDefaultFileFilter
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
KFileFilter defaultFilter() const;
|
||||
|
||||
/**
|
||||
* Sets the current filter. Filter must match one of the filter items
|
||||
* passed before to this widget.
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
void setCurrentFilter(const KFileFilter &filter);
|
||||
|
||||
/**
|
||||
* @return true if the filter's first item is the list of all MIME types
|
||||
*/
|
||||
bool showsAllTypes() const;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *, QEvent *) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted whenever the filter has been changed.
|
||||
*/
|
||||
void filterChanged();
|
||||
|
||||
private:
|
||||
std::unique_ptr<KFileFilterComboPrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
This file is part of the KDE project.
|
||||
SPDX-FileCopyrightText: 2003 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kfilemetapreview_p.h"
|
||||
|
||||
#include <QLayout>
|
||||
#include <QMimeDatabase>
|
||||
|
||||
#include <KPluginFactory>
|
||||
#include <QDebug>
|
||||
#include <kimagefilepreview.h>
|
||||
#include <kio/previewjob.h>
|
||||
|
||||
bool KFileMetaPreview::s_tryAudioPreview = true;
|
||||
|
||||
KFileMetaPreview::KFileMetaPreview(QWidget *parent)
|
||||
: KPreviewWidgetBase(parent)
|
||||
, haveAudioPreview(false)
|
||||
{
|
||||
QHBoxLayout *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
m_stack = new QStackedWidget(this);
|
||||
layout->addWidget(m_stack);
|
||||
|
||||
// ###
|
||||
// m_previewProviders.setAutoDelete( true );
|
||||
initPreviewProviders();
|
||||
}
|
||||
|
||||
KFileMetaPreview::~KFileMetaPreview()
|
||||
{
|
||||
}
|
||||
|
||||
void KFileMetaPreview::initPreviewProviders()
|
||||
{
|
||||
qDeleteAll(m_previewProviders);
|
||||
m_previewProviders.clear();
|
||||
// hardcoded so far
|
||||
|
||||
// image previews
|
||||
KImageFilePreview *imagePreview = new KImageFilePreview(m_stack);
|
||||
(void)m_stack->addWidget(imagePreview);
|
||||
m_stack->setCurrentWidget(imagePreview);
|
||||
resize(imagePreview->sizeHint());
|
||||
|
||||
const QStringList mimeTypes = imagePreview->supportedMimeTypes();
|
||||
QStringList::ConstIterator it = mimeTypes.begin();
|
||||
for (; it != mimeTypes.end(); ++it) {
|
||||
// qDebug(".... %s", (*it).toLatin1().constData());
|
||||
m_previewProviders.insert(*it, imagePreview);
|
||||
}
|
||||
}
|
||||
|
||||
KPreviewWidgetBase *KFileMetaPreview::findExistingProvider(const QString &mimeType, const QMimeType &mimeInfo) const
|
||||
{
|
||||
KPreviewWidgetBase *provider = m_previewProviders.value(mimeType);
|
||||
if (provider) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
if (mimeInfo.isValid()) {
|
||||
// check MIME type inheritance
|
||||
const QStringList parentMimeTypes = mimeInfo.allAncestors();
|
||||
for (const QString &parentMimeType : parentMimeTypes) {
|
||||
provider = m_previewProviders.value(parentMimeType);
|
||||
if (provider) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ### MIME type may be image/* for example, try that
|
||||
const int index = mimeType.indexOf(QLatin1Char('/'));
|
||||
if (index > 0) {
|
||||
provider = m_previewProviders.value(QStringView(mimeType).left(index + 1) + QLatin1Char('*'));
|
||||
if (provider) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KPreviewWidgetBase *KFileMetaPreview::previewProviderFor(const QString &mimeType)
|
||||
{
|
||||
QMimeDatabase db;
|
||||
QMimeType mimeInfo = db.mimeTypeForName(mimeType);
|
||||
|
||||
// qDebug("### looking for: %s", mimeType.toLatin1().constData());
|
||||
// often the first highlighted item, where we can be sure, there is no plugin
|
||||
// (this "folders reflect icons" is a konq-specific thing, right?)
|
||||
if (mimeInfo.inherits(QStringLiteral("inode/directory"))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KPreviewWidgetBase *provider = findExistingProvider(mimeType, mimeInfo);
|
||||
if (provider) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
// qDebug("#### didn't find anything for: %s", mimeType.toLatin1().constData());
|
||||
|
||||
if (s_tryAudioPreview && !mimeType.startsWith(QLatin1String("text/")) && !mimeType.startsWith(QLatin1String("image/"))) {
|
||||
if (!haveAudioPreview) {
|
||||
KPreviewWidgetBase *audioPreview = createAudioPreview(m_stack);
|
||||
if (audioPreview) {
|
||||
haveAudioPreview = true;
|
||||
(void)m_stack->addWidget(audioPreview);
|
||||
const QStringList mimeTypes = audioPreview->supportedMimeTypes();
|
||||
QStringList::ConstIterator it = mimeTypes.begin();
|
||||
for (; it != mimeTypes.end(); ++it) {
|
||||
// only add non already handled MIME types
|
||||
if (m_previewProviders.constFind(*it) == m_previewProviders.constEnd()) {
|
||||
m_previewProviders.insert(*it, audioPreview);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// with the new MIME types from the audio-preview, try again
|
||||
provider = findExistingProvider(mimeType, mimeInfo);
|
||||
if (provider) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
// The logic in this code duplicates the logic in PreviewJob.
|
||||
// But why do we need multiple KPreviewWidgetBase instances anyway?
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void KFileMetaPreview::showPreview(const QUrl &url)
|
||||
{
|
||||
QMimeDatabase db;
|
||||
QMimeType mt = db.mimeTypeForUrl(url);
|
||||
KPreviewWidgetBase *provider = previewProviderFor(mt.name());
|
||||
if (provider) {
|
||||
if (provider != m_stack->currentWidget()) { // stop the previous preview
|
||||
clearPreview();
|
||||
}
|
||||
|
||||
m_stack->setEnabled(true);
|
||||
m_stack->setCurrentWidget(provider);
|
||||
provider->showPreview(url);
|
||||
} else {
|
||||
clearPreview();
|
||||
m_stack->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void KFileMetaPreview::clearPreview()
|
||||
{
|
||||
if (m_stack->currentWidget()) {
|
||||
static_cast<KPreviewWidgetBase *>(m_stack->currentWidget())->clearPreview();
|
||||
}
|
||||
}
|
||||
|
||||
void KFileMetaPreview::addPreviewProvider(const QString &mimeType, KPreviewWidgetBase *provider)
|
||||
{
|
||||
m_previewProviders.insert(mimeType, provider);
|
||||
}
|
||||
|
||||
void KFileMetaPreview::clearPreviewProviders()
|
||||
{
|
||||
for (auto it = m_previewProviders.cbegin(); it != m_previewProviders.cend(); ++it) {
|
||||
m_stack->removeWidget(it.value());
|
||||
}
|
||||
qDeleteAll(m_previewProviders);
|
||||
m_previewProviders.clear();
|
||||
}
|
||||
|
||||
// static
|
||||
KPreviewWidgetBase *KFileMetaPreview::createAudioPreview(QWidget *parent)
|
||||
{
|
||||
KPluginMetaData data(QStringLiteral("kfileaudiopreview"));
|
||||
if (auto plugin = KPluginFactory::instantiatePlugin<KPreviewWidgetBase>(data, parent).plugin) {
|
||||
plugin->setObjectName(QStringLiteral("kfileaudiopreview"));
|
||||
return plugin;
|
||||
} else {
|
||||
s_tryAudioPreview = false;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_kfilemetapreview_p.cpp"
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
This file is part of the KDE project.
|
||||
SPDX-FileCopyrightText: 2003 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KFILEMETAPREVIEW_H
|
||||
#define KFILEMETAPREVIEW_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QMimeType>
|
||||
#include <QStackedWidget>
|
||||
#include <kpreviewwidgetbase.h>
|
||||
|
||||
// Internal, but exported for KDirOperator (kfile) and KPreviewProps (kdelibs4support)
|
||||
class KIOFILEWIDGETS_EXPORT KFileMetaPreview : public KPreviewWidgetBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KFileMetaPreview(QWidget *parent);
|
||||
~KFileMetaPreview() override;
|
||||
|
||||
virtual void addPreviewProvider(const QString &mimeType, KPreviewWidgetBase *provider);
|
||||
virtual void clearPreviewProviders();
|
||||
|
||||
public Q_SLOTS:
|
||||
void showPreview(const QUrl &url) override;
|
||||
void clearPreview() override;
|
||||
|
||||
protected:
|
||||
virtual KPreviewWidgetBase *previewProviderFor(const QString &mimeType);
|
||||
|
||||
private:
|
||||
void initPreviewProviders();
|
||||
KPreviewWidgetBase *findExistingProvider(const QString &mimeType, const QMimeType &mimeInfo) const;
|
||||
|
||||
QStackedWidget *m_stack;
|
||||
QHash<QString, KPreviewWidgetBase *> m_previewProviders;
|
||||
bool haveAudioPreview;
|
||||
|
||||
// may return 0L
|
||||
static KPreviewWidgetBase *createAudioPreview(QWidget *parent);
|
||||
static bool s_tryAudioPreview;
|
||||
|
||||
private:
|
||||
class KFileMetaPreviewPrivate;
|
||||
KFileMetaPreviewPrivate *d;
|
||||
};
|
||||
|
||||
#endif // KFILEMETAPREVIEW_H
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001, 2002, 2003 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kfileplaceeditdialog.h"
|
||||
|
||||
#include <KAboutData>
|
||||
#include <KConfig>
|
||||
#include <KIconButton>
|
||||
#include <KLineEdit> // For KUrlRequester::lineEdit()
|
||||
#include <KLocalizedString>
|
||||
#include <kio/global.h>
|
||||
#include <kprotocolinfo.h>
|
||||
#include <kurlrequester.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFormLayout>
|
||||
#include <qdrawutil.h>
|
||||
|
||||
#include <KConfigGroup>
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
bool KFilePlaceEditDialog::getInformation(bool allowGlobal,
|
||||
QUrl &url,
|
||||
QString &label,
|
||||
QString &icon,
|
||||
bool isAddingNewPlace,
|
||||
bool &appLocal,
|
||||
int iconSize,
|
||||
QWidget *parent)
|
||||
{
|
||||
KFilePlaceEditDialog *dialog = new KFilePlaceEditDialog(allowGlobal, url, label, icon, isAddingNewPlace, appLocal, iconSize, parent);
|
||||
if (dialog->exec() == QDialog::Accepted) {
|
||||
// set the return parameters
|
||||
url = dialog->url();
|
||||
label = dialog->label();
|
||||
icon = dialog->icon();
|
||||
appLocal = dialog->applicationLocal();
|
||||
|
||||
delete dialog;
|
||||
return true;
|
||||
}
|
||||
|
||||
delete dialog;
|
||||
return false;
|
||||
}
|
||||
|
||||
KFilePlaceEditDialog::KFilePlaceEditDialog(bool allowGlobal,
|
||||
const QUrl &url,
|
||||
const QString &label,
|
||||
const QString &icon,
|
||||
bool isAddingNewPlace,
|
||||
bool appLocal,
|
||||
int iconSize,
|
||||
QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_iconButton(nullptr)
|
||||
{
|
||||
if (isAddingNewPlace) {
|
||||
setWindowTitle(i18n("Add Places Entry"));
|
||||
} else {
|
||||
setWindowTitle(i18n("Edit Places Entry"));
|
||||
}
|
||||
setModal(true);
|
||||
|
||||
QVBoxLayout *box = new QVBoxLayout(this);
|
||||
|
||||
QFormLayout *layout = new QFormLayout();
|
||||
box->addLayout(layout);
|
||||
|
||||
QString whatsThisText = i18n(
|
||||
"<qt>This is the text that will appear in the Places panel.<br /><br />"
|
||||
"The label should consist of one or two words "
|
||||
"that will help you remember what this entry refers to. "
|
||||
"If you do not enter a label, it will be derived from "
|
||||
"the location's URL.</qt>");
|
||||
m_labelEdit = new QLineEdit(this);
|
||||
layout->addRow(i18n("L&abel:"), m_labelEdit);
|
||||
m_labelEdit->setText(label);
|
||||
m_labelEdit->setPlaceholderText(i18n("Enter descriptive label here"));
|
||||
m_labelEdit->setWhatsThis(whatsThisText);
|
||||
layout->labelForField(m_labelEdit)->setWhatsThis(whatsThisText);
|
||||
|
||||
whatsThisText = i18n(
|
||||
"<qt>This is the location associated with the entry. Any valid URL may be used. For example:<br /><br />"
|
||||
"%1<br />http://www.kde.org<br />ftp://ftp.kde.org/pub/kde/stable<br /><br />"
|
||||
"By clicking on the button next to the text edit box you can browse to an "
|
||||
"appropriate URL.</qt>",
|
||||
QDir::homePath());
|
||||
m_urlEdit = new KUrlRequester(url, this);
|
||||
m_urlEdit->setMode(KFile::Directory);
|
||||
layout->addRow(i18n("&Location:"), m_urlEdit);
|
||||
m_urlEdit->setWhatsThis(whatsThisText);
|
||||
layout->labelForField(m_urlEdit)->setWhatsThis(whatsThisText);
|
||||
// Room for at least 40 chars (average char width is half of height)
|
||||
m_urlEdit->setMinimumWidth(m_urlEdit->fontMetrics().height() * (40 / 2));
|
||||
|
||||
whatsThisText = i18n(
|
||||
"<qt>This is the icon that will appear in the Places panel.<br /><br />"
|
||||
"Click on the button to select a different icon.</qt>");
|
||||
m_iconButton = new KIconButton(this);
|
||||
m_iconButton->setObjectName(QStringLiteral("icon button"));
|
||||
m_iconButton->setIconSize(iconSize);
|
||||
m_iconButton->setIconType(KIconLoader::NoGroup, KIconLoader::Place);
|
||||
if (icon.isEmpty()) {
|
||||
m_iconButton->setIcon(KIO::iconNameForUrl(url));
|
||||
} else {
|
||||
m_iconButton->setIcon(icon);
|
||||
}
|
||||
m_iconButton->setWhatsThis(whatsThisText);
|
||||
|
||||
if (url.scheme() == QLatin1String("trash")) {
|
||||
// Since there are separate trash icons when it is empty/non-empty,
|
||||
// the trash item's icon is made non-editable for simplicity
|
||||
m_iconButton->hide();
|
||||
// making the trash item's url editable misleads users into
|
||||
// thinking that the actual trash location is configurable here
|
||||
m_urlEdit->setDisabled(true);
|
||||
} else {
|
||||
layout->addRow(i18n("Choose an &icon:"), m_iconButton);
|
||||
layout->labelForField(m_iconButton)->setWhatsThis(whatsThisText);
|
||||
}
|
||||
|
||||
if (allowGlobal) {
|
||||
QString appName;
|
||||
appName = QGuiApplication::applicationDisplayName();
|
||||
if (appName.isEmpty()) {
|
||||
appName = QCoreApplication::applicationName();
|
||||
}
|
||||
m_appLocal = new QCheckBox(i18n("&Only show when using this application (%1)", appName), this);
|
||||
m_appLocal->setChecked(appLocal);
|
||||
m_appLocal->setWhatsThis(
|
||||
i18n("<qt>Select this setting if you want this "
|
||||
"entry to show only when using the current application (%1).<br /><br />"
|
||||
"If this setting is not selected, the entry will be available in all "
|
||||
"applications.</qt>",
|
||||
appName));
|
||||
box->addWidget(m_appLocal);
|
||||
} else {
|
||||
m_appLocal = nullptr;
|
||||
}
|
||||
connect(m_urlEdit->lineEdit(), &QLineEdit::textChanged, this, &KFilePlaceEditDialog::urlChanged);
|
||||
if (!label.isEmpty()) {
|
||||
// editing existing entry
|
||||
m_labelEdit->setFocus();
|
||||
} else {
|
||||
// new entry
|
||||
m_urlEdit->setFocus();
|
||||
}
|
||||
|
||||
m_buttonBox = new QDialogButtonBox(this);
|
||||
m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
box->addWidget(m_buttonBox);
|
||||
}
|
||||
|
||||
KFilePlaceEditDialog::~KFilePlaceEditDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void KFilePlaceEditDialog::urlChanged(const QString &text)
|
||||
{
|
||||
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty());
|
||||
}
|
||||
|
||||
QUrl KFilePlaceEditDialog::url() const
|
||||
{
|
||||
return m_urlEdit->url();
|
||||
}
|
||||
|
||||
QString KFilePlaceEditDialog::label() const
|
||||
{
|
||||
if (!m_labelEdit->text().isEmpty()) {
|
||||
return m_labelEdit->text();
|
||||
}
|
||||
|
||||
// derive descriptive label from the URL
|
||||
QUrl url = m_urlEdit->url();
|
||||
if (!url.fileName().isEmpty()) {
|
||||
return url.fileName();
|
||||
}
|
||||
if (!url.host().isEmpty()) {
|
||||
return url.host();
|
||||
}
|
||||
return url.scheme();
|
||||
}
|
||||
|
||||
QString KFilePlaceEditDialog::icon() const
|
||||
{
|
||||
return m_iconButton->icon();
|
||||
}
|
||||
|
||||
bool KFilePlaceEditDialog::applicationLocal() const
|
||||
{
|
||||
if (!m_appLocal) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_appLocal->isChecked();
|
||||
}
|
||||
|
||||
#include "moc_kfileplaceeditdialog.cpp"
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001, 2002, 2003 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KFILEPLACEEDITDIALOG_H
|
||||
#define KFILEPLACEEDITDIALOG_H
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
#include <KIconLoader>
|
||||
#include <QDialog>
|
||||
#include <QUrl>
|
||||
|
||||
class QCheckBox;
|
||||
class QDialogButtonBox;
|
||||
class QLineEdit;
|
||||
class KIconButton;
|
||||
class KUrlRequester;
|
||||
|
||||
/**
|
||||
* @class KFilePlaceEditDialog kfileplaceeditdialog.h <KFilePlaceEditDialog>
|
||||
*
|
||||
* A dialog that allows editing entries of a KFilePlacesModel.
|
||||
* The dialog offers to configure a given url, label and icon.
|
||||
* See the class-method getInformation() for easy usage.
|
||||
*
|
||||
* @author Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
* @since 5.53
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KFilePlaceEditDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* A convenience method to show the dialog and retrieve all the
|
||||
* properties via the given parameters. The parameters are used to
|
||||
* initialize the dialog and then return the user-configured values.
|
||||
*
|
||||
* @p allowGlobal if you set this to true, the dialog will have a checkbox
|
||||
* for the user to decide if he wants the entry to be
|
||||
* available globally or just for the current application.
|
||||
* @p url the url of the item
|
||||
* @p label a short, translated description of the item
|
||||
* @p icon an icon for the item
|
||||
* @p appLocal tells whether the item should be local for this application
|
||||
* or be available globally
|
||||
* @p iconSize determines the size of the icon that is shown/selectable
|
||||
* @p parent the parent-widget for the dialog
|
||||
*
|
||||
* If you leave the icon empty, the default icon for the given url will be
|
||||
* used (KMimeType::pixmapForUrl()).
|
||||
*/
|
||||
static bool
|
||||
getInformation(bool allowGlobal, QUrl &url, QString &label, QString &icon, bool isAddingNewPlace, bool &appLocal, int iconSize, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Constructs a KFilePlaceEditDialog.
|
||||
*
|
||||
* @p allowGlobal if you set this to true, the dialog will have a checkbox
|
||||
* for the user to decide if he wants the entry to be
|
||||
* available globally or just for the current application.
|
||||
* @p url the url of the item
|
||||
* @p label a short, translated description of the item
|
||||
* @p icon an icon for the item
|
||||
* @p appLocal tells whether the item should be local for this application
|
||||
* or be available globally
|
||||
* @p iconSize determines the size of the icon that is shown/selectable
|
||||
* @p parent the parent-widget for the dialog
|
||||
*
|
||||
* If you leave the icon empty, the default icon for the given url will be
|
||||
* used (KMimeType::pixmapForUrl()).
|
||||
*/
|
||||
KFilePlaceEditDialog(bool allowGlobal,
|
||||
const QUrl &url,
|
||||
const QString &label,
|
||||
const QString &icon,
|
||||
bool isAddingNewPlace,
|
||||
bool appLocal = true,
|
||||
int iconSize = KIconLoader::SizeMedium,
|
||||
QWidget *parent = nullptr);
|
||||
/**
|
||||
* Destroys the dialog.
|
||||
*/
|
||||
~KFilePlaceEditDialog() override;
|
||||
|
||||
/**
|
||||
* @returns the configured url
|
||||
*/
|
||||
QUrl url() const;
|
||||
|
||||
/**
|
||||
* @returns the configured label
|
||||
*/
|
||||
QString label() const;
|
||||
|
||||
/**
|
||||
* @returns the configured icon
|
||||
*/
|
||||
QString icon() const;
|
||||
|
||||
/**
|
||||
* @returns whether the item should be local to the application or global.
|
||||
* If allowGlobal was set to false in the constructor, this will always
|
||||
* return true.
|
||||
*/
|
||||
bool applicationLocal() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void urlChanged(const QString &);
|
||||
|
||||
private:
|
||||
/**
|
||||
* The KUrlRequester used for editing the url
|
||||
*/
|
||||
KUrlRequester *m_urlEdit;
|
||||
/**
|
||||
* The QLineEdit used for editing the label
|
||||
*/
|
||||
QLineEdit *m_labelEdit;
|
||||
/**
|
||||
* The KIconButton to configure the icon
|
||||
*/
|
||||
KIconButton *m_iconButton;
|
||||
/**
|
||||
* The QCheckBox to modify the local/global setting
|
||||
*/
|
||||
QCheckBox *m_appLocal;
|
||||
|
||||
QDialogButtonBox *m_buttonBox;
|
||||
};
|
||||
|
||||
#endif // KFILEPLACEEDITDIALOG_H
|
||||
@@ -0,0 +1,631 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kfileplacesitem_p.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QIcon>
|
||||
|
||||
#include <KBookmarkManager>
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <KIconUtils>
|
||||
#include <KLocalizedString>
|
||||
#include <KMountPoint>
|
||||
#include <kprotocolinfo.h>
|
||||
#include <solid/block.h>
|
||||
#include <solid/genericinterface.h>
|
||||
#include <solid/networkshare.h>
|
||||
#include <solid/opticaldisc.h>
|
||||
#include <solid/opticaldrive.h>
|
||||
#include <solid/portablemediaplayer.h>
|
||||
#include <solid/storageaccess.h>
|
||||
#include <solid/storagedrive.h>
|
||||
#include <solid/storagevolume.h>
|
||||
|
||||
static bool isTrash(const KBookmark &bk)
|
||||
{
|
||||
return bk.url().toString() == QLatin1String("trash:/");
|
||||
}
|
||||
|
||||
KFilePlacesItem::KFilePlacesItem(KBookmarkManager *manager, const QString &address, const QString &udi, KFilePlacesModel *parent)
|
||||
: QObject(static_cast<QObject *>(parent))
|
||||
, m_manager(manager)
|
||||
, m_folderIsEmpty(true)
|
||||
, m_isCdrom(false)
|
||||
, m_isAccessible(false)
|
||||
, m_isTeardownAllowed(false)
|
||||
, m_isTeardownOverlayRecommended(false)
|
||||
, m_isTeardownInProgress(false)
|
||||
, m_isSetupInProgress(false)
|
||||
, m_isEjectInProgress(false)
|
||||
, m_isReadOnly(false)
|
||||
{
|
||||
updateDeviceInfo(udi);
|
||||
|
||||
setBookmark(m_manager->findByAddress(address));
|
||||
|
||||
if (udi.isEmpty() && m_bookmark.metaDataItem(QStringLiteral("ID")).isEmpty()) {
|
||||
m_bookmark.setMetaDataItem(QStringLiteral("ID"), generateNewId());
|
||||
} else if (udi.isEmpty()) {
|
||||
if (isTrash(m_bookmark)) {
|
||||
KConfig cfg(QStringLiteral("trashrc"), KConfig::SimpleConfig);
|
||||
const KConfigGroup group = cfg.group(QStringLiteral("Status"));
|
||||
m_folderIsEmpty = group.readEntry("Empty", true);
|
||||
}
|
||||
}
|
||||
|
||||
// Hide SSHFS network device mounted by kdeconnect, since we already have the kdeconnect:// place.
|
||||
if (isDevice() && m_access && device().vendor() == QLatin1String("fuse.sshfs")) {
|
||||
const QString storageFilePath = m_access->filePath();
|
||||
// Not using findByPath() as it resolves symlinks, potentially blocking,
|
||||
// but here we know we query for an existing actual mount point.
|
||||
const auto mountPoints = KMountPoint::currentMountPoints();
|
||||
auto it = std::find_if(mountPoints.cbegin(), mountPoints.cend(), [&storageFilePath](const KMountPoint::Ptr &mountPoint) {
|
||||
return mountPoint->mountPoint() == storageFilePath;
|
||||
});
|
||||
if (it != mountPoints.cend()) {
|
||||
if ((*it)->mountedFrom().startsWith(QLatin1String("kdeconnect@"))) {
|
||||
// Hide only if the user never set the "Hide" checkbox on the device.
|
||||
if (m_bookmark.metaDataItem(QStringLiteral("IsHidden")).isEmpty()) {
|
||||
setHidden(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KFilePlacesItem::~KFilePlacesItem()
|
||||
{
|
||||
}
|
||||
|
||||
QString KFilePlacesItem::id() const
|
||||
{
|
||||
if (isDevice()) {
|
||||
return bookmark().metaDataItem(QStringLiteral("UDI"));
|
||||
} else {
|
||||
return bookmark().metaDataItem(QStringLiteral("ID"));
|
||||
}
|
||||
}
|
||||
|
||||
bool KFilePlacesItem::hasSupportedScheme(const QStringList &schemes) const
|
||||
{
|
||||
if (schemes.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// StorageAccess is always local, doesn't need to be accessible to know this
|
||||
if (m_access && schemes.contains(QLatin1String("file"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_networkShare && schemes.contains(m_networkShare->url().scheme())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_player) {
|
||||
const QStringList protocols = m_player->supportedProtocols();
|
||||
for (const QString &protocol : protocols) {
|
||||
if (schemes.contains(protocol)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KFilePlacesItem::isDevice() const
|
||||
{
|
||||
return !bookmark().metaDataItem(QStringLiteral("UDI")).isEmpty();
|
||||
}
|
||||
|
||||
KFilePlacesModel::DeviceAccessibility KFilePlacesItem::deviceAccessibility() const
|
||||
{
|
||||
if (m_isTeardownInProgress || m_isEjectInProgress) {
|
||||
return KFilePlacesModel::TeardownInProgress;
|
||||
} else if (m_isSetupInProgress) {
|
||||
return KFilePlacesModel::SetupInProgress;
|
||||
} else if (m_isAccessible) {
|
||||
return KFilePlacesModel::Accessible;
|
||||
} else {
|
||||
return KFilePlacesModel::SetupNeeded;
|
||||
}
|
||||
}
|
||||
|
||||
bool KFilePlacesItem::isTeardownAllowed() const
|
||||
{
|
||||
return m_isTeardownAllowed;
|
||||
}
|
||||
|
||||
bool KFilePlacesItem::isTeardownOverlayRecommended() const
|
||||
{
|
||||
return m_isTeardownOverlayRecommended;
|
||||
}
|
||||
|
||||
bool KFilePlacesItem::isEjectAllowed() const
|
||||
{
|
||||
return m_isCdrom;
|
||||
}
|
||||
|
||||
KBookmark KFilePlacesItem::bookmark() const
|
||||
{
|
||||
return m_bookmark;
|
||||
}
|
||||
|
||||
void KFilePlacesItem::setBookmark(const KBookmark &bookmark)
|
||||
{
|
||||
m_bookmark = bookmark;
|
||||
|
||||
if (m_device.isValid()) {
|
||||
m_bookmark.setMetaDataItem(QStringLiteral("UDI"), m_device.udi());
|
||||
if (m_volume && !m_volume->uuid().isEmpty()) {
|
||||
m_bookmark.setMetaDataItem(QStringLiteral("uuid"), m_volume->uuid());
|
||||
}
|
||||
}
|
||||
|
||||
if (bookmark.metaDataItem(QStringLiteral("isSystemItem")) == QLatin1String("true")) {
|
||||
// This context must stay as it is - the translated system bookmark names
|
||||
// are created with 'KFile System Bookmarks' as their context, so this
|
||||
// ensures the right string is picked from the catalog.
|
||||
// (coles, 13th May 2009)
|
||||
|
||||
m_text = i18nc("KFile System Bookmarks", bookmark.text().toUtf8().data());
|
||||
} else {
|
||||
m_text = bookmark.text();
|
||||
}
|
||||
|
||||
if (!isDevice()) {
|
||||
const QString protocol = bookmark.url().scheme();
|
||||
if (protocol == QLatin1String("timeline") || protocol == QLatin1String("recentlyused")) {
|
||||
m_groupType = KFilePlacesModel::RecentlySavedType;
|
||||
} else if (protocol.contains(QLatin1String("search"))) {
|
||||
m_groupType = KFilePlacesModel::SearchForType;
|
||||
} else if (protocol == QLatin1String("bluetooth") || protocol == QLatin1String("obexftp") || protocol == QLatin1String("kdeconnect")) {
|
||||
m_groupType = KFilePlacesModel::DevicesType;
|
||||
} else if (protocol == QLatin1String("tags")) {
|
||||
m_groupType = KFilePlacesModel::TagsType;
|
||||
} else if (protocol == QLatin1String("remote") || KProtocolInfo::protocolClass(protocol) != QLatin1String(":local")) {
|
||||
m_groupType = KFilePlacesModel::RemoteType;
|
||||
} else {
|
||||
m_groupType = KFilePlacesModel::PlacesType;
|
||||
}
|
||||
} else {
|
||||
if (m_drive && (m_drive->isHotpluggable() || m_drive->isRemovable())) {
|
||||
m_groupType = KFilePlacesModel::RemovableDevicesType;
|
||||
} else if (m_networkShare) {
|
||||
m_groupType = KFilePlacesModel::RemoteType;
|
||||
} else {
|
||||
m_groupType = KFilePlacesModel::DevicesType;
|
||||
}
|
||||
}
|
||||
|
||||
switch (m_groupType) {
|
||||
case KFilePlacesModel::PlacesType:
|
||||
m_groupName = i18nc("@item", "Places");
|
||||
break;
|
||||
case KFilePlacesModel::RemoteType:
|
||||
m_groupName = i18nc("@item", "Remote");
|
||||
break;
|
||||
case KFilePlacesModel::RecentlySavedType:
|
||||
m_groupName = i18nc("@item The place group section name for recent dynamic lists", "Recent");
|
||||
break;
|
||||
case KFilePlacesModel::SearchForType:
|
||||
m_groupName = i18nc("@item", "Search For");
|
||||
break;
|
||||
case KFilePlacesModel::DevicesType:
|
||||
m_groupName = i18nc("@item", "Devices");
|
||||
break;
|
||||
case KFilePlacesModel::RemovableDevicesType:
|
||||
m_groupName = i18nc("@item", "Removable Devices");
|
||||
break;
|
||||
case KFilePlacesModel::TagsType:
|
||||
m_groupName = i18nc("@item", "Tags");
|
||||
break;
|
||||
case KFilePlacesModel::UnknownType:
|
||||
Q_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Solid::Device KFilePlacesItem::device() const
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
QVariant KFilePlacesItem::data(int role) const
|
||||
{
|
||||
if (role == KFilePlacesModel::GroupRole) {
|
||||
return QVariant(m_groupName);
|
||||
} else if (role != KFilePlacesModel::HiddenRole && role != Qt::BackgroundRole && isDevice()) {
|
||||
return deviceData(role);
|
||||
} else {
|
||||
return bookmarkData(role);
|
||||
}
|
||||
}
|
||||
|
||||
KFilePlacesModel::GroupType KFilePlacesItem::groupType() const
|
||||
{
|
||||
return m_groupType;
|
||||
}
|
||||
|
||||
bool KFilePlacesItem::isHidden() const
|
||||
{
|
||||
return m_bookmark.metaDataItem(QStringLiteral("IsHidden")) == QLatin1String("true");
|
||||
}
|
||||
|
||||
void KFilePlacesItem::setHidden(bool hide)
|
||||
{
|
||||
if (m_bookmark.isNull() || isHidden() == hide) {
|
||||
return;
|
||||
}
|
||||
m_bookmark.setMetaDataItem(QStringLiteral("IsHidden"), hide ? QStringLiteral("true") : QStringLiteral("false"));
|
||||
}
|
||||
|
||||
QVariant KFilePlacesItem::bookmarkData(int role) const
|
||||
{
|
||||
KBookmark b = bookmark();
|
||||
|
||||
if (b.isNull()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return m_text;
|
||||
case Qt::DecorationRole:
|
||||
return QIcon::fromTheme(iconNameForBookmark(b));
|
||||
case Qt::ToolTipRole: {
|
||||
const KFilePlacesModel::GroupType type = groupType();
|
||||
// Don't display technical gibberish in the URL, particularly search.
|
||||
if (type != KFilePlacesModel::RecentlySavedType && type != KFilePlacesModel::SearchForType && type != KFilePlacesModel::TagsType) {
|
||||
return b.url().toDisplayString(QUrl::PreferLocalFile);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
case Qt::BackgroundRole:
|
||||
if (isHidden()) {
|
||||
return QColor(Qt::lightGray);
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
case KFilePlacesModel::UrlRole:
|
||||
return b.url();
|
||||
case KFilePlacesModel::SetupNeededRole:
|
||||
return false;
|
||||
case KFilePlacesModel::HiddenRole:
|
||||
return isHidden();
|
||||
case KFilePlacesModel::IconNameRole:
|
||||
return iconNameForBookmark(b);
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant KFilePlacesItem::deviceData(int role) const
|
||||
{
|
||||
Solid::Device d = device();
|
||||
|
||||
if (d.isValid()) {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
if (m_deviceDisplayName.isEmpty()) {
|
||||
m_deviceDisplayName = d.displayName();
|
||||
}
|
||||
return m_deviceDisplayName;
|
||||
case Qt::DecorationRole:
|
||||
// qDebug() << "adding emblems" << m_emblems << "to device icon" << m_deviceIconName;
|
||||
return KIconUtils::addOverlays(m_deviceIconName, m_emblems);
|
||||
case Qt::ToolTipRole: {
|
||||
if (m_access && m_isAccessible) {
|
||||
// For loop devices, show backing file path rather than /dev/loop123.
|
||||
QString mountedFrom = m_backingFile;
|
||||
if (mountedFrom.isEmpty() && m_block) {
|
||||
mountedFrom = m_block->device();
|
||||
}
|
||||
|
||||
if (!mountedFrom.isEmpty()) {
|
||||
return i18nc("@info:tooltip path (mounted from)", "%1 (from %2)", m_access->filePath(), mountedFrom);
|
||||
}
|
||||
} else if (!m_backingFile.isEmpty()) {
|
||||
return m_backingFile;
|
||||
} else if (m_block) {
|
||||
return m_block->device();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
case KFilePlacesModel::UrlRole:
|
||||
if (m_access) {
|
||||
const QString path = m_access->filePath();
|
||||
return path.isEmpty() ? QUrl() : QUrl::fromLocalFile(path);
|
||||
} else if (m_disc && (m_disc->availableContent() & Solid::OpticalDisc::Audio) != 0) {
|
||||
Solid::Block *block = d.as<Solid::Block>();
|
||||
if (block) {
|
||||
QString device = block->device();
|
||||
return QUrl(QStringLiteral("audiocd:/?device=%1").arg(device));
|
||||
}
|
||||
// We failed to get the block device. Assume audiocd:/ can
|
||||
// figure it out, but cannot handle multiple disc drives.
|
||||
// See https://bugs.kde.org/show_bug.cgi?id=314544#c40
|
||||
return QUrl(QStringLiteral("audiocd:/"));
|
||||
} else if (m_player) {
|
||||
const QStringList protocols = m_player->supportedProtocols();
|
||||
if (!protocols.isEmpty()) {
|
||||
const QString protocol = protocols.first();
|
||||
if (protocol == QLatin1String("mtp")) {
|
||||
return QUrl(QStringLiteral("%1:udi=%2").arg(protocol, d.udi()));
|
||||
} else {
|
||||
QUrl url;
|
||||
url.setScheme(protocol);
|
||||
url.setHost(d.udi().section(QLatin1Char('/'), -1));
|
||||
url.setPath(QStringLiteral("/"));
|
||||
return url;
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
case KFilePlacesModel::SetupNeededRole:
|
||||
if (m_access) {
|
||||
return !m_isAccessible;
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
case KFilePlacesModel::TeardownAllowedRole:
|
||||
if (m_access) {
|
||||
return m_isTeardownAllowed;
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
case KFilePlacesModel::EjectAllowedRole:
|
||||
return m_isAccessible && m_isCdrom;
|
||||
|
||||
case KFilePlacesModel::TeardownOverlayRecommendedRole:
|
||||
return m_isTeardownOverlayRecommended;
|
||||
|
||||
case KFilePlacesModel::DeviceAccessibilityRole:
|
||||
return deviceAccessibility();
|
||||
|
||||
case KFilePlacesModel::FixedDeviceRole: {
|
||||
if (m_drive != nullptr) {
|
||||
return !m_drive->isHotpluggable() && !m_drive->isRemovable();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case KFilePlacesModel::CapacityBarRecommendedRole:
|
||||
return m_isAccessible && !m_isCdrom && !m_networkShare && !m_isReadOnly;
|
||||
|
||||
case KFilePlacesModel::IconNameRole:
|
||||
return m_deviceIconName;
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
} else {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
KBookmark KFilePlacesItem::createBookmark(KBookmarkManager *manager, const QString &label, const QUrl &url, const QString &iconName, KFilePlacesItem *after)
|
||||
{
|
||||
KBookmarkGroup root = manager->root();
|
||||
if (root.isNull()) {
|
||||
return KBookmark();
|
||||
}
|
||||
QString empty_icon = iconName;
|
||||
if (url.toString() == QLatin1String("trash:/")) {
|
||||
if (empty_icon.endsWith(QLatin1String("-full"))) {
|
||||
empty_icon.chop(5);
|
||||
} else if (empty_icon.isEmpty()) {
|
||||
empty_icon = QStringLiteral("user-trash");
|
||||
}
|
||||
}
|
||||
KBookmark bookmark = root.addBookmark(label, url, empty_icon);
|
||||
bookmark.setMetaDataItem(QStringLiteral("ID"), generateNewId());
|
||||
|
||||
if (after) {
|
||||
root.moveBookmark(bookmark, after->bookmark());
|
||||
}
|
||||
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
KBookmark KFilePlacesItem::createSystemBookmark(KBookmarkManager *manager,
|
||||
const char *untranslatedLabel,
|
||||
const QUrl &url,
|
||||
const QString &iconName,
|
||||
const KBookmark &after)
|
||||
{
|
||||
KBookmark bookmark = createBookmark(manager, QString::fromUtf8(untranslatedLabel), url, iconName);
|
||||
if (!bookmark.isNull()) {
|
||||
bookmark.setMetaDataItem(QStringLiteral("isSystemItem"), QStringLiteral("true"));
|
||||
}
|
||||
if (!after.isNull()) {
|
||||
manager->root().moveBookmark(bookmark, after);
|
||||
}
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
KBookmark KFilePlacesItem::createDeviceBookmark(KBookmarkManager *manager, const Solid::Device &device)
|
||||
{
|
||||
KBookmarkGroup root = manager->root();
|
||||
if (root.isNull()) {
|
||||
return KBookmark();
|
||||
}
|
||||
KBookmark bookmark = root.createNewSeparator();
|
||||
bookmark.setMetaDataItem(QStringLiteral("UDI"), device.udi());
|
||||
bookmark.setMetaDataItem(QStringLiteral("isSystemItem"), QStringLiteral("true"));
|
||||
|
||||
const auto storage = device.as<Solid::StorageVolume>();
|
||||
if (storage) {
|
||||
bookmark.setMetaDataItem(QStringLiteral("uuid"), storage->uuid());
|
||||
}
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
KBookmark KFilePlacesItem::createTagBookmark(KBookmarkManager *manager, const QString &tag)
|
||||
{
|
||||
// TODO: Currently KFilePlacesItem::setBookmark() only decides by the "isSystemItem" property
|
||||
// if the label text should be looked up for translation. So there is a small risk that
|
||||
// labelTexts which match existing untranslated system labels accidentally get translated.
|
||||
KBookmark bookmark = createBookmark(manager, tag, QUrl(QLatin1String("tags:/") + tag), QStringLiteral("tag"));
|
||||
if (!bookmark.isNull()) {
|
||||
bookmark.setMetaDataItem(QStringLiteral("tag"), tag);
|
||||
bookmark.setMetaDataItem(QStringLiteral("isSystemItem"), QStringLiteral("true"));
|
||||
}
|
||||
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
QString KFilePlacesItem::generateNewId()
|
||||
{
|
||||
static int count = 0;
|
||||
|
||||
// return QString::number(count++);
|
||||
|
||||
return QString::number(QDateTime::currentSecsSinceEpoch()) + QLatin1Char('/') + QString::number(count++);
|
||||
|
||||
// return QString::number(QDateTime::currentSecsSinceEpoch())
|
||||
// + '/' + QString::number(qrand());
|
||||
}
|
||||
|
||||
bool KFilePlacesItem::updateDeviceInfo(const QString &udi)
|
||||
{
|
||||
if (m_device.udi() == udi) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_access) {
|
||||
m_access->disconnect(this);
|
||||
}
|
||||
|
||||
if (m_opticalDrive) {
|
||||
m_opticalDrive->disconnect(this);
|
||||
}
|
||||
|
||||
m_device = Solid::Device(udi);
|
||||
if (m_device.isValid()) {
|
||||
m_access = m_device.as<Solid::StorageAccess>();
|
||||
m_volume = m_device.as<Solid::StorageVolume>();
|
||||
m_block = m_device.as<Solid::Block>();
|
||||
m_disc = m_device.as<Solid::OpticalDisc>();
|
||||
m_player = m_device.as<Solid::PortableMediaPlayer>();
|
||||
m_networkShare = m_device.as<Solid::NetworkShare>();
|
||||
m_deviceIconName = m_device.icon();
|
||||
m_emblems = m_device.emblems();
|
||||
|
||||
if (auto *genericIface = m_device.as<Solid::GenericInterface>()) {
|
||||
m_backingFile = genericIface->property(QStringLiteral("BackingFile")).toString();
|
||||
}
|
||||
|
||||
m_drive = nullptr;
|
||||
m_opticalDrive = nullptr;
|
||||
|
||||
Solid::Device parentDevice = m_device;
|
||||
while (parentDevice.isValid() && !m_drive) {
|
||||
m_drive = parentDevice.as<Solid::StorageDrive>();
|
||||
m_opticalDrive = parentDevice.as<Solid::OpticalDrive>();
|
||||
parentDevice = parentDevice.parent();
|
||||
}
|
||||
|
||||
if (m_access) {
|
||||
connect(m_access.data(), &Solid::StorageAccess::setupRequested, this, [this] {
|
||||
m_isSetupInProgress = true;
|
||||
Q_EMIT itemChanged(id(), {KFilePlacesModel::DeviceAccessibilityRole});
|
||||
});
|
||||
connect(m_access.data(), &Solid::StorageAccess::setupDone, this, [this] {
|
||||
m_isSetupInProgress = false;
|
||||
Q_EMIT itemChanged(id(), {KFilePlacesModel::DeviceAccessibilityRole});
|
||||
});
|
||||
|
||||
connect(m_access.data(), &Solid::StorageAccess::teardownRequested, this, [this] {
|
||||
m_isTeardownInProgress = true;
|
||||
Q_EMIT itemChanged(id(), {KFilePlacesModel::DeviceAccessibilityRole});
|
||||
});
|
||||
connect(m_access.data(), &Solid::StorageAccess::teardownDone, this, [this] {
|
||||
m_isTeardownInProgress = false;
|
||||
Q_EMIT itemChanged(id(), {KFilePlacesModel::DeviceAccessibilityRole});
|
||||
});
|
||||
|
||||
connect(m_access.data(), &Solid::StorageAccess::accessibilityChanged, this, &KFilePlacesItem::onAccessibilityChanged);
|
||||
onAccessibilityChanged(m_access->isAccessible());
|
||||
}
|
||||
|
||||
if (m_opticalDrive) {
|
||||
connect(m_opticalDrive.data(), &Solid::OpticalDrive::ejectRequested, this, [this] {
|
||||
m_isEjectInProgress = true;
|
||||
Q_EMIT itemChanged(id(), {KFilePlacesModel::DeviceAccessibilityRole});
|
||||
});
|
||||
connect(m_opticalDrive.data(), &Solid::OpticalDrive::ejectDone, this, [this] {
|
||||
m_isEjectInProgress = false;
|
||||
Q_EMIT itemChanged(id(), {KFilePlacesModel::DeviceAccessibilityRole});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
m_access = nullptr;
|
||||
m_volume = nullptr;
|
||||
m_disc = nullptr;
|
||||
m_player = nullptr;
|
||||
m_drive = nullptr;
|
||||
m_opticalDrive = nullptr;
|
||||
m_networkShare = nullptr;
|
||||
m_deviceIconName.clear();
|
||||
m_emblems.clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KFilePlacesItem::onAccessibilityChanged(bool isAccessible)
|
||||
{
|
||||
m_isAccessible = isAccessible;
|
||||
m_isCdrom = m_device.is<Solid::OpticalDrive>() || m_opticalDrive || (m_volume && m_volume->fsType() == QLatin1String("iso9660"));
|
||||
m_emblems = m_device.emblems();
|
||||
|
||||
if (auto generic = m_device.as<Solid::GenericInterface>()) {
|
||||
// TODO add Solid API for this.
|
||||
m_isReadOnly = generic->property(QStringLiteral("ReadOnly")).toBool();
|
||||
}
|
||||
|
||||
m_isTeardownAllowed = isAccessible;
|
||||
if (m_isTeardownAllowed) {
|
||||
if (m_access->filePath() == QDir::rootPath()) {
|
||||
m_isTeardownAllowed = false;
|
||||
} else {
|
||||
const auto homeDevice = Solid::Device::storageAccessFromPath(QDir::homePath());
|
||||
const auto *homeAccess = homeDevice.as<Solid::StorageAccess>();
|
||||
if (homeAccess && m_access->filePath() == homeAccess->filePath()) {
|
||||
m_isTeardownAllowed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_isTeardownOverlayRecommended = m_isTeardownAllowed && !m_networkShare;
|
||||
if (m_isTeardownOverlayRecommended) {
|
||||
if (m_drive && !m_drive->isHotpluggable() && !m_drive->isRemovable()) {
|
||||
m_isTeardownOverlayRecommended = false;
|
||||
}
|
||||
}
|
||||
|
||||
Q_EMIT itemChanged(id());
|
||||
}
|
||||
|
||||
QString KFilePlacesItem::iconNameForBookmark(const KBookmark &bookmark) const
|
||||
{
|
||||
if (!m_folderIsEmpty && isTrash(bookmark)) {
|
||||
return bookmark.icon() + QLatin1String("-full");
|
||||
} else {
|
||||
return bookmark.icon();
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_kfileplacesitem_p.cpp"
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KFILEPLACESITEM_P_H
|
||||
#define KFILEPLACESITEM_P_H
|
||||
|
||||
#include "kfileplacesmodel.h"
|
||||
#include <KBookmark>
|
||||
#include <KBookmarkManager>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <solid/device.h>
|
||||
|
||||
class KDirLister;
|
||||
namespace Solid
|
||||
{
|
||||
class Block;
|
||||
class StorageAccess;
|
||||
class StorageVolume;
|
||||
class StorageDrive;
|
||||
class NetworkShare;
|
||||
class OpticalDrive;
|
||||
class OpticalDisc;
|
||||
class PortableMediaPlayer;
|
||||
}
|
||||
|
||||
class KFilePlacesItem : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KFilePlacesItem(KBookmarkManager *manager, const QString &address, const QString &udi, KFilePlacesModel *parent);
|
||||
~KFilePlacesItem() override;
|
||||
|
||||
QString id() const;
|
||||
|
||||
bool isDevice() const;
|
||||
KFilePlacesModel::DeviceAccessibility deviceAccessibility() const;
|
||||
bool isTeardownAllowed() const;
|
||||
bool isTeardownOverlayRecommended() const;
|
||||
bool isEjectAllowed() const;
|
||||
KBookmark bookmark() const;
|
||||
void setBookmark(const KBookmark &bookmark);
|
||||
Solid::Device device() const;
|
||||
QVariant data(int role) const;
|
||||
KFilePlacesModel::GroupType groupType() const;
|
||||
bool isHidden() const;
|
||||
void setHidden(bool hide);
|
||||
|
||||
bool hasSupportedScheme(const QStringList &schemes) const;
|
||||
|
||||
static KBookmark
|
||||
createBookmark(KBookmarkManager *manager, const QString &label, const QUrl &url, const QString &iconName, KFilePlacesItem *after = nullptr);
|
||||
/**
|
||||
* @param untranslatedLabel text for label. If to be translated, should be set by kli18nc("KFile System Bookmarks", "Label text").untranslatedText().
|
||||
*/
|
||||
static KBookmark createSystemBookmark(KBookmarkManager *manager,
|
||||
const char *untranslatedLabel,
|
||||
const QUrl &url,
|
||||
const QString &iconName,
|
||||
const KBookmark &after = KBookmark());
|
||||
static KBookmark createDeviceBookmark(KBookmarkManager *manager, const Solid::Device &device);
|
||||
static KBookmark createTagBookmark(KBookmarkManager *manager, const QString &tag);
|
||||
|
||||
Q_SIGNALS:
|
||||
void itemChanged(const QString &id, const QList<int> &roles = {});
|
||||
|
||||
private Q_SLOTS:
|
||||
void onAccessibilityChanged(bool);
|
||||
|
||||
private:
|
||||
QVariant bookmarkData(int role) const;
|
||||
QVariant deviceData(int role) const;
|
||||
|
||||
QString iconNameForBookmark(const KBookmark &bookmark) const;
|
||||
|
||||
static QString generateNewId();
|
||||
bool updateDeviceInfo(const QString &udi);
|
||||
|
||||
KBookmarkManager *m_manager;
|
||||
KBookmark m_bookmark;
|
||||
bool m_folderIsEmpty;
|
||||
bool m_isCdrom;
|
||||
bool m_isAccessible;
|
||||
bool m_isTeardownAllowed;
|
||||
bool m_isTeardownOverlayRecommended;
|
||||
bool m_isTeardownInProgress;
|
||||
bool m_isSetupInProgress;
|
||||
bool m_isEjectInProgress;
|
||||
bool m_isReadOnly;
|
||||
QString m_text;
|
||||
Solid::Device m_device;
|
||||
QPointer<Solid::StorageAccess> m_access;
|
||||
QPointer<Solid::StorageVolume> m_volume;
|
||||
QPointer<Solid::StorageDrive> m_drive;
|
||||
QPointer<Solid::Block> m_block;
|
||||
QPointer<Solid::OpticalDrive> m_opticalDrive;
|
||||
QPointer<Solid::OpticalDisc> m_disc;
|
||||
QPointer<Solid::PortableMediaPlayer> m_player;
|
||||
QPointer<Solid::NetworkShare> m_networkShare;
|
||||
QString m_deviceIconName;
|
||||
QStringList m_emblems;
|
||||
QString m_backingFile;
|
||||
KFilePlacesModel::GroupType m_groupType = KFilePlacesModel::UnknownType;
|
||||
QString m_groupName;
|
||||
mutable QString m_deviceDisplayName;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KFILEPLACESMODEL_H
|
||||
#define KFILEPLACESMODEL_H
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
#include <KBookmark>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QUrl>
|
||||
|
||||
#include <solid/device.h>
|
||||
#include <solid/solidnamespace.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class KFilePlacesModelPrivate;
|
||||
class KBookmarkManager;
|
||||
|
||||
class QMimeData;
|
||||
class QAction;
|
||||
|
||||
/**
|
||||
* @class KFilePlacesModel kfileplacesmodel.h <KFilePlacesModel>
|
||||
*
|
||||
* This class is a list view model. Each entry represents a "place"
|
||||
* where user can access files. Only relevant when
|
||||
* used with QListView or QTableView.
|
||||
* @note This class is since 6.0 re-entrant
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KFilePlacesModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QStringList supportedSchemes READ supportedSchemes WRITE setSupportedSchemes NOTIFY supportedSchemesChanged)
|
||||
|
||||
public:
|
||||
// Note: run printf "0x%08X\n" $(($RANDOM*$RANDOM))
|
||||
// to define additional roles.
|
||||
enum AdditionalRoles {
|
||||
/** roleName is "url". @see url() */
|
||||
UrlRole = 0x069CD12B,
|
||||
|
||||
/** roleName is "isHidden". @see isHidden() */
|
||||
HiddenRole = 0x0741CAAC,
|
||||
|
||||
/** roleName is "isSetupNeeded". @see setupNeeded() */
|
||||
SetupNeededRole = 0x059A935D,
|
||||
|
||||
/**
|
||||
* Whether the place is a fixed device (neither hotpluggable nor removable).
|
||||
* roleName is "isFixedDevice".
|
||||
*/
|
||||
FixedDeviceRole = 0x332896C1,
|
||||
|
||||
/**
|
||||
* Whether the place should have its free space displayed in a capacity bar.
|
||||
* roleName is "isCapacityBarRecommended".
|
||||
*/
|
||||
CapacityBarRecommendedRole = 0x1548C5C4,
|
||||
|
||||
/**
|
||||
* The name of the group, for example "Remote" or "Devices". roleName is "group".
|
||||
* @since 5.40
|
||||
*/
|
||||
GroupRole = 0x0a5b64ee,
|
||||
|
||||
/**
|
||||
* roleName is "iconName".
|
||||
* @see icon()
|
||||
* @since 5.41
|
||||
*/
|
||||
IconNameRole = 0x00a45c00,
|
||||
|
||||
/** roleName is "isGroupHidden".
|
||||
* @see isGroupHidden()
|
||||
* @since 5.42
|
||||
*/
|
||||
GroupHiddenRole = 0x21a4b936,
|
||||
|
||||
/** roleName is "isTeardownAllowed".
|
||||
* @see isTeardownAllowed().
|
||||
* @since 5.91
|
||||
*/
|
||||
TeardownAllowedRole = 0x02533364,
|
||||
|
||||
/** roleName is "isEjectAllowed".
|
||||
* @since 5.94.
|
||||
*/
|
||||
EjectAllowedRole = 0x0A16AC5B,
|
||||
|
||||
/**
|
||||
* roleName is "isTeardownOverlayRecommended".
|
||||
* @see isTeardownOverlayRecommended()
|
||||
* @since 5.95
|
||||
*/
|
||||
TeardownOverlayRecommendedRole = 0x032EDCCE,
|
||||
|
||||
/**
|
||||
* roleName is "deviceAccessibility".
|
||||
* @see deviceAccessibility()
|
||||
* @since 5.99
|
||||
*/
|
||||
DeviceAccessibilityRole = 0x023FFD93,
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the available group types used in this model.
|
||||
* @since 5.42
|
||||
*/
|
||||
enum GroupType {
|
||||
PlacesType, ///< "Places" section
|
||||
RemoteType, ///< "Remote" section
|
||||
RecentlySavedType, ///< "Recent" section
|
||||
SearchForType, ///< "Search for" section
|
||||
DevicesType, ///< "Devices" section
|
||||
RemovableDevicesType, ///< "Removable Devices" section
|
||||
UnknownType, ///< Unknown GroupType
|
||||
TagsType, ///< "Tags" section. @since 5.54
|
||||
};
|
||||
Q_ENUM(GroupType)
|
||||
|
||||
enum DeviceAccessibility {
|
||||
SetupNeeded,
|
||||
SetupInProgress,
|
||||
Accessible,
|
||||
TeardownInProgress
|
||||
};
|
||||
Q_ENUM(DeviceAccessibility)
|
||||
|
||||
explicit KFilePlacesModel(QObject *parent = nullptr);
|
||||
~KFilePlacesModel() override;
|
||||
|
||||
/**
|
||||
* @return The URL of the place at index @p index.
|
||||
*/
|
||||
Q_INVOKABLE QUrl url(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return Whether the place at index @p index needs to be mounted before it can be used.
|
||||
*/
|
||||
Q_INVOKABLE bool setupNeeded(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return Whether the place is a device that can be unmounted, e.g. it is
|
||||
* mounted but does not point at system Root or the user's Home directory.
|
||||
*
|
||||
* It does not indicate whether the teardown can succeed.
|
||||
* @since 5.91
|
||||
*/
|
||||
Q_INVOKABLE bool isTeardownAllowed(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return Whether the place is a device that can be ejected, e.g. it is
|
||||
* a CD, DVD, etc.
|
||||
*
|
||||
* It does not indicate whether the eject can succeed.
|
||||
* @since 5.94
|
||||
*/
|
||||
Q_INVOKABLE bool isEjectAllowed(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return Whether showing an inline teardown button is recommended,
|
||||
* e.g. when it is a removable drive.
|
||||
*
|
||||
* @since 5.95
|
||||
**/
|
||||
Q_INVOKABLE bool isTeardownOverlayRecommended(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return Whether this device is currently accessible or being (un)mounted.
|
||||
*
|
||||
* @since 5.99
|
||||
*/
|
||||
Q_INVOKABLE KFilePlacesModel::DeviceAccessibility deviceAccessibility(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return The icon of the place at index @p index.
|
||||
*/
|
||||
Q_INVOKABLE QIcon icon(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return The user-visible text of the place at index @p index.
|
||||
*/
|
||||
Q_INVOKABLE QString text(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return Whether the place at index @p index is hidden or is inside an hidden group.
|
||||
*/
|
||||
Q_INVOKABLE bool isHidden(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return Whether the group type @p type is hidden.
|
||||
* @since 5.42
|
||||
*/
|
||||
Q_INVOKABLE bool isGroupHidden(const GroupType type) const;
|
||||
|
||||
/**
|
||||
* @return Whether the group of the place at index @p index is hidden.
|
||||
* @since 5.42
|
||||
*/
|
||||
Q_INVOKABLE bool isGroupHidden(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return Whether the place at index @p index is a device handled by Solid.
|
||||
* @see deviceForIndex()
|
||||
*/
|
||||
Q_INVOKABLE bool isDevice(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return The solid device of the place at index @p index, if it is a device. Otherwise a default Solid::Device() instance is returned.
|
||||
* @see isDevice()
|
||||
*/
|
||||
Solid::Device deviceForIndex(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return The KBookmark instance of the place at index @p index.
|
||||
* If the index is not valid, a default KBookmark instance is returned.
|
||||
*/
|
||||
KBookmark bookmarkForIndex(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return The KBookmark instance of the place with url @p searchUrl.
|
||||
* If the bookmark corresponding to searchUrl is not found, a default KBookmark instance is returned.
|
||||
* @since 5.63
|
||||
*/
|
||||
KBookmark bookmarkForUrl(const QUrl &searchUrl) const;
|
||||
|
||||
/**
|
||||
* @return The group type of the place at index @p index.
|
||||
* @since 5.42
|
||||
*/
|
||||
Q_INVOKABLE GroupType groupType(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return The list of model indexes that have @ type as their group type.
|
||||
* @see groupType()
|
||||
* @since 5.42
|
||||
*/
|
||||
Q_INVOKABLE QModelIndexList groupIndexes(const GroupType type) const;
|
||||
|
||||
/**
|
||||
* @return A QAction with a proper translated label that can be used to trigger the requestTeardown()
|
||||
* method for the place at index @p index.
|
||||
* @see requestTeardown()
|
||||
*/
|
||||
Q_INVOKABLE QAction *teardownActionForIndex(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return A QAction with a proper translated label that can be used to trigger the requestEject()
|
||||
* method for the place at index @p index.
|
||||
* @see requestEject()
|
||||
*/
|
||||
Q_INVOKABLE QAction *ejectActionForIndex(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* @return A QAction with a proper translated label that can be used to open a partitioning menu for the device. nullptr if not a device.
|
||||
*/
|
||||
Q_INVOKABLE QAction *partitionActionForIndex(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* Unmounts the place at index @p index by triggering the teardown functionality of its Solid device.
|
||||
* @see deviceForIndex()
|
||||
*/
|
||||
Q_INVOKABLE void requestTeardown(const QModelIndex &index);
|
||||
|
||||
/**
|
||||
* Ejects the place at index @p index by triggering the eject functionality of its Solid device.
|
||||
* @see deviceForIndex()
|
||||
*/
|
||||
Q_INVOKABLE void requestEject(const QModelIndex &index);
|
||||
|
||||
/**
|
||||
* Mounts the place at index @p index by triggering the setup functionality of its Solid device.
|
||||
* @see deviceForIndex()
|
||||
*/
|
||||
Q_INVOKABLE void requestSetup(const QModelIndex &index);
|
||||
|
||||
/**
|
||||
* Adds a new place to the model.
|
||||
* @param text The user-visible text for the place
|
||||
* @param url The URL of the place. It will be stored in its QUrl::FullyEncoded string format.
|
||||
* @param iconName The icon of the place
|
||||
* @param appName If set as the value of QCoreApplication::applicationName(), will make the place visible only in this application.
|
||||
*/
|
||||
Q_INVOKABLE void addPlace(const QString &text, const QUrl &url, const QString &iconName = QString(), const QString &appName = QString());
|
||||
|
||||
/**
|
||||
* Adds a new place to the model.
|
||||
* @param text The user-visible text for the place
|
||||
* @param url The URL of the place. It will be stored in its QUrl::FullyEncoded string format.
|
||||
* @param iconName The icon of the place
|
||||
* @param appName If set as the value of QCoreApplication::applicationName(), will make the place visible only in this application.
|
||||
* @param after The index after which the new place will be added.
|
||||
*/
|
||||
Q_INVOKABLE void addPlace(const QString &text, const QUrl &url, const QString &iconName, const QString &appName, const QModelIndex &after);
|
||||
|
||||
/**
|
||||
* Edits the place with index @p index.
|
||||
* @param text The new user-visible text for the place
|
||||
* @param url The new URL of the place
|
||||
* @param iconName The new icon of the place
|
||||
* @param appName The new application-local filter for the place (@see addPlace()).
|
||||
*/
|
||||
Q_INVOKABLE void
|
||||
editPlace(const QModelIndex &index, const QString &text, const QUrl &url, const QString &iconName = QString(), const QString &appName = QString());
|
||||
|
||||
/**
|
||||
* Deletes the place with index @p index from the model.
|
||||
*/
|
||||
Q_INVOKABLE void removePlace(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* Changes the visibility of the place with index @p index, but only if the place is not inside an hidden group.
|
||||
* @param hidden Whether the place should be hidden or visible.
|
||||
* @see isGroupHidden()
|
||||
*/
|
||||
Q_INVOKABLE void setPlaceHidden(const QModelIndex &index, bool hidden);
|
||||
|
||||
/**
|
||||
* Changes the visibility of the group with type @p type.
|
||||
* @param hidden Whether the group should be hidden or visible.
|
||||
* @see isGroupHidden()
|
||||
* @since 5.42
|
||||
*/
|
||||
Q_INVOKABLE void setGroupHidden(const GroupType type, bool hidden);
|
||||
|
||||
/**
|
||||
* @brief Move place at @p itemRow to a position before @p row
|
||||
* @return Whether the place has been moved.
|
||||
* @since 5.41
|
||||
*/
|
||||
Q_INVOKABLE bool movePlace(int itemRow, int row);
|
||||
|
||||
/**
|
||||
* @return The number of hidden places in the model.
|
||||
* @see isHidden()
|
||||
*/
|
||||
Q_INVOKABLE int hiddenCount() const;
|
||||
|
||||
/**
|
||||
* @brief Get a visible data based on Qt role for the given index.
|
||||
* Return the device information for the give index.
|
||||
*
|
||||
* @param index The QModelIndex which contains the row, column to fetch the data.
|
||||
* @param role The Interview data role(ex: Qt::DisplayRole).
|
||||
*
|
||||
* @return the data for the given index and role.
|
||||
*/
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
/**
|
||||
* @brief Get the children model index for the given row and column.
|
||||
*/
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* @brief Get the parent QModelIndex for the given model child.
|
||||
*/
|
||||
QModelIndex parent(const QModelIndex &child) const override;
|
||||
|
||||
/// Reimplemented from QAbstractItemModel.
|
||||
/// @see AdditionalRoles
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
/**
|
||||
* @brief Get the number of rows for a model index.
|
||||
*/
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* @brief Get the number of columns for a model index.
|
||||
*/
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
/**
|
||||
* Returns the closest item for the URL \a url.
|
||||
* The closest item is defined as item which is equal to
|
||||
* the URL or at least is a parent URL. If there are more than
|
||||
* one possible parent URL candidates, the item which covers
|
||||
* the bigger range of the URL is returned.
|
||||
*
|
||||
* Example: the url is '/home/peter/Documents/Music'.
|
||||
* Available items are:
|
||||
* - /home/peter
|
||||
* - /home/peter/Documents
|
||||
*
|
||||
* The returned item will the one for '/home/peter/Documents'.
|
||||
*/
|
||||
QModelIndex closestItem(const QUrl &url) const;
|
||||
|
||||
Qt::DropActions supportedDropActions() const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
QStringList mimeTypes() const override;
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
|
||||
|
||||
/**
|
||||
* @brief Reload bookmark information
|
||||
* @since 5.41
|
||||
*/
|
||||
Q_INVOKABLE void refresh() const;
|
||||
|
||||
/**
|
||||
* @brief Converts the URL, which contains "virtual" URLs for system-items like
|
||||
* "timeline:/lastmonth" into a Query-URL "timeline:/2017-10"
|
||||
* that will be handled by the corresponding KIO worker.
|
||||
* Virtual URLs for bookmarks are used to be independent from
|
||||
* internal format changes.
|
||||
* @param an url
|
||||
* @return the converted URL, which can be handled by a KIO worker
|
||||
* @since 5.41
|
||||
*/
|
||||
static QUrl convertedUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Set the URL schemes that the file widget should allow navigating to.
|
||||
*
|
||||
* If the returned list is empty, all schemes are supported. Examples for
|
||||
* schemes are @c "file" or @c "ftp".
|
||||
*
|
||||
* @sa QFileDialog::setSupportedSchemes
|
||||
* @since 5.43
|
||||
*/
|
||||
void setSupportedSchemes(const QStringList &schemes);
|
||||
|
||||
/**
|
||||
* Returns the URL schemes that the file widget should allow navigating to.
|
||||
*
|
||||
* If the returned list is empty, all schemes are supported.
|
||||
*
|
||||
* @sa QFileDialog::supportedSchemes
|
||||
* @since 5.43
|
||||
*/
|
||||
QStringList supportedSchemes() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* @p message An error message explaining what went wrong.
|
||||
*/
|
||||
void errorMessage(const QString &message);
|
||||
|
||||
/**
|
||||
* Emitted after the Solid setup ends.
|
||||
* @param success Whether the Solid setup has been successful.
|
||||
* @see requestSetup()
|
||||
*/
|
||||
void setupDone(const QModelIndex &index, bool success);
|
||||
|
||||
/**
|
||||
* Emitted after the teardown of a device ends.
|
||||
*
|
||||
* @note In case of an error, the @p errorMessage signal
|
||||
* will also be emitted with a message describing the error.
|
||||
*
|
||||
* @param error Type of error that occurred, if any.
|
||||
* @param errorData More information about the error, if any.
|
||||
* @since 5.100
|
||||
*/
|
||||
void teardownDone(const QModelIndex &index, Solid::ErrorType error, const QVariant &errorData);
|
||||
|
||||
/**
|
||||
* Emitted whenever the visibility of the group @p group changes.
|
||||
* @param hidden The new visibility of the group.
|
||||
* @see setGroupHidden()
|
||||
* @since 5.42
|
||||
*/
|
||||
void groupHiddenChanged(KFilePlacesModel::GroupType group, bool hidden);
|
||||
|
||||
/**
|
||||
* Called once the model has been reloaded
|
||||
*
|
||||
* @since 5.71
|
||||
*/
|
||||
void reloaded();
|
||||
|
||||
/**
|
||||
* Emitted whenever the list of supported schemes has been changed
|
||||
*
|
||||
* @since 5.94
|
||||
*/
|
||||
void supportedSchemesChanged();
|
||||
|
||||
private:
|
||||
friend class KFilePlacesModelPrivate;
|
||||
std::unique_ptr<KFilePlacesModelPrivate> d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KFILEPLACESMODEL_P_H
|
||||
#define KFILEPLACESMODEL_P_H
|
||||
|
||||
#include <solid/predicate.h>
|
||||
#include <solid/solidnamespace.h>
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QPersistentModelIndex>
|
||||
#include <QStringList>
|
||||
|
||||
class KBookmarkManager;
|
||||
class KCoreDirLister;
|
||||
class KFilePlacesItem;
|
||||
class KFilePlacesModel;
|
||||
|
||||
class QUrl;
|
||||
|
||||
namespace Solid
|
||||
{
|
||||
class StorageAccess;
|
||||
}
|
||||
|
||||
class KFilePlacesModelPrivate
|
||||
{
|
||||
public:
|
||||
explicit KFilePlacesModelPrivate(KFilePlacesModel *qq);
|
||||
|
||||
KFilePlacesModel *const q;
|
||||
|
||||
static QString ignoreMimeType();
|
||||
static QString internalMimeType(const KFilePlacesModel *model);
|
||||
|
||||
QList<KFilePlacesItem *> items;
|
||||
QList<Solid::Device> availableDevices;
|
||||
QMap<QObject *, QPersistentModelIndex> setupInProgress;
|
||||
QMap<QObject *, QPersistentModelIndex> teardownInProgress;
|
||||
QStringList supportedSchemes;
|
||||
|
||||
Solid::Predicate predicate;
|
||||
KBookmarkManager *bookmarkManager;
|
||||
|
||||
const bool fileIndexingEnabled;
|
||||
|
||||
void reloadAndSignal();
|
||||
QList<KFilePlacesItem *> loadBookmarkList();
|
||||
int findNearestPosition(int source, int target);
|
||||
|
||||
QList<QString> tags;
|
||||
const QString tagsUrlBase = QStringLiteral("tags:/");
|
||||
KCoreDirLister *tagsLister = nullptr;
|
||||
|
||||
void initDeviceList();
|
||||
void deviceAdded(const QString &udi);
|
||||
void deviceRemoved(const QString &udi);
|
||||
void itemChanged(const QString &udi, const QList<int> &roles);
|
||||
void reloadBookmarks();
|
||||
void storageSetupDone(Solid::ErrorType error, const QVariant &errorData, Solid::StorageAccess *sender);
|
||||
void storageTeardownDone(const QString &filePath, Solid::ErrorType error, const QVariant &errorData, QObject *sender);
|
||||
|
||||
private:
|
||||
bool isBalooUrl(const QUrl &url) const;
|
||||
};
|
||||
|
||||
#endif // KFILEPLACESMODEL_P_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 Kai Uwe Broulik <kde@broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KFILEPLACESVIEW_H
|
||||
#define KFILEPLACESVIEW_H
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
#include <QListView>
|
||||
#include <QUrl>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
class QResizeEvent;
|
||||
class QContextMenuEvent;
|
||||
|
||||
class KFilePlacesViewPrivate;
|
||||
|
||||
/**
|
||||
* @class KFilePlacesView kfileplacesview.h <KFilePlacesView>
|
||||
*
|
||||
* This class allows to display a KFilePlacesModel.
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KFilePlacesView : public QListView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit KFilePlacesView(QWidget *parent = nullptr);
|
||||
~KFilePlacesView() override;
|
||||
|
||||
/**
|
||||
* The teardown function signature. Custom teardown logic
|
||||
* may be provided via the setTeardownFunction method.
|
||||
* @since 5.91
|
||||
*/
|
||||
using TeardownFunction = std::function<void(const QModelIndex &)>;
|
||||
|
||||
/**
|
||||
* Whether hidden places, if any, are currently shown.
|
||||
* @since 5.91
|
||||
*/
|
||||
bool allPlacesShown() const;
|
||||
|
||||
/**
|
||||
* If \a enabled is true, it is allowed dropping items
|
||||
* above a place for e. g. copy or move operations. The application
|
||||
* has to take care itself to perform the operation
|
||||
* (see KFilePlacesView::urlsDropped()). If
|
||||
* \a enabled is false, it is only possible adding items
|
||||
* as additional place. Per default dropping on a place is
|
||||
* disabled.
|
||||
*/
|
||||
void setDropOnPlaceEnabled(bool enabled);
|
||||
bool isDropOnPlaceEnabled() const;
|
||||
|
||||
/**
|
||||
* If \a delay (in ms) is greater than zero, the place will
|
||||
* automatically be activated if an item is dragged over
|
||||
* and held on top of a place for at least that duraton.
|
||||
*
|
||||
* @param delay Delay in ms, default is zero.
|
||||
* @since 5.92
|
||||
*/
|
||||
void setDragAutoActivationDelay(int delay);
|
||||
int dragAutoActivationDelay() const;
|
||||
|
||||
/**
|
||||
* If \a enabled is true (the default), items will automatically resize
|
||||
* themselves to fill the view.
|
||||
*
|
||||
*/
|
||||
void setAutoResizeItemsEnabled(bool enabled);
|
||||
bool isAutoResizeItemsEnabled() const;
|
||||
|
||||
/**
|
||||
* Sets a custom function that will be called when teardown of
|
||||
* a device (e.g.\ unmounting a drive) is requested.
|
||||
* @since 5.91
|
||||
*/
|
||||
void setTeardownFunction(TeardownFunction teardownFunc);
|
||||
|
||||
QSize sizeHint() const override; // clazy:exclude=const-signal-or-slot
|
||||
|
||||
public Q_SLOTS:
|
||||
void setUrl(const QUrl &url);
|
||||
void setShowAll(bool showAll);
|
||||
|
||||
void setModel(QAbstractItemModel *model) override;
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void hideEvent(QHideEvent *event) override;
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void dragLeaveEvent(QDragLeaveEvent *event) override;
|
||||
void dragMoveEvent(QDragMoveEvent *event) override;
|
||||
void dropEvent(QDropEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void startDrag(Qt::DropActions supportedActions) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
|
||||
protected Q_SLOTS:
|
||||
void rowsInserted(const QModelIndex &parent, int start, int end) override;
|
||||
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when an item in the places view is clicked on with left mouse
|
||||
* button with no modifier keys pressed.
|
||||
*
|
||||
* If a storage device needs to be mounted first, this signal is emitted once
|
||||
* mounting has completed successfully.
|
||||
*
|
||||
* @param url The URL of the place
|
||||
* @since 5.91
|
||||
*/
|
||||
void placeActivated(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted when the URL \a url should be opened in a new inactive tab because
|
||||
* the user clicked on a place with the middle mouse button or
|
||||
* left-clicked with the Ctrl modifier pressed or selected "Open in New Tab"
|
||||
* from the context menu.
|
||||
*
|
||||
* If a storage device needs to be mounted first, this signal is emitted once
|
||||
* mounting has completed successfully.
|
||||
* @since 5.91
|
||||
*/
|
||||
void tabRequested(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted when the URL \a url should be opened in a new active tab because
|
||||
* the user clicked on a place with the middle mouse button with
|
||||
* the Shift modifier pressed or left-clicked with both the Ctrl and Shift
|
||||
* modifiers pressed.
|
||||
|
||||
* If a storage device needs to be mounted first, this signal is emitted once
|
||||
* mounting has completed successfully.
|
||||
* @since 5.91
|
||||
*/
|
||||
void activeTabRequested(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted when the URL \a url should be opened in a new window because
|
||||
* the user left-clicked on a place with Shift modifier pressed or selected
|
||||
* "Open in New Window" from the context menu.
|
||||
*
|
||||
* If a storage device needs to be mounted first, this signal is emitted once
|
||||
* mounting has completed successfully.
|
||||
* @since 5.91
|
||||
*/
|
||||
void newWindowRequested(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted just before the context menu opens. This can be used to add additional
|
||||
* application actions to the menu.
|
||||
* @param index The model index of the place whose menu is about to open.
|
||||
* @param menu The menu that will be opened.
|
||||
* @since 5.91
|
||||
*/
|
||||
void contextMenuAboutToShow(const QModelIndex &index, QMenu *menu);
|
||||
|
||||
/**
|
||||
* Emitted when allPlacesShown changes
|
||||
* @since 5.91
|
||||
*/
|
||||
void allPlacesShownChanged(bool allPlacesShown);
|
||||
|
||||
void urlChanged(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Is emitted if items are dropped on the place \a dest.
|
||||
* The application has to take care itself about performing the
|
||||
* corresponding action like copying or moving.
|
||||
*/
|
||||
void urlsDropped(const QUrl &dest, QDropEvent *event, QWidget *parent);
|
||||
|
||||
private:
|
||||
friend class KFilePlacesViewPrivate;
|
||||
friend class KFilePlacesEventWatcher;
|
||||
std::unique_ptr<KFilePlacesViewPrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 Kai Uwe Broulik <kde@broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KFILEPLACESVIEW_P_H
|
||||
#define KFILEPLACESVIEW_P_H
|
||||
|
||||
#include <KIO/FileSystemFreeSpaceJob>
|
||||
#include <KIO/Global>
|
||||
|
||||
#include <QAbstractItemDelegate>
|
||||
#include <QDateTime>
|
||||
#include <QDeadlineTimer>
|
||||
#include <QGestureEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QPointer>
|
||||
#include <QScroller>
|
||||
#include <QTimer>
|
||||
|
||||
#include <set>
|
||||
|
||||
class KFilePlacesView;
|
||||
class QTimeLine;
|
||||
|
||||
struct PlaceFreeSpaceInfo {
|
||||
QDeadlineTimer timeout;
|
||||
KIO::filesize_t used = 0;
|
||||
KIO::filesize_t size = 0;
|
||||
QPointer<KIO::FileSystemFreeSpaceJob> job;
|
||||
};
|
||||
|
||||
class KFilePlacesViewDelegate : public QAbstractItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit KFilePlacesViewDelegate(KFilePlacesView *parent);
|
||||
~KFilePlacesViewDelegate() override;
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
|
||||
int iconSize() const;
|
||||
void setIconSize(int newSize);
|
||||
|
||||
void paletteChange();
|
||||
|
||||
void addAppearingItem(const QModelIndex &index);
|
||||
void setAppearingItemProgress(qreal value);
|
||||
void addDisappearingItem(const QModelIndex &index);
|
||||
void addDisappearingItemGroup(const QModelIndex &index);
|
||||
void setDisappearingItemProgress(qreal value);
|
||||
void setDeviceBusyAnimationRotation(qreal angle);
|
||||
|
||||
void setShowHoverIndication(bool show);
|
||||
void setHoveredHeaderArea(const QModelIndex &index);
|
||||
void setHoveredAction(const QModelIndex &index);
|
||||
|
||||
qreal contentsOpacity(const QModelIndex &index) const;
|
||||
|
||||
bool pointIsHeaderArea(const QPoint &pos) const;
|
||||
bool pointIsTeardownAction(const QPoint &pos) const;
|
||||
|
||||
void startDrag();
|
||||
|
||||
int sectionHeaderHeight(const QModelIndex &index) const;
|
||||
bool indexIsSectionHeader(const QModelIndex &index) const;
|
||||
int actionIconSize() const;
|
||||
|
||||
void checkFreeSpace();
|
||||
void checkFreeSpace(const QModelIndex &index) const;
|
||||
void startPollingFreeSpace() const;
|
||||
void stopPollingFreeSpace() const;
|
||||
|
||||
void clearFreeSpaceInfo();
|
||||
|
||||
protected:
|
||||
bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index) override;
|
||||
|
||||
private:
|
||||
QString groupNameFromIndex(const QModelIndex &index) const;
|
||||
QModelIndex previousVisibleIndex(const QModelIndex &index) const;
|
||||
void drawSectionHeader(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
|
||||
QColor textColor(const QStyleOption &option) const;
|
||||
QColor baseColor(const QStyleOption &option) const;
|
||||
QColor mixedColor(const QColor &c1, const QColor &c2, int c1Percent) const;
|
||||
|
||||
KFilePlacesView *m_view;
|
||||
int m_iconSize;
|
||||
|
||||
QList<QPersistentModelIndex> m_appearingItems;
|
||||
qreal m_appearingHeightScale;
|
||||
qreal m_appearingOpacity;
|
||||
|
||||
QList<QPersistentModelIndex> m_disappearingItems;
|
||||
qreal m_disappearingHeightScale;
|
||||
qreal m_disappearingOpacity;
|
||||
|
||||
qreal m_busyAnimationRotation = 0.0;
|
||||
|
||||
bool m_showHoverIndication;
|
||||
QPersistentModelIndex m_hoveredHeaderArea;
|
||||
QPersistentModelIndex m_hoveredAction;
|
||||
mutable bool m_dragStarted;
|
||||
|
||||
QMap<QPersistentModelIndex, QTimeLine *> m_timeLineMap;
|
||||
QMap<QTimeLine *, QPersistentModelIndex> m_timeLineInverseMap;
|
||||
|
||||
mutable QTimer m_pollFreeSpace;
|
||||
mutable QMap<QPersistentModelIndex, PlaceFreeSpaceInfo> m_freeSpaceInfo;
|
||||
|
||||
mutable std::set<QPersistentModelIndex> m_elidedTexts;
|
||||
|
||||
// constructing KColorScheme is expensive, cache the negative color
|
||||
mutable QColor m_warningCapacityBarColor;
|
||||
};
|
||||
|
||||
class KFilePlacesEventWatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KFilePlacesEventWatcher(KFilePlacesView *parent = nullptr)
|
||||
: QObject(parent)
|
||||
, m_scroller(nullptr)
|
||||
, q(parent)
|
||||
, m_rubberBand(nullptr)
|
||||
, m_isTouchEvent(false)
|
||||
, m_mousePressed(false)
|
||||
, m_tapAndHoldActive(false)
|
||||
, m_lastMouseSource(Qt::MouseEventNotSynthesized)
|
||||
{
|
||||
m_rubberBand = new QRubberBand(QRubberBand::Rectangle, parent);
|
||||
}
|
||||
|
||||
const QModelIndex hoveredHeaderAreaIndex()
|
||||
{
|
||||
return m_hoveredHeaderAreaIndex;
|
||||
}
|
||||
|
||||
const QModelIndex hoveredActionIndex()
|
||||
{
|
||||
return m_hoveredActionIndex;
|
||||
}
|
||||
|
||||
QScroller *m_scroller;
|
||||
|
||||
public Q_SLOTS:
|
||||
void qScrollerStateChanged(const QScroller::State newState)
|
||||
{
|
||||
if (newState == QScroller::Inactive) {
|
||||
m_isTouchEvent = false;
|
||||
}
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void entryMiddleClicked(const QModelIndex &index);
|
||||
|
||||
void headerAreaEntered(const QModelIndex &index);
|
||||
void headerAreaLeft(const QModelIndex &index);
|
||||
|
||||
void actionEntered(const QModelIndex &index);
|
||||
void actionLeft(const QModelIndex &index);
|
||||
void actionClicked(const QModelIndex &index);
|
||||
|
||||
void windowActivated();
|
||||
void windowDeactivated();
|
||||
|
||||
void paletteChanged();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override
|
||||
{
|
||||
switch (event->type()) {
|
||||
case QEvent::MouseMove: {
|
||||
if (m_isTouchEvent && !m_tapAndHoldActive) {
|
||||
return true;
|
||||
}
|
||||
|
||||
m_tapAndHoldActive = false;
|
||||
if (m_rubberBand->isVisible()) {
|
||||
m_rubberBand->hide();
|
||||
}
|
||||
|
||||
QAbstractItemView *view = qobject_cast<QAbstractItemView *>(watched->parent());
|
||||
const QPoint pos = static_cast<QMouseEvent *>(event)->pos();
|
||||
const QModelIndex index = view->indexAt(pos);
|
||||
|
||||
QModelIndex headerAreaIndex;
|
||||
QModelIndex actionIndex;
|
||||
if (index.isValid()) {
|
||||
if (auto *delegate = qobject_cast<KFilePlacesViewDelegate *>(view->itemDelegate())) {
|
||||
if (delegate->pointIsHeaderArea(pos)) {
|
||||
headerAreaIndex = index;
|
||||
} else if (delegate->pointIsTeardownAction(pos)) {
|
||||
actionIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (headerAreaIndex != m_hoveredHeaderAreaIndex) {
|
||||
if (m_hoveredHeaderAreaIndex.isValid()) {
|
||||
Q_EMIT headerAreaLeft(m_hoveredHeaderAreaIndex);
|
||||
}
|
||||
m_hoveredHeaderAreaIndex = headerAreaIndex;
|
||||
if (headerAreaIndex.isValid()) {
|
||||
Q_EMIT headerAreaEntered(headerAreaIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (actionIndex != m_hoveredActionIndex) {
|
||||
if (m_hoveredActionIndex.isValid()) {
|
||||
Q_EMIT actionLeft(m_hoveredActionIndex);
|
||||
}
|
||||
m_hoveredActionIndex = actionIndex;
|
||||
if (actionIndex.isValid()) {
|
||||
Q_EMIT actionEntered(actionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case QEvent::Leave:
|
||||
if (m_hoveredHeaderAreaIndex.isValid()) {
|
||||
Q_EMIT headerAreaLeft(m_hoveredHeaderAreaIndex);
|
||||
}
|
||||
m_hoveredHeaderAreaIndex = QModelIndex();
|
||||
|
||||
if (m_hoveredActionIndex.isValid()) {
|
||||
Q_EMIT actionLeft(m_hoveredActionIndex);
|
||||
}
|
||||
m_hoveredActionIndex = QModelIndex();
|
||||
|
||||
break;
|
||||
case QEvent::MouseButtonPress: {
|
||||
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
|
||||
m_mousePressed = true;
|
||||
m_lastMouseSource = mouseEvent->source();
|
||||
|
||||
if (m_isTouchEvent) {
|
||||
return true;
|
||||
}
|
||||
onPressed(mouseEvent);
|
||||
Q_FALLTHROUGH();
|
||||
}
|
||||
case QEvent::MouseButtonDblClick: {
|
||||
// Prevent the selection clearing by clicking on the viewport directly
|
||||
QAbstractItemView *view = qobject_cast<QAbstractItemView *>(watched->parent());
|
||||
if (!view->indexAt(static_cast<QMouseEvent *>(event)->pos()).isValid()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::MouseButtonRelease: {
|
||||
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
|
||||
if (mouseEvent->button() == Qt::LeftButton || mouseEvent->button() == Qt::MiddleButton) {
|
||||
QAbstractItemView *view = qobject_cast<QAbstractItemView *>(watched->parent());
|
||||
const QModelIndex index = view->indexAt(mouseEvent->pos());
|
||||
|
||||
if (mouseEvent->button() == Qt::LeftButton) {
|
||||
if (m_clickedActionIndex.isValid()) {
|
||||
if (auto *delegate = qobject_cast<KFilePlacesViewDelegate *>(view->itemDelegate())) {
|
||||
if (delegate->pointIsTeardownAction(mouseEvent->pos())) {
|
||||
if (m_clickedActionIndex == index) {
|
||||
Q_EMIT actionClicked(m_clickedActionIndex);
|
||||
// filter out, avoid QAbstractItemView::clicked being emitted
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_clickedActionIndex = index;
|
||||
} else if (mouseEvent->button() == Qt::MiddleButton) {
|
||||
if (m_middleClickedIndex.isValid() && m_middleClickedIndex == index) {
|
||||
Q_EMIT entryMiddleClicked(m_middleClickedIndex);
|
||||
}
|
||||
m_middleClickedIndex = QPersistentModelIndex();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QEvent::WindowActivate:
|
||||
Q_EMIT windowActivated();
|
||||
break;
|
||||
case QEvent::WindowDeactivate:
|
||||
Q_EMIT windowDeactivated();
|
||||
break;
|
||||
case QEvent::PaletteChange:
|
||||
Q_EMIT paletteChanged();
|
||||
break;
|
||||
case QEvent::TouchBegin: {
|
||||
m_isTouchEvent = true;
|
||||
m_mousePressed = false;
|
||||
break;
|
||||
}
|
||||
case QEvent::Gesture: {
|
||||
gestureEvent(static_cast<QGestureEvent *>(event));
|
||||
event->accept();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void onPressed(QMouseEvent *mouseEvent)
|
||||
{
|
||||
if (mouseEvent->button() == Qt::LeftButton || mouseEvent->button() == Qt::MiddleButton) {
|
||||
QAbstractItemView *view = qobject_cast<QAbstractItemView *>(q);
|
||||
const QModelIndex index = view->indexAt(mouseEvent->pos());
|
||||
if (index.isValid()) {
|
||||
if (mouseEvent->button() == Qt::LeftButton) {
|
||||
if (auto *delegate = qobject_cast<KFilePlacesViewDelegate *>(view->itemDelegate())) {
|
||||
if (delegate->pointIsTeardownAction(mouseEvent->pos())) {
|
||||
m_clickedActionIndex = index;
|
||||
}
|
||||
}
|
||||
} else if (mouseEvent->button() == Qt::MiddleButton) {
|
||||
m_middleClickedIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gestureEvent(QGestureEvent *event)
|
||||
{
|
||||
if (QGesture *gesture = event->gesture(Qt::TapGesture)) {
|
||||
tapTriggered(static_cast<QTapGesture *>(gesture));
|
||||
}
|
||||
if (QGesture *gesture = event->gesture(Qt::TapAndHoldGesture)) {
|
||||
tapAndHoldTriggered(static_cast<QTapAndHoldGesture *>(gesture));
|
||||
}
|
||||
}
|
||||
|
||||
void tapAndHoldTriggered(QTapAndHoldGesture *tap)
|
||||
{
|
||||
if (tap->state() == Qt::GestureFinished) {
|
||||
if (!m_mousePressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the TapAndHold gesture is triggerable with the mouse and stylus, we don't want this
|
||||
if (m_lastMouseSource == Qt::MouseEventNotSynthesized || !m_isTouchEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_tapAndHoldActive = true;
|
||||
m_scroller->stop();
|
||||
|
||||
// simulate a mousePressEvent, to allow KFilePlacesView to select the items
|
||||
const QPointF tapGlobalPos = tap->position(); // QTapAndHoldGesture::position is global
|
||||
const QPointF tapViewportPos(q->viewport()->mapFromGlobal(tapGlobalPos));
|
||||
QMouseEvent fakeMousePress(QEvent::MouseButtonPress, tapViewportPos, tapGlobalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
|
||||
onPressed(&fakeMousePress);
|
||||
q->mousePressEvent(&fakeMousePress);
|
||||
|
||||
const QPoint tapIndicatorSize(80, 80);
|
||||
const QPoint pos(q->mapFromGlobal(tapGlobalPos.toPoint()));
|
||||
const QRect tapIndicatorRect(pos - (tapIndicatorSize / 2), pos + (tapIndicatorSize / 2));
|
||||
m_rubberBand->setGeometry(tapIndicatorRect.normalized());
|
||||
m_rubberBand->show();
|
||||
}
|
||||
}
|
||||
|
||||
void tapTriggered(QTapGesture *tap)
|
||||
{
|
||||
static bool scrollerWasScrolling = false;
|
||||
|
||||
if (tap->state() == Qt::GestureStarted) {
|
||||
m_tapAndHoldActive = false;
|
||||
// if QScroller state is Scrolling or Dragging, the user makes the tap to stop the scrolling
|
||||
auto const scrollerState = m_scroller->state();
|
||||
if (scrollerState == QScroller::Scrolling || scrollerState == QScroller::Dragging) {
|
||||
scrollerWasScrolling = true;
|
||||
} else {
|
||||
scrollerWasScrolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (tap->state() == Qt::GestureFinished && !scrollerWasScrolling) {
|
||||
m_isTouchEvent = false;
|
||||
|
||||
// with touch you can touch multiple widgets at the same time, but only one widget will get a mousePressEvent.
|
||||
// we use this to select the right window
|
||||
if (!m_mousePressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_rubberBand->isVisible()) {
|
||||
m_rubberBand->hide();
|
||||
}
|
||||
// simulate a mousePressEvent, to allow KFilePlacesView to select the items
|
||||
const QPointF tapPosition = tap->position(); // QTapGesture::position is local
|
||||
const QPointF globalTapPosition = q->mapToGlobal(tapPosition);
|
||||
QMouseEvent fakeMousePress(QEvent::MouseButtonPress,
|
||||
tapPosition,
|
||||
globalTapPosition,
|
||||
m_tapAndHoldActive ? Qt::RightButton : Qt::LeftButton,
|
||||
m_tapAndHoldActive ? Qt::RightButton : Qt::LeftButton,
|
||||
Qt::NoModifier);
|
||||
onPressed(&fakeMousePress);
|
||||
q->mousePressEvent(&fakeMousePress);
|
||||
|
||||
if (m_tapAndHoldActive) {
|
||||
// simulate a contextMenuEvent
|
||||
QContextMenuEvent fakeContextMenu(QContextMenuEvent::Mouse, tapPosition.toPoint(), globalTapPosition.toPoint());
|
||||
q->contextMenuEvent(&fakeContextMenu);
|
||||
}
|
||||
m_tapAndHoldActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QPersistentModelIndex m_hoveredHeaderAreaIndex;
|
||||
QPersistentModelIndex m_middleClickedIndex;
|
||||
QPersistentModelIndex m_hoveredActionIndex;
|
||||
QPersistentModelIndex m_clickedActionIndex;
|
||||
|
||||
KFilePlacesView *const q;
|
||||
|
||||
QRubberBand *m_rubberBand;
|
||||
bool m_isTouchEvent;
|
||||
bool m_mousePressed;
|
||||
bool m_tapAndHoldActive;
|
||||
Qt::MouseEventSource m_lastMouseSource;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008-2009 Peter Penz <peter.penz@gmx.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KFILEPREVIEWGENERATOR_H
|
||||
#define KFILEPREVIEWGENERATOR_H
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class KAbstractViewAdapter;
|
||||
class KDirModel;
|
||||
class QAbstractItemView;
|
||||
class QAbstractProxyModel;
|
||||
|
||||
class KFilePreviewGeneratorPrivate;
|
||||
|
||||
/**
|
||||
* @class KFilePreviewGenerator kfilepreviewgenerator.h <KFilePreviewGenerator>
|
||||
*
|
||||
* @brief Generates previews for files of an item view.
|
||||
*
|
||||
* Per default a preview is generated for each item.
|
||||
* Additionally the clipboard is checked for cut items.
|
||||
* The icon state for cut items gets dimmed automatically.
|
||||
*
|
||||
* The following strategy is used when creating previews:
|
||||
* - The previews for currently visible items are created before
|
||||
* the previews for invisible items.
|
||||
* - If the user changes the visible area by using the scrollbars,
|
||||
* all pending previews get paused. As soon as the user stays
|
||||
* on the same position for a short delay, the previews are
|
||||
* resumed. Also in this case the previews for the visible items
|
||||
* are generated first.
|
||||
*
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KFilePreviewGenerator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param parent Item view containing the file items where previews should
|
||||
* be generated. It is mandatory that the item view specifies
|
||||
* an icon size by QAbstractItemView::setIconSize() and that
|
||||
* the model of the view (or the source model of the proxy model)
|
||||
* is an instance of KDirModel. Otherwise no previews will be generated.
|
||||
*/
|
||||
KFilePreviewGenerator(QAbstractItemView *parent);
|
||||
|
||||
/** @internal */
|
||||
KFilePreviewGenerator(KAbstractViewAdapter *parent, QAbstractProxyModel *model);
|
||||
|
||||
~KFilePreviewGenerator() override;
|
||||
|
||||
/**
|
||||
* If \a show is set to true, a preview is generated for each item. If \a show
|
||||
* is false, the MIME type icon of the item is shown instead. Per default showing
|
||||
* the preview is turned on. Note that it is mandatory that the item view
|
||||
* specifies an icon size by QAbstractItemView::setIconSize(), otherwise
|
||||
* KFilePreviewGenerator::isPreviewShown() will always return false.
|
||||
*/
|
||||
void setPreviewShown(bool show);
|
||||
bool isPreviewShown() const;
|
||||
|
||||
/**
|
||||
* Sets the list of enabled thumbnail plugins.
|
||||
* Per default all plugins enabled in the KConfigGroup "PreviewSettings"
|
||||
* are used.
|
||||
*
|
||||
* Note that this method doesn't cause already generated previews
|
||||
* to be regenerated.
|
||||
*
|
||||
* For a list of available plugins, call KIO::PreviewJob::availableThumbnailerPlugins().
|
||||
*
|
||||
* @see enabledPlugins
|
||||
*/
|
||||
void setEnabledPlugins(const QStringList &list);
|
||||
|
||||
/**
|
||||
* Returns the list of enabled thumbnail plugins.
|
||||
* @see setEnabledPlugins
|
||||
*/
|
||||
QStringList enabledPlugins() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Updates the icons for all items. Usually it is only
|
||||
* necessary to invoke this method when the icon size of the abstract item view
|
||||
* has been changed by QAbstractItemView::setIconSize(). Note that this method
|
||||
* should also be invoked if previews have been turned off, as the icons for
|
||||
* cut items must be updated when the icon size has changed.
|
||||
*/
|
||||
void updateIcons();
|
||||
|
||||
/** Cancels all pending previews. */
|
||||
void cancelPreviews();
|
||||
|
||||
private:
|
||||
friend class KFilePreviewGeneratorPrivate;
|
||||
std::unique_ptr<KFilePreviewGeneratorPrivate> const d;
|
||||
|
||||
Q_DISABLE_COPY(KFilePreviewGenerator)
|
||||
|
||||
Q_PRIVATE_SLOT(d, void pauseIconUpdates())
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,522 @@
|
||||
// -*- c++ -*-
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1997, 1998 Richard Moore <rich@kde.org>
|
||||
SPDX-FileCopyrightText: 1998 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 1998 Daniel Grana <grana@ie.iwi.unibe.ch>
|
||||
SPDX-FileCopyrightText: 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2001 Frerich Raabe <raabe@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KFILEWIDGET_H
|
||||
#define KFILEWIDGET_H
|
||||
|
||||
#include "kfile.h"
|
||||
#include "kiofilewidgets_export.h"
|
||||
#include <QWidget>
|
||||
|
||||
#include <KFileFilter>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QUrl;
|
||||
class QPushButton;
|
||||
class KActionCollection;
|
||||
class KFileWidgetPrivate;
|
||||
class KUrlComboBox;
|
||||
class KFileFilterCombo;
|
||||
|
||||
class KPreviewWidgetBase;
|
||||
class QMimeType;
|
||||
class KConfigGroup;
|
||||
class KJob;
|
||||
class KFileItem;
|
||||
class KDirOperator;
|
||||
|
||||
/**
|
||||
* @class KFileWidget kfilewidget.h <KFileWidget>
|
||||
*
|
||||
* File selector widget.
|
||||
*
|
||||
* This is the contents of the KDE file dialog, without the actual QDialog around it.
|
||||
* It can be embedded directly into applications.
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KFileWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Constructs a file selector widget.
|
||||
*
|
||||
* @param startDir This can either be:
|
||||
* @li An empty URL (QUrl()) to start in the current working directory,
|
||||
* or the last directory where a file has been selected.
|
||||
* @li The path or URL of a starting directory.
|
||||
* @li An initial file name to select, with the starting directory being
|
||||
* the current working directory or the last directory where a file
|
||||
* has been selected.
|
||||
* @li The path or URL of a file, specifying both the starting directory and
|
||||
* an initially selected file name.
|
||||
* @li A URL of the form @c kfiledialog:///<keyword> to start in the
|
||||
* directory last used by a filedialog in the same application that
|
||||
* specified the same keyword.
|
||||
* @li A URL of the form @c kfiledialog:///<keyword>/<filename>
|
||||
* to start in the directory last used by a filedialog in the same
|
||||
* application that specified the same keyword, and to initially
|
||||
* select the specified filename.
|
||||
* @li Deprecated: A URL of the form @c kfiledialog:///<keyword>?global to start
|
||||
* in the directory last used by a filedialog in any application that
|
||||
* specified the same keyword.
|
||||
* @li Deprecated: A URL of the form @c kfiledialog:///<keyword>/<filename>?global
|
||||
* to start in the directory last used by a filedialog in any
|
||||
* application that specified the same keyword, and to initially
|
||||
* select the specified filename.
|
||||
*
|
||||
* @note Since 5.96, the "?global" syntax is deprecated, for lack of usage.
|
||||
*
|
||||
* @param parent The parent widget of this widget
|
||||
*
|
||||
*/
|
||||
explicit KFileWidget(const QUrl &startDir, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~KFileWidget() override;
|
||||
|
||||
/**
|
||||
* Defines some default behavior of the filedialog.
|
||||
* E.g. in mode @p Opening and @p Saving, the selected files/urls will
|
||||
* be added to the "recent documents" list. The Saving mode also implies
|
||||
* setKeepLocation() being set.
|
||||
*
|
||||
* @p Other means that no default actions are performed.
|
||||
*
|
||||
* @see setOperationMode
|
||||
* @see operationMode
|
||||
*/
|
||||
enum OperationMode {
|
||||
Other = 0,
|
||||
Opening,
|
||||
Saving
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns The selected fully qualified filename.
|
||||
*/
|
||||
QUrl selectedUrl() const;
|
||||
|
||||
/**
|
||||
* @returns The list of selected URLs.
|
||||
*/
|
||||
QList<QUrl> selectedUrls() const;
|
||||
|
||||
/**
|
||||
* @returns the currently shown directory.
|
||||
*/
|
||||
QUrl baseUrl() const;
|
||||
|
||||
/**
|
||||
* Returns the full path of the selected file in the local filesystem.
|
||||
* (Local files only)
|
||||
*/
|
||||
QString selectedFile() const;
|
||||
|
||||
/**
|
||||
* Returns a list of all selected local files.
|
||||
*/
|
||||
QStringList selectedFiles() const;
|
||||
|
||||
/**
|
||||
* Sets the directory to view.
|
||||
*
|
||||
* @param url URL to show.
|
||||
* @param clearforward Indicates whether the forward queue
|
||||
* should be cleared.
|
||||
*/
|
||||
void setUrl(const QUrl &url, bool clearforward = true);
|
||||
|
||||
/**
|
||||
* Sets the URL to preselect to @p url
|
||||
*
|
||||
* This method handles absolute URLs (remember to use fromLocalFile for local paths).
|
||||
* It also handles relative URLs, which you should construct like this:
|
||||
* QUrl relativeUrl; relativeUrl.setPath(fileName);
|
||||
*
|
||||
* @since 5.33
|
||||
*/
|
||||
void setSelectedUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Sets a list of URLs as preselected
|
||||
*
|
||||
* @see setSelectedUrl
|
||||
* @since 5.75
|
||||
*/
|
||||
void setSelectedUrls(const QList<QUrl> &urls);
|
||||
|
||||
/**
|
||||
* Sets the operational mode of the filedialog to @p Saving, @p Opening
|
||||
* or @p Other. This will set some flags that are specific to loading
|
||||
* or saving files. E.g. setKeepLocation() makes mostly sense for
|
||||
* a save-as dialog. So setOperationMode( KFileWidget::Saving ); sets
|
||||
* setKeepLocation for example.
|
||||
*
|
||||
* The mode @p Saving, together with a default filter set via
|
||||
* setMimeFilter() will make the filter combobox read-only.
|
||||
*
|
||||
* The default mode is @p Opening.
|
||||
*
|
||||
* Call this method right after instantiating KFileWidget.
|
||||
*
|
||||
* @see operationMode
|
||||
* @see KFileWidget::OperationMode
|
||||
*/
|
||||
void setOperationMode(OperationMode);
|
||||
|
||||
/**
|
||||
* @returns the current operation mode, Opening, Saving or Other. Default
|
||||
* is Other.
|
||||
*
|
||||
* @see operationMode
|
||||
* @see KFileWidget::OperationMode
|
||||
*/
|
||||
OperationMode operationMode() const;
|
||||
|
||||
/**
|
||||
* Sets whether the filename/url should be kept when changing directories.
|
||||
* This is for example useful when having a predefined filename where
|
||||
* the full path for that file is searched.
|
||||
*
|
||||
* This is implicitly set when operationMode() is KFileWidget::Saving
|
||||
*
|
||||
* getSaveFileName() and getSaveUrl() set this to true by default, so that
|
||||
* you can type in the filename and change the directory without having
|
||||
* to type the name again.
|
||||
*/
|
||||
void setKeepLocation(bool keep);
|
||||
|
||||
/**
|
||||
* @returns whether the contents of the location edit are kept when
|
||||
* changing directories.
|
||||
*/
|
||||
bool keepsLocation() const;
|
||||
|
||||
/**
|
||||
* Set the filters to be used.
|
||||
*
|
||||
* Each item of the list corresponds to a selectable filter.
|
||||
*
|
||||
* Only one filter is active at a time.
|
||||
*
|
||||
* @param activeFilter the initially active filter
|
||||
*
|
||||
* @since 6.0
|
||||
*
|
||||
*/
|
||||
void setFilters(const QList<KFileFilter> &filters, const KFileFilter &activeFilter = KFileFilter());
|
||||
|
||||
/**
|
||||
* Returns the current filter as entered by the user or one of the
|
||||
* predefined set via setFilters().
|
||||
*
|
||||
* @see setFilters()
|
||||
* @see filterChanged()
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
KFileFilter currentFilter() const;
|
||||
|
||||
/**
|
||||
* Clears any MIME type or name filter. Does not reload the directory.
|
||||
*/
|
||||
void clearFilter();
|
||||
|
||||
/**
|
||||
* Adds a preview widget and enters the preview mode.
|
||||
*
|
||||
* In this mode the dialog is split and the right part contains your
|
||||
* preview widget.
|
||||
*
|
||||
* Ownership is transferred to KFileWidget. You need to create the
|
||||
* preview-widget with "new", i.e. on the heap.
|
||||
*
|
||||
* @param w The widget to be used for the preview.
|
||||
*/
|
||||
void setPreviewWidget(KPreviewWidgetBase *w);
|
||||
|
||||
/**
|
||||
* Sets the mode of the dialog.
|
||||
*
|
||||
* The mode is defined as (in kfile.h):
|
||||
* \code
|
||||
* enum Mode {
|
||||
* File = 1,
|
||||
* Directory = 2,
|
||||
* Files = 4,
|
||||
* ExistingOnly = 8,
|
||||
* LocalOnly = 16,
|
||||
* };
|
||||
* \endcode
|
||||
* You can OR the values, e.g.
|
||||
* \code
|
||||
* KFile::Modes mode = KFile::Files |
|
||||
* KFile::ExistingOnly |
|
||||
* KFile::LocalOnly );
|
||||
* setMode( mode );
|
||||
* \endcode
|
||||
*/
|
||||
void setMode(KFile::Modes m);
|
||||
|
||||
/**
|
||||
* Returns the mode of the filedialog.
|
||||
* @see setMode()
|
||||
*/
|
||||
KFile::Modes mode() const;
|
||||
|
||||
/**
|
||||
* Sets the text to be displayed in front of the selection.
|
||||
*
|
||||
* The default is "Location".
|
||||
* Most useful if you want to make clear what
|
||||
* the location is used for.
|
||||
*/
|
||||
void setLocationLabel(const QString &text);
|
||||
|
||||
/**
|
||||
* @returns a pointer to the OK-Button in the filedialog.
|
||||
* Note that the button is hidden and unconnected when using KFileWidget alone;
|
||||
* KFileDialog shows it and connects to it.
|
||||
*/
|
||||
QPushButton *okButton() const;
|
||||
|
||||
/**
|
||||
* @returns a pointer to the Cancel-Button in the filedialog.
|
||||
* Note that the button is hidden and unconnected when using KFileWidget alone;
|
||||
* KFileDialog shows it and connects to it.
|
||||
*/
|
||||
QPushButton *cancelButton() const;
|
||||
|
||||
/**
|
||||
* @returns the combobox used to type the filename or full location of the file.
|
||||
*/
|
||||
KUrlComboBox *locationEdit() const;
|
||||
|
||||
/**
|
||||
* @returns the combobox that contains the filters
|
||||
*/
|
||||
KFileFilterCombo *filterWidget() const;
|
||||
|
||||
/**
|
||||
* This method implements the logic to determine the user's default directory
|
||||
* to be listed. E.g. the documents directory, home directory or a recently
|
||||
* used directory.
|
||||
* @param startDir A URL specifying the initial directory, or using the
|
||||
* @c kfiledialog:/// syntax to specify a last used
|
||||
* directory. If this URL specifies a file name, it is
|
||||
* ignored. Refer to the KFileWidget::KFileWidget()
|
||||
* documentation for the @c kfiledialog:/// URL syntax.
|
||||
* @param recentDirClass If the @c kfiledialog:/// syntax is used, this
|
||||
* will return the string to be passed to KRecentDirs::dir() and
|
||||
* KRecentDirs::add().
|
||||
* @return The URL that should be listed by default (e.g. by KFileDialog or
|
||||
* KDirSelectDialog).
|
||||
* @see KFileWidget::KFileWidget()
|
||||
*/
|
||||
static QUrl getStartUrl(const QUrl &startDir, QString &recentDirClass);
|
||||
|
||||
/**
|
||||
* Similar to getStartUrl(const QUrl& startDir,QString& recentDirClass),
|
||||
* but allows both the recent start directory keyword and a suggested file name
|
||||
* to be returned.
|
||||
* @param startDir A URL specifying the initial directory and/or filename,
|
||||
* or using the @c kfiledialog:/// syntax to specify a
|
||||
* last used location.
|
||||
* Refer to the KFileWidget::KFileWidget()
|
||||
* documentation for the @c kfiledialog:/// URL syntax.
|
||||
* @param recentDirClass If the @c kfiledialog:/// syntax is used, this
|
||||
* will return the string to be passed to KRecentDirs::dir() and
|
||||
* KRecentDirs::add().
|
||||
* @param fileName The suggested file name, if specified as part of the
|
||||
* @p StartDir URL.
|
||||
* @return The URL that should be listed by default (e.g. by KFileDialog or
|
||||
* KDirSelectDialog).
|
||||
*
|
||||
* @see KFileWidget::KFileWidget()
|
||||
*/
|
||||
static QUrl getStartUrl(const QUrl &startDir, QString &recentDirClass, QString &fileName);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Used by KDirSelectDialog to share the dialog's start directory.
|
||||
*/
|
||||
static void setStartDir(const QUrl &directory);
|
||||
|
||||
/**
|
||||
* Set a custom widget that should be added to the file dialog.
|
||||
* @param widget A widget, or a widget of widgets, for displaying custom
|
||||
* data in the file widget. This can be used, for example, to
|
||||
* display a check box with the title "Open as read-only".
|
||||
* When creating this widget, you don't need to specify a parent,
|
||||
* since the widget's parent will be set automatically by KFileWidget.
|
||||
*/
|
||||
void setCustomWidget(QWidget *widget);
|
||||
|
||||
/**
|
||||
* Sets a custom widget that should be added below the location and the filter
|
||||
* editors.
|
||||
* @param text Label of the custom widget, which is displayed below the labels
|
||||
* "Location:" and "Filter:".
|
||||
* @param widget Any kind of widget, but preferable a combo box or a line editor
|
||||
* to be compliant with the location and filter layout.
|
||||
* When creating this widget, you don't need to specify a parent,
|
||||
* since the widget's parent will be set automatically by KFileWidget.
|
||||
*/
|
||||
void setCustomWidget(const QString &text, QWidget *widget);
|
||||
|
||||
/**
|
||||
* Sets whether the user should be asked for confirmation
|
||||
* when an overwrite might occur.
|
||||
*
|
||||
* @param enable Set this to true to enable checking.
|
||||
*/
|
||||
void setConfirmOverwrite(bool enable);
|
||||
|
||||
/**
|
||||
* Forces the inline previews to be shown or hidden, depending on @p show.
|
||||
*
|
||||
* @param show Whether to show inline previews or not.
|
||||
*/
|
||||
void setInlinePreviewShown(bool show);
|
||||
|
||||
/**
|
||||
* Provides a size hint, useful for dialogs that embed the widget.
|
||||
*
|
||||
* @return a QSize, calculated to be optimal for a dialog.
|
||||
* @since 5.0
|
||||
*/
|
||||
QSize dialogSizeHint() const;
|
||||
|
||||
/**
|
||||
* Sets how the view should be displayed.
|
||||
*
|
||||
* @see KFile::FileView
|
||||
* @since 5.0
|
||||
*/
|
||||
void setViewMode(KFile::FileView mode);
|
||||
|
||||
/**
|
||||
* Reimplemented
|
||||
*/
|
||||
QSize sizeHint() const override;
|
||||
|
||||
/**
|
||||
* Set the URL schemes that the file widget should allow navigating to.
|
||||
*
|
||||
* If the returned list is empty, all schemes are supported.
|
||||
*
|
||||
* @sa QFileDialog::setSupportedSchemes
|
||||
* @since 5.43
|
||||
*/
|
||||
void setSupportedSchemes(const QStringList &schemes);
|
||||
|
||||
/**
|
||||
* Returns the URL schemes that the file widget should allow navigating to.
|
||||
*
|
||||
* If the returned list is empty, all schemes are supported. Examples for
|
||||
* schemes are @c "file" or @c "ftp".
|
||||
*
|
||||
* @sa QFileDialog::supportedSchemes
|
||||
* @since 5.43
|
||||
*/
|
||||
QStringList supportedSchemes() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Called when clicking ok (when this widget is used in KFileDialog)
|
||||
* Might or might not call accept().
|
||||
*/
|
||||
void slotOk();
|
||||
void accept();
|
||||
void slotCancel();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the user selects a file. It is only emitted in single-
|
||||
* selection mode. The best way to get notified about selected file(s)
|
||||
* is to connect to the okClicked() signal inherited from KDialog
|
||||
* and call selectedFile(), selectedFiles(),
|
||||
* selectedUrl() or selectedUrls().
|
||||
*
|
||||
* \since 4.4
|
||||
*/
|
||||
void fileSelected(const QUrl &);
|
||||
|
||||
/**
|
||||
* Emitted when the user highlights a file.
|
||||
* \since 4.4
|
||||
*/
|
||||
void fileHighlighted(const QUrl &);
|
||||
|
||||
/**
|
||||
* Emitted when the user highlights one or more files in multiselection mode.
|
||||
*
|
||||
* Note: fileHighlighted() or fileSelected() are @em not
|
||||
* emitted in multiselection mode. You may use selectedItems() to
|
||||
* ask for the current highlighted items.
|
||||
* @see fileSelected
|
||||
*/
|
||||
void selectionChanged();
|
||||
|
||||
/**
|
||||
* Emitted when the filter changed, i.e.\ the user entered an own filter
|
||||
* or chose one of the predefined set via setFilters().
|
||||
*
|
||||
* @param filter contains the new filter (only the extension part,
|
||||
* not the explanation), i.e. "*.cpp" or "*.cpp *.cc".
|
||||
*
|
||||
* @see setFilters()
|
||||
* @see currentFilter()
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
void filterChanged(const KFileFilter &filter);
|
||||
|
||||
/**
|
||||
* Emitted by slotOk() (directly or asynchronously) once everything has
|
||||
* been done. Should be used by the caller to call accept().
|
||||
*/
|
||||
void accepted();
|
||||
|
||||
public:
|
||||
/**
|
||||
* @returns the KDirOperator used to navigate the filesystem
|
||||
*/
|
||||
KDirOperator *dirOperator();
|
||||
|
||||
#if KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(6, 3)
|
||||
/**
|
||||
* reads the configuration for this widget from the given config group
|
||||
* @param group the KConfigGroup to read from
|
||||
*
|
||||
* @deprecated since 6.3, no known use case.
|
||||
*/
|
||||
KIOFILEWIDGETS_DEPRECATED_VERSION(6, 3, "No known use case")
|
||||
void readConfig(KConfigGroup &group);
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class KFileWidgetPrivate;
|
||||
std::unique_ptr<KFileWidgetPrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kfilewidgetdocktitlebar_p.h"
|
||||
|
||||
#include <QStyle>
|
||||
|
||||
using namespace KDEPrivate;
|
||||
|
||||
KFileWidgetDockTitleBar::KFileWidgetDockTitleBar(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
}
|
||||
|
||||
KFileWidgetDockTitleBar::~KFileWidgetDockTitleBar()
|
||||
{
|
||||
}
|
||||
|
||||
QSize KFileWidgetDockTitleBar::minimumSizeHint() const
|
||||
{
|
||||
const int border = style()->pixelMetric(QStyle::PM_DockWidgetTitleBarButtonMargin);
|
||||
return QSize(border, border);
|
||||
}
|
||||
|
||||
QSize KFileWidgetDockTitleBar::sizeHint() const
|
||||
{
|
||||
return minimumSizeHint();
|
||||
}
|
||||
|
||||
#include "moc_kfilewidgetdocktitlebar_p.cpp"
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KFILEWIDGETDOCKTITLEBAR_P_H
|
||||
#define KFILEWIDGETDOCKTITLEBAR_P_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
/**
|
||||
* @brief An empty title bar for the Places dock widget
|
||||
*/
|
||||
class KFileWidgetDockTitleBar : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KFileWidgetDockTitleBar(QWidget *parent);
|
||||
~KFileWidgetDockTitleBar() override;
|
||||
|
||||
QSize minimumSizeHint() const override;
|
||||
QSize sizeHint() const override;
|
||||
};
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#endif // KFILEWIDGETDOCKTITLEBAR_P_H
|
||||
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2001 Martin R. Jones <mjones@kde.org>
|
||||
SPDX-FileCopyrightText: 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kimagefilepreview.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
#include <QStyle>
|
||||
#include <QTimeLine>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <KIconLoader>
|
||||
#include <KLocalizedString>
|
||||
#include <kfileitem.h>
|
||||
#include <kio/previewjob.h>
|
||||
|
||||
/**** KImageFilePreview ****/
|
||||
|
||||
class KImageFilePreviewPrivate
|
||||
{
|
||||
public:
|
||||
KImageFilePreviewPrivate(KImageFilePreview *qq)
|
||||
: q(qq)
|
||||
{
|
||||
if (q->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, q)) {
|
||||
m_timeLine = new QTimeLine(150, q);
|
||||
m_timeLine->setEasingCurve(QEasingCurve::InCurve);
|
||||
m_timeLine->setDirection(QTimeLine::Forward);
|
||||
m_timeLine->setFrameRange(0, 100);
|
||||
}
|
||||
}
|
||||
|
||||
void slotResult(KJob *);
|
||||
void slotFailed(const KFileItem &);
|
||||
void slotStepAnimation();
|
||||
void slotFinished();
|
||||
void slotActuallyClear();
|
||||
|
||||
KImageFilePreview *q = nullptr;
|
||||
QUrl currentURL;
|
||||
QUrl lastShownURL;
|
||||
QLabel *imageLabel;
|
||||
KIO::PreviewJob *m_job = nullptr;
|
||||
QTimeLine *m_timeLine = nullptr;
|
||||
QPixmap m_pmCurrent;
|
||||
QPixmap m_pmTransition;
|
||||
float m_pmCurrentOpacity = 1;
|
||||
float m_pmTransitionOpacity = 0;
|
||||
bool clear = true;
|
||||
};
|
||||
|
||||
KImageFilePreview::KImageFilePreview(QWidget *parent)
|
||||
: KPreviewWidgetBase(parent)
|
||||
, d(new KImageFilePreviewPrivate(this))
|
||||
{
|
||||
QVBoxLayout *vb = new QVBoxLayout(this);
|
||||
vb->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
d->imageLabel = new QLabel(this);
|
||||
d->imageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
d->imageLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
|
||||
vb->addWidget(d->imageLabel);
|
||||
|
||||
setSupportedMimeTypes(KIO::PreviewJob::supportedMimeTypes());
|
||||
setMinimumWidth(50);
|
||||
|
||||
if (d->m_timeLine) {
|
||||
connect(d->m_timeLine, &QTimeLine::frameChanged, this, [this]() {
|
||||
d->slotStepAnimation();
|
||||
});
|
||||
connect(d->m_timeLine, &QTimeLine::finished, this, [this]() {
|
||||
d->slotFinished();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
KImageFilePreview::~KImageFilePreview()
|
||||
{
|
||||
if (d->m_job) {
|
||||
d->m_job->kill();
|
||||
}
|
||||
}
|
||||
|
||||
void KImageFilePreview::showPreview()
|
||||
{
|
||||
// Pass a copy since clearPreview() will clear currentURL
|
||||
QUrl url = d->currentURL;
|
||||
showPreview(url, true);
|
||||
}
|
||||
|
||||
// called via KPreviewWidgetBase interface
|
||||
void KImageFilePreview::showPreview(const QUrl &url)
|
||||
{
|
||||
showPreview(url, false);
|
||||
}
|
||||
|
||||
void KImageFilePreview::showPreview(const QUrl &url, bool force)
|
||||
{
|
||||
/* clang-format off */
|
||||
if (!url.isValid()
|
||||
|| (d->lastShownURL.isValid()
|
||||
&& url.matches(d->lastShownURL, QUrl::StripTrailingSlash)
|
||||
&& d->currentURL.isValid())) {
|
||||
return;
|
||||
}
|
||||
/* clang-format on*/
|
||||
|
||||
d->clear = false;
|
||||
d->currentURL = url;
|
||||
d->lastShownURL = url;
|
||||
|
||||
int w = d->imageLabel->contentsRect().width() - 4;
|
||||
int h = d->imageLabel->contentsRect().height() - 4;
|
||||
|
||||
if (d->m_job) {
|
||||
disconnect(d->m_job, nullptr, this, nullptr);
|
||||
|
||||
d->m_job->kill();
|
||||
}
|
||||
|
||||
d->m_job = createJob(url, w, h);
|
||||
if (force) { // explicitly requested previews shall always be generated!
|
||||
d->m_job->setIgnoreMaximumSize(true);
|
||||
}
|
||||
|
||||
connect(d->m_job, &KJob::result, this, [this](KJob *job) {
|
||||
d->slotResult(job);
|
||||
});
|
||||
connect(d->m_job, &KIO::PreviewJob::gotPreview, this, &KImageFilePreview::gotPreview);
|
||||
connect(d->m_job, &KIO::PreviewJob::failed, this, [this](const KFileItem &item) {
|
||||
d->slotFailed(item);
|
||||
});
|
||||
}
|
||||
|
||||
void KImageFilePreview::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
// Nothing to do, if no current preview
|
||||
if (d->imageLabel->pixmap().isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearPreview();
|
||||
d->currentURL = QUrl(); // force this to actually happen
|
||||
showPreview(d->lastShownURL);
|
||||
}
|
||||
|
||||
QSize KImageFilePreview::sizeHint() const
|
||||
{
|
||||
return QSize(100, 200);
|
||||
}
|
||||
|
||||
KIO::PreviewJob *KImageFilePreview::createJob(const QUrl &url, int w, int h)
|
||||
{
|
||||
if (!url.isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
KFileItemList items;
|
||||
items.append(KFileItem(url));
|
||||
QStringList plugins = KIO::PreviewJob::availablePlugins();
|
||||
|
||||
KIO::PreviewJob *previewJob = KIO::filePreview(items, QSize(w, h), &plugins);
|
||||
previewJob->setScaleType(KIO::PreviewJob::Scaled);
|
||||
return previewJob;
|
||||
}
|
||||
|
||||
void KImageFilePreview::gotPreview(const KFileItem &item, const QPixmap &pm)
|
||||
{
|
||||
if (item.url() != d->currentURL) { // Shouldn't happen
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->m_timeLine) {
|
||||
if (d->m_timeLine->state() == QTimeLine::Running) {
|
||||
d->m_timeLine->setCurrentTime(0);
|
||||
}
|
||||
|
||||
d->m_pmTransition = pm;
|
||||
d->m_pmTransitionOpacity = 0;
|
||||
d->m_pmCurrentOpacity = 1;
|
||||
d->m_timeLine->setDirection(QTimeLine::Forward);
|
||||
d->m_timeLine->start();
|
||||
} else {
|
||||
d->imageLabel->setPixmap(pm);
|
||||
}
|
||||
}
|
||||
|
||||
void KImageFilePreviewPrivate::slotFailed(const KFileItem &item)
|
||||
{
|
||||
if (item.isDir()) {
|
||||
imageLabel->clear();
|
||||
} else if (item.url() == currentURL) { // should always be the case
|
||||
imageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("image-missing")).pixmap(KIconLoader::SizeLarge, QIcon::Disabled));
|
||||
}
|
||||
}
|
||||
|
||||
void KImageFilePreviewPrivate::slotResult(KJob *job)
|
||||
{
|
||||
if (job == m_job) {
|
||||
m_job = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void KImageFilePreviewPrivate::slotStepAnimation()
|
||||
{
|
||||
const QSize currSize = m_pmCurrent.size();
|
||||
const QSize transitionSize = m_pmTransition.size();
|
||||
const int width = std::max(currSize.width(), transitionSize.width());
|
||||
const int height = std::max(currSize.height(), transitionSize.height());
|
||||
QPixmap pm(QSize(width, height));
|
||||
pm.fill(Qt::transparent);
|
||||
|
||||
QPainter p(&pm);
|
||||
p.setOpacity(m_pmCurrentOpacity);
|
||||
|
||||
// If we have a current pixmap
|
||||
if (!m_pmCurrent.isNull()) {
|
||||
p.drawPixmap(QPoint(((float)pm.size().width() - m_pmCurrent.size().width()) / 2.0, ((float)pm.size().height() - m_pmCurrent.size().height()) / 2.0),
|
||||
m_pmCurrent);
|
||||
}
|
||||
if (!m_pmTransition.isNull()) {
|
||||
p.setOpacity(m_pmTransitionOpacity);
|
||||
p.drawPixmap(
|
||||
QPoint(((float)pm.size().width() - m_pmTransition.size().width()) / 2.0, ((float)pm.size().height() - m_pmTransition.size().height()) / 2.0),
|
||||
m_pmTransition);
|
||||
}
|
||||
p.end();
|
||||
|
||||
imageLabel->setPixmap(pm);
|
||||
|
||||
m_pmCurrentOpacity = qMax(m_pmCurrentOpacity - 0.4, 0.0); // krazy:exclude=qminmax
|
||||
m_pmTransitionOpacity = qMin(m_pmTransitionOpacity + 0.4, 1.0); // krazy:exclude=qminmax
|
||||
}
|
||||
|
||||
void KImageFilePreviewPrivate::slotFinished()
|
||||
{
|
||||
m_pmCurrent = m_pmTransition;
|
||||
m_pmTransitionOpacity = 0;
|
||||
m_pmCurrentOpacity = 1;
|
||||
m_pmTransition = QPixmap();
|
||||
// The animation might have lost some frames. Be sure that if the last one
|
||||
// was dropped, the last image shown is the opaque one.
|
||||
imageLabel->setPixmap(m_pmCurrent);
|
||||
clear = false;
|
||||
}
|
||||
|
||||
void KImageFilePreview::clearPreview()
|
||||
{
|
||||
if (d->m_job) {
|
||||
d->m_job->kill();
|
||||
d->m_job = nullptr;
|
||||
}
|
||||
|
||||
if (d->clear || (d->m_timeLine && d->m_timeLine->state() == QTimeLine::Running)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->m_timeLine) {
|
||||
d->m_pmTransition = QPixmap();
|
||||
// If we add a previous preview then we run the animation
|
||||
if (!d->m_pmCurrent.isNull()) {
|
||||
d->m_timeLine->setCurrentTime(0);
|
||||
d->m_timeLine->setDirection(QTimeLine::Backward);
|
||||
d->m_timeLine->start();
|
||||
}
|
||||
d->currentURL.clear();
|
||||
d->clear = true;
|
||||
} else {
|
||||
d->imageLabel->clear();
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_kimagefilepreview.cpp"
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
This file is part of the KDE project.
|
||||
SPDX-FileCopyrightText: 2001 Martin R. Jones <mjones@kde.org>
|
||||
SPDX-FileCopyrightText: 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2008 Rafael Fernández López <ereslibre@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KIMAGEFILEPREVIEW_H
|
||||
#define KIMAGEFILEPREVIEW_H
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QUrl>
|
||||
#include <kpreviewwidgetbase.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class KFileItem;
|
||||
class KJob;
|
||||
class KImageFilePreviewPrivate;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class PreviewJob;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class KImageFilePreview kimagefilepreview.h <KImageFilePreview>
|
||||
*
|
||||
* Image preview widget for the file dialog.
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KImageFilePreview : public KPreviewWidgetBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a new image file preview.
|
||||
*
|
||||
* @param parent The parent widget.
|
||||
*/
|
||||
explicit KImageFilePreview(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destroys the image file preview.
|
||||
*/
|
||||
~KImageFilePreview() override;
|
||||
|
||||
/**
|
||||
* Returns the size hint for this widget.
|
||||
*/
|
||||
QSize sizeHint() const override;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Shows a preview for the given @p url.
|
||||
*/
|
||||
void showPreview(const QUrl &url) override;
|
||||
|
||||
/**
|
||||
* Clears the preview.
|
||||
*/
|
||||
void clearPreview() override;
|
||||
|
||||
protected Q_SLOTS:
|
||||
void showPreview();
|
||||
void showPreview(const QUrl &url, bool force);
|
||||
|
||||
virtual void gotPreview(const KFileItem &, const QPixmap &);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
virtual KIO::PreviewJob *createJob(const QUrl &url, int width, int height);
|
||||
|
||||
private:
|
||||
std::unique_ptr<KImageFilePreviewPrivate> const d;
|
||||
|
||||
Q_DISABLE_COPY(KImageFilePreview)
|
||||
};
|
||||
|
||||
#endif // KIMAGEFILEPREVIEW_H
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 1998, 2008, 2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "knameandurlinputdialog.h"
|
||||
|
||||
#include <KLineEdit> // For KUrlRequester::lineEdit()
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFormLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <kprotocolmanager.h>
|
||||
#include <kurlrequester.h>
|
||||
|
||||
class KNameAndUrlInputDialogPrivate
|
||||
{
|
||||
public:
|
||||
explicit KNameAndUrlInputDialogPrivate(KNameAndUrlInputDialog *qq)
|
||||
: m_fileNameEdited(false)
|
||||
, q(qq)
|
||||
{
|
||||
}
|
||||
|
||||
void slotNameTextChanged(const QString &);
|
||||
void slotURLTextChanged(const QString &);
|
||||
|
||||
/**
|
||||
* The line edit widget for the fileName
|
||||
*/
|
||||
QLineEdit *m_leName;
|
||||
/**
|
||||
* The URL requester for the URL :)
|
||||
*/
|
||||
KUrlRequester *m_urlRequester;
|
||||
/**
|
||||
* True if the filename was manually edited.
|
||||
*/
|
||||
bool m_fileNameEdited;
|
||||
|
||||
QDialogButtonBox *m_buttonBox;
|
||||
|
||||
KNameAndUrlInputDialog *const q;
|
||||
};
|
||||
|
||||
KNameAndUrlInputDialog::KNameAndUrlInputDialog(const QString &nameLabel, const QString &urlLabel, const QUrl &startDir, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, d(new KNameAndUrlInputDialogPrivate(this))
|
||||
{
|
||||
QVBoxLayout *topLayout = new QVBoxLayout(this);
|
||||
|
||||
QFormLayout *formLayout = new QFormLayout;
|
||||
formLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// First line: filename
|
||||
d->m_leName = new QLineEdit(this);
|
||||
d->m_leName->setMinimumWidth(d->m_leName->sizeHint().width() * 3);
|
||||
d->m_leName->setSelection(0, d->m_leName->text().length()); // autoselect
|
||||
connect(d->m_leName, &QLineEdit::textChanged, this, [this](const QString &text) {
|
||||
d->slotNameTextChanged(text);
|
||||
});
|
||||
formLayout->addRow(nameLabel, d->m_leName);
|
||||
|
||||
// Second line: url
|
||||
d->m_urlRequester = new KUrlRequester(this);
|
||||
d->m_urlRequester->setStartDir(startDir);
|
||||
d->m_urlRequester->setMode(KFile::File | KFile::Directory);
|
||||
|
||||
d->m_urlRequester->setMinimumWidth(d->m_urlRequester->sizeHint().width() * 3);
|
||||
connect(d->m_urlRequester->lineEdit(), &QLineEdit::textChanged, this, [this](const QString &text) {
|
||||
d->slotURLTextChanged(text);
|
||||
});
|
||||
formLayout->addRow(urlLabel, d->m_urlRequester);
|
||||
|
||||
topLayout->addLayout(formLayout);
|
||||
|
||||
d->m_buttonBox = new QDialogButtonBox(this);
|
||||
d->m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(d->m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(d->m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
topLayout->addWidget(d->m_buttonBox);
|
||||
|
||||
d->m_fileNameEdited = false;
|
||||
d->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!d->m_leName->text().isEmpty() && !d->m_urlRequester->url().isEmpty());
|
||||
d->m_leName->setFocus();
|
||||
}
|
||||
|
||||
KNameAndUrlInputDialog::~KNameAndUrlInputDialog() = default;
|
||||
|
||||
QUrl KNameAndUrlInputDialog::url() const
|
||||
{
|
||||
return d->m_urlRequester->url();
|
||||
}
|
||||
|
||||
QString KNameAndUrlInputDialog::urlText() const
|
||||
{
|
||||
return d->m_urlRequester->text();
|
||||
}
|
||||
|
||||
QString KNameAndUrlInputDialog::name() const
|
||||
{
|
||||
return d->m_leName->text();
|
||||
}
|
||||
|
||||
void KNameAndUrlInputDialogPrivate::slotNameTextChanged(const QString &)
|
||||
{
|
||||
m_fileNameEdited = true;
|
||||
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!m_leName->text().isEmpty() && !m_urlRequester->url().isEmpty());
|
||||
}
|
||||
|
||||
void KNameAndUrlInputDialogPrivate::slotURLTextChanged(const QString &)
|
||||
{
|
||||
if (!m_fileNameEdited) {
|
||||
// use URL as default value for the filename
|
||||
// (we copy only its filename if protocol supports listing,
|
||||
// but for HTTP we don't want tons of index.html links)
|
||||
QUrl url(m_urlRequester->url());
|
||||
if (KProtocolManager::supportsListing(url) && !url.fileName().isEmpty()) {
|
||||
m_leName->setText(url.fileName());
|
||||
} else {
|
||||
m_leName->setText(url.toString());
|
||||
}
|
||||
m_fileNameEdited = false; // slotNameTextChanged set it to true erroneously
|
||||
}
|
||||
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!m_leName->text().isEmpty() && !m_urlRequester->url().isEmpty());
|
||||
}
|
||||
|
||||
void KNameAndUrlInputDialog::setSuggestedName(const QString &name)
|
||||
{
|
||||
d->m_leName->setText(name);
|
||||
d->m_urlRequester->setFocus();
|
||||
}
|
||||
|
||||
void KNameAndUrlInputDialog::setSuggestedUrl(const QUrl &url)
|
||||
{
|
||||
d->m_urlRequester->setUrl(url);
|
||||
}
|
||||
|
||||
#include "moc_knameandurlinputdialog.cpp"
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 1998, 2008, 2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KNAMEANDURLINPUTDIALOG_H
|
||||
#define KNAMEANDURLINPUTDIALOG_H
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
#include <QDialog>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class KUrlRequester;
|
||||
class KNameAndUrlInputDialogPrivate;
|
||||
class QUrl;
|
||||
|
||||
/**
|
||||
* @class KNameAndUrlInputDialog knameandurlinputdialog.h <KNameAndUrlInputDialog>
|
||||
*
|
||||
* Dialog to ask for a name (e.g.\ filename) and a URL
|
||||
* Basically a merge of KLineEditDlg and KUrlRequesterDlg ;)
|
||||
* @author David Faure <faure@kde.org>
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KNameAndUrlInputDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @param nameLabel label for the name field
|
||||
* @param urlLabel label for the URL requester
|
||||
* @param startDir start directory for the URL requester (optional)
|
||||
* @param parent parent widget
|
||||
*/
|
||||
KNameAndUrlInputDialog(const QString &nameLabel, const QString &urlLabel, const QUrl &startDir, QWidget *parent);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~KNameAndUrlInputDialog() override;
|
||||
|
||||
/**
|
||||
* Pre-fill the name lineedit.
|
||||
*/
|
||||
void setSuggestedName(const QString &name);
|
||||
/**
|
||||
* Pre-fill the URL requester.
|
||||
*/
|
||||
void setSuggestedUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* @return the name the user entered
|
||||
*/
|
||||
QString name() const;
|
||||
/**
|
||||
* @return the URL the user entered
|
||||
*/
|
||||
QUrl url() const;
|
||||
/**
|
||||
* @return the URL the user entered, as plain text.
|
||||
* This is only useful for creating relative symlinks.
|
||||
* @since 5.25
|
||||
*/
|
||||
QString urlText() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<KNameAndUrlInputDialogPrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1998-2009 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Sven Leiber <s.leiber@web.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only
|
||||
*/
|
||||
|
||||
#ifndef KNEWFILEMENU_H
|
||||
#define KNEWFILEMENU_H
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
#include <KActionMenu>
|
||||
#include <QUrl>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class KJob;
|
||||
|
||||
class KActionCollection;
|
||||
class KNewFileMenuPrivate;
|
||||
|
||||
/**
|
||||
* @class KNewFileMenu knewfilemenu.h <KNewFileMenu>
|
||||
*
|
||||
* The 'Create New' submenu, for creating files using templates
|
||||
* (e.g.\ "new HTML file") and directories.
|
||||
*
|
||||
* The same instance can be used by both for the File menu and the RMB popup menu,
|
||||
* in a file manager. This is also used in the file dialog's RMB menu.
|
||||
*
|
||||
* To use this class, you need to connect aboutToShow() of the File menu
|
||||
* with slotCheckUpToDate() and to call slotCheckUpToDate() before showing
|
||||
* the RMB popupmenu.
|
||||
*
|
||||
* KNewFileMenu automatically updates the list of templates shown if installed templates
|
||||
* are added/updated/deleted.
|
||||
*
|
||||
* @author Björn Ruberg <bjoern@ruberg-wegener.de>
|
||||
* Made dialogs working asynchronously
|
||||
* @author David Faure <faure@kde.org>
|
||||
* Ideas and code for the new template handling mechanism ('link' desktop files)
|
||||
* from Christoph Pickart <pickart@iam.uni-bonn.de>
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KNewFileMenu : public KActionMenu
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param parent the parent object, for ownership.
|
||||
* If the parent object is a widget, it will also be used as the parent widget
|
||||
* for any dialogs that this class might show. Otherwise, call setParentWidget.
|
||||
*
|
||||
* @since 5.100
|
||||
*/
|
||||
KNewFileMenu(QObject *parent);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
* KNewMenu uses internally a globally shared cache, so that multiple instances
|
||||
* of it don't need to parse the installed templates multiple times. Therefore
|
||||
* you can safely create and delete KNewMenu instances without a performance issue.
|
||||
*/
|
||||
~KNewFileMenu() override;
|
||||
|
||||
/**
|
||||
* Returns the modality of dialogs
|
||||
*/
|
||||
bool isModal() const;
|
||||
|
||||
/**
|
||||
* Sets the modality of dialogs created by KNewFile. Set to false if you do not want to block
|
||||
* your application window when entering a new directory name i.e.
|
||||
*/
|
||||
void setModal(bool modality);
|
||||
|
||||
/**
|
||||
* Sets a parent widget for the dialogs shown by KNewFileMenu.
|
||||
* This is strongly recommended, for apps with a main window.
|
||||
*/
|
||||
void setParentWidget(QWidget *parentWidget);
|
||||
|
||||
/**
|
||||
* Set the working directory.
|
||||
* Files will be created relative to this directory.
|
||||
* @since 5.97.
|
||||
*/
|
||||
void setWorkingDirectory(const QUrl &directory);
|
||||
|
||||
/**
|
||||
* Returns the working directory.
|
||||
* Files will be created relative to this directory.
|
||||
* @since 5.97.
|
||||
*/
|
||||
QUrl workingDirectory() const;
|
||||
|
||||
/**
|
||||
* Only show the files in a given set of MIME types.
|
||||
* This is useful in specialized applications (while file managers, on
|
||||
* the other hand, want to show all MIME types).
|
||||
*/
|
||||
void setSupportedMimeTypes(const QStringList &mime);
|
||||
|
||||
/**
|
||||
* Returns the MIME types set in supportedMimeTypes()
|
||||
*/
|
||||
QStringList supportedMimeTypes() const;
|
||||
|
||||
/**
|
||||
* Whether on not the dialog should emit `selectExistingDir` when trying to create an exist directory
|
||||
*
|
||||
* default: false
|
||||
*
|
||||
* @since 5.76
|
||||
*/
|
||||
void setSelectDirWhenAlreadyExist(bool b);
|
||||
|
||||
/**
|
||||
* Use this to set a shortcut for the "New Folder" action.
|
||||
*
|
||||
* The shortcut is copied from @param action.
|
||||
*
|
||||
* @since 5.100
|
||||
*/
|
||||
void setNewFolderShortcutAction(QAction *action);
|
||||
|
||||
/**
|
||||
* Use this to set a shortcut for the new file action.
|
||||
*
|
||||
* The shortcut is copied from @param action.
|
||||
*
|
||||
* @since 5.100
|
||||
*/
|
||||
void setNewFileShortcutAction(QAction *action);
|
||||
|
||||
/**
|
||||
* Use this to check if namejob for new directory creation still running.
|
||||
* Namejob is what spawns the new directory dialog, which can be slow in,
|
||||
* for example, network folders.
|
||||
*
|
||||
* @since 6.2
|
||||
*/
|
||||
bool isCreateDirectoryRunning();
|
||||
|
||||
/**
|
||||
* Use this to check if the file creation process is still running.
|
||||
* @since 6.2
|
||||
*/
|
||||
bool isCreateFileRunning();
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Checks if updating the list is necessary
|
||||
* IMPORTANT : Call this in the slot for aboutToShow.
|
||||
*/
|
||||
void checkUpToDate();
|
||||
|
||||
/**
|
||||
* Call this to create a new directory as if the user had done it using
|
||||
* a popupmenu. This is useful to make sure that creating a directory with
|
||||
* a key shortcut (e.g. F10) triggers the exact same code as when using
|
||||
* the New menu.
|
||||
* Requirements: since 5.97 call setWorkingDirectory first (for older releases call setPopupFiles first), and keep this KNewFileMenu instance
|
||||
* alive (the mkdir is async).
|
||||
*/
|
||||
void createDirectory();
|
||||
|
||||
/**
|
||||
* Call this to create a new file as if the user had done it using
|
||||
* a popupmenu. This is useful to make sure that creating a directory with
|
||||
* a key shortcut (e.g. Shift-F10) triggers the exact same code as when using
|
||||
* the New menu.
|
||||
* Requirements: since 5.97 call setWorkingDirectory first (for older releases call setPopupFiles first), and keep this KNewFileMenu instance
|
||||
* alive (the copy is async).
|
||||
* @since 5.53
|
||||
*/
|
||||
void createFile();
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
/**
|
||||
* Emitted once the creation job for file @p url has been started
|
||||
* @since 6.2
|
||||
*/
|
||||
void fileCreationStarted(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted once the file (or symlink) @p url has been successfully created
|
||||
*/
|
||||
void fileCreated(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted once the creation for file @p url has been rejected
|
||||
* @since 6.2
|
||||
*/
|
||||
void fileCreationRejected(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted once the creation job for directory @p url has been started
|
||||
* @since 6.2
|
||||
*/
|
||||
void directoryCreationStarted(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted once the directory @p url has been successfully created
|
||||
*/
|
||||
void directoryCreated(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted once the creation for directory @p url has been rejected
|
||||
* @since 6.2
|
||||
*/
|
||||
void directoryCreationRejected(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emitted when trying to create a new directory that has the same name as
|
||||
* an existing one, so that KDirOperator can select the existing item in
|
||||
* the view (in case the user wants to use that directory instead of creating
|
||||
* a new one).
|
||||
*
|
||||
* @since 5.76
|
||||
*/
|
||||
void selectExistingDir(const QUrl &url);
|
||||
|
||||
protected Q_SLOTS:
|
||||
|
||||
/**
|
||||
* Called when the job that copied the template has finished.
|
||||
* This method is virtual so that error handling can be reimplemented.
|
||||
* Make sure to call the base class slotResult when !job->error() though.
|
||||
*/
|
||||
virtual void slotResult(KJob *job);
|
||||
|
||||
private:
|
||||
friend class KNewFileMenuPrivate;
|
||||
std::unique_ptr<KNewFileMenuPrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
This file is part of the KDE project.
|
||||
SPDX-FileCopyrightText: 2003 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kpreviewwidgetbase.h"
|
||||
|
||||
class Q_DECL_HIDDEN KPreviewWidgetBase::KPreviewWidgetBasePrivate
|
||||
{
|
||||
public:
|
||||
QStringList supportedMimeTypes;
|
||||
};
|
||||
|
||||
KPreviewWidgetBase::KPreviewWidgetBase(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, d(new KPreviewWidgetBasePrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KPreviewWidgetBase::~KPreviewWidgetBase() = default;
|
||||
|
||||
void KPreviewWidgetBase::setSupportedMimeTypes(const QStringList &mimeTypes)
|
||||
{
|
||||
d->supportedMimeTypes = mimeTypes;
|
||||
}
|
||||
|
||||
QStringList KPreviewWidgetBase::supportedMimeTypes() const
|
||||
{
|
||||
return d->supportedMimeTypes;
|
||||
}
|
||||
|
||||
#include "moc_kpreviewwidgetbase.cpp"
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001 Frerich Raabe <raabe@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef __KPREVIEWWIDGETBASE_H__
|
||||
#define __KPREVIEWWIDGETBASE_H__
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QUrl;
|
||||
|
||||
/**
|
||||
* @class KPreviewWidgetBase kpreviewwidgetbase.h <KPreviewWidgetBase>
|
||||
*
|
||||
* Abstract baseclass for all preview widgets which shall be used via
|
||||
* KFileDialog::setPreviewWidget(const KPreviewWidgetBase *).
|
||||
* Ownership will be transferred to KFileDialog, so you have to create
|
||||
* the preview with "new" and let KFileDialog delete it.
|
||||
*
|
||||
* Just derive your custom preview widget from KPreviewWidgetBase and implement
|
||||
* all the pure virtual methods. The slot showPreview(const QUrl &) is called
|
||||
* every time the file selection changes.
|
||||
*
|
||||
* @short Abstract baseclass for all preview widgets.
|
||||
* @author Frerich Raabe <raabe@kde.org>
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KPreviewWidgetBase : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor. Construct the user interface of your preview widget here
|
||||
* and pass the KFileDialog this preview widget is going to be used in as
|
||||
* the parent.
|
||||
*
|
||||
* @param parent The KFileDialog this preview widget is going to be used in
|
||||
*/
|
||||
explicit KPreviewWidgetBase(QWidget *parent);
|
||||
~KPreviewWidgetBase() override;
|
||||
|
||||
QStringList supportedMimeTypes() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* This slot is called every time the user selects another file in the
|
||||
* file dialog. Implement the stuff necessary to reflect the change here.
|
||||
*
|
||||
* @param url The URL of the currently selected file.
|
||||
*/
|
||||
virtual void showPreview(const QUrl &url) = 0;
|
||||
|
||||
/**
|
||||
* Reimplement this to clear the preview. This is called when e.g. the
|
||||
* selection is cleared or when multiple selections exist, or the directory
|
||||
* is changed.
|
||||
*/
|
||||
virtual void clearPreview() = 0;
|
||||
|
||||
protected:
|
||||
void setSupportedMimeTypes(const QStringList &mimeTypes);
|
||||
|
||||
private:
|
||||
class KPreviewWidgetBasePrivate;
|
||||
std::unique_ptr<KPreviewWidgetBasePrivate> const d;
|
||||
|
||||
Q_DISABLE_COPY(KPreviewWidgetBase)
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006-2010 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Urs Wolfer <uwolfer @ kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KURLNAVIGATOR_H
|
||||
#define KURLNAVIGATOR_H
|
||||
|
||||
#include "kiofilewidgets_export.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QUrl>
|
||||
#include <QWidget>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QMouseEvent;
|
||||
|
||||
class KFilePlacesModel;
|
||||
class KUrlComboBox;
|
||||
|
||||
class KUrlNavigatorPrivate;
|
||||
|
||||
/**
|
||||
* @class KUrlNavigator kurlnavigator.h <KUrlNavigator>
|
||||
*
|
||||
* @brief Widget that allows to navigate through the paths of an URL.
|
||||
*
|
||||
* The URL navigator offers two modes:
|
||||
* - Editable: The URL of the location is editable inside an editor.
|
||||
* By pressing RETURN the URL will get activated.
|
||||
* - Non editable ("breadcrumb view"): The URL of the location is represented by
|
||||
* a number of buttons, where each button represents a path
|
||||
* of the URL. By clicking on a button the path will get
|
||||
* activated. This mode also supports drag and drop of items.
|
||||
*
|
||||
* The mode can be changed by clicking on the empty area of the URL navigator.
|
||||
* It is recommended that the application remembers the setting
|
||||
* or allows to configure the default mode (see KUrlNavigator::setUrlEditable()).
|
||||
*
|
||||
* The URL navigator remembers the URL history during navigation and allows to go
|
||||
* back and forward within this history.
|
||||
*
|
||||
* In the non editable mode ("breadcrumb view") it can be configured whether
|
||||
* the full path should be shown. It is recommended that the application
|
||||
* remembers the setting or allows to configure the default mode (see
|
||||
* KUrlNavigator::setShowFullPath()).
|
||||
*
|
||||
* The typical usage of the KUrlNavigator is:
|
||||
* - Create an instance providing a places model and an URL.
|
||||
* - Create an instance of QAbstractItemView which shows the content of the URL
|
||||
* given by the URL navigator.
|
||||
* - Connect to the signal KUrlNavigator::urlChanged() and synchronize the content of
|
||||
* QAbstractItemView with the URL given by the URL navigator.
|
||||
*
|
||||
* It is recommended, that the application remembers the state of the QAbstractItemView
|
||||
* when the URL has been changed. This allows to restore the view state when going back in history.
|
||||
* KUrlNavigator offers support for remembering the view state:
|
||||
* - The signal urlAboutToBeChanged() will be emitted before the URL change takes places.
|
||||
* This allows the application to store the view state by KUrlNavigator::saveLocationState().
|
||||
* - The signal urlChanged() will be emitted after the URL change took place. This allows
|
||||
* the application to restore the view state by getting the values from
|
||||
* KUrlNavigator::locationState().
|
||||
*/
|
||||
class KIOFILEWIDGETS_EXPORT KUrlNavigator : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/** @since 4.5 */
|
||||
KUrlNavigator(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @param placesModel Model for the places which are selectable inside a
|
||||
* menu. A place can be a bookmark or a device. If it is 0,
|
||||
* no places selector is displayed.
|
||||
* @param url URL which is used for the navigation or editing.
|
||||
* @param parent Parent widget.
|
||||
*/
|
||||
KUrlNavigator(KFilePlacesModel *placesModel, const QUrl &url, QWidget *parent);
|
||||
~KUrlNavigator() override;
|
||||
|
||||
/**
|
||||
* @return URL of the location given by the \a historyIndex. If \a historyIndex
|
||||
* is smaller than 0, the URL of the current location is returned.
|
||||
* @since 4.5
|
||||
*/
|
||||
QUrl locationUrl(int historyIndex = -1) const;
|
||||
|
||||
/**
|
||||
* Saves the location state described by \a state for the current location. It is recommended
|
||||
* that at least the scroll position of a view is remembered and restored when traversing
|
||||
* through the history. Saving the location state should be done when the signal
|
||||
* KUrlNavigator::urlAboutToBeChanged() has been emitted. Restoring the location state (see
|
||||
* KUrlNavigator::locationState()) should be done when the signal KUrlNavigator::urlChanged()
|
||||
* has been emitted.
|
||||
*
|
||||
* Example:
|
||||
* \code
|
||||
* QByteArray state;
|
||||
* QDataStream data(&state, QIODevice::WriteOnly);
|
||||
* data << QPoint(x, y);
|
||||
* data << ...;
|
||||
* ...
|
||||
* urlNavigator->saveLocationState(state);
|
||||
* \endcode
|
||||
*
|
||||
*/
|
||||
void saveLocationState(const QByteArray &state);
|
||||
|
||||
/**
|
||||
* @return Location state given by \a historyIndex. If \a historyIndex
|
||||
* is smaller than 0, the state of the current location is returned.
|
||||
* @see KUrlNavigator::saveLocationState()
|
||||
* @since 4.5
|
||||
*/
|
||||
QByteArray locationState(int historyIndex = -1) const;
|
||||
|
||||
/**
|
||||
* Goes back one step in the URL history. The signals
|
||||
* KUrlNavigator::urlAboutToBeChanged(), KUrlNavigator::urlChanged() and
|
||||
* KUrlNavigator::historyChanged() are emitted if true is returned. False is returned
|
||||
* if the beginning of the history has already been reached and hence going back was
|
||||
* not possible. The history index (see KUrlNavigator::historyIndex()) is
|
||||
* increased by one if the operation was successful.
|
||||
*/
|
||||
bool goBack();
|
||||
|
||||
/**
|
||||
* Goes forward one step in the URL history. The signals
|
||||
* KUrlNavigator::urlAboutToBeChanged(), KUrlNavigator::urlChanged() and
|
||||
* KUrlNavigator::historyChanged() are emitted if true is returned. False is returned
|
||||
* if the end of the history has already been reached and hence going forward
|
||||
* was not possible. The history index (see KUrlNavigator::historyIndex()) is
|
||||
* decreased by one if the operation was successful.
|
||||
*/
|
||||
bool goForward();
|
||||
|
||||
/**
|
||||
* Goes up one step of the URL path and remembers the old path
|
||||
* in the history. The signals KUrlNavigator::urlAboutToBeChanged(),
|
||||
* KUrlNavigator::urlChanged() and KUrlNavigator::historyChanged() are
|
||||
* emitted if true is returned. False is returned if going up was not
|
||||
* possible as the root has been reached.
|
||||
*/
|
||||
bool goUp();
|
||||
|
||||
/**
|
||||
* Goes to the home URL and remembers the old URL in the history.
|
||||
* The signals KUrlNavigator::urlAboutToBeChanged(), KUrlNavigator::urlChanged()
|
||||
* and KUrlNavigator::historyChanged() are emitted.
|
||||
*
|
||||
* @see KUrlNavigator::setHomeUrl()
|
||||
*/
|
||||
// KDE5: Remove the home-property. It is sufficient to invoke
|
||||
// KUrlNavigator::setLocationUrl(homeUrl) on application-side.
|
||||
void goHome();
|
||||
|
||||
/**
|
||||
* Sets the home URL used by KUrlNavigator::goHome(). If no
|
||||
* home URL is set, the default home path of the user is used.
|
||||
*/
|
||||
// KDE5: Remove the home-property. It is sufficient to invoke
|
||||
// KUrlNavigator::setLocationUrl(homeUrl) on application-side.
|
||||
void setHomeUrl(const QUrl &url);
|
||||
|
||||
QUrl homeUrl() const;
|
||||
|
||||
/**
|
||||
* Allows to edit the URL of the navigation bar if \a editable
|
||||
* is true, and sets the focus accordingly.
|
||||
* If \a editable is false, each part of
|
||||
* the URL is presented by a button for a fast navigation ("breadcrumb view").
|
||||
*/
|
||||
void setUrlEditable(bool editable);
|
||||
|
||||
/**
|
||||
* @return True, if the URL is editable within a line editor.
|
||||
* If false is returned, each part of the URL is presented by a button
|
||||
* for fast navigation ("breadcrumb view").
|
||||
*/
|
||||
bool isUrlEditable() const;
|
||||
|
||||
/**
|
||||
* Shows the full path of the URL even if a place represents a part of the URL.
|
||||
* Assuming that a place called "Pictures" uses the URL /home/user/Pictures.
|
||||
* An URL like /home/user/Pictures/2008 is shown as [Pictures] > [2008]
|
||||
* in the breadcrumb view, if showing the full path is turned off. If
|
||||
* showing the full path is turned on, the URL is shown
|
||||
* as [/] > [home] > [Pictures] > [2008].
|
||||
*/
|
||||
void setShowFullPath(bool show);
|
||||
|
||||
/**
|
||||
* @return True, if the full path of the URL should be shown in the breadcrumb view.
|
||||
* @since 4.2
|
||||
*/
|
||||
bool showFullPath() const;
|
||||
|
||||
/**
|
||||
* Set the URL navigator to the active mode, if \a active
|
||||
* is true. The active mode is default. The inactive mode only differs
|
||||
* visually from the active mode, no change of the behavior is given.
|
||||
*
|
||||
* Using the URL navigator in the inactive mode is useful when having split views,
|
||||
* where the inactive view is indicated by an inactive URL
|
||||
* navigator visually.
|
||||
*/
|
||||
void setActive(bool active);
|
||||
|
||||
/**
|
||||
* @return True, if the URL navigator is in the active mode.
|
||||
* @see KUrlNavigator::setActive()
|
||||
*/
|
||||
bool isActive() const;
|
||||
|
||||
/**
|
||||
* Sets the places selector visible, if \a visible is true.
|
||||
* The places selector allows to select the places provided
|
||||
* by the places model passed in the constructor. Per default
|
||||
* the places selector is visible.
|
||||
*/
|
||||
void setPlacesSelectorVisible(bool visible);
|
||||
|
||||
/** @return True, if the places selector is visible. */
|
||||
bool isPlacesSelectorVisible() const;
|
||||
|
||||
/**
|
||||
* @return The currently entered, but not accepted URL.
|
||||
* It is possible that the returned URL is not valid.
|
||||
*/
|
||||
QUrl uncommittedUrl() const;
|
||||
|
||||
/**
|
||||
* @return The amount of locations in the history. The data for each
|
||||
* location can be retrieved by KUrlNavigator::locationUrl() and
|
||||
* KUrlNavigator::locationState().
|
||||
*/
|
||||
int historySize() const;
|
||||
|
||||
/**
|
||||
* @return The history index of the current location, where
|
||||
* 0 <= history index < KUrlNavigator::historySize(). 0 is the most
|
||||
* recent history entry.
|
||||
*/
|
||||
int historyIndex() const;
|
||||
|
||||
/**
|
||||
* @return The used editor when the navigator is in the edit mode
|
||||
* @see KUrlNavigator::setUrlEditable()
|
||||
*/
|
||||
KUrlComboBox *editor() const;
|
||||
|
||||
/**
|
||||
* Set the URL schemes that the navigator should allow navigating to.
|
||||
*
|
||||
* If the passed list is empty, all schemes are supported. Examples for
|
||||
* schemes are @c "file" or @c "ftp".
|
||||
*
|
||||
* @sa QFileDialog::setSupportedSchemes
|
||||
* @since 5.103
|
||||
*/
|
||||
void setSupportedSchemes(const QStringList &schemes);
|
||||
|
||||
/**
|
||||
* Returns the URL schemes that the navigator should allow navigating to.
|
||||
*
|
||||
* If the returned list is empty, all schemes are supported.
|
||||
*
|
||||
* @sa QFileDialog::supportedSchemes
|
||||
* @since 5.103
|
||||
*/
|
||||
QStringList supportedSchemes() const;
|
||||
|
||||
/**
|
||||
* The child widget that received the QDropEvent when dropping on the URL
|
||||
* navigator. You can pass this widget to KJobWidgets::setWindow()
|
||||
* if you need to show a drop menu with KIO::drop().
|
||||
* @return Child widget that has received the last drop event, or nullptr if
|
||||
* nothing has been dropped yet on the URL navigator.
|
||||
* @since 5.37
|
||||
* @see KIO::drop()
|
||||
*/
|
||||
QWidget *dropWidget() const;
|
||||
|
||||
/**
|
||||
* Sets whether to show hidden folders in the subdirectories popup.
|
||||
* @since 5.87
|
||||
*/
|
||||
void setShowHiddenFolders(bool showHiddenFolders);
|
||||
|
||||
/**
|
||||
* Returns whether to show hidden folders in the subdirectories popup.
|
||||
* @since 5.87
|
||||
*/
|
||||
bool showHiddenFolders() const;
|
||||
|
||||
/**
|
||||
* Sets whether to sort hidden folders in the subdirectories popup last.
|
||||
* @since 5.87
|
||||
*/
|
||||
void setSortHiddenFoldersLast(bool sortHiddenFoldersLast);
|
||||
|
||||
/**
|
||||
* Returns whether to sort hidden folders in the subdirectories popup last.
|
||||
* @since 5.87
|
||||
*/
|
||||
bool sortHiddenFoldersLast() const;
|
||||
|
||||
/**
|
||||
* Puts \a widget to the right of the breadcrumb.
|
||||
*
|
||||
* KUrlNavigator takes ownership over \a widget. Any existing badge widget is deleted.
|
||||
*
|
||||
* NOTE: There is no limit to the size of the badge widget. If your badge widget is taller than other
|
||||
* controls in KUrlNavigator, then the whole KUrlNavigator will be resized to accommodate it. Also,
|
||||
* KUrlNavigator has fixed minimumWidth of 100, so if your badge widget is too wide, it might be clipped
|
||||
* when the space is tight. You might want to call KUrlNavigator::setMinimumWidth() with a larger value
|
||||
* in that case.
|
||||
* In general, it is recommended to keep the badge widget small and not expanding, to avoid layout issues.
|
||||
* @since 6.2
|
||||
*/
|
||||
void setBadgeWidget(QWidget *widget);
|
||||
|
||||
/**
|
||||
* Returns the badge widget set by setBadgeWidget(). If setBadgeWidget() hasn't been called, returns nullptr.
|
||||
* @since 6.2
|
||||
*/
|
||||
QWidget *badgeWidget() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Sets the location to \a url. The old URL is added to the history.
|
||||
* The signals KUrlNavigator::urlAboutToBeChanged(), KUrlNavigator::urlChanged()
|
||||
* and KUrlNavigator::historyChanged() are emitted. Use
|
||||
* KUrlNavigator::locationUrl() to read the location.
|
||||
*/
|
||||
void setLocationUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Activates the URL navigator (KUrlNavigator::isActive() will return true)
|
||||
* and emits the signal KUrlNavigator::activated().
|
||||
* @see KUrlNavigator::setActive()
|
||||
*/
|
||||
void requestActivation();
|
||||
|
||||
#if !defined(K_DOXYGEN)
|
||||
// KDE5: Remove and listen for focus-signal instead
|
||||
void setFocus();
|
||||
#endif
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Is emitted, if the URL navigator has been activated by
|
||||
* an user interaction
|
||||
* @see KUrlNavigator::setActive()
|
||||
*/
|
||||
void activated();
|
||||
|
||||
/**
|
||||
* Is emitted, if the location URL has been changed e. g. by
|
||||
* the user.
|
||||
* @see KUrlNavigator::setUrl()
|
||||
*/
|
||||
void urlChanged(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Is emitted, before the location URL is going to be changed to \a newUrl.
|
||||
* The signal KUrlNavigator::urlChanged() will be emitted after the change
|
||||
* has been done. Connecting to this signal is useful to save the state
|
||||
* of a view with KUrlNavigator::saveLocationState().
|
||||
*/
|
||||
void urlAboutToBeChanged(const QUrl &newUrl);
|
||||
|
||||
/**
|
||||
* Is emitted, if the editable state for the URL has been changed
|
||||
* (see KUrlNavigator::setUrlEditable()).
|
||||
*/
|
||||
void editableStateChanged(bool editable);
|
||||
|
||||
/**
|
||||
* Is emitted, if the history has been changed. Usually
|
||||
* the history is changed if a new URL has been selected.
|
||||
*/
|
||||
void historyChanged();
|
||||
|
||||
/**
|
||||
* Is emitted if a dropping has been done above the destination
|
||||
* \a destination. The receiver must accept the drop event if
|
||||
* the dropped data can be handled.
|
||||
*/
|
||||
void urlsDropped(const QUrl &destination, QDropEvent *event);
|
||||
|
||||
/**
|
||||
* This signal is emitted when the Return or Enter key is pressed.
|
||||
*/
|
||||
void returnPressed();
|
||||
|
||||
/**
|
||||
* Is emitted if the URL \a url should be opened in a new inactive tab because
|
||||
* the user clicked on a breadcrumb with the middle mouse button or
|
||||
* left-clicked with the ctrl modifier pressed or pressed return with
|
||||
* the alt modifier pressed.
|
||||
*/
|
||||
void tabRequested(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Is emitted if the URL \a url should be opened in a new active tab because
|
||||
* the user clicked on a breadcrumb with the middle mouse button with
|
||||
* the shift modifier pressed or left-clicked with both the ctrl and shift
|
||||
* modifiers pressed or pressed return with both the alt and shift modifiers
|
||||
* pressed.
|
||||
* @since 5.89
|
||||
*/
|
||||
void activeTabRequested(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Is emitted if the URL \a url should be opened in a new window because
|
||||
* the user left-clicked on a breadcrumb with the shift modifier pressed
|
||||
* or pressed return with the shift modifier pressed.
|
||||
* @since 5.89
|
||||
*/
|
||||
void newWindowRequested(const QUrl &url);
|
||||
|
||||
/**
|
||||
* When the URL is changed and the new URL (e.g.\ /home/user1/)
|
||||
* is a parent of the previous URL (e.g.\ /home/user1/data/stuff),
|
||||
* then this signal is emitted and \p url is set to the child
|
||||
* directory of the new URL which is an ancestor of the old URL
|
||||
* (in the example paths this would be /home/user1/data/).
|
||||
* This signal allows file managers to pre-select the directory
|
||||
* that the user is navigating up from.
|
||||
* @since 5.37.0
|
||||
*/
|
||||
void urlSelectionRequested(const QUrl &url);
|
||||
|
||||
protected:
|
||||
#if !defined(K_DOXYGEN)
|
||||
/**
|
||||
* If the Escape key is pressed, the navigation bar should switch
|
||||
* to the breadcrumb view.
|
||||
* @see QWidget::keyPressEvent()
|
||||
*/
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
/**
|
||||
* Reimplemented for internal purposes.
|
||||
*/
|
||||
void keyReleaseEvent(QKeyEvent *event) override;
|
||||
|
||||
/**
|
||||
* Paste the clipboard content as URL, if the middle mouse
|
||||
* button has been clicked.
|
||||
* @see QWidget::mouseReleaseEvent()
|
||||
*/
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
/**
|
||||
* Reimplemented to activate on middle mousse button click
|
||||
*/
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class KUrlNavigatorPrivate;
|
||||
std::unique_ptr<KUrlNavigatorPrivate> const d;
|
||||
|
||||
Q_DISABLE_COPY(KUrlNavigator)
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,709 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kurlnavigatorbutton_p.h"
|
||||
|
||||
#include "../utils_p.h"
|
||||
#include "kurlnavigator.h"
|
||||
#include "kurlnavigatormenu_p.h"
|
||||
#include <kio/listjob.h>
|
||||
#include <kio/statjob.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <KStringHandler>
|
||||
|
||||
#include <QCollator>
|
||||
#include <QKeyEvent>
|
||||
#include <QMimeData>
|
||||
#include <QPainter>
|
||||
#include <QStyleOption>
|
||||
#include <QTimer>
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
QPointer<KUrlNavigatorMenu> KUrlNavigatorButton::m_subDirsMenu;
|
||||
|
||||
KUrlNavigatorButton::KUrlNavigatorButton(const QUrl &url, KUrlNavigator *parent)
|
||||
: KUrlNavigatorButtonBase(parent)
|
||||
, m_hoverArrow(false)
|
||||
, m_pendingTextChange(false)
|
||||
, m_replaceButton(false)
|
||||
, m_showMnemonic(false)
|
||||
, m_wheelSteps(0)
|
||||
, m_url(url)
|
||||
, m_subDir()
|
||||
, m_openSubDirsTimer(nullptr)
|
||||
, m_subDirsJob(nullptr)
|
||||
{
|
||||
setAcceptDrops(true);
|
||||
setUrl(url);
|
||||
setMouseTracking(true);
|
||||
|
||||
m_openSubDirsTimer = new QTimer(this);
|
||||
m_openSubDirsTimer->setSingleShot(true);
|
||||
m_openSubDirsTimer->setInterval(300);
|
||||
connect(m_openSubDirsTimer, &QTimer::timeout, this, &KUrlNavigatorButton::startSubDirsJob);
|
||||
|
||||
connect(this, &QAbstractButton::pressed, this, &KUrlNavigatorButton::requestSubDirs);
|
||||
}
|
||||
|
||||
KUrlNavigatorButton::~KUrlNavigatorButton()
|
||||
{
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::setUrl(const QUrl &url)
|
||||
{
|
||||
m_url = url;
|
||||
|
||||
// Doing a text-resolving with KIO::stat() for all non-local
|
||||
// URLs leads to problems for protocols where a limit is given for
|
||||
// the number of parallel connections. A black-list
|
||||
// is given where KIO::stat() should not be used:
|
||||
static const QSet<QString> protocolBlacklist = QSet<QString>{
|
||||
QStringLiteral("nfs"),
|
||||
QStringLiteral("fish"),
|
||||
QStringLiteral("ftp"),
|
||||
QStringLiteral("sftp"),
|
||||
QStringLiteral("smb"),
|
||||
QStringLiteral("webdav"),
|
||||
QStringLiteral("mtp"),
|
||||
};
|
||||
|
||||
const bool startTextResolving = m_url.isValid() && !m_url.isLocalFile() && !protocolBlacklist.contains(m_url.scheme());
|
||||
|
||||
if (startTextResolving) {
|
||||
m_pendingTextChange = true;
|
||||
KIO::StatJob *job = KIO::stat(m_url, KIO::HideProgressInfo);
|
||||
connect(job, &KJob::result, this, &KUrlNavigatorButton::statFinished);
|
||||
Q_EMIT startedTextResolving();
|
||||
} else {
|
||||
setText(m_url.fileName().replace(QLatin1Char('&'), QLatin1String("&&")));
|
||||
}
|
||||
}
|
||||
|
||||
QUrl KUrlNavigatorButton::url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::setText(const QString &text)
|
||||
{
|
||||
QString adjustedText = text;
|
||||
if (adjustedText.isEmpty()) {
|
||||
adjustedText = m_url.scheme();
|
||||
}
|
||||
// Assure that the button always consists of one line
|
||||
adjustedText.remove(QLatin1Char('\n'));
|
||||
|
||||
KUrlNavigatorButtonBase::setText(adjustedText);
|
||||
updateMinimumWidth();
|
||||
|
||||
// Assure that statFinished() does not overwrite a text that has been
|
||||
// set by a client of the URL navigator button
|
||||
m_pendingTextChange = false;
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::setActiveSubDirectory(const QString &subDir)
|
||||
{
|
||||
m_subDir = subDir;
|
||||
|
||||
// We use a different (bold) font on active, so the size hint changes
|
||||
updateGeometry();
|
||||
update();
|
||||
}
|
||||
|
||||
QString KUrlNavigatorButton::activeSubDirectory() const
|
||||
{
|
||||
return m_subDir;
|
||||
}
|
||||
|
||||
QSize KUrlNavigatorButton::sizeHint() const
|
||||
{
|
||||
QFont adjustedFont(font());
|
||||
adjustedFont.setBold(m_subDir.isEmpty());
|
||||
// the minimum size is textWidth + arrowWidth() + 2 * BorderWidth; for the
|
||||
// preferred size we add the BorderWidth 2 times again for having an uncluttered look
|
||||
const int width = QFontMetrics(adjustedFont).size(Qt::TextSingleLine, plainText()).width() + arrowWidth() + 4 * BorderWidth;
|
||||
return QSize(width, KUrlNavigatorButtonBase::sizeHint().height());
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::setShowMnemonic(bool show)
|
||||
{
|
||||
if (m_showMnemonic != show) {
|
||||
m_showMnemonic = show;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
bool KUrlNavigatorButton::showMnemonic() const
|
||||
{
|
||||
return m_showMnemonic;
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
QPainter painter(this);
|
||||
|
||||
QFont adjustedFont(font());
|
||||
adjustedFont.setBold(m_subDir.isEmpty());
|
||||
painter.setFont(adjustedFont);
|
||||
|
||||
int buttonWidth = width();
|
||||
int preferredWidth = sizeHint().width();
|
||||
if (preferredWidth < minimumWidth()) {
|
||||
preferredWidth = minimumWidth();
|
||||
}
|
||||
if (buttonWidth > preferredWidth) {
|
||||
buttonWidth = preferredWidth;
|
||||
}
|
||||
const int buttonHeight = height();
|
||||
|
||||
const QColor fgColor = foregroundColor();
|
||||
drawHoverBackground(&painter);
|
||||
|
||||
int textLeft = 0;
|
||||
int textWidth = buttonWidth;
|
||||
|
||||
const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
|
||||
|
||||
if (!m_subDir.isEmpty()) {
|
||||
// draw arrow
|
||||
const int arrowSize = arrowWidth();
|
||||
const int arrowX = leftToRight ? (buttonWidth - arrowSize) - BorderWidth : BorderWidth;
|
||||
const int arrowY = (buttonHeight - arrowSize) / 2;
|
||||
|
||||
QStyleOption option;
|
||||
option.initFrom(this);
|
||||
option.rect = QRect(arrowX, arrowY, arrowSize, arrowSize);
|
||||
option.palette = palette();
|
||||
option.palette.setColor(QPalette::Text, fgColor);
|
||||
option.palette.setColor(QPalette::WindowText, fgColor);
|
||||
option.palette.setColor(QPalette::ButtonText, fgColor);
|
||||
|
||||
if (m_hoverArrow) {
|
||||
// highlight the background of the arrow to indicate that the directories
|
||||
// popup can be opened by a mouse click
|
||||
QColor hoverColor = palette().color(QPalette::HighlightedText);
|
||||
hoverColor.setAlpha(96);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(hoverColor);
|
||||
|
||||
int hoverX = arrowX;
|
||||
if (!leftToRight) {
|
||||
hoverX -= BorderWidth;
|
||||
}
|
||||
painter.drawRect(QRect(hoverX, 0, arrowSize + BorderWidth, buttonHeight));
|
||||
}
|
||||
|
||||
if (leftToRight) {
|
||||
style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &painter, this);
|
||||
} else {
|
||||
style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &painter, this);
|
||||
textLeft += arrowSize + 2 * BorderWidth;
|
||||
}
|
||||
|
||||
textWidth -= arrowSize + 2 * BorderWidth;
|
||||
}
|
||||
|
||||
painter.setPen(fgColor);
|
||||
const bool clipped = isTextClipped();
|
||||
const QRect textRect(textLeft, 0, textWidth, buttonHeight);
|
||||
if (clipped) {
|
||||
QColor bgColor = fgColor;
|
||||
bgColor.setAlpha(0);
|
||||
QLinearGradient gradient(textRect.topLeft(), textRect.topRight());
|
||||
if (leftToRight) {
|
||||
gradient.setColorAt(0.8, fgColor);
|
||||
gradient.setColorAt(1.0, bgColor);
|
||||
} else {
|
||||
gradient.setColorAt(0.0, bgColor);
|
||||
gradient.setColorAt(0.2, fgColor);
|
||||
}
|
||||
|
||||
QPen pen;
|
||||
pen.setBrush(QBrush(gradient));
|
||||
painter.setPen(pen);
|
||||
}
|
||||
|
||||
int textFlags = clipped ? Qt::AlignVCenter : Qt::AlignCenter;
|
||||
if (m_showMnemonic) {
|
||||
textFlags |= Qt::TextShowMnemonic;
|
||||
painter.drawText(textRect, textFlags, text());
|
||||
} else {
|
||||
painter.drawText(textRect, textFlags, plainText());
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::enterEvent(QEnterEvent *event)
|
||||
{
|
||||
KUrlNavigatorButtonBase::enterEvent(event);
|
||||
|
||||
// if the text is clipped due to a small window width, the text should
|
||||
// be shown as tooltip
|
||||
if (isTextClipped()) {
|
||||
setToolTip(plainText());
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::leaveEvent(QEvent *event)
|
||||
{
|
||||
KUrlNavigatorButtonBase::leaveEvent(event);
|
||||
setToolTip(QString());
|
||||
|
||||
if (m_hoverArrow) {
|
||||
m_hoverArrow = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
Q_EMIT navigatorButtonActivated(m_url, Qt::LeftButton, event->modifiers());
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
case Qt::Key_Space:
|
||||
startSubDirsJob();
|
||||
break;
|
||||
default:
|
||||
KUrlNavigatorButtonBase::keyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::dropEvent(QDropEvent *event)
|
||||
{
|
||||
if (event->mimeData()->hasUrls()) {
|
||||
setDisplayHintEnabled(DraggedHint, true);
|
||||
|
||||
Q_EMIT urlsDroppedOnNavButton(m_url, event);
|
||||
|
||||
setDisplayHintEnabled(DraggedHint, false);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if (event->mimeData()->hasUrls()) {
|
||||
setDisplayHintEnabled(DraggedHint, true);
|
||||
event->acceptProposedAction();
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::dragMoveEvent(QDragMoveEvent *event)
|
||||
{
|
||||
QRect rect = event->answerRect();
|
||||
if (isAboveArrow(rect.center().x())) {
|
||||
m_hoverArrow = true;
|
||||
update();
|
||||
|
||||
if (m_subDirsMenu == nullptr) {
|
||||
requestSubDirs();
|
||||
} else if (m_subDirsMenu->parent() != this) {
|
||||
m_subDirsMenu->close();
|
||||
m_subDirsMenu->deleteLater();
|
||||
m_subDirsMenu = nullptr;
|
||||
|
||||
requestSubDirs();
|
||||
}
|
||||
} else {
|
||||
if (m_openSubDirsTimer->isActive()) {
|
||||
cancelSubDirsRequest();
|
||||
}
|
||||
if (m_subDirsMenu) {
|
||||
m_subDirsMenu->deleteLater();
|
||||
m_subDirsMenu = nullptr;
|
||||
}
|
||||
m_hoverArrow = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::dragLeaveEvent(QDragLeaveEvent *event)
|
||||
{
|
||||
KUrlNavigatorButtonBase::dragLeaveEvent(event);
|
||||
|
||||
m_hoverArrow = false;
|
||||
setDisplayHintEnabled(DraggedHint, false);
|
||||
update();
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (isAboveArrow(qRound(event->position().x())) && (event->button() == Qt::LeftButton)) {
|
||||
// the mouse is pressed above the [>] button
|
||||
startSubDirsJob();
|
||||
}
|
||||
KUrlNavigatorButtonBase::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!isAboveArrow(qRound(event->position().x())) || (event->button() != Qt::LeftButton)) {
|
||||
// the mouse has been released above the text area and not
|
||||
// above the [>] button
|
||||
Q_EMIT navigatorButtonActivated(m_url, event->button(), event->modifiers());
|
||||
cancelSubDirsRequest();
|
||||
}
|
||||
KUrlNavigatorButtonBase::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
KUrlNavigatorButtonBase::mouseMoveEvent(event);
|
||||
|
||||
const bool hoverArrow = isAboveArrow(qRound(event->position().x()));
|
||||
if (hoverArrow != m_hoverArrow) {
|
||||
m_hoverArrow = hoverArrow;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
if (event->angleDelta().y() != 0) {
|
||||
m_wheelSteps = event->angleDelta().y() / 120;
|
||||
m_replaceButton = true;
|
||||
startSubDirsJob();
|
||||
}
|
||||
|
||||
KUrlNavigatorButtonBase::wheelEvent(event);
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::requestSubDirs()
|
||||
{
|
||||
if (!m_openSubDirsTimer->isActive() && (m_subDirsJob == nullptr)) {
|
||||
m_openSubDirsTimer->start();
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::startSubDirsJob()
|
||||
{
|
||||
if (m_subDirsJob != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QUrl url = m_replaceButton ? KIO::upUrl(m_url) : m_url;
|
||||
const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
|
||||
Q_ASSERT(urlNavigator);
|
||||
m_subDirsJob =
|
||||
KIO::listDir(url, KIO::HideProgressInfo, urlNavigator->showHiddenFolders() ? KIO::ListJob::ListFlag::IncludeHidden : KIO::ListJob::ListFlags{});
|
||||
m_subDirs.clear(); // just to be ++safe
|
||||
|
||||
connect(m_subDirsJob, &KIO::ListJob::entries, this, &KUrlNavigatorButton::addEntriesToSubDirs);
|
||||
|
||||
if (m_replaceButton) {
|
||||
connect(m_subDirsJob, &KJob::result, this, &KUrlNavigatorButton::replaceButton);
|
||||
} else {
|
||||
connect(m_subDirsJob, &KJob::result, this, &KUrlNavigatorButton::openSubDirsMenu);
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::addEntriesToSubDirs(KIO::Job *job, const KIO::UDSEntryList &entries)
|
||||
{
|
||||
Q_ASSERT(job == m_subDirsJob);
|
||||
Q_UNUSED(job);
|
||||
|
||||
for (const KIO::UDSEntry &entry : entries) {
|
||||
if (entry.isDir()) {
|
||||
const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME);
|
||||
QString displayName = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME);
|
||||
if (displayName.isEmpty()) {
|
||||
displayName = name;
|
||||
}
|
||||
if (name != QLatin1String(".") && name != QLatin1String("..")) {
|
||||
m_subDirs.push_back({name, displayName});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::slotUrlsDropped(QAction *action, QDropEvent *event)
|
||||
{
|
||||
const int result = action->data().toInt();
|
||||
QUrl url(m_url);
|
||||
url.setPath(Utils::concatPaths(url.path(), m_subDirs.at(result).name));
|
||||
Q_EMIT urlsDroppedOnNavButton(url, event);
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::slotMenuActionClicked(QAction *action, Qt::MouseButton button)
|
||||
{
|
||||
const int result = action->data().toInt();
|
||||
QUrl url(m_url);
|
||||
url.setPath(Utils::concatPaths(url.path(), m_subDirs.at(result).name));
|
||||
Q_EMIT navigatorButtonActivated(url, button, Qt::NoModifier);
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::statFinished(KJob *job)
|
||||
{
|
||||
if (m_pendingTextChange) {
|
||||
m_pendingTextChange = false;
|
||||
|
||||
const KIO::UDSEntry entry = static_cast<KIO::StatJob *>(job)->statResult();
|
||||
QString name = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME);
|
||||
if (name.isEmpty()) {
|
||||
name = m_url.fileName();
|
||||
}
|
||||
setText(name);
|
||||
|
||||
Q_EMIT finishedTextResolving();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper struct for sorting folder names
|
||||
*/
|
||||
struct FolderNameNaturalLessThan {
|
||||
FolderNameNaturalLessThan(bool sortHiddenLast)
|
||||
: m_sortHiddenLast(sortHiddenLast)
|
||||
{
|
||||
m_collator.setCaseSensitivity(Qt::CaseInsensitive);
|
||||
m_collator.setNumericMode(true);
|
||||
}
|
||||
|
||||
bool operator()(const KUrlNavigatorButton::SubDirInfo &a, const KUrlNavigatorButton::SubDirInfo &b)
|
||||
{
|
||||
if (m_sortHiddenLast) {
|
||||
const bool isHiddenA = a.name.startsWith(QLatin1Char('.'));
|
||||
const bool isHiddenB = b.name.startsWith(QLatin1Char('.'));
|
||||
if (isHiddenA && !isHiddenB) {
|
||||
return false;
|
||||
}
|
||||
if (!isHiddenA && isHiddenB) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return m_collator.compare(a.name, b.name) < 0;
|
||||
}
|
||||
|
||||
private:
|
||||
QCollator m_collator;
|
||||
bool m_sortHiddenLast;
|
||||
};
|
||||
|
||||
void KUrlNavigatorButton::openSubDirsMenu(KJob *job)
|
||||
{
|
||||
Q_ASSERT(job == m_subDirsJob);
|
||||
m_subDirsJob = nullptr;
|
||||
|
||||
if (job->error() || m_subDirs.empty()) {
|
||||
// clear listing
|
||||
return;
|
||||
}
|
||||
|
||||
const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
|
||||
Q_ASSERT(urlNavigator);
|
||||
FolderNameNaturalLessThan less(urlNavigator->showHiddenFolders() && urlNavigator->sortHiddenFoldersLast());
|
||||
std::sort(m_subDirs.begin(), m_subDirs.end(), less);
|
||||
setDisplayHintEnabled(PopupActiveHint, true);
|
||||
update(); // ensure the button is drawn highlighted
|
||||
|
||||
if (m_subDirsMenu != nullptr) {
|
||||
m_subDirsMenu->close();
|
||||
m_subDirsMenu->deleteLater();
|
||||
m_subDirsMenu = nullptr;
|
||||
}
|
||||
|
||||
m_subDirsMenu = new KUrlNavigatorMenu(this);
|
||||
initMenu(m_subDirsMenu, 0);
|
||||
|
||||
const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
|
||||
const int popupX = leftToRight ? width() - arrowWidth() - BorderWidth : 0;
|
||||
const QPoint popupPos = parentWidget()->mapToGlobal(geometry().bottomLeft() + QPoint(popupX, 0));
|
||||
|
||||
QPointer<QObject> guard(this);
|
||||
|
||||
m_subDirsMenu->exec(popupPos);
|
||||
|
||||
// If 'this' has been deleted in the menu's nested event loop, we have to return
|
||||
// immediately because any access to a member variable might cause a crash.
|
||||
if (!guard) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_subDirs.clear();
|
||||
delete m_subDirsMenu;
|
||||
m_subDirsMenu = nullptr;
|
||||
|
||||
setDisplayHintEnabled(PopupActiveHint, false);
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::replaceButton(KJob *job)
|
||||
{
|
||||
Q_ASSERT(job == m_subDirsJob);
|
||||
m_subDirsJob = nullptr;
|
||||
m_replaceButton = false;
|
||||
|
||||
if (job->error() || m_subDirs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const KUrlNavigator *urlNavigator = qobject_cast<KUrlNavigator *>(parent());
|
||||
Q_ASSERT(urlNavigator);
|
||||
FolderNameNaturalLessThan less(urlNavigator->showHiddenFolders() && urlNavigator->sortHiddenFoldersLast());
|
||||
std::sort(m_subDirs.begin(), m_subDirs.end(), less);
|
||||
|
||||
// Get index of the directory that is shown currently in the button
|
||||
const QString currentDir = m_url.fileName();
|
||||
int currentIndex = 0;
|
||||
const int subDirsCount = m_subDirs.size();
|
||||
while (currentIndex < subDirsCount) {
|
||||
if (m_subDirs[currentIndex].name == currentDir) {
|
||||
break;
|
||||
}
|
||||
++currentIndex;
|
||||
}
|
||||
|
||||
// Adjust the index by respecting the wheel steps and
|
||||
// trigger a replacing of the button content
|
||||
int targetIndex = currentIndex - m_wheelSteps;
|
||||
if (targetIndex < 0) {
|
||||
targetIndex = 0;
|
||||
} else if (targetIndex >= subDirsCount) {
|
||||
targetIndex = subDirsCount - 1;
|
||||
}
|
||||
|
||||
QUrl url(KIO::upUrl(m_url));
|
||||
url.setPath(Utils::concatPaths(url.path(), m_subDirs[targetIndex].name));
|
||||
Q_EMIT navigatorButtonActivated(url, Qt::LeftButton, Qt::NoModifier);
|
||||
|
||||
m_subDirs.clear();
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::cancelSubDirsRequest()
|
||||
{
|
||||
m_openSubDirsTimer->stop();
|
||||
if (m_subDirsJob != nullptr) {
|
||||
m_subDirsJob->kill();
|
||||
m_subDirsJob = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QString KUrlNavigatorButton::plainText() const
|
||||
{
|
||||
// Replace all "&&" by '&' and remove all single
|
||||
// '&' characters
|
||||
const QString source = text();
|
||||
const int sourceLength = source.length();
|
||||
|
||||
QString dest;
|
||||
dest.resize(sourceLength);
|
||||
|
||||
int sourceIndex = 0;
|
||||
int destIndex = 0;
|
||||
while (sourceIndex < sourceLength) {
|
||||
if (source.at(sourceIndex) == QLatin1Char('&')) {
|
||||
++sourceIndex;
|
||||
if (sourceIndex >= sourceLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dest[destIndex] = source.at(sourceIndex);
|
||||
++sourceIndex;
|
||||
++destIndex;
|
||||
}
|
||||
|
||||
dest.resize(destIndex);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
int KUrlNavigatorButton::arrowWidth() const
|
||||
{
|
||||
// if there isn't arrow then return 0
|
||||
int width = 0;
|
||||
if (!m_subDir.isEmpty()) {
|
||||
width = height() / 2;
|
||||
if (width < 4) {
|
||||
width = 4;
|
||||
}
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
bool KUrlNavigatorButton::isAboveArrow(int x) const
|
||||
{
|
||||
const bool leftToRight = (layoutDirection() == Qt::LeftToRight);
|
||||
return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth());
|
||||
}
|
||||
|
||||
bool KUrlNavigatorButton::isTextClipped() const
|
||||
{
|
||||
int availableWidth = width() - 2 * BorderWidth;
|
||||
if (!m_subDir.isEmpty()) {
|
||||
availableWidth -= arrowWidth() - BorderWidth;
|
||||
}
|
||||
|
||||
QFont adjustedFont(font());
|
||||
adjustedFont.setBold(m_subDir.isEmpty());
|
||||
return QFontMetrics(adjustedFont).size(Qt::TextSingleLine, plainText()).width() >= availableWidth;
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::updateMinimumWidth()
|
||||
{
|
||||
const int oldMinWidth = minimumWidth();
|
||||
|
||||
int minWidth = sizeHint().width();
|
||||
if (minWidth < 40) {
|
||||
minWidth = 40;
|
||||
} else if (minWidth > 150) {
|
||||
// don't let an overlong path name waste all the URL navigator space
|
||||
minWidth = 150;
|
||||
}
|
||||
if (oldMinWidth != minWidth) {
|
||||
setMinimumWidth(minWidth);
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorButton::initMenu(KUrlNavigatorMenu *menu, int startIndex)
|
||||
{
|
||||
connect(menu, &KUrlNavigatorMenu::mouseButtonClicked, this, &KUrlNavigatorButton::slotMenuActionClicked);
|
||||
connect(menu, &KUrlNavigatorMenu::urlsDropped, this, &KUrlNavigatorButton::slotUrlsDropped);
|
||||
|
||||
// So that triggering a menu item with the keyboard works
|
||||
connect(menu, &QMenu::triggered, this, [this](QAction *act) {
|
||||
slotMenuActionClicked(act, Qt::LeftButton);
|
||||
});
|
||||
|
||||
menu->setLayoutDirection(Qt::LeftToRight);
|
||||
|
||||
const int maxIndex = startIndex + 30; // Don't show more than 30 items in a menu
|
||||
const int subDirsSize = m_subDirs.size();
|
||||
const int lastIndex = std::min(subDirsSize - 1, maxIndex);
|
||||
for (int i = startIndex; i <= lastIndex; ++i) {
|
||||
const auto &[subDirName, subDirDisplayName] = m_subDirs[i];
|
||||
QString text = KStringHandler::csqueeze(subDirDisplayName, 60);
|
||||
text.replace(QLatin1Char('&'), QLatin1String("&&"));
|
||||
QAction *action = new QAction(text, this);
|
||||
if (m_subDir == subDirName) {
|
||||
QFont font(action->font());
|
||||
font.setBold(true);
|
||||
action->setFont(font);
|
||||
}
|
||||
action->setData(i);
|
||||
menu->addAction(action);
|
||||
}
|
||||
if (subDirsSize > maxIndex) {
|
||||
// If too much items are shown, move them into a sub menu
|
||||
menu->addSeparator();
|
||||
KUrlNavigatorMenu *subDirsMenu = new KUrlNavigatorMenu(menu);
|
||||
subDirsMenu->setTitle(i18nc("@action:inmenu", "More"));
|
||||
initMenu(subDirsMenu, maxIndex);
|
||||
menu->addMenu(subDirsMenu);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#include "moc_kurlnavigatorbutton_p.cpp"
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KURLNAVIGATORBUTTON_P_H
|
||||
#define KURLNAVIGATORBUTTON_P_H
|
||||
|
||||
#include "kurlnavigatorbuttonbase_p.h"
|
||||
#include "kurlnavigatormenu_p.h"
|
||||
|
||||
#include <kio/global.h>
|
||||
#include <kio/udsentry.h>
|
||||
|
||||
#include <QPointer>
|
||||
#include <QUrl>
|
||||
|
||||
class KJob;
|
||||
class QDropEvent;
|
||||
class QPaintEvent;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class ListJob;
|
||||
class Job;
|
||||
}
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
/**
|
||||
* @brief Button of the URL navigator which contains one part of an URL.
|
||||
*
|
||||
* It is possible to drop a various number of items to an UrlNavigatorButton. In this case
|
||||
* a context menu is opened where the user must select whether he wants
|
||||
* to copy, move or link the dropped items to the URL part indicated by
|
||||
* the button.
|
||||
*/
|
||||
class KUrlNavigatorButton : public KUrlNavigatorButtonBase
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString plainText READ plainText) // for the unittest
|
||||
|
||||
public:
|
||||
explicit KUrlNavigatorButton(const QUrl &url, KUrlNavigator *parent);
|
||||
~KUrlNavigatorButton() override;
|
||||
|
||||
void setUrl(const QUrl &url);
|
||||
QUrl url() const;
|
||||
|
||||
/* Implementation note: QAbstractButton::setText() is not virtual,
|
||||
* but KUrlNavigatorButton needs to adjust the minimum size when
|
||||
* the text has been changed. KUrlNavigatorButton::setText() hides
|
||||
* QAbstractButton::setText() which is not nice, but sufficient for
|
||||
* the usage in KUrlNavigator.
|
||||
*/
|
||||
void setText(const QString &text);
|
||||
|
||||
/**
|
||||
* Sets the name of the sub directory that should be marked when
|
||||
* opening the sub directories popup.
|
||||
*/
|
||||
void setActiveSubDirectory(const QString &subDir);
|
||||
QString activeSubDirectory() const;
|
||||
|
||||
/** @see QWidget::sizeHint() */
|
||||
QSize sizeHint() const override;
|
||||
|
||||
void setShowMnemonic(bool show);
|
||||
bool showMnemonic() const;
|
||||
|
||||
struct SubDirInfo {
|
||||
QString name;
|
||||
QString displayName;
|
||||
};
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when URLs are dropped on the KUrlNavigatorButton associated with
|
||||
* the URL @p destination.
|
||||
*/
|
||||
void urlsDroppedOnNavButton(const QUrl &destination, QDropEvent *event);
|
||||
|
||||
void navigatorButtonActivated(const QUrl &url, Qt::MouseButton button, Qt::KeyboardModifiers modifiers);
|
||||
|
||||
/**
|
||||
* Is emitted, if KUrlNavigatorButton::setUrl() cannot resolve
|
||||
* the text synchronously and KUrlNavigator::text() will return
|
||||
* an empty string in this case. The signal finishedTextResolving() is
|
||||
* emitted, as soon as the text has been resolved.
|
||||
*/
|
||||
void startedTextResolving();
|
||||
|
||||
/**
|
||||
* Is emitted, if the asynchronous resolving of the text has
|
||||
* been finished (see startTextResolving()).
|
||||
* KUrlNavigatorButton::text() contains the resolved text.
|
||||
*/
|
||||
void finishedTextResolving();
|
||||
|
||||
protected:
|
||||
void enterEvent(QEnterEvent *event) override;
|
||||
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void dropEvent(QDropEvent *event) override;
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void dragMoveEvent(QDragMoveEvent *event) override;
|
||||
void dragLeaveEvent(QDragLeaveEvent *event) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
/**
|
||||
* Requests to load the sub-directories after a short delay.
|
||||
* startSubDirsJob() is invoked if the delay is exceeded.
|
||||
*/
|
||||
void requestSubDirs();
|
||||
|
||||
/**
|
||||
* Starts to load the sub directories asynchronously. The directories
|
||||
* are stored in m_subDirs by addEntriesToSubDirs().
|
||||
*/
|
||||
void startSubDirsJob();
|
||||
|
||||
/**
|
||||
* Adds the entries from the sub-directories job to m_subDirs. The entries
|
||||
* will be shown if the job has been finished in openSubDirsMenu() or
|
||||
* replaceButton().
|
||||
*/
|
||||
void addEntriesToSubDirs(KIO::Job *job, const KIO::UDSEntryList &entries);
|
||||
|
||||
/**
|
||||
* Is called after the sub-directories job has been finished and opens a menu
|
||||
* showing all sub directories.
|
||||
*/
|
||||
void openSubDirsMenu(KJob *job);
|
||||
|
||||
/**
|
||||
* Is called after the sub-directories job has been finished and replaces
|
||||
* the button content by the current sub directory (triggered by
|
||||
* the scroll wheel).
|
||||
*/
|
||||
void replaceButton(KJob *job);
|
||||
|
||||
void slotUrlsDropped(QAction *action, QDropEvent *event);
|
||||
|
||||
/**
|
||||
* Is called, if an action of a sub-menu has been triggered by
|
||||
* a click.
|
||||
*/
|
||||
void slotMenuActionClicked(QAction *action, Qt::MouseButton button);
|
||||
|
||||
void statFinished(KJob *);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Cancels any request done by requestSubDirs().
|
||||
*/
|
||||
void cancelSubDirsRequest();
|
||||
|
||||
/**
|
||||
* @return Text without mnemonic characters.
|
||||
*/
|
||||
QString plainText() const;
|
||||
|
||||
int arrowWidth() const;
|
||||
bool isAboveArrow(int x) const;
|
||||
bool isTextClipped() const;
|
||||
void updateMinimumWidth();
|
||||
void initMenu(KUrlNavigatorMenu *menu, int startIndex);
|
||||
|
||||
private:
|
||||
bool m_hoverArrow;
|
||||
bool m_pendingTextChange;
|
||||
bool m_replaceButton;
|
||||
bool m_showMnemonic;
|
||||
int m_wheelSteps;
|
||||
QUrl m_url;
|
||||
|
||||
QString m_subDir;
|
||||
QTimer *m_openSubDirsTimer;
|
||||
static QPointer<KUrlNavigatorMenu> m_subDirsMenu;
|
||||
KIO::ListJob *m_subDirsJob;
|
||||
std::vector<SubDirInfo> m_subDirs;
|
||||
};
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006-2010 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kurlnavigatorbuttonbase_p.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <KUrlNavigator>
|
||||
|
||||
#include <QStyle>
|
||||
#include <QStyleOptionViewItem>
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
KUrlNavigatorButtonBase::KUrlNavigatorButtonBase(KUrlNavigator *parent)
|
||||
: QPushButton(parent)
|
||||
, m_active(true)
|
||||
, m_displayHint(0)
|
||||
{
|
||||
setFocusPolicy(Qt::TabFocus);
|
||||
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
|
||||
setMinimumHeight(parent->minimumHeight());
|
||||
setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
|
||||
connect(this, &KUrlNavigatorButtonBase::pressed, parent, &KUrlNavigator::requestActivation);
|
||||
}
|
||||
|
||||
KUrlNavigatorButtonBase::~KUrlNavigatorButtonBase()
|
||||
{
|
||||
}
|
||||
|
||||
void KUrlNavigatorButtonBase::setActive(bool active)
|
||||
{
|
||||
if (m_active != active) {
|
||||
m_active = active;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
bool KUrlNavigatorButtonBase::isActive() const
|
||||
{
|
||||
return m_active;
|
||||
}
|
||||
|
||||
void KUrlNavigatorButtonBase::setDisplayHintEnabled(DisplayHint hint, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
m_displayHint = m_displayHint | hint;
|
||||
} else {
|
||||
m_displayHint = m_displayHint & ~hint;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
bool KUrlNavigatorButtonBase::isDisplayHintEnabled(DisplayHint hint) const
|
||||
{
|
||||
return (m_displayHint & hint) > 0;
|
||||
}
|
||||
|
||||
void KUrlNavigatorButtonBase::focusInEvent(QFocusEvent *event)
|
||||
{
|
||||
setDisplayHintEnabled(EnteredHint, true);
|
||||
QPushButton::focusInEvent(event);
|
||||
}
|
||||
|
||||
void KUrlNavigatorButtonBase::focusOutEvent(QFocusEvent *event)
|
||||
{
|
||||
setDisplayHintEnabled(EnteredHint, false);
|
||||
QPushButton::focusOutEvent(event);
|
||||
}
|
||||
|
||||
void KUrlNavigatorButtonBase::enterEvent(QEnterEvent *event)
|
||||
{
|
||||
QPushButton::enterEvent(event);
|
||||
setDisplayHintEnabled(EnteredHint, true);
|
||||
update();
|
||||
}
|
||||
|
||||
void KUrlNavigatorButtonBase::leaveEvent(QEvent *event)
|
||||
{
|
||||
QPushButton::leaveEvent(event);
|
||||
setDisplayHintEnabled(EnteredHint, false);
|
||||
update();
|
||||
}
|
||||
|
||||
void KUrlNavigatorButtonBase::drawHoverBackground(QPainter *painter)
|
||||
{
|
||||
const bool isHighlighted = isDisplayHintEnabled(EnteredHint) || isDisplayHintEnabled(DraggedHint) || isDisplayHintEnabled(PopupActiveHint);
|
||||
|
||||
QColor backgroundColor = isHighlighted ? palette().color(QPalette::Highlight) : Qt::transparent;
|
||||
if (!m_active && isHighlighted) {
|
||||
backgroundColor.setAlpha(128);
|
||||
}
|
||||
|
||||
if (backgroundColor != Qt::transparent) {
|
||||
// TODO: the backgroundColor should be applied to the style
|
||||
QStyleOptionViewItem option;
|
||||
option.initFrom(this);
|
||||
option.state = QStyle::State_Enabled | QStyle::State_MouseOver;
|
||||
option.viewItemPosition = QStyleOptionViewItem::OnlyOne;
|
||||
style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, this);
|
||||
}
|
||||
}
|
||||
|
||||
QColor KUrlNavigatorButtonBase::foregroundColor() const
|
||||
{
|
||||
const bool isHighlighted = isDisplayHintEnabled(EnteredHint) || isDisplayHintEnabled(DraggedHint) || isDisplayHintEnabled(PopupActiveHint);
|
||||
|
||||
QColor foregroundColor = palette().color(foregroundRole());
|
||||
|
||||
int alpha = m_active ? 255 : 128;
|
||||
if (!m_active && !isHighlighted) {
|
||||
alpha -= alpha / 4;
|
||||
}
|
||||
foregroundColor.setAlpha(alpha);
|
||||
|
||||
return foregroundColor;
|
||||
}
|
||||
|
||||
void KUrlNavigatorButtonBase::activate()
|
||||
{
|
||||
setActive(true);
|
||||
}
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#include "moc_kurlnavigatorbuttonbase_p.cpp"
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006-2010 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KURLNAVIGATORBUTTONBASE_P_H
|
||||
#define KURLNAVIGATORBUTTONBASE_P_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QPushButton>
|
||||
|
||||
class QUrl;
|
||||
class QEvent;
|
||||
|
||||
class KUrlNavigator;
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
/**
|
||||
* @brief Base class for buttons of the URL navigator.
|
||||
*
|
||||
* Buttons of the URL navigator offer an active/inactive
|
||||
* state and custom display hints.
|
||||
*/
|
||||
class KUrlNavigatorButtonBase : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KUrlNavigatorButtonBase(KUrlNavigator *parent);
|
||||
~KUrlNavigatorButtonBase() override;
|
||||
|
||||
/**
|
||||
* When having several URL navigator instances, it is important
|
||||
* to provide a visual difference to indicate which URL navigator
|
||||
* is active (usecase: split view in Dolphin). The activation state
|
||||
* is independent from the focus or hover state.
|
||||
* Per default the URL navigator button is marked as active.
|
||||
*/
|
||||
void setActive(bool active);
|
||||
bool isActive() const;
|
||||
|
||||
protected:
|
||||
enum DisplayHint {
|
||||
EnteredHint = 1,
|
||||
DraggedHint = 2,
|
||||
PopupActiveHint = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
BorderWidth = 2
|
||||
};
|
||||
|
||||
void setDisplayHintEnabled(DisplayHint hint, bool enable);
|
||||
bool isDisplayHintEnabled(DisplayHint hint) const;
|
||||
|
||||
void focusInEvent(QFocusEvent *event) override;
|
||||
void focusOutEvent(QFocusEvent *event) override;
|
||||
|
||||
void enterEvent(QEnterEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
|
||||
void drawHoverBackground(QPainter *painter);
|
||||
|
||||
/** Returns the foreground color by respecting the current display hint. */
|
||||
QColor foregroundColor() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
/** Invokes setActive(true). */
|
||||
void activate();
|
||||
|
||||
private:
|
||||
bool m_active;
|
||||
int m_displayHint;
|
||||
};
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kurlnavigator.h"
|
||||
#include "kurlnavigatordropdownbutton_p.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
#include <QStyleOption>
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
KUrlNavigatorDropDownButton::KUrlNavigatorDropDownButton(KUrlNavigator *parent)
|
||||
: KUrlNavigatorButtonBase(parent)
|
||||
{
|
||||
setText(i18nc("@action:button opening a list of locations", "Go to Location on Path"));
|
||||
}
|
||||
|
||||
KUrlNavigatorDropDownButton::~KUrlNavigatorDropDownButton()
|
||||
{
|
||||
}
|
||||
|
||||
QSize KUrlNavigatorDropDownButton::sizeHint() const
|
||||
{
|
||||
QSize size = KUrlNavigatorButtonBase::sizeHint();
|
||||
size.setWidth(size.height() / 2);
|
||||
return size;
|
||||
}
|
||||
|
||||
void KUrlNavigatorDropDownButton::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
QPainter painter(this);
|
||||
drawHoverBackground(&painter);
|
||||
|
||||
const QColor fgColor = foregroundColor();
|
||||
|
||||
QStyleOption option;
|
||||
option.initFrom(this);
|
||||
option.rect = QRect(0, 0, width(), height());
|
||||
option.palette = palette();
|
||||
option.palette.setColor(QPalette::Text, fgColor);
|
||||
option.palette.setColor(QPalette::WindowText, fgColor);
|
||||
option.palette.setColor(QPalette::ButtonText, fgColor);
|
||||
|
||||
if (layoutDirection() == Qt::LeftToRight) {
|
||||
style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &option, &painter, this);
|
||||
} else {
|
||||
style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &option, &painter, this);
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorDropDownButton::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Down:
|
||||
Q_EMIT clicked();
|
||||
break;
|
||||
default:
|
||||
KUrlNavigatorButtonBase::keyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#include "moc_kurlnavigatordropdownbutton_p.cpp"
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KURLNAVIGATORDROPDOWNBUTTON_P_H
|
||||
#define KURLNAVIGATORDROPDOWNBUTTON_P_H
|
||||
|
||||
#include "kurlnavigatorbuttonbase_p.h"
|
||||
|
||||
class KUrlNavigator;
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
/**
|
||||
* @brief Button of the URL navigator which offers a drop down menu
|
||||
* of the hidden portion of the path.
|
||||
*
|
||||
* The button will only be shown if the width of the URL navigator is
|
||||
* too small to show the whole path or if some part of the path is
|
||||
* expected to be a known location like "home".
|
||||
*/
|
||||
class KUrlNavigatorDropDownButton : public KUrlNavigatorButtonBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KUrlNavigatorDropDownButton(KUrlNavigator *parent);
|
||||
~KUrlNavigatorDropDownButton() override;
|
||||
|
||||
/** @see QWidget::sizeHint() */
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2009 Rahman Duran <rahman.duran@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "kurlnavigatormenu_p.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QKeyEvent>
|
||||
#include <QMimeData>
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
KUrlNavigatorMenu::KUrlNavigatorMenu(QWidget *parent)
|
||||
: QMenu(parent)
|
||||
, m_initialMousePosition(QCursor::pos())
|
||||
, m_mouseMoved(false)
|
||||
{
|
||||
setAcceptDrops(true);
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
KUrlNavigatorMenu::~KUrlNavigatorMenu()
|
||||
{
|
||||
}
|
||||
|
||||
void KUrlNavigatorMenu::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if (event->mimeData()->hasUrls()) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorMenu::dragMoveEvent(QDragMoveEvent *event)
|
||||
{
|
||||
const QPointF eventPosition = event->position();
|
||||
const QPointF globalEventPosition = mapToGlobal(eventPosition);
|
||||
QMouseEvent mouseEvent(QMouseEvent(QEvent::MouseMove, eventPosition, globalEventPosition, Qt::LeftButton, event->buttons(), event->modifiers()));
|
||||
mouseMoveEvent(&mouseEvent);
|
||||
}
|
||||
|
||||
void KUrlNavigatorMenu::dropEvent(QDropEvent *event)
|
||||
{
|
||||
QAction *action = actionAt(event->position().toPoint());
|
||||
if (action != nullptr) {
|
||||
Q_EMIT urlsDropped(action, event);
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorMenu::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!m_mouseMoved) {
|
||||
QPoint moveDistance = mapToGlobal(event->pos()) - m_initialMousePosition;
|
||||
m_mouseMoved = (moveDistance.manhattanLength() >= QApplication::startDragDistance());
|
||||
}
|
||||
// Don't pass the event to the base class until we consider
|
||||
// that the mouse has moved. This prevents menu items from
|
||||
// being highlighted too early.
|
||||
if (m_mouseMoved) {
|
||||
QMenu::mouseMoveEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorMenu::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
Qt::MouseButton btn = event->button();
|
||||
// Since menu is opened on mouse press, we may receive
|
||||
// the corresponding mouse release event. Let's ignore
|
||||
// it unless mouse was moved.
|
||||
if (m_mouseMoved || (btn != Qt::LeftButton)) {
|
||||
QAction *action = actionAt(event->pos());
|
||||
if (action != nullptr) {
|
||||
Q_EMIT mouseButtonClicked(action, btn);
|
||||
|
||||
// Prevent QMenu default activation, in case
|
||||
// triggered signal is used
|
||||
setActiveAction(nullptr);
|
||||
}
|
||||
QMenu::mouseReleaseEvent(event);
|
||||
}
|
||||
m_mouseMoved = true;
|
||||
}
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#include "moc_kurlnavigatormenu_p.cpp"
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2009 Rahman Duran <rahman.duran@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KURLNAVIGATORMENU_P_H
|
||||
#define KURLNAVIGATORMENU_P_H
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
/**
|
||||
* @brief Provides drop-down menus for the URL navigator.
|
||||
*
|
||||
* The implementation extends QMenu with drag & drop support.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class KUrlNavigatorMenu : public QMenu
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KUrlNavigatorMenu(QWidget *parent);
|
||||
~KUrlNavigatorMenu() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Is emitted when drop event occurs.
|
||||
*/
|
||||
void urlsDropped(QAction *action, QDropEvent *event);
|
||||
|
||||
/**
|
||||
* Is emitted, if the action \p action has been clicked.
|
||||
*/
|
||||
void mouseButtonClicked(QAction *action, Qt::MouseButton button);
|
||||
|
||||
protected:
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void dragMoveEvent(QDragMoveEvent *event) override;
|
||||
void dropEvent(QDropEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
const QPoint m_initialMousePosition;
|
||||
bool m_mouseMoved;
|
||||
};
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#endif
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "kurlnavigatorpathselectoreventfilter_p.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
|
||||
using namespace KDEPrivate;
|
||||
|
||||
KUrlNavigatorPathSelectorEventFilter::KUrlNavigatorPathSelectorEventFilter(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
KUrlNavigatorPathSelectorEventFilter::~KUrlNavigatorPathSelectorEventFilter()
|
||||
{
|
||||
}
|
||||
|
||||
bool KUrlNavigatorPathSelectorEventFilter::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
||||
if (me->button() == Qt::MiddleButton) {
|
||||
if (QMenu *menu = qobject_cast<QMenu *>(watched)) {
|
||||
if (QAction *action = menu->activeAction()) {
|
||||
const QUrl url(action->data().toString());
|
||||
if (url.isValid()) {
|
||||
menu->close();
|
||||
|
||||
Q_EMIT tabRequested(url);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
#include "moc_kurlnavigatorpathselectoreventfilter_p.cpp"
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KURLNAVIGATORPATHSELECTOREVENTFILTER_P_H
|
||||
#define KURLNAVIGATORPATHSELECTOREVENTFILTER_P_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
class KUrlNavigatorPathSelectorEventFilter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KUrlNavigatorPathSelectorEventFilter(QObject *parent);
|
||||
~KUrlNavigatorPathSelectorEventFilter() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void tabRequested(const QUrl &url);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
};
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kurlnavigatorplacesselector_p.h"
|
||||
|
||||
#include <KProtocolInfo>
|
||||
#include <KUrlMimeData>
|
||||
#include <kfileplacesmodel.h>
|
||||
|
||||
#include <QDragEnterEvent>
|
||||
#include <QDragLeaveEvent>
|
||||
#include <QDropEvent>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QMimeDatabase>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QStyle>
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
KUrlNavigatorPlacesSelector::KUrlNavigatorPlacesSelector(KUrlNavigator *parent, KFilePlacesModel *placesModel)
|
||||
: KUrlNavigatorButtonBase(parent)
|
||||
, m_selectedItem(-1)
|
||||
, m_placesModel(placesModel)
|
||||
{
|
||||
connect(m_placesModel, &KFilePlacesModel::reloaded, this, [this] {
|
||||
updateSelection(m_selectedUrl);
|
||||
});
|
||||
|
||||
m_placesMenu = new QMenu(this);
|
||||
m_placesMenu->installEventFilter(this);
|
||||
connect(m_placesMenu, &QMenu::aboutToShow, this, &KUrlNavigatorPlacesSelector::updateMenu);
|
||||
connect(m_placesMenu, &QMenu::triggered, this, [this](QAction *action) {
|
||||
activatePlace(action, &KUrlNavigatorPlacesSelector::placeActivated);
|
||||
});
|
||||
|
||||
setMenu(m_placesMenu);
|
||||
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
|
||||
KUrlNavigatorPlacesSelector::~KUrlNavigatorPlacesSelector()
|
||||
{
|
||||
}
|
||||
|
||||
void KUrlNavigatorPlacesSelector::updateMenu()
|
||||
{
|
||||
m_placesMenu->clear();
|
||||
|
||||
// Submenus have to be deleted explicitly (QTBUG-11070)
|
||||
for (QObject *obj : QObjectList(m_placesMenu->children())) {
|
||||
delete qobject_cast<QMenu *>(obj); // Noop for nullptr
|
||||
}
|
||||
|
||||
QString previousGroup;
|
||||
QMenu *subMenu = nullptr;
|
||||
|
||||
const int rowCount = m_placesModel->rowCount();
|
||||
for (int i = 0; i < rowCount; ++i) {
|
||||
QModelIndex index = m_placesModel->index(i, 0);
|
||||
if (m_placesModel->isHidden(index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QAction *placeAction = new QAction(m_placesModel->icon(index), m_placesModel->text(index), m_placesMenu);
|
||||
placeAction->setData(i);
|
||||
|
||||
const QString &groupName = index.data(KFilePlacesModel::GroupRole).toString();
|
||||
if (previousGroup.isEmpty()) { // Skip first group heading.
|
||||
previousGroup = groupName;
|
||||
}
|
||||
|
||||
// Put all subsequent categories into a submenu.
|
||||
if (previousGroup != groupName) {
|
||||
QAction *subMenuAction = new QAction(groupName, m_placesMenu);
|
||||
subMenu = new QMenu(m_placesMenu);
|
||||
subMenu->installEventFilter(this);
|
||||
subMenuAction->setMenu(subMenu);
|
||||
|
||||
m_placesMenu->addAction(subMenuAction);
|
||||
|
||||
previousGroup = groupName;
|
||||
}
|
||||
|
||||
if (subMenu) {
|
||||
subMenu->addAction(placeAction);
|
||||
} else {
|
||||
m_placesMenu->addAction(placeAction);
|
||||
}
|
||||
|
||||
if (i == m_selectedItem) {
|
||||
setIcon(m_placesModel->icon(index));
|
||||
}
|
||||
}
|
||||
|
||||
const QModelIndex index = m_placesModel->index(m_selectedItem, 0);
|
||||
if (QAction *teardown = m_placesModel->teardownActionForIndex(index)) {
|
||||
m_placesMenu->addSeparator();
|
||||
|
||||
teardown->setParent(m_placesMenu);
|
||||
m_placesMenu->addAction(teardown);
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorPlacesSelector::updateSelection(const QUrl &url)
|
||||
{
|
||||
const QModelIndex index = m_placesModel->closestItem(url);
|
||||
if (index.isValid()) {
|
||||
m_selectedItem = index.row();
|
||||
m_selectedUrl = url;
|
||||
setIcon(m_placesModel->icon(index));
|
||||
} else {
|
||||
m_selectedItem = -1;
|
||||
// No bookmark has been found which matches to the given Url.
|
||||
// Show the protocol's icon as pixmap for indication, if available:
|
||||
QIcon icon;
|
||||
if (!url.scheme().isEmpty()) {
|
||||
if (const QString iconName = KProtocolInfo::icon(url.scheme()); !iconName.isEmpty()) {
|
||||
icon = QIcon::fromTheme(iconName);
|
||||
}
|
||||
}
|
||||
if (icon.isNull()) {
|
||||
icon = QIcon::fromTheme(QStringLiteral("folder"));
|
||||
}
|
||||
setIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
QUrl KUrlNavigatorPlacesSelector::selectedPlaceUrl() const
|
||||
{
|
||||
const QModelIndex index = m_placesModel->index(m_selectedItem, 0);
|
||||
return index.isValid() ? m_placesModel->url(index) : QUrl();
|
||||
}
|
||||
|
||||
QString KUrlNavigatorPlacesSelector::selectedPlaceText() const
|
||||
{
|
||||
const QModelIndex index = m_placesModel->index(m_selectedItem, 0);
|
||||
return index.isValid() ? m_placesModel->text(index) : QString();
|
||||
}
|
||||
|
||||
QSize KUrlNavigatorPlacesSelector::sizeHint() const
|
||||
{
|
||||
const int height = KUrlNavigatorButtonBase::sizeHint().height();
|
||||
return QSize(height, height);
|
||||
}
|
||||
|
||||
void KUrlNavigatorPlacesSelector::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
QPainter painter(this);
|
||||
drawHoverBackground(&painter);
|
||||
|
||||
// draw icon
|
||||
const QPixmap pixmap = icon().pixmap(QSize(22, 22).expandedTo(iconSize()), QIcon::Normal);
|
||||
style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, pixmap);
|
||||
}
|
||||
|
||||
void KUrlNavigatorPlacesSelector::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if (event->mimeData()->hasUrls()) {
|
||||
setDisplayHintEnabled(DraggedHint, true);
|
||||
event->acceptProposedAction();
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorPlacesSelector::dragLeaveEvent(QDragLeaveEvent *event)
|
||||
{
|
||||
KUrlNavigatorButtonBase::dragLeaveEvent(event);
|
||||
|
||||
setDisplayHintEnabled(DraggedHint, false);
|
||||
update();
|
||||
}
|
||||
|
||||
void KUrlNavigatorPlacesSelector::dropEvent(QDropEvent *event)
|
||||
{
|
||||
setDisplayHintEnabled(DraggedHint, false);
|
||||
update();
|
||||
|
||||
QMimeDatabase db;
|
||||
const QList<QUrl> urlList = KUrlMimeData::urlsFromMimeData(event->mimeData());
|
||||
for (const QUrl &url : urlList) {
|
||||
QMimeType mimetype = db.mimeTypeForUrl(url);
|
||||
if (mimetype.inherits(QStringLiteral("inode/directory"))) {
|
||||
m_placesModel->addPlace(url.fileName(), url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorPlacesSelector::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::MiddleButton && geometry().contains(event->pos())) {
|
||||
Q_EMIT tabRequested(KFilePlacesModel::convertedUrl(m_placesModel->url(m_placesModel->index(m_selectedItem, 0))));
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
KUrlNavigatorButtonBase::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void KUrlNavigatorPlacesSelector::activatePlace(QAction *action, ActivationSignal activationSignal)
|
||||
{
|
||||
Q_ASSERT(action != nullptr);
|
||||
if (action->data().toString() == QLatin1String("teardownAction")) {
|
||||
QModelIndex index = m_placesModel->index(m_selectedItem, 0);
|
||||
m_placesModel->requestTeardown(index);
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndex index = m_placesModel->index(action->data().toInt(), 0);
|
||||
|
||||
m_lastClickedIndex = QPersistentModelIndex();
|
||||
m_lastActivationSignal = nullptr;
|
||||
|
||||
if (m_placesModel->setupNeeded(index)) {
|
||||
connect(m_placesModel, &KFilePlacesModel::setupDone, this, &KUrlNavigatorPlacesSelector::onStorageSetupDone);
|
||||
|
||||
m_lastClickedIndex = index;
|
||||
m_lastActivationSignal = activationSignal;
|
||||
m_placesModel->requestSetup(index);
|
||||
return;
|
||||
} else if (index.isValid()) {
|
||||
if (activationSignal == &KUrlNavigatorPlacesSelector::placeActivated) {
|
||||
m_selectedItem = index.row();
|
||||
setIcon(m_placesModel->icon(index));
|
||||
}
|
||||
|
||||
const QUrl url = KFilePlacesModel::convertedUrl(m_placesModel->url(index));
|
||||
/*Q_EMIT*/ std::invoke(activationSignal, this, url);
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorPlacesSelector::onStorageSetupDone(const QModelIndex &index, bool success)
|
||||
{
|
||||
disconnect(m_placesModel, &KFilePlacesModel::setupDone, this, &KUrlNavigatorPlacesSelector::onStorageSetupDone);
|
||||
|
||||
if (m_lastClickedIndex == index) {
|
||||
if (success) {
|
||||
if (m_lastActivationSignal == &KUrlNavigatorPlacesSelector::placeActivated) {
|
||||
m_selectedItem = index.row();
|
||||
setIcon(m_placesModel->icon(index));
|
||||
}
|
||||
|
||||
const QUrl url = KFilePlacesModel::convertedUrl(m_placesModel->url(index));
|
||||
/*Q_EMIT*/ std::invoke(m_lastActivationSignal, this, url);
|
||||
}
|
||||
m_lastClickedIndex = QPersistentModelIndex();
|
||||
m_lastActivationSignal = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool KUrlNavigatorPlacesSelector::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (auto *menu = qobject_cast<QMenu *>(watched)) {
|
||||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
||||
if (me->button() == Qt::MiddleButton) {
|
||||
if (QAction *action = menu->activeAction()) {
|
||||
m_placesMenu->close(); // always close top menu
|
||||
|
||||
activatePlace(action, &KUrlNavigatorPlacesSelector::tabRequested);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return KUrlNavigatorButtonBase::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#include "moc_kurlnavigatorplacesselector_p.cpp"
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KURLNAVIGATORPLACESSELECTOR_P_H
|
||||
#define KURLNAVIGATORPLACESSELECTOR_P_H
|
||||
|
||||
#include "kurlnavigatorbuttonbase_p.h"
|
||||
#include <QUrl>
|
||||
|
||||
#include <QPersistentModelIndex>
|
||||
|
||||
class KFilePlacesModel;
|
||||
class QMenu;
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
/**
|
||||
* @brief Allows to select a bookmark from a popup menu.
|
||||
*
|
||||
* The icon from the current selected bookmark is shown
|
||||
* inside the bookmark selector.
|
||||
*
|
||||
* @see KUrlNavigator
|
||||
* @internal
|
||||
*/
|
||||
class KUrlNavigatorPlacesSelector : public KUrlNavigatorButtonBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param parent Parent widget where the bookmark selector
|
||||
* is embedded into.
|
||||
*/
|
||||
KUrlNavigatorPlacesSelector(KUrlNavigator *parent, KFilePlacesModel *placesModel);
|
||||
|
||||
~KUrlNavigatorPlacesSelector() override;
|
||||
|
||||
using ActivationSignal = void (KUrlNavigatorPlacesSelector::*)(const QUrl &);
|
||||
|
||||
/**
|
||||
* Updates the selection dependent from the given URL \a url. The
|
||||
* URL must not match exactly to one of the available bookmarks:
|
||||
* The bookmark which is equal to the URL or at least is a parent URL
|
||||
* is selected. If there are more than one possible parent URL candidates,
|
||||
* the bookmark which covers the bigger range of the URL is selected.
|
||||
*/
|
||||
void updateSelection(const QUrl &url);
|
||||
|
||||
/** Returns the selected bookmark. */
|
||||
QUrl selectedPlaceUrl() const;
|
||||
/** Returns the selected bookmark. */
|
||||
QString selectedPlaceText() const;
|
||||
|
||||
/** @see QWidget::sizeHint() */
|
||||
QSize sizeHint() const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Is send when a bookmark has been activated by the user.
|
||||
* @param url URL of the selected place.
|
||||
*/
|
||||
void placeActivated(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Is sent when a bookmark was middle clicked by the user
|
||||
* and thus should be opened in a new tab.
|
||||
*/
|
||||
void tabRequested(const QUrl &url);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Draws the icon of the selected Url as content of the Url
|
||||
* selector.
|
||||
*/
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void dragLeaveEvent(QDragLeaveEvent *event) override;
|
||||
void dropEvent(QDropEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
/**
|
||||
* Updates the selected index and the icon to the bookmark
|
||||
* which is indicated by the triggered action \a action.
|
||||
*/
|
||||
void activatePlace(QAction *action, ActivationSignal activationSignal);
|
||||
|
||||
void updateMenu();
|
||||
|
||||
void onStorageSetupDone(const QModelIndex &index, bool success);
|
||||
|
||||
private:
|
||||
int m_selectedItem;
|
||||
QPersistentModelIndex m_lastClickedIndex;
|
||||
ActivationSignal m_lastActivationSignal = nullptr;
|
||||
QMenu *m_placesMenu;
|
||||
KFilePlacesModel *m_placesModel;
|
||||
QUrl m_selectedUrl;
|
||||
};
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
|
||||
SPDX-FileCopyrightText: 2009 Peter Penz <peter.penz@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kurlnavigatorschemecombo_p.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QStyleOption>
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <kprotocolinfo.h>
|
||||
#include <kprotocolmanager.h>
|
||||
#include <kurlnavigator.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
const int ArrowSize = 10;
|
||||
}
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
KUrlNavigatorSchemeCombo::KUrlNavigatorSchemeCombo(const QString &scheme, KUrlNavigator *parent)
|
||||
: KUrlNavigatorButtonBase(parent)
|
||||
, m_menu(nullptr)
|
||||
, m_schemes()
|
||||
, m_categories()
|
||||
{
|
||||
m_menu = new QMenu(this);
|
||||
connect(m_menu, &QMenu::triggered, this, &KUrlNavigatorSchemeCombo::setSchemeFromMenu);
|
||||
setText(scheme);
|
||||
setMenu(m_menu);
|
||||
}
|
||||
|
||||
void KUrlNavigatorSchemeCombo::setSupportedSchemes(const QStringList &schemes)
|
||||
{
|
||||
m_schemes = schemes;
|
||||
m_menu->clear();
|
||||
|
||||
for (const QString &scheme : schemes) {
|
||||
QAction *action = m_menu->addAction(scheme);
|
||||
action->setData(scheme);
|
||||
}
|
||||
}
|
||||
|
||||
QSize KUrlNavigatorSchemeCombo::sizeHint() const
|
||||
{
|
||||
const QSize size = KUrlNavigatorButtonBase::sizeHint();
|
||||
|
||||
int width = fontMetrics().boundingRect(KLocalizedString::removeAcceleratorMarker(text())).width();
|
||||
width += (3 * BorderWidth) + ArrowSize;
|
||||
|
||||
return QSize(width, size.height());
|
||||
}
|
||||
|
||||
void KUrlNavigatorSchemeCombo::setScheme(const QString &scheme)
|
||||
{
|
||||
setText(scheme);
|
||||
}
|
||||
|
||||
QString KUrlNavigatorSchemeCombo::currentScheme() const
|
||||
{
|
||||
return text();
|
||||
}
|
||||
|
||||
void KUrlNavigatorSchemeCombo::showEvent(QShowEvent *event)
|
||||
{
|
||||
KUrlNavigatorButtonBase::showEvent(event);
|
||||
if (!event->spontaneous() && m_schemes.isEmpty()) {
|
||||
m_schemes = KProtocolInfo::protocols();
|
||||
|
||||
auto it = std::remove_if(m_schemes.begin(), m_schemes.end(), [](const QString &s) {
|
||||
QUrl url;
|
||||
url.setScheme(s);
|
||||
return !KProtocolManager::supportsListing(url);
|
||||
});
|
||||
m_schemes.erase(it, m_schemes.end());
|
||||
|
||||
std::sort(m_schemes.begin(), m_schemes.end());
|
||||
|
||||
updateMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorSchemeCombo::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
QPainter painter(this);
|
||||
const int buttonWidth = width();
|
||||
const int buttonHeight = height();
|
||||
|
||||
drawHoverBackground(&painter);
|
||||
|
||||
const QColor fgColor = foregroundColor();
|
||||
painter.setPen(fgColor);
|
||||
|
||||
// draw arrow
|
||||
const int arrowX = buttonWidth - ArrowSize - BorderWidth;
|
||||
const int arrowY = (buttonHeight - ArrowSize) / 2;
|
||||
|
||||
QStyleOption option;
|
||||
option.rect = QRect(arrowX, arrowY, ArrowSize, ArrowSize);
|
||||
option.palette = palette();
|
||||
option.palette.setColor(QPalette::Text, fgColor);
|
||||
option.palette.setColor(QPalette::WindowText, fgColor);
|
||||
option.palette.setColor(QPalette::ButtonText, fgColor);
|
||||
style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &option, &painter, this);
|
||||
|
||||
// draw text
|
||||
const int textWidth = arrowX - (2 * BorderWidth);
|
||||
int alignment = Qt::AlignCenter | Qt::TextShowMnemonic;
|
||||
if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &option, this)) {
|
||||
alignment |= Qt::TextHideMnemonic;
|
||||
}
|
||||
style()->drawItemText(&painter, QRect(BorderWidth, 0, textWidth, buttonHeight), alignment, option.palette, isEnabled(), text());
|
||||
}
|
||||
|
||||
void KUrlNavigatorSchemeCombo::setSchemeFromMenu(QAction *action)
|
||||
{
|
||||
const QString scheme = action->data().toString();
|
||||
setText(scheme);
|
||||
Q_EMIT activated(scheme);
|
||||
}
|
||||
|
||||
void KUrlNavigatorSchemeCombo::updateMenu()
|
||||
{
|
||||
initializeCategories();
|
||||
std::sort(m_schemes.begin(), m_schemes.end());
|
||||
|
||||
// move all schemes into the corresponding category of 'items'
|
||||
QList<QString> items[CategoryCount];
|
||||
for (const QString &scheme : std::as_const(m_schemes)) {
|
||||
if (m_categories.contains(scheme)) {
|
||||
const SchemeCategory category = m_categories.value(scheme);
|
||||
items[category].append(scheme);
|
||||
} else {
|
||||
items[OtherCategory].append(scheme);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the menu that includes all entries from 'items'. The categories
|
||||
// CoreCategory and PlacesCategory are placed at the top level, the remaining
|
||||
// categories are placed in sub menus.
|
||||
QMenu *menu = m_menu;
|
||||
for (int category = 0; category < CategoryCount; ++category) {
|
||||
if (!items[category].isEmpty()) {
|
||||
switch (category) {
|
||||
case DevicesCategory:
|
||||
menu = m_menu->addMenu(i18nc("@item:inmenu", "Devices"));
|
||||
break;
|
||||
|
||||
case SubversionCategory:
|
||||
menu = m_menu->addMenu(i18nc("@item:inmenu", "Subversion"));
|
||||
break;
|
||||
|
||||
case OtherCategory:
|
||||
menu = m_menu->addMenu(i18nc("@item:inmenu", "Other"));
|
||||
break;
|
||||
|
||||
case CoreCategory:
|
||||
case PlacesCategory:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (const QString &scheme : std::as_const(items[category])) {
|
||||
QAction *action = menu->addAction(scheme);
|
||||
action->setData(scheme);
|
||||
}
|
||||
|
||||
if (menu == m_menu) {
|
||||
menu->addSeparator();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorSchemeCombo::initializeCategories()
|
||||
{
|
||||
if (m_categories.isEmpty()) {
|
||||
m_categories.insert(QStringLiteral("file"), CoreCategory);
|
||||
m_categories.insert(QStringLiteral("ftp"), CoreCategory);
|
||||
m_categories.insert(QStringLiteral("fish"), CoreCategory);
|
||||
m_categories.insert(QStringLiteral("nfs"), CoreCategory);
|
||||
m_categories.insert(QStringLiteral("sftp"), CoreCategory);
|
||||
m_categories.insert(QStringLiteral("smb"), CoreCategory);
|
||||
m_categories.insert(QStringLiteral("webdav"), CoreCategory);
|
||||
|
||||
m_categories.insert(QStringLiteral("desktop"), PlacesCategory);
|
||||
m_categories.insert(QStringLiteral("fonts"), PlacesCategory);
|
||||
m_categories.insert(QStringLiteral("programs"), PlacesCategory);
|
||||
m_categories.insert(QStringLiteral("settings"), PlacesCategory);
|
||||
m_categories.insert(QStringLiteral("trash"), PlacesCategory);
|
||||
|
||||
m_categories.insert(QStringLiteral("floppy"), DevicesCategory);
|
||||
m_categories.insert(QStringLiteral("camera"), DevicesCategory);
|
||||
m_categories.insert(QStringLiteral("remote"), DevicesCategory);
|
||||
|
||||
m_categories.insert(QStringLiteral("svn"), SubversionCategory);
|
||||
m_categories.insert(QStringLiteral("svn+file"), SubversionCategory);
|
||||
m_categories.insert(QStringLiteral("svn+http"), SubversionCategory);
|
||||
m_categories.insert(QStringLiteral("svn+https"), SubversionCategory);
|
||||
m_categories.insert(QStringLiteral("svn+ssh"), SubversionCategory);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#include "moc_kurlnavigatorschemecombo_p.cpp"
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
|
||||
SPDX-FileCopyrightText: 2009 Peter Penz <peter.penz@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KURLNAVIGATORSCHEMECOMBO_P_H
|
||||
#define KURLNAVIGATORSCHEMECOMBO_P_H
|
||||
|
||||
#include "kurlnavigatorbuttonbase_p.h"
|
||||
|
||||
#include <QHash>
|
||||
|
||||
class QMenu;
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
/**
|
||||
* @brief A combobox listing available schemes.
|
||||
*
|
||||
* The widget is used by the URL navigator for offering the available
|
||||
* schemes for non-local URLs.
|
||||
*
|
||||
* @see KUrlNavigator
|
||||
*/
|
||||
class KUrlNavigatorSchemeCombo : public KUrlNavigatorButtonBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KUrlNavigatorSchemeCombo(const QString &scheme, KUrlNavigator *parent = nullptr);
|
||||
|
||||
QString currentScheme() const;
|
||||
|
||||
void setSupportedSchemes(const QStringList &schemes);
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void setScheme(const QString &scheme);
|
||||
|
||||
Q_SIGNALS:
|
||||
void activated(const QString &scheme);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void setSchemeFromMenu(QAction *action);
|
||||
|
||||
private:
|
||||
void updateMenu();
|
||||
void initializeCategories();
|
||||
|
||||
enum SchemeCategory {
|
||||
CoreCategory,
|
||||
PlacesCategory,
|
||||
DevicesCategory,
|
||||
SubversionCategory,
|
||||
OtherCategory,
|
||||
CategoryCount, // mandatory last entry
|
||||
};
|
||||
|
||||
QMenu *m_menu;
|
||||
QStringList m_schemes;
|
||||
QHash<QString, SchemeCategory> m_categories;
|
||||
};
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kurlnavigatortogglebutton_p.h"
|
||||
|
||||
#include <KIconLoader>
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
static constexpr int s_iconSize = KIconLoader::SizeSmallMedium;
|
||||
|
||||
KUrlNavigatorToggleButton::KUrlNavigatorToggleButton(KUrlNavigator *parent)
|
||||
: KUrlNavigatorButtonBase(parent)
|
||||
{
|
||||
setCheckable(true);
|
||||
connect(this, &QAbstractButton::toggled, this, &KUrlNavigatorToggleButton::updateToolTip);
|
||||
connect(this, &QAbstractButton::clicked, this, &KUrlNavigatorToggleButton::updateCursor);
|
||||
|
||||
#ifndef QT_NO_ACCESSIBILITY
|
||||
setAccessibleName(i18n("Edit mode"));
|
||||
#endif
|
||||
|
||||
updateToolTip();
|
||||
}
|
||||
|
||||
KUrlNavigatorToggleButton::~KUrlNavigatorToggleButton()
|
||||
{
|
||||
}
|
||||
|
||||
QSize KUrlNavigatorToggleButton::sizeHint() const
|
||||
{
|
||||
QSize size = KUrlNavigatorButtonBase::sizeHint();
|
||||
size.setWidth(qMax(s_iconSize, iconSize().width()) + 4);
|
||||
return size;
|
||||
}
|
||||
|
||||
void KUrlNavigatorToggleButton::enterEvent(QEnterEvent *event)
|
||||
{
|
||||
KUrlNavigatorButtonBase::enterEvent(event);
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
void KUrlNavigatorToggleButton::leaveEvent(QEvent *event)
|
||||
{
|
||||
KUrlNavigatorButtonBase::leaveEvent(event);
|
||||
setCursor(Qt::ArrowCursor);
|
||||
}
|
||||
|
||||
void KUrlNavigatorToggleButton::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.setClipRect(event->rect());
|
||||
|
||||
const int buttonWidth = width();
|
||||
const int buttonHeight = height();
|
||||
if (isChecked()) {
|
||||
drawHoverBackground(&painter);
|
||||
|
||||
if (m_pixmap.isNull() || m_pixmap.devicePixelRatioF() != devicePixelRatioF()) {
|
||||
const QSize tickIconSize = QSize(s_iconSize, s_iconSize).expandedTo(iconSize());
|
||||
m_pixmap = QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(tickIconSize, devicePixelRatioF());
|
||||
}
|
||||
style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, m_pixmap);
|
||||
} else if (isDisplayHintEnabled(EnteredHint)) {
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(palette().color(foregroundRole()));
|
||||
|
||||
const int verticalGap = 4;
|
||||
const int caretWidth = 2;
|
||||
const int x = (layoutDirection() == Qt::LeftToRight) ? 0 : buttonWidth - caretWidth;
|
||||
painter.drawRect(x, verticalGap, caretWidth, buttonHeight - 2 * verticalGap);
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorToggleButton::updateToolTip()
|
||||
{
|
||||
if (isChecked()) {
|
||||
setToolTip(i18n("Click for Location Navigation"));
|
||||
} else {
|
||||
setToolTip(i18n("Click to Edit Location"));
|
||||
}
|
||||
}
|
||||
|
||||
void KUrlNavigatorToggleButton::updateCursor()
|
||||
{
|
||||
setCursor(isChecked() ? Qt::ArrowCursor : Qt::IBeamCursor);
|
||||
}
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#include "moc_kurlnavigatortogglebutton_p.cpp"
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Peter Penz <peter.penz@gmx.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KURLNAVIGATORTOGGLEBUTTON_P_H
|
||||
#define KURLNAVIGATORTOGGLEBUTTON_P_H
|
||||
|
||||
#include "kurlnavigatorbuttonbase_p.h"
|
||||
#include <QPixmap>
|
||||
|
||||
namespace KDEPrivate
|
||||
{
|
||||
/**
|
||||
* @brief Represents the button of the URL navigator to switch to
|
||||
* the editable mode.
|
||||
*
|
||||
* A cursor is shown when hovering the button.
|
||||
*/
|
||||
class KUrlNavigatorToggleButton : public KUrlNavigatorButtonBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KUrlNavigatorToggleButton(KUrlNavigator *parent);
|
||||
~KUrlNavigatorToggleButton() override;
|
||||
|
||||
/** @see QWidget::sizeHint() */
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
void enterEvent(QEnterEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateToolTip();
|
||||
void updateCursor();
|
||||
|
||||
private:
|
||||
QPixmap m_pixmap;
|
||||
};
|
||||
|
||||
} // namespace KDEPrivate
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user