+/*
+ 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();
+}