/* This file is part of the KDE project SPDX-FileCopyrightText: 2020 Felix Ernst 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 #include #include #include #include #include #include #include DolphinNavigatorsWidgetAction::DolphinNavigatorsWidgetAction(QWidget *parent) : QWidgetAction{parent}, m_splitter{new QSplitter(Qt::Horizontal)}, m_adjustSpacingTimer{new QTimer(this)}, m_globalXOfSplitter{INT_MIN}, m_globalXOfPrimary{INT_MIN}, m_widthOfPrimary{INT_MIN}, m_globalXOfSecondary{INT_MIN}, m_widthOfSecondary{INT_MIN} { updateText(); setIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts"))); 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.firstChildElement(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); updateText(); } void DolphinNavigatorsWidgetAction::followViewContainerGeometry( int globalXOfPrimary, int widthOfPrimary) { followViewContainersGeometry(globalXOfPrimary, widthOfPrimary, INT_MIN, INT_MIN); } 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 m_splitter->widget(0)->findChild(); } DolphinUrlNavigator* DolphinNavigatorsWidgetAction::secondaryUrlNavigator() const { Q_ASSERT(m_splitter); if (m_splitter->count() < 2) { return nullptr; } return m_splitter->widget(1)->findChild(); } 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); } updateText(); } void DolphinNavigatorsWidgetAction::adjustSpacing() { Q_ASSERT(m_globalXOfSplitter != INT_MIN); Q_ASSERT(m_globalXOfPrimary != INT_MIN); Q_ASSERT(m_widthOfPrimary != INT_MIN); const int widthOfSplitterPrimary = m_globalXOfPrimary + m_widthOfPrimary - m_globalXOfSplitter; const QList 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 == INT_MIN) { Q_ASSERT(m_widthOfSecondary == INT_MIN); 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(m_splitter->widget(sideIndex)->layout()->itemAt(2)->widget()); } return static_cast(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(); } void DolphinNavigatorsWidgetAction::updateText() { const int urlNavigatorsAmount = m_splitter->count() > 1 && m_splitter->widget(1)->isVisible() ? 2 : 1; setText(i18ncp("@action:inmenu", "Url Navigator", "Url Navigators", urlNavigatorsAmount)); }