diff --git a/kcms/hardware/joystick/joywidget.cpp b/kcms/hardware/joystick/joywidget.cpp index b5a451884..84d7494b2 100644 --- a/kcms/hardware/joystick/joywidget.cpp +++ b/kcms/hardware/joystick/joywidget.cpp @@ -1,417 +1,416 @@ /*************************************************************************** * Copyright (C) 2003,2012 by Martin Koller * * kollix@aon.at * * This file is part of the KDE Control Center Module for Joysticks * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "joywidget.h" #include "joydevice.h" #include "poswidget.h" #include "caldialog.h" #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include //-------------------------------------------------------------- static QString PRESSED = I18N_NOOP("PRESSED"); //-------------------------------------------------------------- class TableWidget : public QTableWidget { public: TableWidget(int row, int col) : QTableWidget(row, col) {} QSize sizeHint() const override { return QSize(150, 100); // return a smaller size than the Qt default(256, 192) } }; //-------------------------------------------------------------- JoyWidget::JoyWidget(QWidget *parent) : QWidget(parent), idle(nullptr), joydev(nullptr) { QVBoxLayout *mainVbox = new QVBoxLayout(this); mainVbox->setSpacing(KDialog::spacingHint()); mainVbox->setContentsMargins(0, 0, 0, 0); // create area to show an icon + message if no joystick was detected { messageBox = new KMessageWidget(this); messageBox->setMessageType(KMessageWidget::Error); messageBox->setCloseButtonVisible(false); messageBox->hide(); mainVbox->addWidget(messageBox); } QHBoxLayout *devHbox = new QHBoxLayout; devHbox->setSpacing(KDialog::spacingHint()); devHbox->addWidget(new QLabel(i18n("Device:"))); devHbox->addWidget(device = new KComboBox(true)); device->setInsertPolicy(QComboBox::NoInsert); KUrlCompletion *kc = new KUrlCompletion(KUrlCompletion::FileCompletion); device->setCompletionObject(kc); device->setAutoDeleteCompletionObject(true); connect(device, SIGNAL(activated(QString)), this, SLOT(deviceChanged(QString))); connect(device, SIGNAL(returnPressed(QString)), this, SLOT(deviceChanged(QString))); devHbox->setStretchFactor(device, 3); QHBoxLayout *hbox = new QHBoxLayout; hbox->setSpacing(KDialog::spacingHint()); mainVbox->addLayout(devHbox); mainVbox->addLayout(hbox); QVBoxLayout *vboxLeft = new QVBoxLayout; vboxLeft->setSpacing(KDialog::spacingHint()); vboxLeft->addWidget(new QLabel(i18nc("Cue for deflection of the stick", "Position:"))); vboxLeft->addWidget(xyPos = new PosWidget); vboxLeft->addWidget(trace = new QCheckBox(i18n("Show trace"))); connect(trace, &QAbstractButton::toggled, this, &JoyWidget::traceChanged); QVBoxLayout *vboxMid = new QVBoxLayout; vboxMid->setSpacing(KDialog::spacingHint()); QVBoxLayout *vboxRight = new QVBoxLayout; vboxRight->setSpacing(KDialog::spacingHint()); // calculate the column width we need QFontMetrics fm(font()); int colWidth = qMax(fm.width(PRESSED), fm.width(QStringLiteral("-32767"))) + 10; // -32767 largest string vboxMid->addWidget(new QLabel(i18n("Buttons:"))); buttonTbl = new TableWidget(0, 1); buttonTbl->setSelectionMode(QAbstractItemView::NoSelection); buttonTbl->setEditTriggers(QAbstractItemView::NoEditTriggers); buttonTbl->setHorizontalHeaderLabels(QStringList(i18n("State"))); buttonTbl->setSortingEnabled(false); buttonTbl->horizontalHeader()->setSectionsClickable(false); buttonTbl->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); buttonTbl->horizontalHeader()->resizeSection(0, colWidth); buttonTbl->verticalHeader()->setSectionsClickable(false); vboxMid->addWidget(buttonTbl); vboxRight->addWidget(new QLabel(i18n("Axes:"))); axesTbl = new TableWidget(0, 1); axesTbl->setSelectionMode(QAbstractItemView::NoSelection); axesTbl->setEditTriggers(QAbstractItemView::NoEditTriggers); axesTbl->setHorizontalHeaderLabels(QStringList(i18n("Value"))); axesTbl->setSortingEnabled(false); axesTbl->horizontalHeader()->setSectionsClickable(false); axesTbl->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); axesTbl->horizontalHeader()->resizeSection(0, colWidth); axesTbl->verticalHeader()->setSectionsClickable(false); vboxRight->addWidget(axesTbl); hbox->addLayout(vboxLeft); hbox->addLayout(vboxMid); hbox->addLayout(vboxRight); // calibrate button calibrate = new QPushButton(i18n("Calibrate")); connect(calibrate, &QAbstractButton::clicked, this, &JoyWidget::calibrateDevice); calibrate->setEnabled(false); vboxLeft->addStretch(); vboxLeft->addWidget(calibrate); // set up a timer for idle processing of joystick events idle = new QTimer(this); connect(idle, &QTimer::timeout, this, &JoyWidget::checkDevice); // check which devicefiles we have init(); } //-------------------------------------------------------------- JoyWidget::~JoyWidget() { delete joydev; } //-------------------------------------------------------------- void JoyWidget::init() { // check which devicefiles we have int i; bool first = true; char dev[30]; device->clear(); buttonTbl->setRowCount(0); axesTbl->setRowCount(0); for (i = 0; i < 5; i++) // check the first 5 devices { sprintf(dev, "/dev/js%d", i); // first look in /dev JoyDevice *joy = new JoyDevice(dev); if ( joy->open() != JoyDevice::SUCCESS ) { delete joy; sprintf(dev, "/dev/input/js%d", i); // then look in /dev/input joy = new JoyDevice(dev); if ( joy->open() != JoyDevice::SUCCESS ) { delete joy; continue; // try next number } } // we found one device->addItem(QStringLiteral("%1 (%2)").arg(joy->text()).arg(joy->device())); // display values for first device if ( first ) { showDeviceProps(joy); // this sets the joy object into this->joydev first = false; } else delete joy; } /* KDE 4: Remove this check(and i18n) when all KCM wrappers properly test modules */ if ( device->count() == 0 ) { messageBox->show(); messageBox->setText(QStringLiteral("%1").arg( i18n("No joystick device automatically found on this computer.
" "Checks were done in /dev/js[0-4] and /dev/input/js[0-4]
" "If you know that there is one attached, please enter the correct device file."))); } } //-------------------------------------------------------------- void JoyWidget::traceChanged(bool state) { xyPos->showTrace(state); } //-------------------------------------------------------------- void JoyWidget::restoreCurrDev() { if ( !joydev ) // no device open { device->setEditText(QLatin1String("")); calibrate->setEnabled(false); } else { // try to find the current open device in the combobox list int index = device->findText(joydev->device(), Qt::MatchContains); if ( index == -1 ) // the current open device is one the user entered (not in the list) device->setEditText(joydev->device()); else device->setEditText(device->itemText(index)); } } //-------------------------------------------------------------- void JoyWidget::deviceChanged(const QString &dev) { // find "/dev" in given string int start, stop; QString devName; if ( (start = dev.indexOf(QLatin1String("/dev"))) == -1 ) { KMessageBox::sorry(this, i18n("The given device name is invalid (does not contain /dev).\n" "Please select a device from the list or\n" "enter a device file, like /dev/js0."), i18n("Unknown Device")); restoreCurrDev(); return; } if ( (stop = dev.indexOf(QLatin1Char(')'), start)) != -1 ) // seems to be text selected from our list devName = dev.mid(start, stop - start); else devName = dev.mid(start); if ( joydev && (devName == joydev->device()) ) return; // user selected the current device; ignore it JoyDevice *joy = new JoyDevice(devName); JoyDevice::ErrorCode ret = joy->open(); if ( ret != JoyDevice::SUCCESS ) { KMessageBox::error(this, joy->errText(ret), i18n("Device Error")); delete joy; restoreCurrDev(); return; } showDeviceProps(joy); } //-------------------------------------------------------------- void JoyWidget::showDeviceProps(JoyDevice *joy) { joydev = joy; buttonTbl->setRowCount(joydev->numButtons()); axesTbl->setRowCount(joydev->numAxes()); if ( joydev->numAxes() >= 2 ) { axesTbl->setVerticalHeaderItem(0, new QTableWidgetItem(i18n("1(x)"))); axesTbl->setVerticalHeaderItem(1, new QTableWidgetItem(i18n("2(y)"))); } calibrate->setEnabled(true); idle->start(0); // make both tables use the same space for header; this looks nicer // TODO: Don't know how to do this in Qt4; the following does no longer work // Probably by setting a sizeHint for every single header item ? /* buttonTbl->verticalHeader()->setFixedWidth(qMax(buttonTbl->verticalHeader()->width(), axesTbl->verticalHeader()->width())); axesTbl->verticalHeader()->setFixedWidth(buttonTbl->verticalHeader()->width()); */ } //-------------------------------------------------------------- void JoyWidget::checkDevice() { if ( !joydev ) return; // no open device yet JoyDevice::EventType type; int number, value; if ( !joydev->getEvent(type, number, value) ) return; if ( type == JoyDevice::BUTTON ) { if ( ! buttonTbl->item(number, 0) ) buttonTbl->setItem(number, 0, new QTableWidgetItem()); if ( value == 0 ) // button release buttonTbl->item(number, 0)->setText(QStringLiteral("-")); else buttonTbl->item(number, 0)->setText(PRESSED); } if ( type == JoyDevice::AXIS ) { if ( number == 0 ) // x-axis xyPos->changeX(value); if ( number == 1 ) // y-axis xyPos->changeY(value); if ( ! axesTbl->item(number, 0) ) axesTbl->setItem(number, 0, new QTableWidgetItem()); axesTbl->item(number, 0)->setText(QStringLiteral("%1").arg(int(value))); } } //-------------------------------------------------------------- void JoyWidget::calibrateDevice() { if ( !joydev ) return; // just to be save JoyDevice::ErrorCode ret = joydev->initCalibration(); if ( ret != JoyDevice::SUCCESS ) { KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error")); return; } if ( KMessageBox::messageBox(this, KMessageBox::Information, i18n("Calibration is about to check the precision.

" "Please move all axes to their center position and then " "do not touch the joystick anymore.

" "Click OK to start the calibration.
"), i18n("Calibration"), KStandardGuiItem::ok(), KStandardGuiItem::cancel()) != KMessageBox::Ok ) return; idle->stop(); // stop the joystick event getting; this must be done inside the calibrate dialog CalDialog dlg(this, joydev); dlg.calibrate(); // user canceled somewhere during calibration, therefore the device is in a bad state if ( dlg.result() == QDialog::Rejected ) joydev->restoreCorr(); idle->start(0); // continue with event getting } //-------------------------------------------------------------- void JoyWidget::resetCalibration() { if ( !joydev ) return; // just to be save JoyDevice::ErrorCode ret = joydev->restoreCorr(); if ( ret != JoyDevice::SUCCESS ) { KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error")); } else { KMessageBox::information(this, i18n("Restored all calibration values for joystick device %1.", joydev->device()), i18n("Calibration Success")); } } //-------------------------------------------------------------- diff --git a/kcms/keys/CMakeLists.txt b/kcms/keys/CMakeLists.txt index 9e27ef84a..871fc305b 100644 --- a/kcms/keys/CMakeLists.txt +++ b/kcms/keys/CMakeLists.txt @@ -1,58 +1,57 @@ # KI18N Translation Domain for this library add_definitions(-DTRANSLATION_DOMAIN=\"kcmkeys\") ########### next target ############### set(kcm_keys_PART_SRCS kglobalshortcutseditor.cpp globalshortcuts.cpp select_scheme_dialog.cpp kglobalaccel_interface.cpp kglobalaccel_component_interface.cpp export_scheme_dialog.cpp ) ki18n_wrap_ui( kcm_keys_PART_SRCS export_scheme_dialog.ui kglobalshortcutseditor.ui select_application.ui select_scheme_dialog.ui ) set(kglobalaccel_xml ${KGLOBALACCEL_DBUS_INTERFACES_DIR}/kf5_org.kde.KGlobalAccel.xml) set_source_files_properties(${kglobalaccel_xml} PROPERTIES INCLUDE "kglobalshortcutinfo.h") qt5_add_dbus_interface(kdeui_LIB_SRCS ${kglobalaccel_xml} kglobalaccel_interface ) set(kglobalaccel_component_xml ${KGLOBALACCEL_DBUS_INTERFACES_DIR}/kf5_org.kde.kglobalaccel.Component.xml) set_source_files_properties(${kglobalaccel_component_xml} PROPERTIES INCLUDE "kglobalshortcutinfo.h") qt5_add_dbus_interface(kdeui_LIB_SRCS ${kglobalaccel_component_xml} kglobalaccel_component_interface ) add_library(kcm_keys MODULE ${kcm_keys_PART_SRCS}) target_link_libraries(kcm_keys Qt5::DBus KF5::KCMUtils KF5::GlobalAccel KF5::I18n - KF5::IconThemes KF5::KIOWidgets KF5::XmlGui KF5::ItemModels KF5::ItemViews ) install(TARGETS kcm_keys DESTINATION ${KDE_INSTALL_PLUGINDIR} ) ########### install files ############### install( FILES keys.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install( FILES schemes/kde3.kksrc schemes/kde4.kksrc schemes/mac4.kksrc schemes/unix3.kksrc schemes/win3.kksrc schemes/win4.kksrc schemes/wm3.kksrc DESTINATION ${KDE_INSTALL_DATADIR}/kcmkeys ) diff --git a/kcms/keys/kglobalshortcutseditor.cpp b/kcms/keys/kglobalshortcutseditor.cpp index e7e78ba64..8f71d0d4c 100644 --- a/kcms/keys/kglobalshortcutseditor.cpp +++ b/kcms/keys/kglobalshortcutseditor.cpp @@ -1,848 +1,846 @@ /* * Copyright 2008 Michael Jansen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kglobalshortcutseditor.h" #include "ui_kglobalshortcutseditor.h" #include "ui_select_application.h" #include "export_scheme_dialog.h" #include "select_scheme_dialog.h" #include "globalshortcuts.h" #include "kglobalaccel_interface.h" #include "kglobalaccel_component_interface.h" #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * README * * This class was created because the kshortcutseditor class has some shortcomings. That class uses * QTreeWidget and therefore makes it impossible for an outsider to switch the models. But the * global shortcuts editor did that. Each global component ( kded, krunner, kopete ... ) was * destined to be separately edited. If you selected another component the kshortcutseditor was * cleared and refilled. But the items take care of undoing. Therefore when switching the component * you lost the undo history. * * To solve that problem this class keeps one kshortcuteditor for each component. That is easier * than rewrite that dialog to a model/view framework. * * It perfectly covers a bug of KExtedableItemDelegate when clearing and refilling the associated * model. */ class ComponentData { public: ComponentData( const QString &uniqueName, const QDBusObjectPath &path, KShortcutsEditor *_editor); ~ComponentData(); QString uniqueName() const; KShortcutsEditor *editor(); QDBusObjectPath dbusPath(); private: QString _uniqueName; QDBusObjectPath _path; QPointer _editor; }; ComponentData::ComponentData( const QString &uniqueName, const QDBusObjectPath &path, KShortcutsEditor *editor) : _uniqueName(uniqueName), _path(path), _editor(editor) {} ComponentData::~ComponentData() { delete _editor; _editor = nullptr; } QString ComponentData::uniqueName() const { return _uniqueName; } QDBusObjectPath ComponentData::dbusPath() { return _path; } KShortcutsEditor *ComponentData::editor() { return _editor; } class KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate { public: KGlobalShortcutsEditorPrivate(KGlobalShortcutsEditor *q) : q(q), bus(QDBusConnection::sessionBus()) {} //! Setup the gui void initGUI(); //! Load the component at @a componentPath bool loadComponent(const QDBusObjectPath &componentPath); //! Return the componentPath for component QDBusObjectPath componentPath(const QString &componentUnique); //! Remove the component void removeComponent(const QString &componentUnique); KGlobalShortcutsEditor *q; Ui::KGlobalShortcutsEditor ui; Ui::SelectApplicationDialog selectApplicationDialogUi; QDialog *selectApplicationDialog = nullptr; QStackedWidget *stack = nullptr; KShortcutsEditor::ActionTypes actionTypes; QHash components; QDBusConnection bus; QStandardItemModel *model = nullptr; KCategorizedSortFilterProxyModel *proxyModel = nullptr; }; void loadAppsCategory(KServiceGroup::Ptr group, QStandardItemModel *model, QStandardItem *item) { if (group && group->isValid()) { KServiceGroup::List list = group->entries(); for( KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) { const KSycocaEntry::Ptr p = (*it); if (p->isType(KST_KService)) { const KService::Ptr service(static_cast(p.data())); if (!service->noDisplay()) { QString genericName = service->genericName(); if (genericName.isNull()) { genericName = service->comment(); } QString description; if (!service->genericName().isEmpty() && service->genericName() != service->name()) { description = service->genericName(); } else if (!service->comment().isEmpty()) { description = service->comment(); } QStandardItem *subItem = new QStandardItem(QIcon::fromTheme(service->icon()), service->name()); subItem->setData(service->entryPath()); if (item) { item->appendRow(subItem); } else { model->appendRow(subItem); } } } else if (p->isType(KST_KServiceGroup)) { KServiceGroup::Ptr subGroup(static_cast(p.data())); if (!subGroup->noDisplay() && subGroup->childCount() > 0) { if (item) { loadAppsCategory(subGroup, model, item); } else { QStandardItem *subItem = new QStandardItem(QIcon::fromTheme(subGroup->icon()), subGroup->caption()); model->appendRow(subItem); loadAppsCategory(subGroup, model, subItem); } } } } } } void KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::initGUI() { ui.setupUi(q); selectApplicationDialog = new QDialog(); selectApplicationDialogUi.setupUi(selectApplicationDialog); // Create a stacked widget. stack = new QStackedWidget(q); ui.currentComponentLayout->addWidget(stack); //HACK to make those two un-alignable components, aligned ui.componentLabel->setMinimumHeight(ui.lineEditSpacer->sizeHint().height()); ui.lineEditSpacer->setVisible(false); ui.addButton->setIcon(QIcon::fromTheme("list-add")); ui.removeButton->setIcon(QIcon::fromTheme("list-remove")); ui.components->setCategoryDrawer(new KCategoryDrawer(ui.components)); ui.components->setModelColumn(0); // Build the menu QMenu *menu = new QMenu(q); menu->addAction( QIcon::fromTheme(QStringLiteral("document-import")), i18n("Import Scheme..."), q, SLOT(importScheme())); menu->addAction( QIcon::fromTheme(QStringLiteral("document-export")), i18n("Export Scheme..."), q, SLOT(exportScheme())); menu->addAction( i18n("Set All Shortcuts to None"), q, SLOT(clearConfiguration())); connect(ui.addButton, &QToolButton::clicked, [this]() { if (!selectApplicationDialogUi.treeView->model()) { KRecursiveFilterProxyModel *filterModel = new KRecursiveFilterProxyModel(selectApplicationDialogUi.treeView); QStandardItemModel *appModel = new QStandardItemModel(selectApplicationDialogUi.treeView); selectApplicationDialogUi.kfilterproxysearchline->setProxy(filterModel); filterModel->setSourceModel(appModel); appModel->setHorizontalHeaderLabels({i18n("Applications")}); loadAppsCategory(KServiceGroup::root(), appModel, nullptr); selectApplicationDialogUi.treeView->setModel(filterModel); } selectApplicationDialog->show(); }); connect(selectApplicationDialog, &QDialog::accepted, [this]() { if (selectApplicationDialogUi.treeView->selectionModel()->selectedIndexes().length() == 1) { const QString desktopPath = selectApplicationDialogUi.treeView->model()->data(selectApplicationDialogUi.treeView->selectionModel()->selectedIndexes().first(), Qt::UserRole+1).toString(); if (!desktopPath.isEmpty() &&QFile::exists(desktopPath) ) { const QString desktopFile = desktopPath.split(QLatin1Char('/')).last(); if (!desktopPath.isEmpty()) { KDesktopFile sourceDF(desktopPath); KDesktopFile *destinationDF = sourceDF.copyTo(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kglobalaccel/") + desktopFile); qWarning()<sync(); //TODO: a DBUS call to tell the daemon to refresh desktop files // Create a action collection for our current component:context KActionCollection *col = new KActionCollection(q, desktopFile); foreach(const QString &actionId, sourceDF.readActions()) { const QString friendlyName = sourceDF.actionGroup(actionId).readEntry(QStringLiteral("Name")); QAction *action = col->addAction(actionId); action->setProperty("isConfigurationAction", QVariant(true)); // see KAction::~KAction action->setProperty("componentDisplayName", friendlyName); action->setText(friendlyName); KGlobalAccel::self()->setShortcut(action, QList()); QStringList sequencesStrings = sourceDF.actionGroup(actionId).readEntry(QStringLiteral("X-KDE-Shortcuts"), QString()).split(QLatin1Char('/')); QList sequences; if (!sequencesStrings.isEmpty()) { Q_FOREACH (const QString &seqString, sequencesStrings) { sequences.append(QKeySequence(seqString)); } } if (!sequences.isEmpty()) { KGlobalAccel::self()->setDefaultShortcut(action, sequences); } } //Global launch action { const QString friendlyName = i18n("Launch %1", sourceDF.readName()); QAction *action = col->addAction(QStringLiteral("_launch")); action->setProperty("isConfigurationAction", QVariant(true)); // see KAction::~KAction action->setProperty("componentDisplayName", friendlyName); action->setText(friendlyName); KGlobalAccel::self()->setShortcut(action, QList()); QStringList sequencesStrings = sourceDF.desktopGroup().readEntry(QStringLiteral("X-KDE-Shortcuts"), QString()).split(QLatin1Char('/')); QList sequences; if (!sequencesStrings.isEmpty()) { Q_FOREACH (const QString &seqString, sequencesStrings) { sequences.append(QKeySequence(seqString)); } } if (!sequences.isEmpty()) { KGlobalAccel::self()->setDefaultShortcut(action, sequences); } } q->addCollection(col, QDBusObjectPath(), desktopFile, sourceDF.readName()); } } } }); connect(ui.removeButton, &QToolButton::clicked, [this]() { //TODO: different way to remove components that are desktop files //disabled desktop files need Hidden=true key QString name = proxyModel->data(ui.components->currentIndex()).toString(); QString componentUnique = components.value(name)->uniqueName(); // The confirmation text is different when the component is active if (KGlobalAccel::isComponentActive(componentUnique)) { if (KMessageBox::questionYesNo( q, i18n("Component '%1' is currently active. Only global shortcuts currently not active will be removed from the list.\n" "All global shortcuts will reregister themselves with their defaults when they are next started.", componentUnique), i18n("Remove component")) != KMessageBox::Yes) { return; } } else { if (KMessageBox::questionYesNo( q, i18n("Are you sure you want to remove the registered shortcuts for component '%1'? " "The component and shortcuts will reregister themselves with their default settings" " when they are next started.", componentUnique), i18n("Remove component")) != KMessageBox::Yes) { return; } } // Initiate the removing of the component. if (KGlobalAccel::cleanComponent(componentUnique)) { // Get the objectPath BEFORE we delete the source of it QDBusObjectPath oPath = components.value(name)->dbusPath(); // Remove the component from the gui removeComponent(componentUnique); // Load it again // ############# if (loadComponent(oPath)) { // Active it q->activateComponent(name); } } }); ui.menu_button->setMenu(menu); proxyModel = new KCategorizedSortFilterProxyModel(q); proxyModel->setCategorizedModel(true); model = new QStandardItemModel(0, 1, proxyModel); proxyModel->setSourceModel(model); proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); ui.components->setModel(proxyModel); connect(ui.components->selectionModel(), &QItemSelectionModel::currentChanged, q, [this](const QModelIndex &index) { QString name = proxyModel->data(index).toString(); q->activateComponent(name); }); } KGlobalShortcutsEditor::KGlobalShortcutsEditor(QWidget *parent, KShortcutsEditor::ActionTypes actionTypes) : QWidget(parent), d(new KGlobalShortcutsEditorPrivate(this)) { d->actionTypes = actionTypes; // Setup the ui d->initGUI(); } KGlobalShortcutsEditor::~KGlobalShortcutsEditor() { // Before closing the door, undo all changes undo(); delete d->selectApplicationDialog; qDeleteAll(d->components); delete d; } void KGlobalShortcutsEditor::activateComponent(const QString &component) { QHash::Iterator iter = d->components.find(component); if (iter == d->components.end()) { Q_ASSERT(iter != d->components.end()); return; } else { QModelIndexList results = d->proxyModel->match(d->proxyModel->index(0, 0), Qt::DisplayRole, component); Q_ASSERT(!results.isEmpty()); if (results.first().isValid()) { // Known component. Get it. d->ui.components->setCurrentIndex(results.first()); d->stack->setCurrentWidget((*iter)->editor()); } } } void KGlobalShortcutsEditor::addCollection( KActionCollection *collection, const QDBusObjectPath &objectPath, const QString &id, const QString &friendlyName) { KShortcutsEditor *editor; // Check if this component is known QHash::Iterator iter = d->components.find(friendlyName); if (iter == d->components.end()) { // Unknown component. Create an editor. editor = new KShortcutsEditor(this, d->actionTypes); d->stack->addWidget(editor); - // try to find one appropriate icon ( allowing NULL pixmap to be returned) - QPixmap pixmap = KIconLoader::global()->loadIcon(id, KIconLoader::Small, 0, - KIconLoader::DefaultState, QStringList(), nullptr, true); - if (pixmap.isNull()) { + // try to find one appropriate icon + QIcon icon = QIcon::fromTheme(id); + if (icon.isNull()) { KService::Ptr service = KService::serviceByStorageId(id); - if(service) { - pixmap = KIconLoader::global()->loadIcon(service->icon(), KIconLoader::Small, 0, - KIconLoader::DefaultState, QStringList(), nullptr, true); + if (service) { + icon = QIcon::fromTheme(service->icon()); } } - // if NULL pixmap is returned, use the F.D.O "system-run" icon - if (pixmap.isNull()) { - pixmap = KIconLoader::global()->loadIcon(QStringLiteral("system-run"), KIconLoader::Small); + + // if NULL icon is returned, use the F.D.O "system-run" icon + if (icon.isNull()) { + icon = QIcon::fromTheme(QStringLiteral("system-run")); } // Add to the component list - QStandardItem *item = new QStandardItem(pixmap, friendlyName); + QStandardItem *item = new QStandardItem(icon, friendlyName); if (id.endsWith(QLatin1String(".desktop"))) { item->setData(i18n("Application Launchers"), KCategorizedSortFilterProxyModel::CategoryDisplayRole); item->setData(0, KCategorizedSortFilterProxyModel::CategorySortRole); } else { item->setData(i18n("Other Shortcuts"), KCategorizedSortFilterProxyModel::CategoryDisplayRole); item->setData(1, KCategorizedSortFilterProxyModel::CategorySortRole); } d->model->appendRow(item); d->proxyModel->sort(0); // Add to our component registry ComponentData *cd = new ComponentData(id, objectPath, editor); d->components.insert(friendlyName, cd); connect(editor, &KShortcutsEditor::keyChange, this, &KGlobalShortcutsEditor::_k_key_changed); } else { // Known component. editor = (*iter)->editor(); } // Add the collection to the editor of the component editor->addCollection(collection, friendlyName); if (d->proxyModel->rowCount() > -1) { d->ui.components->setCurrentIndex(d->proxyModel->index(0, 0)); const QString name = d->proxyModel->data(d->proxyModel->index(0, 0)).toString(); activateComponent(name); } } void KGlobalShortcutsEditor::clearConfiguration() { const QString name = d->proxyModel->data(d->ui.components->currentIndex()).toString(); d->components[name]->editor()->clearConfiguration(); } void KGlobalShortcutsEditor::defaults(ComponentScope scope) { switch (scope) { case AllComponents: for (ComponentData *cd : qAsConst(d->components)) { // The editors are responsible for the reset cd->editor()->allDefault(); } break; case CurrentComponent: { const QString name = d->proxyModel->data(d->ui.components->currentIndex()).toString(); // The editors are responsible for the reset d->components[name]->editor()->allDefault(); } break; default: Q_ASSERT(false); }; } void KGlobalShortcutsEditor::clear() { // Remove all components and their associated editors qDeleteAll(d->components); d->components.clear(); d->model->clear(); } static bool compare(const QString &a, const QString &b) { return a.toLower().localeAwareCompare(b.toLower()) < 0; } void KGlobalShortcutsEditor::exportScheme() { QStringList keys = d->components.keys(); std::sort(keys.begin(), keys.end(), compare); ExportSchemeDialog dia(keys); if (dia.exec() != KMessageBox::Ok) { return; } const QUrl url = QFileDialog::getSaveFileUrl(this, QString(), QUrl(), QStringLiteral("*.kksrc")); if (!url.isEmpty()) { KConfig config(url.path(), KConfig::SimpleConfig); // TODO: Bug ossi to provide a method for this Q_FOREACH(const QString &group, config.groupList()) { // do not overwrite the Settings group. That makes it possible to // update the standard scheme kksrc file with the editor. if (group == QLatin1String("Settings")) continue; config.deleteGroup(group); } exportConfiguration(dia.selectedComponents(), &config); } } void KGlobalShortcutsEditor::importScheme() { // Check for unsaved modifications if (isModified()) { int choice = KMessageBox::warningContinueCancel( this, i18n("Your current changes will be lost if you load another scheme before saving this one"), i18n("Load Shortcut Scheme"), KGuiItem(i18n("Load"))); if (choice != KMessageBox::Continue) { return; } } SelectSchemeDialog dialog(this); if (dialog.exec()) { QUrl url = dialog.selectedScheme(); if (!url.isLocalFile()) { KMessageBox::sorry(this, i18n("This file (%1) does not exist. You can only select local files.", url.url())); return; } KConfig config(url.path(), KConfig::SimpleConfig); importConfiguration(&config); } } void KGlobalShortcutsEditor::load() { // Connect to kglobalaccel. If that fails there is no need to continue. qRegisterMetaType >(); qDBusRegisterMetaType >(); qDBusRegisterMetaType >(); qDBusRegisterMetaType(); org::kde::KGlobalAccel kglobalaccel( QStringLiteral("org.kde.kglobalaccel"), QStringLiteral("/kglobalaccel"), d->bus); if (!kglobalaccel.isValid()) { QString errorString; QDBusError error = kglobalaccel.lastError(); // The global shortcuts DBus service manages all global shortcuts and we // can't do anything useful without it. if (error.isValid()) { errorString = i18n("Message: %1\nError: %2", error.message(), error.name()); } KMessageBox::sorry( this, i18n("Failed to contact the KDE global shortcuts daemon\n") + errorString ); return; } // Undo all changes not yet applied undo(); clear(); QDBusReply< QList > componentsRc = kglobalaccel.allComponents(); if (!componentsRc.isValid()) { // Sometimes error pop up only after the first real call. QString errorString; QDBusError error = componentsRc.error(); // The global shortcuts DBus service manages all global shortcuts and we // can't do anything useful without it. if (error.isValid()) { errorString = i18n("Message: %1\nError: %2", error.message(), error.name()); } KMessageBox::sorry( this, i18n("Failed to contact the KDE global shortcuts daemon\n") + errorString ); return; } QList components = componentsRc; Q_FOREACH(const QDBusObjectPath &componentPath, components) { d->loadComponent(componentPath); } // Q_FOREACH(component) } void KGlobalShortcutsEditor::save() { // The editors are responsible for the saving //qDebug() << "Save the changes"; Q_FOREACH (ComponentData *cd, d->components) { cd->editor()->commit(); } } void KGlobalShortcutsEditor::importConfiguration(KConfigBase *config) { //qDebug() << config->groupList(); // In a first step clean out the current configurations. We do this // because we want to minimize the chance of conflicts. Q_FOREACH (ComponentData *cd, d->components) { KConfigGroup group(config, cd->uniqueName()); //qDebug() << cd->uniqueName() << group.name(); if (group.exists()) { //qDebug() << "Removing" << cd->uniqueName(); cd->editor()->clearConfiguration(); } } // Now import the new configurations. Q_FOREACH (ComponentData *cd, d->components) { KConfigGroup group(config, cd->uniqueName()); if (group.exists()) { //qDebug() << "Importing" << cd->uniqueName(); cd->editor()->importConfiguration(&group); } } } void KGlobalShortcutsEditor::exportConfiguration(QStringList components, KConfig *config) const { Q_FOREACH (const QString &componentFriendly, components) { QHash::Iterator iter = d->components.find(componentFriendly); if (iter == d->components.end()) { Q_ASSERT(iter != d->components.end()); continue; } else { KConfigGroup group(config, (*iter)->uniqueName()); (*iter)->editor()->exportConfiguration(&group); } } } void KGlobalShortcutsEditor::undo() { // The editors are responsible for the undo //qDebug() << "Undo the changes"; Q_FOREACH (ComponentData *cd, d->components) { cd->editor()->undoChanges(); } } bool KGlobalShortcutsEditor::isModified() const { Q_FOREACH (ComponentData *cd, d->components) { if (cd->editor()->isModified()) { return true; } } return false; } void KGlobalShortcutsEditor::_k_key_changed() { emit changed(isModified()); } QDBusObjectPath KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::componentPath(const QString &componentUnique) { return QDBusObjectPath(QStringLiteral("/component/") + componentUnique); } bool KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::loadComponent(const QDBusObjectPath &componentPath) { // Get the component org::kde::kglobalaccel::Component component( QStringLiteral("org.kde.kglobalaccel"), componentPath.path(), bus); if (!component.isValid()) { //qDebug() << "Component " << componentPath.path() << "not valid! Skipping!"; return false; } // Get the shortcut contexts. QDBusReply shortcutContextsRc = component.getShortcutContexts(); if (!shortcutContextsRc.isValid()) { //qDebug() << "Failed to get contexts for component " //<< componentPath.path() <<"! Skipping!"; //qDebug() << shortcutContextsRc.error(); return false; } QStringList shortcutContexts = shortcutContextsRc; // We add the shortcuts for all shortcut contexts to the editor. This // way the user keeps full control of it's shortcuts. Q_FOREACH (const QString &shortcutContext, shortcutContexts) { QDBusReply< QList > shortcutsRc = component.allShortcutInfos(shortcutContext); if (!shortcutsRc.isValid()) { //qDebug() << "allShortcutInfos() failed for " << componentPath.path() << shortcutContext; continue; } QList shortcuts = shortcutsRc; // Shouldn't happen. But you never know if (shortcuts.isEmpty()) { //qDebug() << "Got shortcut context" << shortcutContext << "without shortcuts for" //<< componentPath.path(); continue; } // It's safe now const QString componentUnique = shortcuts[0].componentUniqueName(); QString componentContextId = componentUnique; // kglobalaccel knows that '|' is our separator between // component and context if (shortcutContext != QLatin1String("default")) { componentContextId += QLatin1String("|") + shortcutContext; } // Create a action collection for our current component:context KActionCollection* col = new KActionCollection( q, componentContextId); // Now add the shortcuts. Q_FOREACH (const KGlobalShortcutInfo &shortcut, shortcuts) { const QString &objectName = shortcut.uniqueName(); QAction *action = col->addAction(objectName); action->setProperty("isConfigurationAction", QVariant(true)); // see KAction::~KAction action->setProperty("componentDisplayName", shortcut.componentFriendlyName()); action->setText(shortcut.friendlyName()); // Always call this to enable global shortcuts for the action. The editor widget // checks it. // Also actually loads the shortcut using the KAction::Autoloading mechanism. // Avoid setting the default shortcut; it would just be written to the global // configuration so we would not get the real one below. KGlobalAccel::self()->setShortcut(action, QList()); // The default shortcut will never be loaded because it's pointless in a real // application. There are no scarce resources [i.e. physical keys] to manage // so applications can set them at will and there's no autoloading. QList sc = shortcut.defaultKeys(); if (sc.count()>0) { KGlobalAccel::self()->setDefaultShortcut(action, sc); } } // Q_FOREACH(shortcut) QString componentFriendlyName = shortcuts[0].componentFriendlyName(); if (shortcuts[0].contextUniqueName() != QLatin1String("default")) { componentFriendlyName += QString('[') + shortcuts[0].contextFriendlyName() + QString(']'); } q->addCollection(col, componentPath, componentContextId, componentFriendlyName ); } // Q_FOREACH(context) return true; } void KGlobalShortcutsEditor::KGlobalShortcutsEditorPrivate::removeComponent( const QString &componentUnique ) { // TODO: Remove contexts too. Q_FOREACH (const QString &text, components.keys()) { if (components.value(text)->uniqueName() == componentUnique) { // Remove from QComboBox QModelIndexList results = proxyModel->match(proxyModel->index(0, 0), Qt::DisplayRole, text); Q_ASSERT(!results.isEmpty()); model->removeRow(proxyModel->mapToSource(results.first()).row()); // Remove from QStackedWidget stack->removeWidget(components[text]->editor()); // Remove the componentData delete components.take(text); } } } diff --git a/kcms/runners/CMakeLists.txt b/kcms/runners/CMakeLists.txt index 48b938618..81b917358 100644 --- a/kcms/runners/CMakeLists.txt +++ b/kcms/runners/CMakeLists.txt @@ -1,24 +1,23 @@ # KI18N Translation Domain for this library add_definitions(-DTRANSLATION_DOMAIN=\"kcm_search\") set(kcm_search_SRCS kcm.cpp ) add_library(kcm_plasmasearch MODULE ${kcm_search_SRCS}) target_link_libraries(kcm_plasmasearch KF5::KIOWidgets KF5::CoreAddons KF5::KCMUtils KF5::Runner KF5::I18n - KF5::IconThemes Qt5::DBus Qt5::Widgets ) install(FILES kcm_plasmasearch.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) install(TARGETS kcm_plasmasearch DESTINATION ${KDE_INSTALL_PLUGINDIR}) diff --git a/kcms/runners/kcm.cpp b/kcms/runners/kcm.cpp index 3ce5f4f38..29acec0dd 100644 --- a/kcms/runners/kcm.cpp +++ b/kcms/runners/kcm.cpp @@ -1,108 +1,107 @@ /* This file is part of the KDE Project Copyright (c) 2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcm.h" #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(SearchConfigModuleFactory, registerPlugin();) SearchConfigModule::SearchConfigModule(QWidget* parent, const QVariantList& args) : KCModule(parent, args) , m_config("krunnerrc") { KAboutData* about = new KAboutData(QStringLiteral("kcm_search"), i18nc("kcm name for About dialog", "Configure Search Bar"), QStringLiteral("0.1"), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("Vishesh Handa"), QString(), QStringLiteral("vhanda@kde.org")); setAboutData(about); setButtons(Apply | Default); QVBoxLayout* layout = new QVBoxLayout(this); QHBoxLayout *headerLayout = new QHBoxLayout(this); QLabel *label = new QLabel(i18n("Enable or disable KRunner plugins:")); QPushButton *clearHistoryButton = new QPushButton(i18n("Clear History")); clearHistoryButton->setIcon(QIcon::fromTheme(isRightToLeft() ? QStringLiteral("edit-clear-locationbar-ltr") : QStringLiteral("edit-clear-locationbar-rtl"))); connect(clearHistoryButton, &QPushButton::clicked, this, [this] { KConfigGroup generalConfig(m_config.group("General")); generalConfig.deleteEntry("history"); generalConfig.sync(); }); headerLayout->addWidget(label); headerLayout->addStretch(); headerLayout->addWidget(clearHistoryButton); m_pluginSelector = new KPluginSelector(this); //overload, can't use the new syntax connect(m_pluginSelector, SIGNAL(changed(bool)), this, SIGNAL(changed(bool))); layout->addLayout(headerLayout); layout->addWidget(m_pluginSelector); Plasma::RunnerManager *manager = new Plasma::RunnerManager(this); manager->reloadConfiguration(); } void SearchConfigModule::load() { // Set focus on the pluginselector to pass focus to search bar. m_pluginSelector->setFocus(Qt::OtherFocusReason); m_pluginSelector->addPlugins(Plasma::RunnerManager::listRunnerInfo(), KPluginSelector::ReadConfigFile, i18n("Available Plugins"), QString(), KSharedConfig::openConfig(QLatin1String( "krunnerrc" ))); } void SearchConfigModule::save() { m_pluginSelector->save(); } void SearchConfigModule::defaults() { } #include "kcm.moc"