Git commit f0bcd8cf623f154b821d9e4e7dab16d2d3bb7886 by Henri Chain. Committed on 21/04/2021 at 08:40. Pushed by hchain into branch 'master'.
Import autostart KCM from plasma-desktop. Taken from commit 4ce9a4a915511acbd41450da486d8e5d5bae10da in plasma-desktop The commit history of the KCM can be followed in 9db1c0593e84cdb705fc61cddbdc4e5a9c318b97 A +3 -0 doc/kcontrol/autostart/CMakeLists.txt A +156 -0 doc/kcontrol/autostart/index.docbook A +3 -0 kcms/autostart/AUTHORS A +16 -0 kcms/autostart/CMakeLists.txt A +3 -0 kcms/autostart/Messages.sh A +70 -0 kcms/autostart/autostart.cpp [License: GPL (v2+)] A +48 -0 kcms/autostart/autostart.h [License: GPL (v2+)] A +435 -0 kcms/autostart/autostartmodel.cpp [License: GPL (v2+)] A +87 -0 kcms/autostart/autostartmodel.h [License: GPL (v2+)] A +206 -0 kcms/autostart/package/contents/ui/main.qml [License: GPL (v2+)] A +195 -0 kcms/autostart/package/metadata.desktop https://invent.kde.org/plasma/plasma-workspace/commit/f0bcd8cf623f154b821d9e4e7dab16d2d3bb7886 diff --git a/doc/kcontrol/autostart/CMakeLists.txt b/doc/kcontrol/autostart/CMakeLists.txt new file mode 100644 index 000000000..fa673b22f --- /dev/null +++ b/doc/kcontrol/autostart/CMakeLists.txt @@ -0,0 +1,3 @@ +########### install files ############### +# +kdoctools_create_handbook(index.docbook INSTALL_DESTINATION ${KDE_INSTALL_DOCBUNDLEDIR}/en SUBDIR kcontrol/autostart) diff --git a/doc/kcontrol/autostart/index.docbook b/doc/kcontrol/autostart/index.docbook new file mode 100644 index 000000000..e5e7ed442 --- /dev/null +++ b/doc/kcontrol/autostart/index.docbook @@ -0,0 +1,156 @@ +<?xml version="1.0" ?> +<!DOCTYPE article PUBLIC "-//KDE//DTD DocBook XML V4.5-Based Variant V1.1//EN" +"dtd/kdedbx45.dtd" [ + <!ENTITY % addindex "IGNORE"> + <!ENTITY % English "INCLUDE"> +]> +<article id="autostart" lang="&language;"> +<articleinfo> +<title>Autostart</title> +<authorgroup> +<author>&Anne-Marie.Mahfouf;</author> +<!-- TRANS:ROLES_OF_TRANSLATORS --> +</authorgroup> + +<date>2021-04-05</date> +<releaseinfo>&plasma; 5.20</releaseinfo> + +<keywordset> +<keyword>KDE</keyword> +<keyword>System Settings</keyword> +<keyword>autostart</keyword> +<keyword>desktop file</keyword> +<keyword>script file</keyword> +</keywordset> +</articleinfo> + +<sect1 id="kcm_autostart"> +<title>Autostart Manager</title> + +<para>This module is a configuration tool for managing what programs start up with your personal &plasma;. It allows you to add programs or scripts so they automatically run during startup or shutdown of your &plasma; session and to manage them.</para> + +<note><para>Please note that in this module all changes are immediately applied.</para></note> + +<para>The program scans <filename>$HOME/.config/autostart/</filename>, +<filename class="directory">$HOME/.config/plasma-workspace/env</filename> and <filename class="directory">$HOME/.config/plasma-workspace/shutdown</filename> folders to check what programs and scripts are already there and displays them. It allows you to manage them easily. +</para> + +<note><para>Note that you can change the location of your <filename class="directory">Autostart</filename> +folder in <menuchoice><guimenu>Applications</guimenu> <guimenuitem>Locations</guimenuitem></menuchoice> +in the <guilabel>Personalization</guilabel> category of the &systemsettings; and set a different folder +than <filename class="directory">$HOME/.config/autostart</filename>.</para></note> + +<para>Please read also <ulink url="help:/kcontrol/kcmsmserver">Desktop Session</ulink> and <ulink url="help:/kcontrol/kded">Background Services</ulink> for information how to configure the startup behavior of your &plasma; session.</para> + +<para>Some &kde; applications handle the autostart behavior on their own, ⪚ you can enable or disable autostart of an application in the settings dialog (&kalarm;) or you have to use <menuchoice><guimenu>File</guimenu><guimenuitem>Quit</guimenuitem></menuchoice> (&konversation;, &kopete;), otherwise the application is still running in the systemtray and will be restarted on next login. +</para> + +<sect2 id="migration"> +<title>Migration from &kde; Workspaces 4</title> +<para>To migrate your personal autostart setting from &kde; Workspaces 4:</para> +<simplelist> +<member>Copy desktop files from <filename class="directory">$HOME/.kde/Autostart</filename> to <filename class="directory">$HOME/.config/autostart</filename></member> +<member>Copy pre startup script files from <filename class="directory">$HOME/.kde/Autostart</filename> to <filename class="directory">$HOME/.config/plasma-workspace/env</filename></member> +<member>Copy shutdown script files from <filename class="directory">$HOME/.kde/Autostart</filename> to <filename class="directory">$HOME/.config/plasma-workspace/shutdown</filename></member> +</simplelist> +</sect2> + +<sect2 id="disabling_autostart"> +<title>Disabling Autostart Files Provided by Your Distribution</title> +<para>The correct way to disable an autostart item, for example the printer-applet if you use printer from time to time, is to copy its <filename class="extension">.desktop</filename> file to your personal autostart folder. Anything of the same name in <filename class="directory">$HOME/.config/autostart</filename> overrides the <filename class="extension">.desktop</filename> file in the default package. Add the following line to the copied <filename class="extension">.desktop</filename> file:</para> +<programlisting> +Hidden=true +</programlisting> +</sect2> + +<sect2 id="current"> +<title>Files display</title> +<para>The main part of the module displays the programs that are loaded when &plasma; starts and scripts that are run when &plasma; starts or shutdowns.</para> + +<variablelist> +<varlistentry><term><guilabel>Icon</guilabel></term> +<listitem> +<para> +This column shows the icon of the program or script you want to start with &plasma;. The icon is extracted from the Desktop file from the Icon key for a program and is the default icon for a script. +</para> +</listitem> +</varlistentry> + +<varlistentry><term><guilabel>Name</guilabel></term> +<listitem> +<para> +This column shows the name of the program or script you want to start with &plasma;. The name is extracted from the <filename class="extension">.desktop</filename> file from the <literal>Name</literal> key for a program and is the filename for a script. +</para> +</listitem> +</varlistentry> + +<varlistentry><term><guilabel>Properties</guilabel></term> +<listitem> +<para> +This button is only shown when you hover the item with the mouse pointer. The button (only enabled for programs &ie; <filename class="extension">.desktop</filename> files) allows you to change the properties of the program or script. You have general properties, permissions properties, a preview when applicable, and properties related to the application for programs. The default command is extracted from the <filename class="extension">.desktop</filename> file from the <literal>Exec</literal> key. +</para> +<para> +For a script, the command is the path to the script and can not be modified. +</para> +</listitem> +</varlistentry> + +<varlistentry><term><guilabel>Remove</guilabel></term> +<listitem> +<para> +This button is only shown when you hover the item with the mouse pointer. Pressing the <guibutton>−</guibutton> button will immediately remove the Desktop file for the program or the script or symbolic link in the <filename class="directory">Autostart</filename> folder. +</para> +</listitem> +</varlistentry> +</variablelist> +</sect2> + +<sect2 id="actions"> +<title>Actions</title> +<para> +On the bottom, you have the combined <guibutton>Add...</guibutton> button to choose the type of item you want to add. You can add programs and login or logout scripts. +</para> + +<variablelist> +<varlistentry><term><guimenuitem>Add Program</guimenuitem></term> +<listitem> +<para> +Clicking this item displays the standard &plasma; <guilabel>Choose Application</guilabel> dialog and allows you to choose which program you want to start. After choosing the program, clicking <guibutton>OK</guibutton> brings you the properties for this program. +</para> +<para> +This will copy the program <filename class="extension">.desktop</filename> file in your <filename class="directory">Autostart</filename> folder. +</para> +</listitem> +</varlistentry> + +<varlistentry><term><guimenuitem>Add Login Script...</guimenuitem></term> +<listitem> +<para> +This item opens a dialog that asks you for the location of the script you want to add. Scripts set to run on login are copied or symlinked in <filename class="directory">$HOME/.config/autostart</filename> and will be run during Plasma startup. +</para> +</listitem> +</varlistentry> +<varlistentry><term><guimenuitem>Add Logout Script...</guimenuitem></term> +<listitem> +<para> +This item opens a dialog that asks you for the location of the script you want to add. Scripts set on to be run on logout are copied or symlinked in the <filename class="directory">$HOME/.config/plasma-workspace/shutdown</filename> directory and will be automatically run during &plasma; shutdown after the user has logged out. +</para> +</listitem> +</varlistentry> +</variablelist> +</sect2> +</sect1> +</article> + +<!-- +Local Variables: +mode: xml +sgml-minimize-attributes:nil +sgml-general-insert-case:lower +sgml-indent-step:0 +sgml-indent-data:nil +End: + +vim:tabstop=2:shiftwidth=2:expandtab +kate: space-indent on; indent-width 2; tab-width 2; indent-mode none; +--> diff --git a/kcms/autostart/AUTHORS b/kcms/autostart/AUTHORS new file mode 100644 index 000000000..040409dba --- /dev/null +++ b/kcms/autostart/AUTHORS @@ -0,0 +1,3 @@ +Stephen Leaf <[email protected]> +Montel Laurent <[email protected]> +Nicolas Fella <[email protected]> diff --git a/kcms/autostart/CMakeLists.txt b/kcms/autostart/CMakeLists.txt new file mode 100644 index 000000000..93644f68c --- /dev/null +++ b/kcms/autostart/CMakeLists.txt @@ -0,0 +1,16 @@ +# KI18N Translation Domain for this library +add_definitions(-DTRANSLATION_DOMAIN=\"kcm_autostart\") + +set(kcm_autostart_PART_SRCS + autostartmodel.cpp + autostart.cpp ) + +add_library(kcm_autostart MODULE ${kcm_autostart_PART_SRCS}) + +target_link_libraries(kcm_autostart KF5::I18n KF5::KIOCore KF5::KIOWidgets KF5::QuickAddons) + +kcoreaddons_desktop_to_json(kcm_autostart "package/metadata.desktop") + +install(TARGETS kcm_autostart DESTINATION ${KDE_INSTALL_PLUGINDIR}/kcms ) +install(FILES package/metadata.desktop RENAME autostart.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}) +kpackage_install_package(package kcm_autostart kcms) diff --git a/kcms/autostart/Messages.sh b/kcms/autostart/Messages.sh new file mode 100644 index 000000000..6a41f58b1 --- /dev/null +++ b/kcms/autostart/Messages.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +$XGETTEXT `find . -name '*.cpp' -o -name '*.qml'` -o $podir/kcm_autostart.pot diff --git a/kcms/autostart/autostart.cpp b/kcms/autostart/autostart.cpp new file mode 100644 index 000000000..50512c821 --- /dev/null +++ b/kcms/autostart/autostart.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2006-2007 by Stephen Leaf * + * [email protected] * + * Copyright (C) 2008 by Montel Laurent <[email protected]> * + * * + * 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 "autostart.h" + +#include <KAboutData> +#include <KLocalizedString> + +K_PLUGIN_CLASS_WITH_JSON(Autostart, "metadata.json") + +Autostart::Autostart(QObject *parent, const QVariantList &) + : KQuickAddons::ConfigModule(parent) + , m_model(new AutostartModel(this)) +{ + setButtons(Help); + + qmlRegisterUncreatableType<AutostartModel>("org.kde.plasma.kcm.autostart", 1, 0, "AutostartModel", QStringLiteral("Only for enums")); + + KAboutData *about = new KAboutData(QStringLiteral("kcm_autostart"), + i18n("Autostart"), + QStringLiteral("1.0"), + i18n("Session Autostart Manager Control Panel Module"), + KAboutLicense::GPL, + i18n("Copyright © 2006–2020 Autostart Manager team")); + about->addAuthor(i18n("Stephen Leaf"), QString(), QStringLiteral("[email protected]")); + about->addAuthor(i18n("Montel Laurent"), i18n("Maintainer"), QStringLiteral("[email protected]")); + about->addAuthor(i18n("Nicolas Fella"), QString(), QStringLiteral("[email protected]")); + setAboutData(about); +} + +Autostart::~Autostart() +{ +} + +AutostartModel *Autostart::model() const +{ + return m_model; +} + +void Autostart::load() +{ + m_model->load(); +} + +void Autostart::defaults() +{ +} + +void Autostart::save() +{ +} + +#include "autostart.moc" diff --git a/kcms/autostart/autostart.h b/kcms/autostart/autostart.h new file mode 100644 index 000000000..208c9f6e0 --- /dev/null +++ b/kcms/autostart/autostart.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2006-2007 by Stephen Leaf * + * [email protected] * + * Copyright (C) 2008 by Montel Laurent <[email protected]> * + * * + * 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 * + ***************************************************************************/ + +#ifndef AUTOSTART_H +#define AUTOSTART_H + +#include <KQuickAddons/ConfigModule> + +#include "autostartmodel.h" + +class Autostart : public KQuickAddons::ConfigModule +{ + Q_OBJECT + Q_PROPERTY(AutostartModel *model READ model CONSTANT) + +public: + explicit Autostart(QObject *parent, const QVariantList &); + ~Autostart() override; + + void load() override; + void save() override; + void defaults() override; + + AutostartModel *model() const; + +private: + AutostartModel *m_model; +}; + +#endif diff --git a/kcms/autostart/autostartmodel.cpp b/kcms/autostart/autostartmodel.cpp new file mode 100644 index 000000000..94d800c84 --- /dev/null +++ b/kcms/autostart/autostartmodel.cpp @@ -0,0 +1,435 @@ +/*************************************************************************** + * Copyright (C) 2020 by Méven Car <[email protected]> * + * * + * 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 "autostartmodel.h" + +#include <KConfigGroup> +#include <KDesktopFile> +#include <KShell> +#include <QDebug> +#include <QDir> +#include <QQuickItem> +#include <QQuickRenderControl> +#include <QStandardPaths> +#include <QWindow> + +#include <KFileItem> +#include <KIO/CopyJob> +#include <KIO/DeleteJob> +#include <KLocalizedString> +#include <KOpenWithDialog> +#include <KPropertiesDialog> + +#include <optional> + +// FDO user autostart directories are +// .config/autostart which has .desktop files executed by klaunch + +// Then we have Plasma-specific locations which run scripts +// .config/autostart-scripts which has scripts executed by ksmserver +// .config/plasma-workspace/shutdown which has scripts executed by startkde +// .config/plasma-workspace/env which has scripts executed by startkde + +// in the case of pre-startup they have to end in .sh +// everywhere else it doesn't matter + +// the comment above describes how autostart *currently* works, it is not definitive documentation on how autostart *should* work + +// share/autostart shouldn't be an option as this should be reserved for global autostart entries + +static std::optional<AutostartEntry> loadDesktopEntry(const QString &fileName) +{ + KDesktopFile config(fileName); + const KConfigGroup grp = config.desktopGroup(); + const auto name = config.readName(); + + const bool hidden = grp.readEntry("Hidden", false); + + if (hidden) { + return {}; + } + + const QStringList notShowList = grp.readXdgListEntry("NotShowIn"); + const QStringList onlyShowList = grp.readXdgListEntry("OnlyShowIn"); + const bool enabled = !(notShowList.contains(QLatin1String("KDE")) || (!onlyShowList.isEmpty() && !onlyShowList.contains(QLatin1String("KDE")))); + + if (!enabled) { + return {}; + } + + const auto lstEntry = grp.readXdgListEntry("OnlyShowIn"); + const bool onlyInPlasma = lstEntry.contains(QLatin1String("KDE")); + const QString iconName = config.readIcon(); + + const QString tryCommand = grp.readEntry("TryExec"); + + // Try to filter out entries that point to nonexistant programs + // If TryExec is either found in $PATH or is an absolute file path that exists + // This doesn't detect uninstalled Flatpaks for example though + if (!tryCommand.isEmpty() && QStandardPaths::findExecutable(tryCommand).isEmpty() && !QFile::exists(tryCommand)) { + return {}; + } + + return std::optional<AutostartEntry>({name, + AutostartModel::AutostartEntrySource::XdgAutoStart, // .config/autostart load desktop at startup + enabled, + fileName, + onlyInPlasma, + iconName}); +} + +AutostartModel::AutostartModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +QString AutostartModel::XdgAutoStartPath() const +{ + return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/autostart/"); +} + +void AutostartModel::load() +{ + beginResetModel(); + + m_entries.clear(); + + QDir autostartdir(XdgAutoStartPath()); + if (!autostartdir.exists()) { + autostartdir.mkpath(XdgAutoStartPath()); + } + + autostartdir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + + const auto filesInfo = autostartdir.entryInfoList(); + for (const QFileInfo &fi : filesInfo) { + if (!KDesktopFile::isDesktopFile(fi.fileName())) { + continue; + } + + const std::optional<AutostartEntry> entry = loadDesktopEntry(fi.absoluteFilePath()); + + if (!entry) { + continue; + } + + m_entries.push_back(entry.value()); + } + + loadScriptsFromDir(QStringLiteral("/autostart-scripts/"), AutostartModel::AutostartEntrySource::XdgScripts); + // Treat them as XdgScripts so they appear together in the UI + loadScriptsFromDir(QStringLiteral("/plasma-workspace/env/"), AutostartModel::AutostartEntrySource::XdgScripts); + + loadScriptsFromDir(QStringLiteral("/plasma-workspace/shutdown/"), AutostartModel::AutostartEntrySource::PlasmaShutdown); + + endResetModel(); +} + +void AutostartModel::loadScriptsFromDir(const QString &subDir, AutostartModel::AutostartEntrySource kind) +{ + const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + subDir; + QDir dir(path); + if (!dir.exists()) { + dir.mkpath(path); + } + + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + + const auto autostartDirFilesInfo = dir.entryInfoList(); + for (const QFileInfo &fi : autostartDirFilesInfo) { + QString fileName = fi.absoluteFilePath(); + const bool isSymlink = fi.isSymLink(); + if (isSymlink) { + fileName = fi.symLinkTarget(); + } + + m_entries.push_back({fileName, kind, true, fi.absoluteFilePath(), false, QStringLiteral("dialog-scripts")}); + } +} + +int AutostartModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return 0; + } + + return m_entries.count(); +} + +bool AutostartModel::reloadEntry(const QModelIndex &index, const QString &fileName) +{ + if (!checkIndex(index)) { + return false; + } + + const std::optional<AutostartEntry> newEntry = loadDesktopEntry(fileName); + + if (!newEntry) { + return false; + } + + m_entries.replace(index.row(), newEntry.value()); + Q_EMIT dataChanged(index, index); + return true; +} + +QVariant AutostartModel::data(const QModelIndex &index, int role) const +{ + if (!checkIndex(index)) { + return QVariant(); + } + + const auto &entry = m_entries.at(index.row()); + + switch (role) { + case Qt::DisplayRole: + return entry.name; + case Enabled: + return entry.enabled; + case Source: + return entry.source; + case FileName: + return entry.fileName; + case OnlyInPlasma: + return entry.onlyInPlasma; + case IconName: + return entry.iconName; + } + + return QVariant(); +} + +void AutostartModel::addApplication(const KService::Ptr &service) +{ + QString desktopPath; + // It is important to ensure that we make an exact copy of an existing + // desktop file (if selected) to enable users to override global autostarts. + // Also see + // https://bugs.launchpad.net/ubuntu/+source/kde-workspace/+bug/923360 + if (service->desktopEntryName().isEmpty() || service->entryPath().isEmpty()) { + // create a new desktop file in s_desktopPath + desktopPath = XdgAutoStartPath() + service->name() + QStringLiteral(".desktop"); + + KDesktopFile desktopFile(desktopPath); + KConfigGroup kcg = desktopFile.desktopGroup(); + kcg.writeEntry("Name", service->name()); + kcg.writeEntry("Exec", service->exec()); + kcg.writeEntry("Icon", service->icon()); + kcg.writeEntry("Path", ""); + kcg.writeEntry("Terminal", service->terminal() ? "True" : "False"); + kcg.writeEntry("Type", "Application"); + desktopFile.sync(); + + } else { + desktopPath = XdgAutoStartPath() + service->desktopEntryName() + QStringLiteral(".desktop"); + + QFile::remove(desktopPath); + + // copy original desktop file to new path + KDesktopFile desktopFile(service->entryPath()); + auto newDeskTopFile = desktopFile.copyTo(desktopPath); + newDeskTopFile->sync(); + } + + const auto entry = AutostartEntry{service->name(), + AutostartModel::AutostartEntrySource::XdgAutoStart, // .config/autostart load desktop at startup + true, + desktopPath, + false, + service->icon()}; + + int lastApplication = -1; + for (const AutostartEntry &e : qAsConst(m_entries)) { + if (e.source == AutostartModel::AutostartEntrySource::XdgScripts) { + break; + } + ++lastApplication; + } + + // push before the script items + const int index = lastApplication + 1; + + beginInsertRows(QModelIndex(), index, index); + + m_entries.insert(index, entry); + + endInsertRows(); +} + +void AutostartModel::showApplicationDialog(QQuickItem *context) +{ + KOpenWithDialog *owdlg = new KOpenWithDialog(); + owdlg->setAttribute(Qt::WA_DeleteOnClose); + + if (context && context->window()) { + if (QWindow *actualWindow = QQuickRenderControl::renderWindowFor(context->window())) { + owdlg->winId(); // so it creates windowHandle + owdlg->windowHandle()->setTransientParent(actualWindow); + owdlg->setModal(true); + } + } + + connect(owdlg, &QDialog::finished, this, [this, owdlg](int result) { + if (result != QDialog::Accepted) { + return; + } + + const KService::Ptr service = owdlg->service(); + + Q_ASSERT(service); + if (!service) { + return; // Don't crash if KOpenWith wasn't able to create service. + } + + addApplication(service); + }); + owdlg->open(); +} + +void AutostartModel::addScript(const QUrl &url, AutostartModel::AutostartEntrySource kind) +{ + const QFileInfo file(url.toLocalFile()); + + if (!file.isAbsolute()) { + Q_EMIT error(i18n("\"%1\" is not an absolute url.", url.toLocalFile())); + return; + } else if (!file.exists()) { + Q_EMIT error(i18n("\"%1\" does not exist.", url.toLocalFile())); + return; + } else if (!file.isFile()) { + Q_EMIT error(i18n("\"%1\" is not a file.", url.toLocalFile())); + return; + } else if (!file.isReadable()) { + Q_EMIT error(i18n("\"%1\" is not readable.", url.toLocalFile())); + return; + } + + const QString fileName = url.fileName(); + int index = 0; + QString folder; + + if (kind == AutostartModel::AutostartEntrySource::XdgScripts) { + int lastLoginScript = -1; + for (const AutostartEntry &e : qAsConst(m_entries)) { + if (e.source == AutostartModel::AutostartEntrySource::PlasmaShutdown) { + break; + } + ++lastLoginScript; + } + + index = lastLoginScript + 1; + folder = QStringLiteral("/autostart-scripts/"); + } else if (kind == AutostartModel::AutostartEntrySource::PlasmaShutdown) { + index = m_entries.size(); + folder = QStringLiteral("/plasma-workspace/shutdown/"); + } else { + Q_ASSERT(0); + } + + QUrl destinationScript = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + folder + fileName); + KIO::CopyJob *job = KIO::link(url, destinationScript, KIO::HideProgressInfo); + job->setAutoRename(true); + job->setProperty("finalUrl", destinationScript); + + connect(job, &KIO::CopyJob::renamed, this, [](KIO::Job *job, const QUrl &from, const QUrl &to) { + Q_UNUSED(from) + // in case the destination filename had to be renamed + job->setProperty("finalUrl", to); + }); + + connect(job, &KJob::finished, this, [this, index, url, kind](KJob *theJob) { + if (theJob->error()) { + qWarning() << "Could add script entry" << theJob->errorString(); + return; + } + + beginInsertRows(QModelIndex(), index, index); + + const QUrl dest = theJob->property("finalUrl").toUrl(); + + AutostartEntry entry = AutostartEntry{dest.fileName(), kind, true, dest.path(), false, QStringLiteral("dialog-scripts")}; + + m_entries.insert(index, entry); + + endInsertRows(); + }); + + job->start(); +} + +void AutostartModel::removeEntry(int row) +{ + const auto entry = m_entries.at(row); + + KIO::DeleteJob *job = KIO::del(QUrl::fromLocalFile(entry.fileName), KIO::HideProgressInfo); + + connect(job, &KJob::finished, this, [this, row, entry](KJob *theJob) { + if (theJob->error()) { + qWarning() << "Could not remove entry" << theJob->errorString(); + return; + } + + beginRemoveRows(QModelIndex(), row, row); + m_entries.remove(row); + + endRemoveRows(); + }); + + job->start(); +} + +QHash<int, QByteArray> AutostartModel::roleNames() const +{ + QHash<int, QByteArray> roleNames = QAbstractListModel::roleNames(); + + roleNames.insert(Name, QByteArrayLiteral("name")); + roleNames.insert(Enabled, QByteArrayLiteral("enabled")); + roleNames.insert(Source, QByteArrayLiteral("source")); + roleNames.insert(FileName, QByteArrayLiteral("fileName")); + roleNames.insert(OnlyInPlasma, QByteArrayLiteral("onlyInPlasma")); + roleNames.insert(IconName, QByteArrayLiteral("iconName")); + + return roleNames; +} + +void AutostartModel::editApplication(int row, QQuickItem *context) +{ + const QModelIndex idx = index(row, 0); + + const QString fileName = data(idx, AutostartModel::Roles::FileName).toString(); + KFileItem kfi(QUrl::fromLocalFile(fileName)); + kfi.setDelayedMimeTypes(true); + + KPropertiesDialog *dlg = new KPropertiesDialog(kfi, nullptr); + dlg->setAttribute(Qt::WA_DeleteOnClose); + + if (context && context->window()) { + if (QWindow *actualWindow = QQuickRenderControl::renderWindowFor(context->window())) { + dlg->winId(); // so it creates windowHandle + dlg->windowHandle()->setTransientParent(actualWindow); + dlg->setModal(true); + } + } + + connect(dlg, &QDialog::finished, this, [this, idx, dlg](int result) { + if (result == QDialog::Accepted) { + reloadEntry(idx, dlg->item().localPath()); + } + }); + dlg->open(); +} diff --git a/kcms/autostart/autostartmodel.h b/kcms/autostart/autostartmodel.h new file mode 100644 index 000000000..201725d5f --- /dev/null +++ b/kcms/autostart/autostartmodel.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2020 by Méven Car <[email protected]> * + * * + * 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 * + ***************************************************************************/ +#ifndef AUTOSTARTMODEL_H +#define AUTOSTARTMODEL_H + +#include <QAbstractListModel> + +#include <KService> + +struct AutostartEntry; +class QQuickItem; + +class AutostartModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit AutostartModel(QObject *parent = nullptr); + + enum Roles { + Name = Qt::DisplayRole, + IconName = Qt::DecorationRole, + Enabled = Qt::UserRole + 1, + Source, + FileName, + OnlyInPlasma, + }; + + enum AutostartEntrySource { + XdgAutoStart = 0, + XdgScripts = 1, + PlasmaShutdown = 2, + PlasmaStart = 3, + }; + Q_ENUM(AutostartEntrySource) + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash<int, QByteArray> roleNames() const override; + + bool reloadEntry(const QModelIndex &index, const QString &fileName); + + Q_INVOKABLE void removeEntry(int row); + Q_INVOKABLE void editApplication(int row, QQuickItem *context); + Q_INVOKABLE void addScript(const QUrl &url, AutostartEntrySource kind); + Q_INVOKABLE void showApplicationDialog(QQuickItem *context); + + void load(); + +Q_SIGNALS: + void error(const QString &message); + +private: + void addApplication(const KService::Ptr &service); + void loadScriptsFromDir(const QString &subDir, AutostartEntrySource kind); + QString XdgAutoStartPath() const; + + QVector<AutostartEntry> m_entries; +}; + +struct AutostartEntry { + QString name; // Human readable name or script file path. In case of symlinks the target file path + AutostartModel::AutostartEntrySource source; + bool enabled; + QString fileName; // the file backing the entry + bool onlyInPlasma; + QString iconName; +}; +Q_DECLARE_TYPEINFO(AutostartEntry, Q_MOVABLE_TYPE); + +#endif // AUTOSTARTMODEL_H diff --git a/kcms/autostart/package/contents/ui/main.qml b/kcms/autostart/package/contents/ui/main.qml new file mode 100644 index 000000000..644c417cc --- /dev/null +++ b/kcms/autostart/package/contents/ui/main.qml @@ -0,0 +1,206 @@ +/*************************************************************************** + * Copyright (C) 2020 by Nicolas Fella <[email protected] * + * * + * 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 * + ***************************************************************************/ + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.11 +import org.kde.kirigami 2.13 as Kirigami +import QtQuick.Dialogs 1.3 +import org.kde.kcm 1.2 as KCM +import org.kde.plasma.kcm.autostart 1.0 + +KCM.ScrollViewKCM { + + id: root + + implicitHeight: Kirigami.Units.gridUnit * 28 + implicitWidth: Kirigami.Units.gridUnit * 28 + + header: Kirigami.InlineMessage { + id: errorMessage + type: Kirigami.MessageType.Error + showCloseButton: true + + Connections { + target: kcm.model + function onError(message) { + errorMessage.visible = true + errorMessage.text = message + } + } + } + + view: ListView { + clip: true + model: kcm.model + + delegate: Kirigami.SwipeListItem { + + Item { + Kirigami.Icon { + id: appIcon + source: model.iconName + width: Kirigami.Units.iconSizes.medium + height: Kirigami.Units.iconSizes.medium + } + + Label { + height: appIcon.height + text: model.name + elide: Text.ElideRight + anchors.left: appIcon.right + anchors.leftMargin: Kirigami.Units.largeSpacing + anchors.right: parent.right + } + } + + actions: [ + Kirigami.Action { + text: i18n("Properties") + icon.name: "document-properties" + onTriggered: kcm.model.editApplication(model.index, root) + visible: model.source === AutostartModel.XdgAutoStart + }, + Kirigami.Action { + text: i18n("Remove") + icon.name: "list-remove" + onTriggered: kcm.model.removeEntry(model.index) + } + ] + } + + section.property: "source" + section.delegate: Kirigami.ListSectionHeader { + text: { + if (section == AutostartModel.XdgAutoStart) { + return i18n("Applications") + } + if (section == AutostartModel.XdgScripts || section == AutostartModel.PlasmaStart) { + return i18n("Login Scripts") + } + if (section == AutostartModel.PlasmaShutdown) { + return i18n("Logout Scripts") + } + } + } + + Kirigami.PlaceholderMessage { + anchors.centerIn: parent + width: parent.width - (Kirigami.Units.largeSpacing * 4) + visible: parent.count === 0 + text: i18n("No user-specified autostart items") + explanation: xi18nc("@info", "Click the <interface>Add...</interface> button below to add some") + } + } + + footer: Row { + spacing: Kirigami.Units.largeSpacing + + Loader { + id: loginFileDialogLoader + + active: false + + sourceComponent: FileDialog { + id: loginFileDialog + title: i18n("Choose Login Script") + folder: shortcuts.home + selectMultiple: false + onAccepted: { + kcm.model.addScript(loginFileDialog.fileUrl, AutostartModel.XdgScripts) + loginFileDialogLoader.active = false + } + + onRejected: loginFileDialogLoader.active = false + + Component.onCompleted: open() + } + } + + Loader { + id: logoutFileDialogLoader + + active: false + + sourceComponent: FileDialog { + id: logoutFileDialog + title: i18n("Choose Logout Script") + folder: shortcuts.home + selectMultiple: false + onAccepted: { + kcm.model.addScript(logoutFileDialog.fileUrl, AutostartModel.PlasmaShutdown) + logoutFileDialogLoader.active = false + } + + onRejected: logoutFileDialogLoader.active = false + + Component.onCompleted: open() + } + } + + Button { + id: menuButton + + icon.name: "list-add" + text: i18n("Add...") + + checkable: true + checked: menu.opened + + onPressed: { + // Appear above the button, not below it, since the button is at + // the bottom of the window and QQC2 items can't leave the window + + // HACK: since we want to position the menu above the button, + // we need to know the menu's height, but it only has a height + // after the first time it's been shown, so until then, we need + // to provide an artificially-synthesized-and-hopefully-good-enough + // height value + var menuHeight = menu.height && menu.height > 0 ? menu.height : Kirigami.Units.gridUnit * 3 + menu.popup(menuButton, 0, -menuHeight) + } + } + + Menu { + id: menu + + modal: true + dim: false + + MenuItem { + text: i18n("Add Application...") + icon.name: "list-add" + + onClicked: kcm.model.showApplicationDialog(root) + } + MenuItem { + text: i18n("Add Login Script...") + icon.name: "list-add" + + onClicked: loginFileDialogLoader.active = true + } + MenuItem { + text: i18n("Add Logout Script...") + icon.name: "list-add" + + onClicked: logoutFileDialogLoader.active = true + } + } + } +} diff --git a/kcms/autostart/package/metadata.desktop b/kcms/autostart/package/metadata.desktop new file mode 100644 index 000000000..7251f5922 --- /dev/null +++ b/kcms/autostart/package/metadata.desktop @@ -0,0 +1,195 @@ +[Desktop Entry] +Name=Autostart +Name[af]=Outomatiese begin +Name[ar]=البدء الآليّ +Name[ast]=Aniciu automáticu +Name[az]=Avtomatik başlama +Name[be]=Аўтаматычны запуск +Name[be@latin]=Aŭtamatyčnaje ŭklučeńnie +Name[bg]=Автоматично стартиране +Name[bn]=অটো-স্টার্ট +Name[bn_IN]=স্বয়ং-আরম্ভ +Name[br]=Emloc'h +Name[bs]=Samopokretanje +Name[ca]=Inici automàtic +Name[ca@valencia]=Inici automàtic +Name[cs]=Automatické spuštění +Name[csb]=Aùtomatné zrëszanié +Name[cy]=Hunan-gychwyn +Name[da]=Autostart +Name[de]=Autostart +Name[el]=Αυτόματη εκκίνηση +Name[en_GB]=Autostart +Name[eo]=Aŭtomata lanĉo +Name[es]=Autoarranque +Name[et]=Autostart +Name[eu]=Hasiera automatikoa +Name[fa]=آغاز خودکار +Name[fi]=Automaattikäynnistys +Name[fr]=Démarrage automatique +Name[fy]=Auto-úteinsette +Name[ga]=Tosú uathoibríoch +Name[gl]=Inicio automático +Name[gu]=આપમેળેશરૂ +Name[he]=הפעלה אוטומטית +Name[hi]=स्वतःप्रारंभ +Name[hne]=खुद से चालू +Name[hr]=Automatsko pokretanje +Name[hsb]=Awtostart +Name[hu]=Automatikus indítás +Name[ia]=Auto starta +Name[id]=Automulai +Name[is]=Sjálfræsing +Name[it]=Avvio automatico +Name[ja]=自動起動 +Name[ka]=ავტოგაშვება +Name[kk]=Автобастау +Name[km]=ចាប់ផ្ដើមស្វ័យប្រវត្តិ +Name[kn]=ಸ್ವಯಮಾರಂಭ +Name[ko]=자동 시작 +Name[ku]=Bixweber Bide Destpêkirin +Name[lt]=Automatinis paleidimas +Name[lv]=Autopalaišana +Name[mai]=स्वतः चालू +Name[mk]=Автостарт +Name[ml]=ഓട്ടോസ്റ്റാര്ട്ട് +Name[mr]=स्वप्रारंभ +Name[ms]=Automula +Name[nb]=Autostart +Name[nds]=Autostart +Name[nl]=Autostart +Name[nn]=Autostart +Name[or]=ସ୍ୱୟଂଚାଳନ +Name[pa]=ਆਟੋ-ਸਟਾਰਟ +Name[pl]=Samo-uruchamiane +Name[pt]=Arranque +Name[pt_BR]=Iniciar automaticamente +Name[ro]=Pornire automată +Name[ru]=Автозапуск +Name[si]=ක්ෂණික ඇරඹුම +Name[sk]=Automatické spustenie +Name[sl]=Samodejni zagon +Name[sr]=Самопокретање +Name[sr@ijekavian]=Самопокретање +Name[sr@ijekavianlatin]=Samopokretanje +Name[sr@latin]=Samopokretanje +Name[sv]=Automatisk start +Name[ta]=சுயதுவக்கம் +Name[te]=స్వయంచాలకప్రారంభం +Name[tg]=Оғози худкор +Name[th]=เริ่มอัตโนมัติ +Name[tr]=Otomatik Başlat +Name[ug]=ئۆزلۈكىدىن قوزغات +Name[uk]=Автозапуск +Name[uz]=Avto-boshlash +Name[uz@cyrillic]=Авто-бошлаш +Name[vi]=Khởi động tự động +Name[wa]=Enondaedje tot seu +Name[xh]=Isiqalo esizenzekelayo +Name[x-test]=xxAutostartxx +Name[zh_CN]=自动启动 +Name[zh_TW]=自動啟動 +Comment=Automatically Started Applications +Comment[ar]=التّطبيقات التي تبدأ آليًّا +Comment[az]=Avtomatik işə düşən tətbiqlər +Comment[bs]=Aplikacije pokrenute automatski +Comment[ca]=Aplicacions iniciades automàticament +Comment[ca@valencia]=Aplicacions iniciades automàticament +Comment[cs]=Automaticky spouštěné aplikace +Comment[da]=Programmer der startes automatisk +Comment[de]=Automatisch zu startende Anwendungen +Comment[el]=Εφαρμογές που ξεκινούν αυτόματα +Comment[en_GB]=Automatically Started Applications +Comment[es]=Aplicaciones iniciadas automáticamente +Comment[et]=Automaatselt käivituvad rakendused +Comment[eu]=Automatikoki abiarazitako aplikazioak +Comment[fi]=Automaattisesti käynnistetyt ohjelmat +Comment[fr]=Applications lancées automatiquement +Comment[gl]=Aplicacións iniciadas automaticamente +Comment[he]=פתח אוטומטית יישומים +Comment[hu]=Automatikusan indított alkalmazások +Comment[ia]=Applicationes initiate automaticamente +Comment[id]=Secara Otomatis Menjalankan Aplikasi +Comment[is]=Sjálfkrafa ræst forrit +Comment[it]=Applicazioni avviate automaticamente +Comment[ja]=自動的に起動されるアプリケーション +Comment[ko]=자동으로 시작되는 프로그램 +Comment[lt]=Automatiškai paleidžiamos programos +Comment[ml]=താനേ തുടങ്ങിയ പ്രയോഗങ്ങൾ +Comment[mr]=स्वप्रारंभअनुप्रयोग +Comment[nb]=Programmer som startes automatisk +Comment[nds]=Automaatsch opropen Programmen +Comment[nl]=Automatisch gestarte toepassingen +Comment[nn]=Program som vert starta automatisk +Comment[pa]=ਆਟੋਮੈਟਿਕ ਸ਼ੁਰੂ ਹੋਣ ਵਾਲੀਆਂ ਐਪਲੀਕੇਸ਼ਨਾਂ +Comment[pl]=Samoczynnie uruchamiane programy +Comment[pt]=Aplicações Iniciadas Automaticamente +Comment[pt_BR]=Aplicativos iniciados automaticamente +Comment[ro]=Aplicații pornite automat +Comment[ru]=Автоматический запуск приложений +Comment[sk]=Automaticky spustené aplikácie +Comment[sl]=Samodejno zagnani programi +Comment[sr]=Програми који се аутоматски покрећу +Comment[sr@ijekavian]=Програми који се аутоматски покрећу +Comment[sr@ijekavianlatin]=Programi koji se automatski pokreću +Comment[sr@latin]=Programi koji se automatski pokreću +Comment[sv]=Program som startas automatiskt +Comment[tr]=Otomatik Başlatılan Uygulamalar +Comment[uk]=Програми, що запускаються автоматично +Comment[vi]=Các ứng dụng được khởi động tự động +Comment[x-test]=xxAutomatically Started Applicationsxx +Comment[zh_CN]=自动启动的应用程序 +Comment[zh_TW]=自動啟動應用程式 +Encoding=UTF-8 +Type=Service +Icon=system-run +X-KDE-Library=kcm_autostart +X-KDE-ServiceTypes=KCModule +X-KDE-FormFactors=desktop +X-Plasma-MainScript=ui/main.qml +X-KDE-System-Settings-Parent-Category=session +X-KDE-ParentApp=kcontrol +X-KDE-Keywords=Autostart Manager,autostart,startup,system startup,plasma start,cron +X-KDE-Keywords[az]=Autostart Manager,autostart,startup,system startup,plasma start,cronAvtomatik başlama meneceri, avtomatik başlama, sistemin işə salınması Plasmanın başladılması +X-KDE-Keywords[ca]=Gestor de l'inici automàtic,inici automàtic,inici,inici del sistema,inici del Plasma,cron +X-KDE-Keywords[ca@valencia]=Gestor de l'inici automàtic,inici automàtic,inici,inici del sistema,inici del plasma,cron +X-KDE-Keywords[cs]=správce automatického spuštění,automatické spuštění,spuštění systému,spuštění plasma,cron +X-KDE-Keywords[da]=autostart,opstart,systemstart,plasma start,cron +X-KDE-Keywords[de]=Autostartverwaltung,Autostart,Startvorgang,Systemstart,Plasma-Start,Cron +X-KDE-Keywords[el]=διαχειριστής αυτόματης εκκίνησης,αυτόματη εκκίνηση,έναρξη,έναρξη συστήματος,έναρξη plasma,cron +X-KDE-Keywords[en_GB]=Autostart Manager,autostart,startup,system startup,plasma start,cron +X-KDE-Keywords[es]=Gestor de inicio automático,inicio automático,inicio,inicio del sistema,inicio de plasma,cron +X-KDE-Keywords[et]=Autostardi haldur,autostart,käivitamine,süsteemi käivitamine,plasma käivitamine,cron +X-KDE-Keywords[eu]=Hasiera automatikoaren kudeatzailea,hasiera automatikoa,abioa,sistemaren abioa,plasma abiatu,cron +X-KDE-Keywords[fi]=automaattikäynnistys,hallinta,käynnistys,järjestelmän käynnistys,Plasman käynnistys,cron +X-KDE-Keywords[fr]=Gestionnaire de démarrage, démarrage automatique, démarrage, démarrage du système, démarrage de plasma, cron +X-KDE-Keywords[gl]=Xestor de inicio, inicio automático, inicio, sistema de arranque, inicio de plasma, cron +X-KDE-Keywords[hu]=Automatikus indítás kezelő,automatikus indítás,indulás,rendszerindulás,plasma indulás,cron +X-KDE-Keywords[ia]=Gestor de autoinitio,autoinitio,initio,initio de systema.initio de plasma,cron +X-KDE-Keywords[id]=Pengelola Automulai,automulai,pemulaian,pemulaian sistem,mulai plasma,cron +X-KDE-Keywords[it]=Gestore avvio automatico,avvio automatico,avvio,avvio del sistema,avvio di plasma,cron +X-KDE-Keywords[ja]=自動起動マネージャ,自動起動,起動 +X-KDE-Keywords[ko]=Autostart Manager,autostart,startup,system startup,plasma start,cron,자동 시작,자동시작,시작프로그램,시스템 시작,plasma 시작 +X-KDE-Keywords[lt]=Automatinio paleidimo tvarkytuvė,automatinio paleidimo tvakrytuve,automatinis paleidimas,paleistis,sistemos paleidimas,kde paleidimas,plasma paleidimas,cron +X-KDE-Keywords[nl]=Autostartbeheerder,autostarten,opstarten,opstarten systeem,opstarten plasma,cron +X-KDE-Keywords[nn]=autostart-handsaming,autostart,oppstart,systemstart,plasma-start,cron +X-KDE-Keywords[pl]=Menadżer autostartu,autostart,uruchamianie,uruchamianie systemu,plasma,kde start,cron +X-KDE-Keywords[pt]=Gestor de arranque,arranque,início,arranque do sistema,início do plasma,cron +X-KDE-Keywords[pt_BR]=Gerenciado de inicialização,inicialização,início,inicialização do sistema,início do plasma,cron +X-KDE-Keywords[ro]=gestionar pornire automată,pornire,pornire sistem,pornire plasma,cron +X-KDE-Keywords[ru]=Autostart Manager,autostart,startup,system startup,kde start,cron,диспетчер автоматического запуска,автоматический запуск,автозапуск,загрузка системы,запуск Plasma +X-KDE-Keywords[sk]=Správca automatického spustenia,autostart,spustenie,spustenie systému,štart kde,cron +X-KDE-Keywords[sl]=upravljalnik samodejnega zagona,zagon,samodejni zagon,sistemski zagon,zagon plasme,system startup,plasma start,cron +X-KDE-Keywords[sr]=Autostart Manager,autostart,startup,system startup,plasma start,cron,самопокретање,менаџер самопокретања,покретање система,покретање Плазме +X-KDE-Keywords[sr@ijekavian]=Autostart Manager,autostart,startup,system startup,plasma start,cron,самопокретање,менаџер самопокретања,покретање система,покретање Плазме +X-KDE-Keywords[sr@ijekavianlatin]=Autostart Manager,autostart,startup,system startup,plasma start,cron,samopokretanje,menadžer samopokretanja,pokretanje sistema,pokretanje Plasme +X-KDE-Keywords[sr@latin]=Autostart Manager,autostart,startup,system startup,plasma start,cron,samopokretanje,menadžer samopokretanja,pokretanje sistema,pokretanje Plasme +X-KDE-Keywords[sv]=Hantering av autostart,autostart,start,systemstart,plasma-start,cron +X-KDE-Keywords[tr]=Otomatik Başlatma Yöneticisi, otomatik,başlatma,sistem başlangıcı,plasma başlangıcı,görevler,cron +X-KDE-Keywords[uk]=Autostart Manager,autostart,startup,system startup,plasma start,cron,керування автозапуском,автозапуск,запуск,запуск системи,запуск плазми +X-KDE-Keywords[vi]=Autostart Manager,autostart,startup,system startup,plasma start,cron,trình quản lí khởi động tự động,khởi động tự động,khởi động,khởi động hệ thống,khởi động plasma +X-KDE-Keywords[x-test]=xxAutostart Managerxx,xxautostartxx,xxstartupxx,xxsystem startupxx,xxplasma startxx,xxcronxx +X-KDE-Keywords[zh_CN]=Autostart Manager,autostart,startup,system startup,plasma start,cron,自动启动管理器,自动启动,启动,系统启动,plasma 启动,任务调度 +X-KDE-Keywords[zh_TW]=Autostart Manager,autostart,startup,system startup,plasma start,cron +X-DocPath=kcontrol/autostart/index.html +X-KDE-Weight=30
