File indexing completed on 2024-04-28 15:26:50

0001 /*
0002     SPDX-FileCopyrightText: 2006 Aaron J. Seigo <aseigo@kde.org>
0003     SPDX-FileCopyrightText: 2009 Peter Penz <peter.penz@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kurlnavigatorprotocolcombo_p.h"
0009 
0010 #include <QAction>
0011 #include <QMenu>
0012 #include <QPaintEvent>
0013 #include <QPainter>
0014 #include <QStyleOption>
0015 
0016 #include <KLocalizedString>
0017 #include <kprotocolinfo.h>
0018 #include <kprotocolmanager.h>
0019 #include <kurlnavigator.h>
0020 
0021 namespace
0022 {
0023 const int ArrowSize = 10;
0024 }
0025 
0026 namespace KDEPrivate
0027 {
0028 KUrlNavigatorProtocolCombo::KUrlNavigatorProtocolCombo(const QString &protocol, KUrlNavigator *parent)
0029     : KUrlNavigatorButtonBase(parent)
0030     , m_menu(nullptr)
0031     , m_protocols()
0032     , m_categories()
0033 {
0034     m_menu = new QMenu(this);
0035     connect(m_menu, &QMenu::triggered, this, &KUrlNavigatorProtocolCombo::setProtocolFromMenu);
0036     setText(protocol);
0037     setMenu(m_menu);
0038 }
0039 
0040 void KUrlNavigatorProtocolCombo::setCustomProtocols(const QStringList &protocols)
0041 {
0042     m_protocols = protocols;
0043     m_menu->clear();
0044 
0045     for (const QString &protocol : protocols) {
0046         QAction *action = m_menu->addAction(protocol);
0047         action->setData(protocol);
0048     }
0049 }
0050 
0051 QSize KUrlNavigatorProtocolCombo::sizeHint() const
0052 {
0053     const QSize size = KUrlNavigatorButtonBase::sizeHint();
0054 
0055     int width = fontMetrics().boundingRect(KLocalizedString::removeAcceleratorMarker(text())).width();
0056     width += (3 * BorderWidth) + ArrowSize;
0057 
0058     return QSize(width, size.height());
0059 }
0060 
0061 void KUrlNavigatorProtocolCombo::setProtocol(const QString &protocol)
0062 {
0063     setText(protocol);
0064 }
0065 
0066 QString KUrlNavigatorProtocolCombo::currentProtocol() const
0067 {
0068     return text();
0069 }
0070 
0071 void KUrlNavigatorProtocolCombo::showEvent(QShowEvent *event)
0072 {
0073     KUrlNavigatorButtonBase::showEvent(event);
0074     if (!event->spontaneous() && m_protocols.isEmpty()) {
0075         m_protocols = KProtocolInfo::protocols();
0076 
0077         auto it = std::remove_if(m_protocols.begin(), m_protocols.end(), [](const QString &s) {
0078             QUrl url;
0079             url.setScheme(s);
0080             return !KProtocolManager::supportsListing(url);
0081         });
0082         m_protocols.erase(it, m_protocols.end());
0083 
0084         std::sort(m_protocols.begin(), m_protocols.end());
0085 
0086         updateMenu();
0087     }
0088 }
0089 
0090 void KUrlNavigatorProtocolCombo::paintEvent(QPaintEvent *event)
0091 {
0092     Q_UNUSED(event);
0093 
0094     QPainter painter(this);
0095     const int buttonWidth = width();
0096     const int buttonHeight = height();
0097 
0098     drawHoverBackground(&painter);
0099 
0100     const QColor fgColor = foregroundColor();
0101     painter.setPen(fgColor);
0102 
0103     // draw arrow
0104     const int arrowX = buttonWidth - ArrowSize - BorderWidth;
0105     const int arrowY = (buttonHeight - ArrowSize) / 2;
0106 
0107     QStyleOption option;
0108     option.rect = QRect(arrowX, arrowY, ArrowSize, ArrowSize);
0109     option.palette = palette();
0110     option.palette.setColor(QPalette::Text, fgColor);
0111     option.palette.setColor(QPalette::WindowText, fgColor);
0112     option.palette.setColor(QPalette::ButtonText, fgColor);
0113     style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &option, &painter, this);
0114 
0115     // draw text
0116     const int textWidth = arrowX - (2 * BorderWidth);
0117     int alignment = Qt::AlignCenter | Qt::TextShowMnemonic;
0118     if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &option, this)) {
0119         alignment |= Qt::TextHideMnemonic;
0120     }
0121     style()->drawItemText(&painter, QRect(BorderWidth, 0, textWidth, buttonHeight), alignment, option.palette, isEnabled(), text());
0122 }
0123 
0124 void KUrlNavigatorProtocolCombo::setProtocolFromMenu(QAction *action)
0125 {
0126     const QString protocol = action->data().toString();
0127     setText(protocol);
0128     Q_EMIT activated(protocol);
0129 }
0130 
0131 void KUrlNavigatorProtocolCombo::updateMenu()
0132 {
0133     initializeCategories();
0134     std::sort(m_protocols.begin(), m_protocols.end());
0135 
0136     // move all protocols into the corresponding category of 'items'
0137     QList<QString> items[CategoryCount];
0138     for (const QString &protocol : std::as_const(m_protocols)) {
0139         if (m_categories.contains(protocol)) {
0140             const ProtocolCategory category = m_categories.value(protocol);
0141             items[category].append(protocol);
0142         } else {
0143             items[OtherCategory].append(protocol);
0144         }
0145     }
0146 
0147     // Create the menu that includes all entries from 'items'. The categories
0148     // CoreCategory and PlacesCategory are placed at the top level, the remaining
0149     // categories are placed in sub menus.
0150     QMenu *menu = m_menu;
0151     for (int category = 0; category < CategoryCount; ++category) {
0152         if (!items[category].isEmpty()) {
0153             switch (category) {
0154             case DevicesCategory:
0155                 menu = m_menu->addMenu(i18nc("@item:inmenu", "Devices"));
0156                 break;
0157 
0158             case SubversionCategory:
0159                 menu = m_menu->addMenu(i18nc("@item:inmenu", "Subversion"));
0160                 break;
0161 
0162             case OtherCategory:
0163                 menu = m_menu->addMenu(i18nc("@item:inmenu", "Other"));
0164                 break;
0165 
0166             case CoreCategory:
0167             case PlacesCategory:
0168             default:
0169                 break;
0170             }
0171 
0172             for (const QString &protocol : std::as_const(items[category])) {
0173                 QAction *action = menu->addAction(protocol);
0174                 action->setData(protocol);
0175             }
0176 
0177             if (menu == m_menu) {
0178                 menu->addSeparator();
0179             }
0180         }
0181     }
0182 }
0183 
0184 void KUrlNavigatorProtocolCombo::initializeCategories()
0185 {
0186     if (m_categories.isEmpty()) {
0187         m_categories.insert(QStringLiteral("file"), CoreCategory);
0188         m_categories.insert(QStringLiteral("ftp"), CoreCategory);
0189         m_categories.insert(QStringLiteral("fish"), CoreCategory);
0190         m_categories.insert(QStringLiteral("nfs"), CoreCategory);
0191         m_categories.insert(QStringLiteral("sftp"), CoreCategory);
0192         m_categories.insert(QStringLiteral("smb"), CoreCategory);
0193         m_categories.insert(QStringLiteral("webdav"), CoreCategory);
0194 
0195         m_categories.insert(QStringLiteral("desktop"), PlacesCategory);
0196         m_categories.insert(QStringLiteral("fonts"), PlacesCategory);
0197         m_categories.insert(QStringLiteral("programs"), PlacesCategory);
0198         m_categories.insert(QStringLiteral("settings"), PlacesCategory);
0199         m_categories.insert(QStringLiteral("trash"), PlacesCategory);
0200 
0201         m_categories.insert(QStringLiteral("floppy"), DevicesCategory);
0202         m_categories.insert(QStringLiteral("camera"), DevicesCategory);
0203         m_categories.insert(QStringLiteral("remote"), DevicesCategory);
0204 
0205         m_categories.insert(QStringLiteral("svn"), SubversionCategory);
0206         m_categories.insert(QStringLiteral("svn+file"), SubversionCategory);
0207         m_categories.insert(QStringLiteral("svn+http"), SubversionCategory);
0208         m_categories.insert(QStringLiteral("svn+https"), SubversionCategory);
0209         m_categories.insert(QStringLiteral("svn+ssh"), SubversionCategory);
0210     }
0211 }
0212 
0213 } // namespace KDEPrivate
0214 
0215 #include "moc_kurlnavigatorprotocolcombo_p.cpp"