dolphinmainwindow.cpp
dolphinviewcontainer.cpp
dolphincontextmenu.cpp
+ dolphinnavigatorswidgetaction.cpp
dolphintabbar.cpp
dolphinplacesmodelsingleton.cpp
dolphinrecenttabsmenu.cpp
statusbar/mountpointobservercache.cpp
statusbar/spaceinfoobserver.cpp
statusbar/statusbarspaceinfo.cpp
- views/dolphinurlnavigatorwidgetaction.cpp
views/zoomlevelinfo.cpp
dolphindebug.cpp
global.cpp
#include "views/draganddrophelper.h"
#include "views/viewproperties.h"
#include "views/dolphinnewfilemenuobserver.h"
-#include "views/dolphinurlnavigatorwidgetaction.h"
+#include "dolphinnavigatorswidgetaction.h"
#include "dolphin_generalsettings.h"
#include <KActionCollection>
#include <KJobWidgets>
#include <KLocalizedString>
#include <KMessageBox>
-#include <KMessageWidget>
#include <KNS3/KMoreToolsMenuFactory>
#include <KProtocolInfo>
#include <KProtocolManager>
m_placesPanel(nullptr),
m_tearDownFromPlacesRequested(false),
m_backAction(nullptr),
- m_forwardAction(nullptr)
+ m_forwardAction(nullptr),
+ m_updateHistoryConnection{}
{
Q_INIT_RESOURCE(dolphin);
setAcceptDrops(true);
- m_tabWidget = new DolphinTabWidget(this);
+ auto *navigatorsWidgetAction = new DolphinNavigatorsWidgetAction(this);
+ navigatorsWidgetAction->setText(i18nc(
+ "@action:inmenu When split view is enabled there are two otherwise one.",
+ "Url Navigator(s)"));
+ actionCollection()->addAction(QStringLiteral("url_navigators"), navigatorsWidgetAction);
+ m_tabWidget = new DolphinTabWidget(navigatorsWidgetAction, this);
m_tabWidget->setObjectName("tabWidget");
connect(m_tabWidget, &DolphinTabWidget::activeViewChanged,
this, &DolphinMainWindow::activeViewChanged);
setupGUI(Keys | Save | Create | ToolBar);
stateChanged(QStringLiteral("new_file"));
+ toolBar()->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
+ toolBar()->setFloatable(false);
+ if (!toolBar()->actions().contains(navigatorsWidgetAction)) {
+ navigatorsWidgetAction->addToToolbarAndSave(this);
+ }
QClipboard* clipboard = QApplication::clipboard();
connect(clipboard, &QClipboard::dataChanged,
this, &DolphinMainWindow::updatePasteAction);
m_activeViewContainer->setFilterBarVisible(true);
}
-void DolphinMainWindow::toggleLocationInToolbar()
-{
- // collect needed variables
- QAction *locationInToolbarAction = actionCollection()->action(QStringLiteral("location_in_toolbar"));
- const bool locationInToolbar = locationInToolbarAction->isChecked();
- auto viewContainers = this->viewContainers();
- auto urlNavigatorWidgetAction = static_cast<DolphinUrlNavigatorWidgetAction *>
- (actionCollection()->action(QStringLiteral("url_navigator")));
- const bool isEditable = m_activeViewContainer->urlNavigator()->isUrlEditable();
- const QLineEdit *lineEdit = m_activeViewContainer->urlNavigator()->editor()->lineEdit();
- const bool hasFocus = lineEdit->hasFocus();
- const int cursorPosition = lineEdit->cursorPosition();
- const int selectionStart = lineEdit->selectionStart();
- const int selectionLength = lineEdit->selectionLength();
-
- if (locationInToolbar && !toolBar()->actions().contains(urlNavigatorWidgetAction)) {
- // There is no UrlNavigator on the toolbar. Try to fix it. Otherwise show an error.
- if (!urlNavigatorWidgetAction->addToToolbarAndSave(this)) {
- QAction *configureToolbars = actionCollection()->action(KStandardAction::name(KStandardAction::ConfigureToolbars));
- KMessageWidget *messageWidget = m_activeViewContainer->showMessage(
- xi18nc("@info 2 is the visible text on a button just below the message",
- "The location could not be moved onto the toolbar because there is currently "
- "no \"%1\" item on the toolbar. Select <interface>%2</interface> and add the "
- "\"%1\" item. Then this will work.", urlNavigatorWidgetAction->iconText(),
- configureToolbars->iconText()), DolphinViewContainer::Information);
- messageWidget->addAction(configureToolbars);
- messageWidget->addAction(locationInToolbarAction);
- locationInToolbarAction->setChecked(false);
- return;
- }
- }
-
- // do the switching
- GeneralSettings::setLocationInToolbar(locationInToolbar);
- if (locationInToolbar) {
- for (const auto viewContainer : viewContainers) {
- viewContainer->disconnectUrlNavigator();
- }
- m_activeViewContainer->connectUrlNavigator(urlNavigatorWidgetAction->urlNavigator());
- } else {
- m_activeViewContainer->disconnectUrlNavigator();
- for (const auto viewContainer : viewContainers) {
- viewContainer->connectToInternalUrlNavigator();
- }
- }
-
- urlNavigatorWidgetAction->setUrlNavigatorVisible(locationInToolbar);
- m_activeViewContainer->urlNavigator()->setUrlEditable(isEditable);
- if (hasFocus) { // the rest of this method is unneeded perfectionism
- m_activeViewContainer->urlNavigator()->editor()->lineEdit()->setText(lineEdit->text());
- m_activeViewContainer->urlNavigator()->editor()->lineEdit()->setFocus();
- m_activeViewContainer->urlNavigator()->editor()->lineEdit()->setCursorPosition(cursorPosition);
- if (selectionStart != -1) {
- m_activeViewContainer->urlNavigator()->editor()->lineEdit()->setSelection(selectionStart, selectionLength);
- }
- }
-}
-
void DolphinMainWindow::toggleEditLocation()
{
clearStatusBar();
// view and url navigator) and main window.
oldViewContainer->disconnect(this);
oldViewContainer->view()->disconnect(this);
- oldViewContainer->urlNavigator()->disconnect(this);
- if (GeneralSettings::locationInToolbar()) {
- oldViewContainer->disconnectUrlNavigator();
+ auto navigators = static_cast<DolphinNavigatorsWidgetAction *>
+ (actionCollection()->action(QStringLiteral("url_navigators")));
+ navigators->primaryUrlNavigator()->disconnect(this);
+ if (auto secondaryUrlNavigator = navigators->secondaryUrlNavigator()) {
+ secondaryUrlNavigator->disconnect(this);
}
// except the requestItemInfo so that on hover the information panel can still be updated
this, &DolphinMainWindow::requestItemInfo);
}
- if (GeneralSettings::locationInToolbar()) {
- viewContainer->connectUrlNavigator(static_cast<DolphinUrlNavigatorWidgetAction *>
- (actionCollection()->action(QStringLiteral("url_navigator")))->urlNavigator());
- }
connectViewSignals(viewContainer);
m_actionHandler->setCurrentView(viewContainer->view());
stop->setIcon(QIcon::fromTheme(QStringLiteral("process-stop")));
connect(stop, &QAction::triggered, this, &DolphinMainWindow::stopLoading);
- KToggleAction* locationInToolbar = actionCollection()->add<KToggleAction>(QStringLiteral("location_in_toolbar"));
- locationInToolbar->setText(i18nc("@action:inmenu Navigation Bar", "Location in Toolbar"));
- locationInToolbar->setWhatsThis(xi18nc("@info:whatsthis",
- "This toggles between showing the <emphasis>path</emphasis> in the "
- "<emphasis>Location Bar</emphasis> and in the <emphasis>Toolbar</emphasis>."));
- actionCollection()->setDefaultShortcut(locationInToolbar, Qt::Key_F12);
- locationInToolbar->setChecked(GeneralSettings::locationInToolbar());
- connect(locationInToolbar, &KToggleAction::triggered, this, &DolphinMainWindow::toggleLocationInToolbar);
- DolphinUrlNavigator::addToContextMenu(locationInToolbar);
-
KToggleAction* editableLocation = actionCollection()->add<KToggleAction>(QStringLiteral("editable_location"));
editableLocation->setText(i18nc("@action:inmenu Navigation Bar", "Editable Location"));
editableLocation->setWhatsThis(xi18nc("@info:whatsthis",
connect(activatePrevTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activatePrevTab);
actionCollection()->setDefaultShortcuts(activatePrevTab, prevTabKeys);
- auto *urlNavigatorWidgetAction = new DolphinUrlNavigatorWidgetAction(this);
- urlNavigatorWidgetAction->setText(i18nc("@action:inmenu auto-hide: "
- "Depending on the settings this Widget is blank/invisible.",
- "Url Navigator (auto-hide)"));
- actionCollection()->addAction(QStringLiteral("url_navigator"), urlNavigatorWidgetAction);
-
// for context menu
QAction* showTarget = actionCollection()->addAction(QStringLiteral("show_target"));
showTarget->setText(i18nc("@action:inmenu", "Show Target"));
showFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible());
updateSplitAction();
-
- QAction* editableLocactionAction = actionCollection()->action(QStringLiteral("editable_location"));
- const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator();
- editableLocactionAction->setChecked(urlNavigator->isUrlEditable());
}
void DolphinMainWindow::updateGoActions()
connect(view, &DolphinView::goUpRequested,
this, &DolphinMainWindow::goUp);
- const KUrlNavigator* navigator = container->urlNavigator();
+ auto navigators = static_cast<DolphinNavigatorsWidgetAction *>
+ (actionCollection()->action(QStringLiteral("url_navigators")));
+ KUrlNavigator *navigator = m_tabWidget->currentTabPage()->primaryViewActive() ?
+ navigators->primaryUrlNavigator() :
+ navigators->secondaryUrlNavigator();
+
connect(navigator, &KUrlNavigator::urlChanged,
this, &DolphinMainWindow::changeUrl);
+ QAction* editableLocactionAction = actionCollection()->action(QStringLiteral("editable_location"));
+ editableLocactionAction->setChecked(navigator->isUrlEditable());
connect(navigator, &KUrlNavigator::editableStateChanged,
this, &DolphinMainWindow::slotEditableStateChanged);
connect(navigator, &KUrlNavigator::tabRequested,
this, &DolphinMainWindow::openNewTabAfterLastTab);
- connect(container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged,
+ disconnect(m_updateHistoryConnection);
+ m_updateHistoryConnection = connect(
+ container->urlNavigatorInternalWithHistory(), &KUrlNavigator::historyChanged,
this, &DolphinMainWindow::updateHistory);
}
/**
* Returns the active view containers of all tabs.
- * @see activeViewContainer()
- * Use viewContainers() to also include the inactive ones.
*/
QVector<DolphinViewContainer *> activeViewContainers() const;
- /**
- * Returns all view containers.
- */
- QVector<DolphinViewContainer *> viewContainers() const;
-
/**
* Opens each directory in \p dirs in a separate tab. If \a splitView is set,
* 2 directories are collected within one tab.
void showFilterBar();
- /**
- * Toggle between either using an UrlNavigator in the toolbar or the
- * ones in the location bar for navigating.
- */
- void toggleLocationInToolbar();
-
/**
* Toggles between edit and browse mode of the navigation bar.
*/
KToolBarPopupAction* m_backAction;
KToolBarPopupAction* m_forwardAction;
+ /** Makes sure that only one object is ever connected to the history. */
+ QMetaObject::Connection m_updateHistoryConnection;
+
QMenu m_searchTools;
};
--- /dev/null
+/*
+ This file is part of the KDE project
+ SPDX-FileCopyrightText: 2020 Felix Ernst <fe.a.ernst@gmail.com>
+
+ SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
+*/
+
+#include "dolphinnavigatorswidgetaction.h"
+
+#include "trash/dolphintrash.h"
+
+#include <KLocalizedString>
+#include <KXMLGUIFactory>
+#include <KXmlGuiWindow>
+
+#include <QDomDocument>
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <QSplitter>
+
+DolphinNavigatorsWidgetAction::DolphinNavigatorsWidgetAction(QWidget *parent) :
+ QWidgetAction{parent},
+ m_splitter{new QSplitter(Qt::Horizontal)},
+ m_adjustSpacingTimer{new QTimer(this)},
+ m_globalXOfPrimary{-1},
+ m_widthOfPrimary{-1},
+ m_globalXOfSecondary{-1},
+ m_widthOfSecondary{-1}
+{
+ setText(i18nc("@action:inmenu", "Url navigator"));
+
+ m_splitter->setChildrenCollapsible(false);
+ setDefaultWidget(m_splitter.get());
+
+ m_splitter->addWidget(createNavigatorWidget(Primary));
+
+ m_adjustSpacingTimer->setInterval(100);
+ m_adjustSpacingTimer->setSingleShot(true);
+ connect(m_adjustSpacingTimer.get(), &QTimer::timeout,
+ this, &DolphinNavigatorsWidgetAction::adjustSpacing);
+}
+
+bool DolphinNavigatorsWidgetAction::addToToolbarAndSave(KXmlGuiWindow *mainWindow)
+{
+ const QString rawXml = KXMLGUIFactory::readConfigFile(mainWindow->xmlFile());
+ QDomDocument domDocument;
+ if (rawXml.isEmpty() || !domDocument.setContent(rawXml) || domDocument.isNull()) {
+ return false;
+ }
+ QDomNode toolbar = domDocument.elementsByTagName(QStringLiteral("ToolBar")).at(0);
+ if (toolbar.isNull()) {
+ return false;
+ }
+
+ QDomElement urlNavigatorElement = domDocument.createElement(QStringLiteral("Action"));
+ urlNavigatorElement.setAttribute(QStringLiteral("name"), QStringLiteral("url_navigators"));
+
+ QDomNode position = toolbar.lastChildElement(QStringLiteral("Spacer"));
+ if (position.isNull()) {
+ toolbar.appendChild(urlNavigatorElement);
+ } else {
+ toolbar.replaceChild(urlNavigatorElement, position);
+ }
+
+ KXMLGUIFactory::saveConfigFile(domDocument, mainWindow->xmlFile());
+ mainWindow->reloadXML();
+ mainWindow->createGUI();
+ return true;
+}
+
+void DolphinNavigatorsWidgetAction::createSecondaryUrlNavigator()
+{
+ Q_ASSERT(m_splitter->count() == 1);
+ m_splitter->addWidget(createNavigatorWidget(Secondary));
+ Q_ASSERT(m_splitter->count() == 2);
+}
+
+void DolphinNavigatorsWidgetAction::followViewContainerGeometry(
+ int globalXOfPrimary, int widthOfPrimary)
+{
+ followViewContainersGeometry(globalXOfPrimary, widthOfPrimary, -1, -1);
+}
+
+void DolphinNavigatorsWidgetAction::followViewContainersGeometry(
+ int globalXOfPrimary, int widthOfPrimary,
+ int globalXOfSecondary, int widthOfSecondary)
+{
+ m_globalXOfSplitter = m_splitter->mapToGlobal(QPoint(0,0)).x();
+ m_globalXOfPrimary = globalXOfPrimary;
+ m_widthOfPrimary = widthOfPrimary;
+ m_globalXOfSecondary = globalXOfSecondary;
+ m_widthOfSecondary = widthOfSecondary;
+ adjustSpacing();
+}
+
+DolphinUrlNavigator* DolphinNavigatorsWidgetAction::primaryUrlNavigator() const
+{
+ Q_ASSERT(m_splitter);
+ return static_cast<DolphinUrlNavigator *>(m_splitter->widget(0)->findChild<KUrlNavigator *>());
+}
+
+DolphinUrlNavigator* DolphinNavigatorsWidgetAction::secondaryUrlNavigator() const
+{
+ Q_ASSERT(m_splitter);
+ if (m_splitter->count() < 2) {
+ return nullptr;
+ }
+ return static_cast<DolphinUrlNavigator *>(m_splitter->widget(1)->findChild<KUrlNavigator *>());
+}
+
+void DolphinNavigatorsWidgetAction::setSecondaryNavigatorVisible(bool visible)
+{
+ if (visible) {
+ Q_ASSERT(m_splitter->count() == 2);
+ m_splitter->widget(1)->setVisible(true);
+ } else if (m_splitter->count() > 1) {
+ m_splitter->widget(1)->setVisible(false);
+ // Fix an unlikely event of wrong trash button visibility.
+ emptyTrashButton(Secondary)->setVisible(false);
+ }
+}
+
+void DolphinNavigatorsWidgetAction::adjustSpacing()
+{
+ const int widthOfSplitterPrimary = m_globalXOfPrimary + m_widthOfPrimary - m_globalXOfSplitter;
+ const QList<int> splitterSizes = {widthOfSplitterPrimary,
+ m_splitter->width() - widthOfSplitterPrimary};
+ m_splitter->setSizes(splitterSizes);
+
+ // primary side of m_splitter
+ int leadingSpacing = m_globalXOfPrimary - m_globalXOfSplitter;
+ if (leadingSpacing < 0) {
+ leadingSpacing = 0;
+ }
+ int trailingSpacing = (m_globalXOfSplitter + m_splitter->width())
+ - (m_globalXOfPrimary + m_widthOfPrimary);
+ if (trailingSpacing < 0 || emptyTrashButton(Primary)->isVisible()) {
+ trailingSpacing = 0;
+ }
+ const int widthLeftForUrlNavigator = m_splitter->widget(0)->width() - leadingSpacing - trailingSpacing;
+ const int widthNeededForUrlNavigator = primaryUrlNavigator()->sizeHint().width() - widthLeftForUrlNavigator;
+ if (widthNeededForUrlNavigator > 0) {
+ trailingSpacing -= widthNeededForUrlNavigator;
+ if (trailingSpacing < 0) {
+ leadingSpacing += trailingSpacing;
+ trailingSpacing = 0;
+ }
+ if (leadingSpacing < 0) {
+ leadingSpacing = 0;
+ }
+ }
+ spacing(Primary, Leading)->setMinimumWidth(leadingSpacing);
+ spacing(Primary, Trailing)->setFixedWidth(trailingSpacing);
+
+ // secondary side of m_splitter
+ if (m_globalXOfSecondary == -1) {
+ Q_ASSERT(m_widthOfSecondary == -1);
+ return;
+ }
+ spacing(Primary, Trailing)->setFixedWidth(0);
+
+ trailingSpacing = (m_globalXOfSplitter + m_splitter->width())
+ - (m_globalXOfSecondary + m_widthOfSecondary);
+ if (trailingSpacing < 0 || emptyTrashButton(Secondary)->isVisible()) {
+ trailingSpacing = 0;
+ } else {
+ const int widthLeftForUrlNavigator2 = m_splitter->widget(1)->width() - trailingSpacing;
+ const int widthNeededForUrlNavigator2 = secondaryUrlNavigator()->sizeHint().width() - widthLeftForUrlNavigator2;
+ if (widthNeededForUrlNavigator2 > 0) {
+ trailingSpacing -= widthNeededForUrlNavigator2;
+ if (trailingSpacing < 0) {
+ trailingSpacing = 0;
+ }
+ }
+ }
+ spacing(Secondary, Trailing)->setMinimumWidth(trailingSpacing);
+}
+
+QWidget *DolphinNavigatorsWidgetAction::createNavigatorWidget(Side side) const
+{
+ auto navigatorWidget = new QWidget(m_splitter.get());
+ auto layout = new QHBoxLayout{navigatorWidget};
+ layout->setSpacing(0);
+ layout->setContentsMargins(0, 0, 0, 0);
+ if (side == Primary) {
+ auto leadingSpacing = new QWidget{navigatorWidget};
+ layout->addWidget(leadingSpacing);
+ }
+ auto urlNavigator = new DolphinUrlNavigator(navigatorWidget);
+ layout->addWidget(urlNavigator);
+
+ auto emptyTrashButton = newEmptyTrashButton(urlNavigator, navigatorWidget);
+ layout->addWidget(emptyTrashButton);
+
+ connect(urlNavigator, &KUrlNavigator::urlChanged, [this]() {
+ // We have to wait for DolphinUrlNavigator::sizeHint() to update which
+ // happens a little bit later than when urlChanged is emitted.
+ this->m_adjustSpacingTimer->start();
+ });
+
+ auto trailingSpacing = new QWidget{navigatorWidget};
+ layout->addWidget(trailingSpacing);
+ return navigatorWidget;
+}
+
+QPushButton * DolphinNavigatorsWidgetAction::emptyTrashButton(DolphinNavigatorsWidgetAction::Side side)
+{
+ int sideIndex = (side == Primary ? 0 : 1);
+ if (side == Primary) {
+ return static_cast<QPushButton *>(m_splitter->widget(sideIndex)->layout()->itemAt(2)->widget());
+ }
+ return static_cast<QPushButton *>(m_splitter->widget(sideIndex)->layout()->itemAt(1)->widget());
+}
+
+QPushButton *DolphinNavigatorsWidgetAction::newEmptyTrashButton(const DolphinUrlNavigator *urlNavigator, QWidget *parent) const
+{
+ auto emptyTrashButton = new QPushButton(QIcon::fromTheme(QStringLiteral("user-trash")),
+ i18nc("@action:button", "Empty Trash"), parent);
+ emptyTrashButton->setFlat(true);
+ connect(emptyTrashButton, &QPushButton::clicked,
+ this, [parent]() { Trash::empty(parent); });
+ connect(&Trash::instance(), &Trash::emptinessChanged,
+ emptyTrashButton, &QPushButton::setDisabled);
+ emptyTrashButton->hide();
+ connect(urlNavigator, &KUrlNavigator::urlChanged, [emptyTrashButton, urlNavigator]() {
+ emptyTrashButton->setVisible(urlNavigator->locationUrl().scheme() == QLatin1String("trash"));
+ });
+ emptyTrashButton->setDisabled(Trash::isEmpty());
+ return emptyTrashButton;
+}
+
+QWidget *DolphinNavigatorsWidgetAction::spacing(Side side, Position position) const
+{
+ int sideIndex = (side == Primary ? 0 : 1);
+ if (position == Leading) {
+ Q_ASSERT(side == Primary); // The secondary side of the splitter has no leading spacing.
+ return m_splitter->widget(sideIndex)->layout()->itemAt(0)->widget();
+ }
+ if (side == Primary) {
+ return m_splitter->widget(sideIndex)->layout()->itemAt(3)->widget();
+ }
+ return m_splitter->widget(sideIndex)->layout()->itemAt(2)->widget();
+}
--- /dev/null
+/*
+ This file is part of the KDE project
+ SPDX-FileCopyrightText: 2020 Felix Ernst <fe.a.ernst@gmail.com>
+
+ SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
+*/
+
+#ifndef DOLPHINNAVIGATORSWIDGETACTION_H
+#define DOLPHINNAVIGATORSWIDGETACTION_H
+
+#include "dolphinurlnavigator.h"
+
+#include <QSplitter>
+#include <QTimer>
+#include <QWidgetAction>
+
+#include <memory>
+
+class KXmlGuiWindow;
+class QPushButton;
+
+/**
+ * @brief QWidgetAction that allows to use DolphinUrlNavigators in a toolbar.
+ *
+ * This class is mainly a container that manages up to two DolphinUrlNavigator objects so they
+ * can be added to a toolbar. It also deals with alignment.
+ *
+ * The structure of the defaultWidget() of this QWidgetAction is as follows:
+ * - A QSplitter manages up to two sides which each correspond to one DolphinViewContainer.
+ * The secondary side only exists for split view and is created by
+ * createSecondaryUrlNavigator() when necessary.
+ * - Each side is a QWidget which I call NavigatorWidget with a QHBoxLayout.
+ * - Each NavigatorWidget consists an UrlNavigator, an emptyTrashButton and spacing.
+ * - Only the primary navigatorWidget has leading spacing. Both have trailing spacing.
+ * The spacing is there to align the UrlNavigator with its DolphinViewContainer.
+ */
+class DolphinNavigatorsWidgetAction : public QWidgetAction
+{
+ Q_OBJECT
+
+ /**
+ * In Left-to-right languages the Primary side will be the left one.
+ */
+ enum Side {
+ Primary,
+ Secondary
+ };
+
+public:
+ DolphinNavigatorsWidgetAction(QWidget *parent = nullptr);
+
+ /**
+ * Adds this action to the mainWindow's toolbar and saves the change
+ * in the users ui configuration file.
+ * @return true if successful. Otherwise false.
+ */
+ bool addToToolbarAndSave(KXmlGuiWindow *mainWindow);
+
+ /**
+ * Different to the primary UrlNavigator, the secondary UrlNavigator is only created on-demand.
+ */
+ void createSecondaryUrlNavigator();
+
+ /**
+ * Notify the primary UrlNavigator of changes in geometry of the ViewContainer it tries to be
+ * aligned with. Only call this method if there is no secondary UrlNavigator.
+ */
+ void followViewContainerGeometry(int globalXOfPrimary, int widthOfPrimary);
+ /**
+ * Notify this widget of changes in geometry of the ViewContainers it tries to be
+ * aligned with.
+ */
+ void followViewContainersGeometry(int globalXOfPrimary, int widthOfPrimary,
+ int globalXOfSecondary, int widthOfSecondary);
+
+ /**
+ * @return the primary UrlNavigator.
+ */
+ DolphinUrlNavigator *primaryUrlNavigator() const;
+ /**
+ * @return the secondary UrlNavigator and nullptr if it doesn't exist.
+ */
+ DolphinUrlNavigator *secondaryUrlNavigator() const;
+
+ /**
+ * Change the visibility of the secondary UrlNavigator including spacing.
+ * @param visible Setting this to false will completely hide the secondary side of this
+ * WidgetAction's QSplitter making the QSplitter effectively disappear.
+ */
+ void setSecondaryNavigatorVisible(bool visible);
+
+protected:
+ /**
+ * Adjusts the width of the spacings used to align the UrlNavigators with ViewContainers.
+ * This can only work nicely if up-to-date geometry of ViewContainers is cached so
+ * followViewContainersGeometry() has to have been called at least once before.
+ */
+ void adjustSpacing();
+
+ /**
+ * Used to create the navigatorWidgets for both sides of the QSplitter.
+ */
+ QWidget *createNavigatorWidget(Side side) const;
+
+ /**
+ * Used to retrieve the emptyTrashButtons for the navigatorWidgets on both sides.
+ */
+ QPushButton *emptyTrashButton(Side side);
+
+ /**
+ * Creates a new empty trash button.
+ * @param urlNavigator Only when this UrlNavigator shows the trash directory
+ * will the the button be visible.
+ * @param parent Aside from the usual QObject deletion mechanisms,
+ * this parameter influences the positioning of dialog windows
+ * pertaining to this trash button.
+ */
+ QPushButton *newEmptyTrashButton(const DolphinUrlNavigator *urlNavigator, QWidget *parent) const;
+
+ enum Position {
+ Leading,
+ Trailing
+ };
+ /**
+ * Used to retrieve both the leading and trailing spacing for the navigatorWidgets
+ * on both sides. A secondary leading spacing does not exist.
+ */
+ QWidget *spacing(Side side, Position position) const;
+
+ /**
+ * The defaultWidget() of this QWidgetAction.
+ */
+ std::unique_ptr<QSplitter> m_splitter;
+
+ /**
+ * adjustSpacing() has to be called slightly later than when urlChanged is emitted.
+ * This timer bridges that time.
+ */
+ std::unique_ptr<QTimer> m_adjustSpacingTimer;
+
+ // cached values
+ int m_globalXOfSplitter;
+ int m_globalXOfPrimary;
+ int m_widthOfPrimary;
+ int m_globalXOfSecondary;
+ int m_widthOfSecondary;
+};
+
+#endif // DOLPHINNAVIGATORSWIDGETACTION_H
#include "dolphin_generalsettings.h"
#include "dolphinviewcontainer.h"
+#include <QPropertyAnimation>
#include <QSplitter>
#include <QVBoxLayout>
m_splitter = new QSplitter(Qt::Horizontal, this);
m_splitter->setChildrenCollapsible(false);
+ connect(m_splitter, &QSplitter::splitterMoved,
+ this, &DolphinTabPage::splitterMoved);
layout->addWidget(m_splitter);
// Create a new primary view
this, &DolphinTabPage::slotViewUrlRedirection);
m_splitter->addWidget(m_primaryViewContainer);
+ m_primaryViewContainer->installEventFilter(this);
m_primaryViewContainer->show();
if (secondaryUrl.isValid() || GeneralSettings::splitView()) {
const QUrl& url = secondaryUrl.isValid() ? secondaryUrl : primaryUrl;
m_secondaryViewContainer = createViewContainer(url);
m_splitter->addWidget(m_secondaryViewContainer);
+ m_secondaryViewContainer->installEventFilter(this);
m_secondaryViewContainer->show();
}
m_splitViewEnabled = enabled;
if (enabled) {
+ int splitterTotalWidth = m_splitter->width();
const QUrl& url = (secondaryUrl.isEmpty()) ? m_primaryViewContainer->url() : secondaryUrl;
m_secondaryViewContainer = createViewContainer(url);
+ auto secondaryNavigator = m_navigatorsWidget->secondaryUrlNavigator();
+ if (!secondaryNavigator) {
+ m_navigatorsWidget->createSecondaryUrlNavigator();
+ secondaryNavigator = m_navigatorsWidget->secondaryUrlNavigator();
+ }
+ m_secondaryViewContainer->connectUrlNavigator(secondaryNavigator);
+ m_navigatorsWidget->setSecondaryNavigatorVisible(true);
+
m_splitter->addWidget(m_secondaryViewContainer);
- m_secondaryViewContainer->show();
+ m_secondaryViewContainer->installEventFilter(this);
m_secondaryViewContainer->setActive(true);
+
+ // opening animation
+ m_splitter->widget(1)->setMinimumWidth(1);
+ const QList<int> splitterSizes = {m_splitter->width(), 0};
+ m_splitter->setSizes(splitterSizes);
+
+ // TODO: This is only here to test the robustness of DolphinNavigatorsWidgetAction! I still have to move it to another merge request!
+ m_splitViewAnimation = new QVariantAnimation(m_splitter);
+ m_splitViewAnimation->setDuration(200); // TODO: where do I get the animation times from again?
+ m_splitViewAnimation->setStartValue(splitterTotalWidth);
+ m_splitViewAnimation->setEndValue(splitterTotalWidth / 2);
+ m_splitViewAnimation->setEasingCurve(QEasingCurve::OutCubic);
+
+ connect(m_splitViewAnimation, &QVariantAnimation::valueChanged, [=]() {
+ if (m_splitter->count() != 2) {
+ return;
+ }
+ int value = m_splitViewAnimation->currentValue().toInt();
+ const QList<int> splitterSizes = {value, m_splitter->width() - value};
+ m_splitter->setSizes(splitterSizes);
+ if (value == m_splitViewAnimation->endValue().toInt()) {
+ m_splitter->widget(1)->setMinimumWidth(m_splitter->widget(1)->minimumSizeHint().width());
+ }
+ });
+ m_splitViewAnimation->start(QAbstractAnimation::DeleteWhenStopped);
+ m_secondaryViewContainer->show();
} else {
+ m_navigatorsWidget->setSecondaryNavigatorVisible(false);
+ m_secondaryViewContainer->disconnectUrlNavigator();
+
DolphinViewContainer* view;
if (GeneralSettings::closeActiveSplitView()) {
view = activeViewContainer();
if (m_primaryViewActive) {
+ m_primaryViewContainer->disconnectUrlNavigator();
+ m_secondaryViewContainer->connectUrlNavigator(
+ m_navigatorsWidget->primaryUrlNavigator());
+
// If the primary view is active, we have to swap the pointers
// because the secondary view will be the new primary view.
qSwap(m_primaryViewContainer, m_secondaryViewContainer);
} else {
view = m_primaryViewActive ? m_secondaryViewContainer : m_primaryViewContainer;
if (!m_primaryViewActive) {
+ m_primaryViewContainer->disconnectUrlNavigator();
+ m_secondaryViewContainer->connectUrlNavigator(
+ m_navigatorsWidget->primaryUrlNavigator());
+
// If the secondary view is active, we have to swap the pointers
// because the secondary view will be the new primary view.
qSwap(m_primaryViewContainer, m_secondaryViewContainer);
m_primaryViewContainer->setActive(true);
view->close();
view->deleteLater();
+ if (m_splitViewAnimation) {
+ delete m_splitViewAnimation;
+ m_splitter->widget(0)->setMinimumWidth(m_splitter->widget(0)->minimumSizeHint().width());
+ }
}
}
}
return selectedItemsCount;
}
+void DolphinTabPage::connectNavigators(DolphinNavigatorsWidgetAction *navigatorsWidget)
+{
+ m_navigatorsWidget = navigatorsWidget;
+ auto primaryNavigator = navigatorsWidget->primaryUrlNavigator();
+ primaryNavigator->setActive(m_primaryViewActive);
+ m_primaryViewContainer->connectUrlNavigator(primaryNavigator);
+ if (m_splitViewEnabled) {
+ auto secondaryNavigator = navigatorsWidget->secondaryUrlNavigator();
+ if (!secondaryNavigator) {
+ navigatorsWidget->createSecondaryUrlNavigator();
+ secondaryNavigator = navigatorsWidget->secondaryUrlNavigator();
+ }
+ secondaryNavigator->setActive(!m_primaryViewActive);
+ m_secondaryViewContainer->connectUrlNavigator(secondaryNavigator);
+ }
+ resizeNavigators();
+}
+
+void DolphinTabPage::disconnectNavigators()
+{
+ m_navigatorsWidget = nullptr;
+ m_primaryViewContainer->disconnectUrlNavigator();
+ if (m_splitViewEnabled) {
+ m_secondaryViewContainer->disconnectUrlNavigator();
+ }
+}
+
+bool DolphinTabPage::eventFilter(QObject */* watched */, QEvent *event)
+{
+ if (event->type() == QEvent::Resize && m_navigatorsWidget) {
+ resizeNavigators();
+ }
+ return false;
+}
+
+void DolphinTabPage::resizeNavigators() const
+{
+ if (!m_splitViewEnabled) {
+ m_navigatorsWidget->followViewContainerGeometry(
+ m_primaryViewContainer->mapToGlobal(QPoint(0,0)).x(),
+ m_primaryViewContainer->width());
+ } else {
+ m_navigatorsWidget->followViewContainersGeometry(
+ m_primaryViewContainer->mapToGlobal(QPoint(0,0)).x(),
+ m_primaryViewContainer->width(),
+ m_secondaryViewContainer->mapToGlobal(QPoint(0,0)).x(),
+ m_secondaryViewContainer->width());
+ }
+}
+
void DolphinTabPage::markUrlsAsSelected(const QList<QUrl>& urls)
{
m_primaryViewContainer->view()->markUrlsAsSelected(urls);
stream >> m_primaryViewActive;
if (m_primaryViewActive) {
m_primaryViewContainer->setActive(true);
+ m_navigatorsWidget->primaryUrlNavigator()->setActive(true);
} else {
Q_ASSERT(m_splitViewEnabled);
m_secondaryViewContainer->setActive(true);
+ m_navigatorsWidget->primaryUrlNavigator()->setActive(false);
}
+ if (m_splitViewAnimation) {
+ delete m_splitViewAnimation;
+ m_splitter->widget(0)->setMinimumWidth(m_splitter->widget(0)->minimumSizeHint().width());
+ }
QByteArray splitterState;
stream >> splitterState;
m_splitter->restoreState(splitterState);
#include <QUrl>
#include <QWidget>
-class QSplitter;
+class DolphinNavigatorsWidgetAction;
class DolphinViewContainer;
+class QSplitter;
+class QVariantAnimation;
class KFileItemList;
class DolphinTabPage : public QWidget
*/
int selectedItemsCount() const;
+ /**
+ * Connects a navigatorsWidget to this. It will be connected to the DolphinViewContainers
+ * managed by this tab. For alignment purposes this will from now on notify the
+ * navigatorsWidget when this tab or its viewContainers are resized.
+ */
+ void connectNavigators(DolphinNavigatorsWidgetAction *navigatorsWidget);
+
+ /**
+ * Makes it so this tab and its DolphinViewContainers aren't controlled by any
+ * UrlNavigators anymore.
+ */
+ void disconnectNavigators();
+
+ /**
+ * Calls resizeNavigators() when a watched object is resized.
+ */
+ bool eventFilter(QObject */* watched */, QEvent *event) override;
+
+ /**
+ * Notify the connected DolphinNavigatorsWidgetAction of geometry changes which it
+ * needs for visual alignment.
+ */
+ void resizeNavigators() const;
+
/**
* Marks the items indicated by \p urls to get selected after the
* directory DolphinView::url() has been loaded. Note that nothing
*/
void markUrlAsCurrent(const QUrl& url);
- /**
- * 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);
-
/**
* Refreshes the views of the main window by recreating them according to
* the given Dolphin settings.
signals:
void activeViewChanged(DolphinViewContainer* viewContainer);
void activeViewUrlChanged(const QUrl& url);
+ void splitterMoved(int pos, int index);
private slots:
/**
private:
QSplitter* m_splitter;
+ QPointer<DolphinNavigatorsWidgetAction> m_navigatorsWidget;
QPointer<DolphinViewContainer> m_primaryViewContainer;
QPointer<DolphinViewContainer> m_secondaryViewContainer;
+ QPointer<QVariantAnimation> m_splitViewAnimation;
bool m_primaryViewActive;
bool m_splitViewEnabled;
#include <QApplication>
#include <QDropEvent>
-DolphinTabWidget::DolphinTabWidget(QWidget* parent) :
+DolphinTabWidget::DolphinTabWidget(DolphinNavigatorsWidgetAction *navigatorsWidget, QWidget* parent) :
QTabWidget(parent),
- m_lastViewedTab(0)
+ m_lastViewedTab(nullptr),
+ m_navigatorsWidget{navigatorsWidget}
{
KAcceleratorManager::setNoAccel(this);
void DolphinTabWidget::openNewActivatedTab()
{
+ std::unique_ptr<DolphinUrlNavigator::VisualState> oldNavigatorState;
+ if (currentTabPage()->primaryViewActive()) {
+ oldNavigatorState = m_navigatorsWidget->primaryUrlNavigator()->visualState();
+ } else {
+ if (!m_navigatorsWidget->secondaryUrlNavigator()) {
+ m_navigatorsWidget->createSecondaryUrlNavigator();
+ }
+ oldNavigatorState = m_navigatorsWidget->secondaryUrlNavigator()->visualState();
+ }
+
const DolphinViewContainer* oldActiveViewContainer = currentTabPage()->activeViewContainer();
Q_ASSERT(oldActiveViewContainer);
- const bool isUrlEditable = oldActiveViewContainer->urlNavigator()->isUrlEditable();
-
openNewActivatedTab(oldActiveViewContainer->url());
DolphinViewContainer* newActiveViewContainer = currentTabPage()->activeViewContainer();
// The URL navigator of the new tab should have the same editable state
// as the current tab
- newActiveViewContainer->urlNavigator()->setUrlEditable(isUrlEditable);
+ newActiveViewContainer->urlNavigator()->setVisualState(*oldNavigatorState.get());
// Always focus the new tab's view
newActiveViewContainer->view()->setFocus();
void DolphinTabWidget::currentTabChanged(int index)
{
- // last-viewed tab deactivation
- if (DolphinTabPage* tabPage = tabPageAt(m_lastViewedTab)) {
- tabPage->setActive(false);
+ DolphinTabPage *tabPage = tabPageAt(index);
+ if (tabPage == m_lastViewedTab) {
+ return;
+ }
+ if (m_lastViewedTab) {
+ m_lastViewedTab->disconnectNavigators();
+ m_lastViewedTab->setActive(false);
}
- DolphinTabPage* tabPage = tabPageAt(index);
DolphinViewContainer* viewContainer = tabPage->activeViewContainer();
Q_EMIT activeViewChanged(viewContainer);
Q_EMIT currentUrlChanged(viewContainer->url());
tabPage->setActive(true);
- m_lastViewedTab = index;
+ tabPage->connectNavigators(m_navigatorsWidget);
+ m_navigatorsWidget->setSecondaryNavigatorVisible(tabPage->splitViewEnabled());
+ m_lastViewedTab = tabPageAt(index);
}
void DolphinTabWidget::tabInserted(int index)
#ifndef DOLPHIN_TAB_WIDGET_H
#define DOLPHIN_TAB_WIDGET_H
+#include "dolphinnavigatorswidgetaction.h"
+
#include <QTabWidget>
#include <QUrl>
-class DolphinViewContainer;
class DolphinTabPage;
+class DolphinViewContainer;
class KConfigGroup;
class DolphinTabWidget : public QTabWidget
*/
AfterLastTab
};
- explicit DolphinTabWidget(QWidget* parent);
+
+ /**
+ * @param navigatorsWidget The navigatorsWidget which is always going to be connected
+ * to the active tabPage.
+ */
+ explicit DolphinTabWidget(DolphinNavigatorsWidgetAction *navigatorsWidget, QWidget *parent);
/**
* @return Tab page at the current index (can be 0 if tabs count is smaller than 1)
QPair<int, bool> indexByUrl(const QUrl& url) const;
private:
- int m_lastViewedTab;
+ QPointer<DolphinTabPage> m_lastViewedTab;
+ QPointer<DolphinNavigatorsWidgetAction> m_navigatorsWidget;
};
#endif
<text context="@title:menu">Location Bar</text>
<Action name="editable_location" />
<Action name="replace_location" />
- <Action name="location_in_toolbar" />
</Menu>
<Separator/>
<Action name="view_properties" />
<Action name="icons" />
<Action name="compact" />
<Action name="details" />
- <Separator name="separator_0" />
- <Action name="sort" />
- <Action name="url_navigator" />
+ <Action name="url_navigators" />
<Action name="split_view" />
<Action name="split_stash" />
<Action name="toggle_search" />
/*
- * Copyright 2020 Felix Ernst <fe.a.ernst@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) version 3, or any
- * later version accepted by the membership of KDE e.V. (or its
- * successor approved by the membership of KDE e.V.), which shall
- * act as a proxy defined in Section 6 of version 3 of the license.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <https://www.gnu.org/licenses/>.
- */
+ This file is part of the KDE project
+ SPDX-FileCopyrightText: 2020 Felix Ernst <fe.a.ernst@gmail.com>
+
+ SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
+*/
#include "dolphinurlnavigator.h"
#include "dolphinplacesmodelsingleton.h"
#include "global.h"
-#include <KToggleAction>
#include <KUrlComboBox>
#include <KLocalizedString>
+#include <QAbstractButton>
+#include <QLayout>
#include <QLineEdit>
-#include <QMenu>
DolphinUrlNavigator::DolphinUrlNavigator(QWidget *parent) :
KUrlNavigator(DolphinPlacesModelSingleton::instance().placesModel(), QUrl(), parent)
setHomeUrl(Dolphin::homeUrl());
setPlacesSelectorVisible(s_placesSelectorVisible);
editor()->setCompletionMode(KCompletion::CompletionMode(settings->urlCompletionMode()));
- editor()->lineEdit()->installEventFilter(this);
- installEventFilter(this);
setWhatsThis(xi18nc("@info:whatsthis location bar",
- "<para>This line describes the location of the files and folders "
+ "<para>This describes the location of the files and folders "
"displayed below.</para><para>The name of the currently viewed "
"folder can be read at the very right. To the left of it is the "
"name of the folder that contains it. The whole line is called "
s_instances.remove(this);
}
-bool DolphinUrlNavigator::eventFilter(QObject* watched, QEvent* event)
+QSize DolphinUrlNavigator::sizeHint() const
{
- Q_UNUSED(watched)
- if (event->type() == QEvent::ChildPolished) {
- QChildEvent *childEvent = static_cast<QChildEvent *>(event);
- QMenu *popup = qobject_cast<QMenu *>(childEvent->child());
- if (popup) {
- // The popups of the "breadcrumb mode" navigation buttons
- // should not get the action added. They can currently be
- // identified by their number of separators: 0 or 1
- // The popups we are interested in have 2 or more separators.
- int separatorCount = 0;
- for (QAction *action : popup->actions()) {
- if (action->isSeparator()) {
- separatorCount++;
- }
- }
- if (separatorCount > 1) {
- q_check_ptr(s_ActionForContextMenu);
- popup->addAction(s_ActionForContextMenu);
- }
+ // Change sizeHint() in KUrlNavigator instead.
+ if (isUrlEditable()) {
+ return editor()->lineEdit()->sizeHint();
+ }
+ int widthHint = 0;
+ for (int i = 0; i < layout()->count(); ++i) {
+ QWidget *widget = layout()->itemAt(i)->widget();
+ const QAbstractButton *button = qobject_cast<QAbstractButton *>(widget);
+ if (button && button->icon().isNull()) {
+ widthHint += widget->minimumSizeHint().width();
+ }
+ }
+ return QSize(widthHint, KUrlNavigator::sizeHint().height());
+}
+
+std::unique_ptr<DolphinUrlNavigator::VisualState> DolphinUrlNavigator::visualState() const
+{
+ std::unique_ptr<VisualState> visualState{new VisualState};
+ visualState->isUrlEditable = (isUrlEditable());
+ const QLineEdit *lineEdit = editor()->lineEdit();
+ visualState->hasFocus = lineEdit->hasFocus();
+ visualState->text = lineEdit->text();
+ visualState->cursorPosition = lineEdit->cursorPosition();
+ visualState->selectionStart = lineEdit->selectionStart();
+ visualState->selectionLength = lineEdit->selectionLength();
+ return visualState;
+}
+
+void DolphinUrlNavigator::setVisualState(const VisualState& visualState)
+{
+ setUrlEditable(visualState.isUrlEditable);
+ if (!visualState.isUrlEditable) {
+ return;
+ }
+ editor()->lineEdit()->setText(visualState.text);
+ if (visualState.hasFocus) {
+ editor()->lineEdit()->setFocus();
+ editor()->lineEdit()->setCursorPosition(visualState.cursorPosition);
+ if (visualState.selectionStart != -1) {
+ editor()->lineEdit()->setSelection(visualState.selectionStart, visualState.selectionLength);
}
}
- return false;
}
void DolphinUrlNavigator::slotReadSettings()
}
}
-void DolphinUrlNavigator::addToContextMenu(QAction* action)
-{
- s_ActionForContextMenu = action;
-}
-
-
void DolphinUrlNavigator::slotPlacesPanelVisibilityChanged(bool visible)
{
// The places-selector from the URL navigator should only be shown
std::forward_list<DolphinUrlNavigator *> DolphinUrlNavigator::s_instances;
bool DolphinUrlNavigator::s_placesSelectorVisible = true;
-QAction *DolphinUrlNavigator::s_ActionForContextMenu = nullptr;
/*
- * Copyright 2020 Felix Ernst <fe.a.ernst@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) version 3, or any
- * later version accepted by the membership of KDE e.V. (or its
- * successor approved by the membership of KDE e.V.), which shall
- * act as a proxy defined in Section 6 of version 3 of the license.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <https://www.gnu.org/licenses/>.
- */
+ This file is part of the KDE project
+ SPDX-FileCopyrightText: 2020 Felix Ernst <fe.a.ernst@gmail.com>
+
+ SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
+*/
#ifndef DOLPHINURLNAVIGATOR_H
#define DOLPHINURLNAVIGATOR_H
/**
* @brief Extends KUrlNavigator in a Dolphin-specific way
- *
+ *
* Makes sure that Dolphin preferences, settings and settings changes are
* applied to all constructed DolphinUrlNavigators.
- *
+ *
* @see KUrlNavigator
*/
class DolphinUrlNavigator : public KUrlNavigator
virtual ~DolphinUrlNavigator();
+ /**
+ * This method is needed so the DolphinNavigatorWidgetAction knows when there is not enough
+ * space to neatly align the UrlNavigator with the ViewContainers. Unfortunately KUrlNavigator
+ * does not have a useful sizeHint() currently. It would make more sense to change
+ * KUrlNavigator instead.
+ */
+ QSize sizeHint() const override;
+
+ /**
+ * Wraps the visual state of a DolphinUrlNavigator so it can be passed around.
+ * This notably doesn't involve the locationUrl or history.
+ */
+ struct VisualState {
+ bool isUrlEditable;
+ bool hasFocus;
+ QString text;
+ int cursorPosition;
+ int selectionStart;
+ int selectionLength;
+ };
+ /**
+ * Retrieve the visual state of this DolphinUrlNavigator.
+ * If two DolphinUrlNavigators have the same visual state they should look identical.
+ */
+ std::unique_ptr<VisualState> visualState() const;
+ /**
+ * @param visualState A struct describing the new visual state of this object.
+ */
+ void setVisualState(const VisualState &visualState);
+
public slots:
/**
* Refreshes all DolphinUrlNavigators to get synchronized with the
*/
void slotReturnPressed();
- /**
- * This method is specifically here so the locationInToolbar
- * KToggleAction that is created in DolphinMainWindow can be passed to
- * this class and then appear in all context menus. This last part is
- * done by eventFilter().
- * For any other use parts of this class need to be rewritten.
- * @param action The locationInToolbar-action from DolphinMainWindow
- */
- static void addToContextMenu(QAction *action);
-
static void slotPlacesPanelVisibilityChanged(bool visible);
protected:
*/
void init();
- /**
- * This filter adds the s_ActionForContextMenu action to QMenus which
- * are spawned by the watched QObject if that QMenu contains at least
- * two separators.
- * @see addToContextMenu()
- */
- bool eventFilter(QObject * watched, QEvent * event) override;
-
protected slots:
/**
* Sets the completion mode for all DolphinUrlNavigators
/** Caches the (negated) places panel visibility */
static bool s_placesSelectorVisible;
-
- /** An action that is added to the context menu */
- static QAction *s_ActionForContextMenu;
};
#endif // DOLPHINURLNAVIGATOR_H
#include "global.h"
#include "search/dolphinsearchbox.h"
#include "statusbar/dolphinstatusbar.h"
-#include "trash/dolphintrash.h"
#include "views/viewmodecontroller.h"
#include "views/viewproperties.h"
#include "dolphin_detailsmodesettings.h"
DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) :
QWidget(parent),
m_topLayout(nullptr),
- m_navigatorWidget(nullptr),
- m_urlNavigator(nullptr),
- m_urlNavigatorConnected(nullptr),
- m_emptyTrashButton(nullptr),
+ m_urlNavigator{new DolphinUrlNavigator(url)},
+ m_urlNavigatorConnected{nullptr},
m_searchBox(nullptr),
m_searchModeEnabled(false),
m_messageWidget(nullptr),
m_statusBar(nullptr),
m_statusBarTimer(nullptr),
m_statusBarTimestamp(),
- m_autoGrabFocus(true)
+ m_autoGrabFocus(true),
+ m_urlNavigatorVisualState{}
#ifdef HAVE_KACTIVITIES
, m_activityResourceInstance(nullptr)
#endif
m_topLayout->setSpacing(0);
m_topLayout->setContentsMargins(0, 0, 0, 0);
- m_navigatorWidget = new QWidget(this);
- m_navigatorWidget->setVisible(false);
- QHBoxLayout* navigatorLayout = new QHBoxLayout(m_navigatorWidget);
- navigatorLayout->setSpacing(0);
- navigatorLayout->setContentsMargins(0, 0, 0, 0);
- m_urlNavigator = new DolphinUrlNavigator(url, m_navigatorWidget);
-
- m_emptyTrashButton = new QPushButton(QIcon::fromTheme(QStringLiteral("user-trash")), i18nc("@action:button", "Empty Trash"), this);
- m_emptyTrashButton->setFlat(true);
- connect(m_emptyTrashButton, &QPushButton::clicked, this, [this]() { Trash::empty(this); });
- connect(&Trash::instance(), &Trash::emptinessChanged, m_emptyTrashButton, &QPushButton::setDisabled);
- m_emptyTrashButton->setDisabled(Trash::isEmpty());
- m_emptyTrashButton->hide();
-
m_searchBox = new DolphinSearchBox(this);
m_searchBox->hide();
connect(m_searchBox, &DolphinSearchBox::activated, this, &DolphinViewContainer::activate);
// m_urlNavigator stays in sync with m_view's location changes and
// keeps track of them so going back and forth in the history works.
connect(m_view, &DolphinView::urlChanged,
- m_urlNavigator, &DolphinUrlNavigator::setLocationUrl);
- connect(m_urlNavigator, &DolphinUrlNavigator::urlChanged,
+ m_urlNavigator.get(), &DolphinUrlNavigator::setLocationUrl);
+ connect(m_urlNavigator.get(), &DolphinUrlNavigator::urlChanged,
this, &DolphinViewContainer::slotUrlNavigatorLocationChanged);
connect(m_view, &DolphinView::writeStateChanged,
this, &DolphinViewContainer::writeStateChanged);
connect(m_view, &DolphinView::activated,
this, &DolphinViewContainer::activate);
- connect(m_view, &DolphinView::directoryLoadingCompleted, this, [this]() {
- m_emptyTrashButton->setVisible(m_view->url().scheme() == QLatin1String("trash"));
- });
-
// Initialize status bar
m_statusBar = new DolphinStatusBar(this);
m_statusBar->setUrl(m_view->url());
connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished,
this, &DolphinViewContainer::delayedStatusBarUpdate);
- navigatorLayout->addWidget(m_urlNavigator);
- navigatorLayout->addWidget(m_emptyTrashButton);
-
- m_topLayout->addWidget(m_navigatorWidget);
m_topLayout->addWidget(m_searchBox);
m_topLayout->addWidget(m_messageWidget);
m_topLayout->addWidget(m_view);
m_topLayout->addWidget(m_filterBar);
m_topLayout->addWidget(m_statusBar);
- if (!GeneralSettings::locationInToolbar()) {
- connectToInternalUrlNavigator();
- }
setSearchModeEnabled(isSearchUrl(url));
connect(DetailsModeSettings::self(), &KCoreConfigSkeleton::configChanged, this, [=]() {
void DolphinViewContainer::setActive(bool active)
{
m_searchBox->setActive(active);
- m_urlNavigator->setActive(active);
+ if (m_urlNavigatorConnected) {
+ m_urlNavigatorConnected->setActive(active);
+ }
m_view->setActive(active);
#ifdef HAVE_KACTIVITIES
bool DolphinViewContainer::isActive() const
{
- Q_ASSERT(!m_urlNavigatorConnected || m_urlNavigatorConnected->isActive() == m_view->isActive());
return m_view->isActive();
}
const DolphinUrlNavigator *DolphinViewContainer::urlNavigatorInternalWithHistory() const
{
- return m_urlNavigator;
+ return m_urlNavigator.get();
}
DolphinUrlNavigator *DolphinViewContainer::urlNavigatorInternalWithHistory()
{
- return m_urlNavigator;
+ return m_urlNavigator.get();
}
const DolphinView* DolphinViewContainer::view() const
{
Q_CHECK_PTR(urlNavigator);
Q_ASSERT(!m_urlNavigatorConnected);
+ Q_ASSERT(m_urlNavigator.get() != urlNavigator);
Q_CHECK_PTR(m_view);
- m_urlNavigatorConnected = urlNavigator;
+ urlNavigator->setLocationUrl(m_view->url());
+ urlNavigator->setActive(isActive());
+ if (m_urlNavigatorVisualState) {
+ urlNavigator->setVisualState(*m_urlNavigatorVisualState.get());
+ m_urlNavigatorVisualState.reset();
+ }
- // m_urlNavigator is already connected through urlChanged signals.
- if (urlNavigator != m_urlNavigator) {
- urlNavigator->setLocationUrl(m_view->url());
- connect(m_view, &DolphinView::urlChanged,
+ connect(m_view, &DolphinView::urlChanged,
urlNavigator, &DolphinUrlNavigator::setLocationUrl);
- connect(urlNavigator, &DolphinUrlNavigator::urlChanged,
+ connect(urlNavigator, &DolphinUrlNavigator::urlChanged,
this, &DolphinViewContainer::slotUrlNavigatorLocationChanged);
- }
-
connect(urlNavigator, &DolphinUrlNavigator::activated,
this, &DolphinViewContainer::activate);
connect(urlNavigator, &DolphinUrlNavigator::urlAboutToBeChanged,
m_view->dropUrls(destination, event, urlNavigator->dropWidget());
});
- updateNavigatorWidgetVisibility();
+ m_urlNavigatorConnected = urlNavigator;
}
void DolphinViewContainer::disconnectUrlNavigator()
return;
}
- // m_urlNavigator stays connected through the urlChanged signals.
- if (m_urlNavigatorConnected != m_urlNavigator) {
- disconnect(m_view, &DolphinView::urlChanged,
- m_urlNavigatorConnected, &DolphinUrlNavigator::setLocationUrl);
- disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlChanged,
- this, &DolphinViewContainer::slotUrlNavigatorLocationChanged);
- }
-
+ disconnect(m_view, &DolphinView::urlChanged,
+ m_urlNavigatorConnected, &DolphinUrlNavigator::setLocationUrl);
+ disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlChanged,
+ this, &DolphinViewContainer::slotUrlNavigatorLocationChanged);
disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::activated,
- this, &DolphinViewContainer::activate);
+ this, &DolphinViewContainer::activate);
disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlAboutToBeChanged,
- this, &DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged);
+ this, &DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged);
disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlSelectionRequested,
- this, &DolphinViewContainer::slotUrlSelectionRequested);
+ this, &DolphinViewContainer::slotUrlSelectionRequested);
disconnect(m_urlNavigatorConnected, &DolphinUrlNavigator::urlsDropped,
- this, nullptr);
+ this, nullptr);
+ m_urlNavigatorVisualState = m_urlNavigatorConnected->visualState();
m_urlNavigatorConnected = nullptr;
- updateNavigatorWidgetVisibility();
}
-KMessageWidget *DolphinViewContainer::showMessage(const QString& msg, MessageType type)
+void DolphinViewContainer::showMessage(const QString& msg, MessageType type)
{
if (msg.isEmpty()) {
- return m_messageWidget;
- }
- for (auto action : m_messageWidget->actions()) {
- m_messageWidget->removeAction(action);
+ return;
}
m_messageWidget->setText(msg);
m_messageWidget->hide();
}
m_messageWidget->animatedShow();
- return m_messageWidget;
}
void DolphinViewContainer::readSettings()
void DolphinViewContainer::setSearchModeEnabled(bool enabled)
{
m_searchBox->setVisible(enabled);
- updateNavigatorWidgetVisibility();
if (enabled) {
const QUrl& locationUrl = m_urlNavigatorConnected->locationUrl();
void DolphinViewContainer::setUrl(const QUrl& newUrl)
{
- Q_CHECK_PTR(m_urlNavigatorConnected);
- if (newUrl != m_urlNavigatorConnected->locationUrl()) {
- m_urlNavigatorConnected->setLocationUrl(newUrl);
+ if (newUrl != m_urlNavigator->locationUrl()) {
+ m_urlNavigator->setLocationUrl(newUrl);
}
#ifdef HAVE_KACTIVITIES
m_statusBar->setProgress(percent);
}
-void DolphinViewContainer::updateNavigatorWidgetVisibility()
-{
- if (m_urlNavigatorConnected == m_urlNavigator && !m_searchBox->isVisible()) {
- m_navigatorWidget->setVisible(true);
- } else {
- m_navigatorWidget->setVisible(false);
- }
-}
-
void DolphinViewContainer::slotDirectoryLoadingStarted()
{
if (isSearchUrl(url())) {
* or nullptr if there is none.
* @see connectUrlNavigator()
* @see disconnectUrlNavigator()
- *
+ *
* Use urlNavigatorInternalWithHistory() if you want to access the history.
* @see urlNavigatorInternalWithHistory()
*/
* or nullptr if there is none.
* @see connectUrlNavigator()
* @see disconnectUrlNavigator()
- *
+ *
* Use urlNavigatorInternalWithHistory() if you want to access the history.
* @see urlNavigatorInternalWithHistory()
*/
*/
void connectUrlNavigator(DolphinUrlNavigator *urlNavigator);
- inline void connectToInternalUrlNavigator()
- {
- connectUrlNavigator(m_urlNavigator);
- }
-
/**
* Disconnects the navigator that is currently controling the view.
* This method completely reverses connectUrlNavigator().
/**
* Shows the message \msg with the given type non-modal above
* the view-content.
- * @return the KMessageWidget used to show the message
*/
- KMessageWidget *showMessage(const QString& msg, MessageType type);
+ void showMessage(const QString& msg, MessageType type);
/**
* Refreshes the view container to get synchronized with the (updated) Dolphin settings.
void updateDirectorySortingProgress(int percent);
- void updateNavigatorWidgetVisibility();
-
/**
* Updates the statusbar to show an undetermined progress with the correct
* context information whether a searching or a directory loading is done.
private:
QVBoxLayout* m_topLayout;
- QWidget* m_navigatorWidget;
/**
- * The UrlNavigator within the m_navigatorWidget. m_urlNavigator is
- * used even when another UrlNavigator is controlling the view to keep
- * track of this view containers history.
+ * The internal UrlNavigator which is never visible to the user.
+ * m_urlNavigator is used even when another UrlNavigator is controlling
+ * the view to keep track of this object's history.
*/
- DolphinUrlNavigator *m_urlNavigator;
+ std::unique_ptr<DolphinUrlNavigator> m_urlNavigator;
/**
- * The UrlNavigator that is currently connected to the view. This could
- * either be m_urlNavigator, the urlNavigator in the toolbar or nullptr.
+ * The UrlNavigator that is currently connected to the view.
+ * This is a nullptr if no UrlNavigator is connected.
+ * Otherwise it's one of the UrlNavigators visible in the toolbar.
*/
QPointer<DolphinUrlNavigator> m_urlNavigatorConnected;
- QPushButton* m_emptyTrashButton;
DolphinSearchBox* m_searchBox;
bool m_searchModeEnabled;
KMessageWidget* m_messageWidget;
QTimer* m_statusBarTimer; // Triggers a delayed update
QElapsedTimer m_statusBarTimestamp; // Time in ms since last update
bool m_autoGrabFocus;
+ /**
+ * The visual state to be applied to the next UrlNavigator that gets
+ * connected to this ViewContainer.
+ */
+ std::unique_ptr<DolphinUrlNavigator::VisualState> m_urlNavigatorVisualState;
#ifdef HAVE_KACTIVITIES
private:
#include <KCrash>
#include <KDBusService>
#include <KLocalizedString>
+#include <KToolBar>
#include <Kdelibs4ConfigMigrator>
#include <KConfigGui>
}
}
}
+ Qt::ToolBarArea area = mainWindow->toolBarArea(mainWindow->toolBar());
+ if (area != Qt::TopToolBarArea && area != Qt::BottomToolBarArea) {
+ // Migrate users with disabled tool bar positions.
+ // Remove this a few years from now (2020).
+ mainWindow->addToolBar(Qt::TopToolBarArea, mainWindow->toolBar());
+ }
#ifdef HAVE_KUSERFEEDBACK
auto feedbackProvider = DolphinFeedbackProvider::instance();
<label>Split the view into two panes</label>
<default>false</default>
</entry>
- <entry name="LocationInToolbar" type="Bool">
- <label>Should the location be shown in the toolbar instead</label>
- <default>false</default>
- </entry>
<entry name="FilterBar" type="Bool">
<label>Should the filter bar be shown</label>
<default>false</default>
+++ /dev/null
-/*
- * Copyright 2020 Felix Ernst <fe.a.ernst@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) version 3, or any
- * later version accepted by the membership of KDE e.V. (or its
- * successor approved by the membership of KDE e.V.), which shall
- * act as a proxy defined in Section 6 of version 3 of the license.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <https://www.gnu.org/licenses/>.
- */
-
-#include "dolphinurlnavigatorwidgetaction.h"
-
-#include "dolphin_generalsettings.h"
-#include "dolphinviewcontainer.h"
-
-#include <KLocalizedString>
-#include <KXMLGUIFactory>
-#include <KXmlGuiWindow>
-
-#include <QDomDocument>
-#include <QStackedWidget>
-
-DolphinUrlNavigatorWidgetAction::DolphinUrlNavigatorWidgetAction(QWidget *parent) :
- QWidgetAction(parent)
-{
- setText(i18nc("@action:inmenu", "Url navigator"));
-
- m_stackedWidget = new QStackedWidget(parent);
-
- auto expandingSpacer = new QWidget(m_stackedWidget);
- expandingSpacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
- m_stackedWidget->addWidget(expandingSpacer); // index 0 of QStackedWidget
-
- auto urlNavigator = new DolphinUrlNavigator(m_stackedWidget);
- m_stackedWidget->addWidget(urlNavigator); // index 1 of QStackedWidget
-
- setDefaultWidget(m_stackedWidget);
- setUrlNavigatorVisible(GeneralSettings::locationInToolbar());
-}
-
-DolphinUrlNavigator* DolphinUrlNavigatorWidgetAction::urlNavigator() const
-{
- return static_cast<DolphinUrlNavigator *>(m_stackedWidget->widget(1));
-}
-
-void DolphinUrlNavigatorWidgetAction::setUrlNavigatorVisible(bool visible)
-{
- if (!visible) {
- m_stackedWidget->setCurrentIndex(0); // expandingSpacer
- } else {
- m_stackedWidget->setCurrentIndex(1); // urlNavigator
- }
-}
-
-bool DolphinUrlNavigatorWidgetAction::addToToolbarAndSave(KXmlGuiWindow *mainWindow)
-{
- const QString rawXml = KXMLGUIFactory::readConfigFile(mainWindow->xmlFile());
- QDomDocument domDocument;
- if (rawXml.isEmpty() || !domDocument.setContent(rawXml) || domDocument.isNull()) {
- return false;
- }
- QDomNode toolbar = domDocument.elementsByTagName(QStringLiteral("ToolBar")).at(0);
- if (toolbar.isNull()) {
- return false;
- }
-
- QDomElement urlNavigatorElement = domDocument.createElement(QStringLiteral("Action"));
- urlNavigatorElement.setAttribute(QStringLiteral("name"), QStringLiteral("url_navigator"));
-
- QDomNode position = toolbar.lastChildElement(QStringLiteral("Spacer"));
- if (position.isNull()) {
- toolbar.appendChild(urlNavigatorElement);
- } else {
- toolbar.replaceChild(urlNavigatorElement, position);
- }
-
- KXMLGUIFactory::saveConfigFile(domDocument, mainWindow->xmlFile());
- mainWindow->reloadXML();
- mainWindow->createGUI();
- return true;
-}
+++ /dev/null
-/*
- * Copyright 2020 Felix Ernst <fe.a.ernst@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) version 3, or any
- * later version accepted by the membership of KDE e.V. (or its
- * successor approved by the membership of KDE e.V.), which shall
- * act as a proxy defined in Section 6 of version 3 of the license.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <https://www.gnu.org/licenses/>.
- */
-
-#ifndef DOLPHINURLNAVIGATORWIDGETACTION_H
-#define DOLPHINURLNAVIGATORWIDGETACTION_H
-
-#include "dolphinurlnavigator.h"
-
-#include <QWidgetAction>
-
-class KXmlGuiWindow;
-class QStackedWidget;
-
-/**
- * @brief QWidgetAction that allows to use a KUrlNavigator in a toolbar.
- *
- * When the UrlNavigator of this object is not in use,
- * setUrlNavigatorVisible(false) is used to hide it. It will then be
- * replaced in the toolbar by an empty expanding spacer. This makes sure
- * that the other widgets in the toolbar will not change location when
- * switching the UrlNavigators visibility.
- */
-class DolphinUrlNavigatorWidgetAction : public QWidgetAction
-{
- Q_OBJECT
-
-public:
- DolphinUrlNavigatorWidgetAction(QWidget *parent = nullptr);
-
- DolphinUrlNavigator *urlNavigator() const;
-
- /**
- * Sets the QStackedWidget which is the defaultWidget() to either
- * show a KUrlNavigator or an expanding spacer.
- */
- void setUrlNavigatorVisible(bool visible);
-
- /**
- * Adds this action to the mainWindow's toolbar and saves the change
- * in the users ui configuration file.
- * @return true if successful. Otherwise false.
- * @note This method does multiple things which are discouraged in
- * the API documentation.
- */
- bool addToToolbarAndSave(KXmlGuiWindow *mainWindow);
-
-private:
- QStackedWidget *m_stackedWidget;
-};
-
-#endif // DOLPHINURLNAVIGATORWIDGETACTION_H