plasma: reimplement launcher applet

very much WIP but functional

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2024-04-12 03:33:58 +03:00
parent 4f448f84ec
commit 97f73b2608
60 changed files with 1025 additions and 11070 deletions

View file

@ -11,7 +11,7 @@ add_subdirectory(system-monitor)
add_subdirectory(notifications)
add_subdirectory(systemtray)
add_subdirectory(keyboard)
add_subdirectory(kickoff)
add_subdirectory(launcher)
add_subdirectory(trash)
add_subdirectory(folderview)
add_subdirectory(weather)

View file

@ -1,125 +0,0 @@
#######################################################################################
# Kickoff Library
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
)
set(libkickoff_SRCS
core/kickoffmodel.cpp
core/kickoffabstractmodel.cpp
core/kickoffproxymodel.cpp
core/applicationmodel.cpp
core/favoritesmodel.cpp
core/leavemodel.cpp
core/models.cpp
core/recentapplications.cpp
core/recentlyusedmodel.cpp
core/krunnermodel.cpp
core/systemmodel.cpp
core/urlitemlauncher.cpp
core/itemhandlers.cpp
)
qt4_add_dbus_adaptor(libkickoff_SRCS core/org.kde.kickoff.xml core/applicationmodel.h Kickoff::ApplicationModel)
qt4_add_dbus_adaptor(libkickoff_SRCS core/org.kde.kickoff.recent.xml core/recentlyusedmodel.h Kickoff::RecentlyUsedModel)
set(screensaver_xml ${CMAKE_SOURCE_DIR}/kscreensaver/org.freedesktop.ScreenSaver.xml)
QT4_ADD_DBUS_INTERFACE(libkickoff_SRCS ${screensaver_xml} screensaver_interface)
set(ksmserver_xml ${CMAKE_SOURCE_DIR}/ksmserver/org.kde.KSMServerInterface.xml)
QT4_ADD_DBUS_INTERFACE(libkickoff_SRCS ${ksmserver_xml} ksmserver_interface)
set(Kickoff_LIBS KDE4::kio KDE4::solid kworkspace)
add_library(kickoff SHARED ${libkickoff_SRCS})
target_link_libraries(kickoff KDE4::plasma ${Kickoff_LIBS})
generate_export_header(kickoff)
install(
TARGETS kickoff
DESTINATION ${KDE4_LIB_INSTALL_DIR}
)
#######################################################################################
# Kickoff Plasma Applet
set(Applet_SRCS
ui/contextmenufactory.cpp
ui/flipscrollview.cpp
ui/itemdelegate.cpp
ui/contentareacap.cpp
ui/launcher.cpp
ui/searchbar.cpp
ui/tabbar.cpp
ui/urlitemview.cpp
applet/applet.cpp
applet/kickoffConfig.ui
)
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/ui/contextmenufactory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ui/launcher.cpp
PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE
)
kde4_add_plugin(plasma_applet_launcher ${Applet_SRCS})
target_link_libraries(plasma_applet_launcher
KDE4::kcmutils
KDE4::plasma
${QT_QTNETWORK_LIBRARY}
${Kickoff_LIBS}
kickoff
)
install(
TARGETS plasma_applet_launcher
DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}
)
install(
FILES applet/plasma-applet-launcher.desktop
DESTINATION ${KDE4_SERVICES_INSTALL_DIR}
)
#######################################################################################
# Kickoff Simple KMenu Plasma Applet
set(SimpleApplet_SRCS
ui/contextmenufactory.cpp
simpleapplet/menuview.cpp
simpleapplet/simpleapplet.cpp
)
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/ui/contextmenufactory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/simpleapplet/menuview.cpp
PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE
)
kde4_add_plugin(plasma_applet_simplelauncher ${SimpleApplet_SRCS})
target_link_libraries(plasma_applet_simplelauncher
KDE4::kcmutils
KDE4::plasma
${Kickoff_LIBS}
kickoff
)
install(
TARGETS plasma_applet_simplelauncher
DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}
)
install(
FILES simpleapplet/plasma-applet-simplelauncher.desktop
DESTINATION ${KDE4_SERVICES_INSTALL_DIR}
)
# Kickoff Standalone Test Application
#IF (CMAKE_BUILD_TYPE MATCHES Debug)
# set(Application_SRCS ${Kickoff_SRCS} main.cpp)
# add_executable(kickoff ${Application_SRCS})
# target_link_libraries(kickoff KDE4::plasma ${Kickoff_LIBS})
# install(TARGETS kickoff DESTINATION ${KDE4_BIN_INSTALL_DIR})
#ENDIF (CMAKE_BUILD_TYPE MATCHES Debug)

View file

@ -1,69 +0,0 @@
Project Proposal: Kickoff KDE 4 Rewrite
This is a re-implementation of the Kickoff menu for KDE 3 originally created
by the OpenSuSE team.
Overall goals:
1. Implement the Kickoff user interface using Qt/KDE 4 technology
2. Make it easy for distributions to modify the menu contents
3. Make it easy for distributions to add their own branding
Detailed goals:
1. Implement the Kickoff user interface using Qt/KDE 4 technology
-> Clean separation of core and user interface
-> eg. Make good use of Qt 4's model/view classes
-> Use Strigi for search and analysis (or possibly just Xesam interface?)
-> Use Solid for getting data for removable device list
-> Attractive, original, but not overbearing graphical effects
using Qt 4's new painting and animation features.
To decide on:
-> Consult the Kickoff/KDE 3 developers and OpenSuSE users about
any desired changes to the user interface.
-> Whether to use Plasma applets and widgets or Qt's widgets and
QAbstractItemView as the base for the view classes.
-> Provision of Plasma data engines for applets to use to query
applications, recent documents, favorites etc.
-> Should facilities be provided for distributions to replace
Strigi with their choice of search engine?
Ideas to explore in future:
-> Use Sonnet for spell-checking the user's search query
-> Light KWin integration for interesting effects for revealing,
hiding or rendering the Kickoff launcher on composited '3D'
desktops.
Goals 2 and 3 are derived from looking at the KDE 3 implementation
of Kickoff and also the way in which distributions customise KDE.
Consulting distribution representatives is important to clarify
these particular goals.
2. Make it easy for distributions to modify the menu contents
-> Different distributions may have different tools for system
configuration ( eg. System Settings , YaST , Mandriva's tools )
-> The available options on the leave page and the way in which
those tools work varies depending on the distribution
3. Make it easy for distributions to add their own branding
-> The launcher menu is a highly visible part of the desktop,
distributions are therefore likely to want to brand it with
their own logos, colors, icons etc.
This does not mean infinite theme-ability, but rather a good
out-of-the-box look which distros can easily tweak so that
it is obvious which distribution is being used from looking
at a screenshot, and also to allow distros to make some
obvious cosmetic changes between versions.
(See the Ubuntu art specs for example which state that each
new version is supposed to have a visual look which is
distinct from the previous version)

View file

@ -1,334 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "applet/applet.h"
// Katie
#include <QtGui/QAction>
#include <QtGui/QApplication>
#include <QtGui/QGraphicsView>
#include <QtGui/QVBoxLayout>
#include <QtGui/QLabel>
#include <QtGui/QGraphicsLinearLayout>
// KDE
#include <KIcon>
#include <KDebug>
#include <KConfigDialog>
#include <KPluginSelector>
#include <KToolInvocation>
#include <KCategorizedView>
#include <Plasma/IconWidget>
#include <Plasma/Containment>
#include <Plasma/View>
#include <Plasma/ToolTipManager>
#include <Plasma/RunnerManager>
// Local
#include "ui_kickoffConfig.h"
#include "ui/launcher.h"
#include "core/recentapplications.h"
#include "core/models.h"
#include "core/krunnermodel.h"
// NOTE: keep in sync with:
// kdelibs/kutils/kpluginselector_p.h
static const int s_pluginnamerole = 0x0CBBBB00;
static QString kMakeToolTip(const QString &pluginname)
{
QString result;
Plasma::AbstractRunner* runner = Kickoff::KRunnerModel::runnerManager()->runner(pluginname);
if (!runner) {
return result;
}
const QList<Plasma::RunnerSyntax> syntaxes = runner->syntaxes();
if (syntaxes.isEmpty()) {
return result;
}
QStringList uniqueexamples;
foreach (const Plasma::RunnerSyntax &syntax, syntaxes) {
foreach (const QString &example, syntax.exampleQueriesWithTermDescription()) {
uniqueexamples.append(example);
}
}
uniqueexamples.removeDuplicates();
if (uniqueexamples.isEmpty()) {
return result;
}
result.append(i18n("<b>Examples:</b><br/>"));
foreach (const QString &example, uniqueexamples) {
result.append(QString::fromLatin1("<i>%1</i><br/>").arg(Qt::escape(example)));
}
// qDebug() << Q_FUNC_INFO << result;
return result;
}
class LauncherApplet::Private
{
public:
Private(LauncherApplet *lApplet) : launcher(0), switcher(0), q(lApplet) { }
~Private() {
delete launcher;
}
void createLauncher();
void initToolTip();
Kickoff::Launcher *launcher;
QList<QAction*> actions;
QAction* switcher;
LauncherApplet *q;
Ui::kickoffConfig ui;
KPluginSelector* selector;
};
void LauncherApplet::Private::createLauncher()
{
if (launcher) {
return;
}
launcher = new Kickoff::Launcher(q);
launcher->setAttribute(Qt::WA_NoSystemBackground);
launcher->setAutoHide(true);
QObject::connect(launcher, SIGNAL(aboutToHide()), q, SLOT(hidePopup()));
QObject::connect(launcher, SIGNAL(configNeedsSaving()), q, SIGNAL(configNeedsSaving()));
//launcher->resize(launcher->sizeHint());
//QObject::connect(launcher, SIGNAL(aboutToHide()), icon, SLOT(setUnpressed()));
}
void LauncherApplet::Private::initToolTip()
{
Plasma::ToolTipContent data(i18n("Kickoff Application Launcher"),
i18n("Favorites, applications, computer places, "
"recently used items and desktop sessions"),
q->popupIcon().pixmap(IconSize(KIconLoader::Desktop)));
Plasma::ToolTipManager::self()->setContent(q, data);
}
LauncherApplet::LauncherApplet(QObject *parent, const QVariantList &args)
: Plasma::PopupApplet(parent, args),
d(new Private(this))
{
KGlobal::locale()->insertCatalog("plasma_applet_launcher");
setAspectRatioMode(Plasma::IgnoreAspectRatio);
setHasConfigurationInterface(true);
}
LauncherApplet::~LauncherApplet()
{
delete d;
}
void LauncherApplet::init()
{
if (KService::serviceByStorageId("kmenuedit.desktop")) {
QAction* menueditor = new QAction(i18n("Edit Applications..."), this);
d->actions.append(menueditor);
connect(menueditor, SIGNAL(triggered(bool)), this, SLOT(startMenuEditor()));
}
Q_ASSERT(! d->switcher);
d->switcher = new QAction(i18n("Switch to Classic Menu Style"), this);
d->actions.append(d->switcher);
connect(d->switcher, SIGNAL(triggered(bool)), this, SLOT(switchMenuStyle()));
setGlobalShortcut(KShortcut(Qt::ALT+Qt::Key_F2));
configChanged();
Plasma::ToolTipManager::self()->registerWidget(this);
}
void LauncherApplet::constraintsEvent(Plasma::Constraints constraints)
{
if ((constraints & Plasma::ImmutableConstraint) && d->switcher) {
d->switcher->setVisible(immutability() == Plasma::Mutable);
}
}
void LauncherApplet::switchMenuStyle()
{
if (containment()) {
Plasma::Applet * simpleLauncher =
containment()->addApplet("simplelauncher", QVariantList() << true, geometry());
//Copy all the config items to the simple launcher
QMetaObject::invokeMethod(simpleLauncher, "saveConfigurationFromKickoff",
Qt::DirectConnection, Q_ARG(KConfigGroup, config()),
Q_ARG(KConfigGroup, globalConfig()));
//Switch shortcuts with the new launcher to avoid losing it
KShortcut currentShortcut = globalShortcut();
setGlobalShortcut(KShortcut());
simpleLauncher->setGlobalShortcut(currentShortcut);
//Destroy this widget
destroy();
}
}
void LauncherApplet::startMenuEditor()
{
KToolInvocation::kdeinitExec("kmenuedit");
}
void LauncherApplet::createConfigurationInterface(KConfigDialog *parent)
{
QWidget *widget = new QWidget();
d->ui.setupUi(widget);
parent->addPage(widget, i18nc("General configuration page", "General"), icon());
const QList<KPluginInfo> plugins = Plasma::RunnerManager::listRunnerInfo();
d->selector = new KPluginSelector(widget);
d->selector->addPlugins(
plugins,
KPluginSelector::ReadConfigFile,
i18n("Available Plugins"), QString(),
Kickoff::componentData().config()
);
connect(d->selector, SIGNAL(changed(bool)), parent, SLOT(settingsModified()));
parent->addPage(d->selector, i18n("Runners"), "preferences-plugin");
foreach (const KPluginInfo& plugin, plugins) {
Kickoff::KRunnerModel::runnerManager()->loadRunner(plugin.service());
}
// HACK: setup tooltips for the plugins
KCategorizedView* selectorview = d->selector->findChild<KCategorizedView*>();
if (selectorview) {
QAbstractItemModel* selectormodel = selectorview->model();
if (selectormodel) {
for (int i = 0; i < selectormodel->rowCount(); i++) {
QModelIndex selectorindex = selectormodel->index(i, 0);
const QString pluginname = selectormodel->data(selectorindex, s_pluginnamerole).toString();
selectormodel->setData(selectorindex, kMakeToolTip(pluginname), Qt::ToolTipRole);
}
}
}
// forces unload of the disabled plugins that have been loaded for the tooltip
Kickoff::KRunnerModel::runnerManager()->reloadConfiguration();
connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
d->createLauncher();
d->ui.iconButton->setIcon(popupIcon());
d->ui.switchOnHoverCheckBox->setChecked(d->launcher->switchTabsOnHover());
d->ui.appsByNameCheckBox->setChecked(d->launcher->showAppsByName());
d->ui.showRecentlyInstalledCheckBox->setChecked(d->launcher->showRecentlyInstalled());
connect(d->ui.iconButton, SIGNAL(iconChanged(QString)), parent, SLOT(settingsModified()));
connect(d->ui.switchOnHoverCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
connect(d->ui.appsByNameCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
connect(d->ui.showRecentlyInstalledCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
}
void LauncherApplet::popupEvent(bool show)
{
if (show) {
Plasma::ToolTipManager::self()->clearContent(this);
d->createLauncher();
d->launcher->setLauncherOrigin(popupPlacement(), location());
}
}
void LauncherApplet::toolTipAboutToShow()
{
if (d->launcher->isVisible()) {
Plasma::ToolTipManager::self()->clearContent(this);
} else {
d->initToolTip();
}
}
void LauncherApplet::configChanged()
{
KConfigGroup cg = config();
setPopupIcon(cg.readEntry("icon", "start-here-kde"));
constraintsEvent(Plasma::ImmutableConstraint);
if (d->launcher) {
d->launcher->setApplet(this);
}
}
void LauncherApplet::configAccepted()
{
d->selector->save();
KConfigGroup pcg = Kickoff::componentData().config()->group("Plugins");
QStringList allowed;
foreach (KPluginInfo plugin, Plasma::RunnerManager::listRunnerInfo()) {
plugin.load(pcg);
if (plugin.isPluginEnabled()) {
allowed.append(plugin.pluginName());
}
}
Kickoff::KRunnerModel::runnerManager()->setAllowedRunners(allowed);
bool switchTabsOnHover = d->ui.switchOnHoverCheckBox->isChecked();
bool showAppsByName = d->ui.appsByNameCheckBox->isChecked();
bool showRecentlyInstalled = d->ui.showRecentlyInstalledCheckBox->isChecked();
const QString iconname = d->ui.iconButton->icon();
// TODO: should this be moved into Launcher as well? perhaps even the config itself?
d->createLauncher();
KConfigGroup cg = config();
const QString oldIcon = cg.readEntry("icon", "start-here-kde");
if (!iconname.isEmpty() && iconname != oldIcon) {
cg.writeEntry("icon", iconname);
if (!iconname.isEmpty()) {
setPopupIcon(iconname);
}
emit configNeedsSaving();
}
d->launcher->setSwitchTabsOnHover(switchTabsOnHover);
d->launcher->setShowAppsByName(showAppsByName);
d->launcher->setShowRecentlyInstalled(showRecentlyInstalled);
}
QList<QAction*> LauncherApplet::contextualActions()
{
return d->actions;
}
QWidget *LauncherApplet::widget()
{
d->createLauncher();
return d->launcher;
}
void LauncherApplet::saveConfigurationFromSimpleLauncher(const KConfigGroup & configGroup, const KConfigGroup & globalConfigGroup)
{
//Copy configuration values
KConfigGroup cg = config();
configGroup.copyTo(&cg);
KConfigGroup gcg = globalConfig();
globalConfigGroup.copyTo(&gcg);
configChanged();
emit configNeedsSaving();
}
#include "moc_applet.cpp"

View file

@ -1,82 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef APPLET_H
#define APPLET_H
// KDE
// Plasma
#include <Plasma/PopupApplet>
namespace Kickoff
{
class Launcher;
}
namespace Plasma
{
}
class LauncherApplet : public Plasma::PopupApplet
{
Q_OBJECT
public:
LauncherApplet(QObject *parent, const QVariantList &args);
virtual ~LauncherApplet();
void init();
void constraintsEvent(Plasma::Constraints constraints);
virtual QList<QAction*> contextualActions();
QWidget *widget();
public slots:
void switchMenuStyle();
void startMenuEditor();
void toolTipAboutToShow();
void configChanged();
/**
* Save config values stored on SimpleLauncher after a menu switch
*/
void saveConfigurationFromSimpleLauncher(const KConfigGroup & configGroup,
const KConfigGroup & globalConfigGroup);
protected slots:
void configAccepted();
//void toggleMenu();
//void toggleMenu(bool pressed);
protected:
void createConfigurationInterface(KConfigDialog *parent);
void popupEvent(bool show);
private:
friend class Kickoff::Launcher;
class Private;
Private * const d;
};
K_EXPORT_PLASMA_APPLET(launcher, LauncherApplet)
#endif

View file

@ -1,126 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>kickoffConfig</class>
<widget class="QWidget" name="kickoffConfig">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="KIconButton" name="iconButton"/>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="switchOnHoverCheckBox">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Show applications by &amp;name:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>appsByNameCheckBox</cstring>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="appsByNameCheckBox">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>131</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>204</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QLabel" name="iconLabel">
<property name="text">
<string>&amp;Icon:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>iconButton</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Switch &amp;tabs on hover:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>switchOnHoverCheckBox</cstring>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QCheckBox" name="showRecentlyInstalledCheckBox">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Show 'Recently Installed':</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>showRecentlyInstalledCheckBox</cstring>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KIconButton</class>
<extends>QPushButton</extends>
<header>kicondialog.h</header>
</customwidget>
</customwidgets>
<connections/>
</ui>

View file

@ -1,708 +0,0 @@
/*
Copyright 2007 Pino Toscano <pino@kde.org>
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "applicationmodel.h"
// Qt
#include <QtCore/qalgorithms.h>
#include <QtCore/QList>
#include <QtGui/QLabel>
#include <QtGui/QLayout>
#include <QtGui/QCheckBox>
// KDE
#include <khistorycombobox.h>
#include <kdesktopfile.h>
#include <klineedit.h>
#include <klocale.h>
#include <kiconloader.h>
#include <krun.h>
#include <kstandarddirs.h>
#include <kstringhandler.h>
#include <kmimetypetrader.h>
#include <kurlcompletion.h>
#include <kurlrequester.h>
#include <kmimetype.h>
#include <kservicegroup.h>
#include <ksycoca.h>
#include <kdebug.h>
#include <assert.h>
#include <stdlib.h>
#include <kconfiggroup.h>
#include "kickoffadaptor.h"
// Local
#include "core/models.h"
#include <Plasma/Applet>
template <> inline
void KConfigGroup::writeEntry(const char *pKey,
const KGlobalSettings::Completion& aValue,
KConfigBase::WriteConfigFlags flags)
{
writeEntry(pKey, int(aValue), flags);
}
namespace Kickoff
{
class AppNode
{
public:
AppNode()
: parent(0),
fetched(false),
isDir(false),
isSeparator(false),
subTitleMandatory(false)
{
}
~AppNode()
{
qDeleteAll(children);
}
QList<AppNode*> children;
QIcon icon;
QString iconName;
QString genericName;
QString appName;
QString relPath;
QString desktopEntry;
AppNode *parent;
DisplayOrder displayOrder;
bool fetched : 1;
bool isDir : 1;
bool isSeparator : 1;
bool subTitleMandatory : 1;
};
class ApplicationModelPrivate
{
public:
ApplicationModelPrivate(ApplicationModel *qq, bool _allowSeparators)
: q(qq),
root(new AppNode()),
duplicatePolicy(ApplicationModel::ShowDuplicatesPolicy),
systemApplicationPolicy(ApplicationModel::ShowApplicationAndSystemPolicy),
primaryNamePolicy(ApplicationModel::GenericNamePrimary),
displayOrder(NameAfterDescription),
allowSeparators(_allowSeparators),
showRecentlyInstalled(true)
{
systemApplications = Kickoff::systemApplicationList();
reloadTimer = new QTimer(qq);
reloadTimer->setSingleShot(true);
QObject::connect(reloadTimer, SIGNAL(timeout()), qq, SLOT(delayedReloadMenu()));
}
~ApplicationModelPrivate()
{
delete root;
}
void fillNode(const QString &relPath, AppNode *node);
static QHash<QString, QString> iconNameMap();
ApplicationModel *q;
QWeakPointer<Plasma::Applet> applet;
AppNode *root;
ApplicationModel::DuplicatePolicy duplicatePolicy;
ApplicationModel::SystemApplicationPolicy systemApplicationPolicy;
ApplicationModel::PrimaryNamePolicy primaryNamePolicy;
QStringList systemApplications;
DisplayOrder displayOrder;
bool allowSeparators;
bool showRecentlyInstalled;
QTimer *reloadTimer;
QStringList newInstalledPrograms;
QHash<QString, QDate> seenPrograms;
};
void ApplicationModelPrivate::fillNode(const QString &_relPath, AppNode *node)
{
if (_relPath=="new/") {
Q_FOREACH (const QString &it, newInstalledPrograms) {
KService::Ptr p = KService::serviceByStorageId(it);
if (p->noDisplay()) {
continue;
}
AppNode *newnode = new AppNode();
newnode->icon = KIcon(p->icon());
newnode->appName = p->name();
newnode->genericName = p->genericName();
newnode->desktopEntry = p->entryPath();
newnode->parent = node;
node->children.append(newnode);
}
return;
}
KServiceGroup::Ptr root = KServiceGroup::group(_relPath);
if (!root || !root->isValid()) {
return;
}
const KServiceGroup::List list = root->entries(true /* sorted */,
true /* exclude no display entries */,
allowSeparators /* allow separators */,
primaryNamePolicy == ApplicationModel::GenericNamePrimary /* sort by generic name */);
// application name <-> service map for detecting duplicate entries
QHash<QString, KService::Ptr> existingServices;
// generic name <-> node mapping to determinate duplicate generic names
QHash<QString,QList<AppNode*> > genericNames;
for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
QString icon;
QString appName;
QString genericName;
QString relPath = _relPath;
QString desktopEntry;
bool isDir = false;
bool isSeparator = false;
const KSycocaEntry::Ptr p = (*it);
if (p->isType(KST_KService)) {
const KService::Ptr service = KService::Ptr::staticCast(p);
if (service->noDisplay()) {
continue;
}
icon = service->icon();
appName = service->name();
genericName = service->genericName();
desktopEntry = service->entryPath();
// check for duplicates (eg. KDE 3 and KDE 4 versions of application
// both present)
if (duplicatePolicy == ApplicationModel::ShowLatestOnlyPolicy &&
existingServices.contains(appName)) {
if (Kickoff::isLaterVersion(existingServices[appName], service)) {
continue;
} else {
// find and remove the existing entry with the same name
for (int i = node->children.count() - 1; i >= 0; --i) {
AppNode *app = node->children.at(i);
if (app->appName == appName &&
app->genericName == genericName &&
app->iconName == icon) {
app = node->children.takeAt(i);
const QString s = app->genericName.toLower();
if (genericNames.contains(s)) {
QList<AppNode*> list = genericNames[s];
for (int j = list.count() - 1; j >= 0; --j) {
if(list.at(j) == app) {
list.takeAt(j);
}
}
genericNames[s] = list;
}
delete app;
}
}
}
}
if (systemApplicationPolicy == ApplicationModel::ShowSystemOnlyPolicy &&
systemApplications.contains(service->desktopEntryName())) {
// don't duplicate applications that are configured to appear in the System tab
// in the Applications tab
continue;
}
existingServices[appName] = service;
} else if (p->isType(KST_KServiceGroup)) {
const KServiceGroup::Ptr serviceGroup = KServiceGroup::Ptr::staticCast(p);
if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0) {
continue;
}
kDebug() << "Service group" << serviceGroup->entryPath() << serviceGroup->icon()
<< serviceGroup->relPath() << serviceGroup->directoryEntryPath();
icon = serviceGroup->icon();
if (iconNameMap().contains(icon)) {
icon = iconNameMap().value(icon);
}
desktopEntry = serviceGroup->entryPath();
genericName = serviceGroup->caption();
relPath = serviceGroup->relPath();
appName = serviceGroup->comment();
isDir = true;
} else if (p->isType(KST_KServiceSeparator)) {
isSeparator = true;
} else {
kWarning() << "KServiceGroup: Unexpected object in list!";
continue;
}
AppNode *newnode = new AppNode();
newnode->iconName = icon;
newnode->icon = KIcon(icon);
newnode->appName = appName;
newnode->genericName = genericName;
newnode->relPath = relPath;
newnode->desktopEntry = desktopEntry;
newnode->isDir = isDir;
newnode->isSeparator = isSeparator;
newnode->parent = node;
node->children.append(newnode);
if (p->isType(KST_KService)) {
const QString s = genericName.toLower();
QList<AppNode*> list = genericNames.value(s);
list.append(newnode);
genericNames[s] = list;
}
}
if (showRecentlyInstalled && _relPath.isEmpty() && !newInstalledPrograms.isEmpty()) {
AppNode *newnode = new AppNode();
newnode->icon = KIcon("chronometer");
newnode->appName = i18n("Recently Installed");
newnode->relPath = "new/";
newnode->isDir = true;
newnode->parent = node;
node->children.prepend(newnode);
}
// set the subTitleMandatory field for nodes that do not provide a unique generic
// name what may help us on display to show in such cases also the subtitle to
// provide a hint to the user what the duplicate entries are about.
foreach (const QList<AppNode*> &list, genericNames) {
if (list.count() > 1) {
foreach (AppNode* n, list) {
n->subTitleMandatory = true;
}
}
}
}
ApplicationModel::ApplicationModel(QObject *parent, bool allowSeparators)
: KickoffAbstractModel(parent),
d(new ApplicationModelPrivate(this, allowSeparators))
{
QDBusConnection dbus = QDBusConnection::sessionBus();
(void)new KickoffAdaptor(this);
QDBusConnection::sessionBus().registerObject("/kickoff", this);
dbus.connect(QString(), "/kickoff", "org.kde.plasma", "reloadMenu", this, SLOT(reloadMenu()));
connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), this, SLOT(checkSycocaChange(QStringList)));
}
ApplicationModel::~ApplicationModel()
{
delete d;
}
bool ApplicationModel::canFetchMore(const QModelIndex &parent) const
{
if (!parent.isValid())
return false;
AppNode *node = static_cast<AppNode*>(parent.internalPointer());
return node->isDir && !node->fetched;
}
void ApplicationModel::setNameDisplayOrder(DisplayOrder displayOrder)
{
d->displayOrder = displayOrder;
}
DisplayOrder ApplicationModel::nameDisplayOrder() const
{
return d->displayOrder;
}
void ApplicationModel::setShowRecentlyInstalled(bool showRecentlyInstalled)
{
if (d->showRecentlyInstalled != showRecentlyInstalled) {
d->showRecentlyInstalled = showRecentlyInstalled;
reloadMenu();
}
}
bool ApplicationModel::showRecentlyInstalled() const
{
return d->showRecentlyInstalled;
}
int ApplicationModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
bool ApplicationModel::nameAfterDescription(const QModelIndex &index) const
{
AppNode *node = static_cast<AppNode*>(index.internalPointer());
if (node->isDir) {
return true;
}
QModelIndex parent = index.parent();
while (parent.parent().isValid()) {
parent = parent.parent();
}
if (parent.isValid()) {
// nasty little hack to always makes games show their unique name
// there is no such thing as a "generic name" for a game in practice
// though this is apparently quite true for all other kinds of apps
AppNode *node = static_cast<AppNode*>(parent.internalPointer());
if (node->isDir && node->genericName == i18n("Games")) {
return false;
}
}
return d->displayOrder == NameAfterDescription;
}
QVariant ApplicationModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
AppNode *node = static_cast<AppNode*>(index.internalPointer());
switch (role) {
case Qt::DisplayRole:
if (nameAfterDescription(index) && !node->genericName.isEmpty()) {
return node->genericName;
}
return node->appName;
case Kickoff::SubTitleRole:
if (!nameAfterDescription(index) && !node->genericName.isEmpty()) {
return node->genericName;
}
return node->appName;
case Kickoff::UrlRole:
if (node->isDir) {
return QString();
}
return node->desktopEntry;
case Kickoff::SubTitleMandatoryRole:
return nameAfterDescription(index) && node->subTitleMandatory;
case Kickoff::SeparatorRole:
return node->isSeparator;
case Qt::DecorationRole:
return node->icon;
case Kickoff::RelPathRole:
return node->relPath;
case Kickoff::IconNameRole:
return node->iconName;
default:
;
}
return QVariant();
}
void ApplicationModel::fetchMore(const QModelIndex &parent)
{
if (!parent.isValid()) {
return;
}
AppNode *node = static_cast<AppNode*>(parent.internalPointer());
if (!node->isDir) {
return;
}
emit layoutAboutToBeChanged();
d->fillNode(node->relPath, node);
node->fetched = true;
emit layoutChanged();
}
bool ApplicationModel::hasChildren(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return true;
}
AppNode *node = static_cast<AppNode*>(parent.internalPointer());
return node->isDir;
}
QVariant ApplicationModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal || section != 0) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole:
return i18n("All Applications");
break;
default:
return QVariant();
}
}
QModelIndex ApplicationModel::index(int row, int column, const QModelIndex &parent) const
{
if (row < 0 || column != 0)
return QModelIndex();
AppNode *node = d->root;
if (parent.isValid())
node = static_cast<AppNode*>(parent.internalPointer());
if (row >= node->children.count())
return QModelIndex();
else
return createIndex(row, 0, node->children.at(row));
}
QModelIndex ApplicationModel::parent(const QModelIndex &index) const
{
if (!index.isValid()) {
return QModelIndex();
}
AppNode *node = static_cast<AppNode*>(index.internalPointer());
if (node->parent->parent) {
int id = node->parent->parent->children.indexOf(node->parent);
if (id >= 0 && id < node->parent->parent->children.count()) {
return createIndex(id, 0, node->parent);
}
}
return QModelIndex();
}
int ApplicationModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return d->root->children.count();
}
AppNode *node = static_cast<AppNode*>(parent.internalPointer());
return node->children.count();
}
void ApplicationModel::setDuplicatePolicy(DuplicatePolicy policy)
{
if (d->duplicatePolicy != policy) {
d->duplicatePolicy = policy;
reloadMenu();
}
}
void ApplicationModel::setSystemApplicationPolicy(SystemApplicationPolicy policy)
{
if (d->systemApplicationPolicy != policy) {
d->systemApplicationPolicy = policy;
reloadMenu();
}
}
void ApplicationModel::setPrimaryNamePolicy(PrimaryNamePolicy policy)
{
if (policy != d->primaryNamePolicy) {
d->primaryNamePolicy = policy;
reloadMenu();
}
}
ApplicationModel::PrimaryNamePolicy ApplicationModel::primaryNamePolicy() const
{
return d->primaryNamePolicy;
}
void ApplicationModel::delayedReloadMenu()
{
if (!d->reloadTimer->isActive()) {
d->reloadTimer->start(200);
}
}
void ApplicationModel::reloadMenu()
{
delete d->root;
d->root = new AppNode();
createNewProgramList();
d->fillNode(QString(), d->root);
reset();
}
void ApplicationModel::checkSycocaChange(const QStringList &changes)
{
if (changes.contains("services") || changes.contains("apps") || changes.contains("xdgdata-apps")) {
reloadMenu();
}
}
ApplicationModel::DuplicatePolicy ApplicationModel::duplicatePolicy() const
{
return d->duplicatePolicy;
}
ApplicationModel::SystemApplicationPolicy ApplicationModel::systemApplicationPolicy() const
{
return d->systemApplicationPolicy;
}
void ApplicationModel::setApplet(Plasma::Applet *applet)
{
if (d->applet.data() != applet) {
d->applet = applet;
createNewProgramList();
}
}
void ApplicationModel::createNewProgramList()
{
if (!d->applet) {
return;
}
d->newInstalledPrograms.clear();
if (!d->showRecentlyInstalled) {
return;
}
KConfigGroup kickoffrc = d->applet.data()->globalConfig();
foreach (const QString &it, kickoffrc.keyList()) {
d->seenPrograms.insert(it, QDate::fromString(kickoffrc.readEntry(it), Qt::ISODate));
}
bool initialize = (d->seenPrograms.isEmpty());
bool seenProgramsChanged = createNewProgramListForPath(QString());
if (initialize) {
// on first start, set all entries' dates to empty (means: they are not new)
for (QHash<QString, QDate>::Iterator it = d->seenPrograms.begin(); it != d->seenPrograms.end(); ++it)
*it = QDate();
d->newInstalledPrograms.clear();
}
if (seenProgramsChanged) {
for (QHash<QString, QDate>::Iterator it = d->seenPrograms.begin(); it != d->seenPrograms.end(); ++it) {
kickoffrc.writeEntry(it.key(), it.value().toString(Qt::ISODate));
}
kickoffrc.sync();
}
}
bool ApplicationModel::createNewProgramListForPath(const QString &relPath)
{
bool seenProgramsChanged = false;
KServiceGroup::Ptr group = KServiceGroup::group(relPath);
if (!group || !group->isValid()) {
return false;
}
const KServiceGroup::List list = group->entries();
KServiceGroup::List::ConstIterator it = list.begin();
for (; it != list.end(); ++it) {
KSycocaEntry::Ptr e = (*it);
if (e->isType(KST_KServiceGroup)) {
KServiceGroup::Ptr g(KServiceGroup::Ptr::staticCast(e));
if (!g->noDisplay()) {
if (createNewProgramListForPath(g->relPath()))
seenProgramsChanged = true;
}
} else if (e->isType(KST_KService)) {
KService::Ptr s(KService::Ptr::staticCast(e));
if (s->isApplication() && !s->noDisplay()) {
QString shortStorageId = s->storageId().remove(".desktop");
QHash<QString, QDate>::Iterator it_find = d->seenPrograms.find(shortStorageId);
if (it_find == d->seenPrograms.end()) {
seenProgramsChanged = true;
d->seenPrograms.insert(shortStorageId, QDate::currentDate());
if (!d->newInstalledPrograms.contains(s->storageId())) {
d->newInstalledPrograms += s->storageId();
}
}
else {
QDate date = it_find.value();
if (date.isValid()) {
if (date.daysTo(QDate::currentDate()) < 3) {
if (!d->newInstalledPrograms.contains(s->storageId())) {
d->newInstalledPrograms += s->storageId();
}
}
else {
seenProgramsChanged = true;
(*it_find) = QDate(); // this entry is not new anymore
}
}
}
}
}
}
return seenProgramsChanged;
}
/**
* FIXME This is a temporary workaround to map the icon names found
* in the desktop directory files (from /usr/share/desktop-directories)
* into the Oxygen icon names. (Only applies if the Gnome menu files
* are also installed)
*
* This list was compiled from Kubuntu 7.04 with the gnome-menus
* package present.
*
* This needs to be discussed on kde-core-devel and fixed
*/
QHash<QString, QString> ApplicationModelPrivate::iconNameMap()
{
static QHash<QString, QString> map;
if (map.isEmpty()) {
map.insert("gnome-util", "applications-accessories");
// accessibility Oxygen icon was missing when this list was compiled
map.insert("accessibility-directory", "applications-other");
map.insert("gnome-devel", "applications-development");
map.insert("package_edutainment", "applications-education");
map.insert("gnome-joystick", "applications-games");
map.insert("gnome-graphics", "applications-graphics");
map.insert("gnome-globe", "applications-internet");
map.insert("gnome-multimedia", "applications-multimedia");
map.insert("gnome-applications", "applications-office");
map.insert("gnome-system", "applications-system");
}
return map;
}
} // namespace Kickoff
#include "moc_applicationmodel.cpp"

View file

@ -1,145 +0,0 @@
/*
Copyright 2007 Pino Toscano <pino@kde.org>
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef APPLICATIONMODEL_H
#define APPLICATIONMODEL_H
#include "kickoff_export.h"
#include "core/kickoffabstractmodel.h"
#include "core/models.h"
namespace Plasma
{
class Applet;
} // namespace Plasma
namespace Kickoff
{
class ApplicationModelPrivate;
/**
* ApplicationModel provides a tree model containing all of the user's installed graphical programs.
* The applications are arranged into categories, based on the information in their .desktop files.
*/
class KICKOFF_EXPORT ApplicationModel : public KickoffAbstractModel
{
Q_OBJECT
public:
ApplicationModel(QObject *parent = 0, bool allowSeparators = false);
virtual ~ApplicationModel();
/**
* This enum describes the policy for
* handling duplicate applications (that is,
* two applications with the same name in the same group)
*/
enum DuplicatePolicy {
/** Display duplicate entries. */
ShowDuplicatesPolicy,
/**
* Show only the entry for the most recent
* version of the application.
*
* Currently only a crude heuristic to determine whether the
* application is from KDE 3 or KDE 4 is used to determine
* recent-ness.
*
* eg. If MyGame/KDE 3 and MyGame/KDE 4 are found
* show only MyGame/KDE 4
*/
ShowLatestOnlyPolicy
};
/**
* This enum describes the policy for
* handling applications that are configured to appear
* in the System tab.
*/
enum SystemApplicationPolicy {
/** Display entries in Applications tab and System tab. */
ShowApplicationAndSystemPolicy,
/** Display entry only in System tab. */
ShowSystemOnlyPolicy
};
enum PrimaryNamePolicy {
AppNamePrimary,
GenericNamePrimary
};
void setNameDisplayOrder(DisplayOrder displayOrder);
DisplayOrder nameDisplayOrder() const;
//DisplayOrder m_displayOrder;
/**
* Sets the policy for handling duplicate applications.
* See DuplicatePolicy
*/
void setDuplicatePolicy(DuplicatePolicy policy);
/** See setDuplicatePolicy() */
DuplicatePolicy duplicatePolicy() const;
/**
* Sets the policy for handling System applications.
* See SystemApplicationPolicy
*/
void setSystemApplicationPolicy(SystemApplicationPolicy policy);
/** See setSystemApplicationPolicy() */
SystemApplicationPolicy systemApplicationPolicy() const;
void setPrimaryNamePolicy(PrimaryNamePolicy policy);
PrimaryNamePolicy primaryNamePolicy() const;
// reimplemented from QAbstractItemModel
virtual bool canFetchMore(const QModelIndex &parent) const;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual void fetchMore(const QModelIndex &parent);
virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
virtual QModelIndex parent(const QModelIndex &index) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
void setApplet(Plasma::Applet *applet);
void setShowRecentlyInstalled(bool showRecentlyInstalled);
bool showRecentlyInstalled() const;
public slots:
void reloadMenu();
void delayedReloadMenu();
void checkSycocaChange(const QStringList &changes);
private:
bool nameAfterDescription(const QModelIndex &index) const;
friend class ApplicationModelPrivate;
ApplicationModelPrivate *const d;
void createNewProgramList();
bool createNewProgramListForPath(const QString &relPath);
Q_DISABLE_COPY(ApplicationModel)
};
}
#endif

View file

@ -1,351 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
//Own
#include "core/favoritesmodel.h"
// Qt
#include <QHash>
#include <QList>
#include <QMimeData>
#include <QFileInfo>
#include <QUrl>
// KDE
#include <KConfigGroup>
#include <KService>
#include <KDesktopFile>
#include <kdebug.h>
using namespace Kickoff;
class FavoritesModel::Private
{
public:
Private(FavoritesModel *parent)
: q(parent),
displayOrder(NameAfterDescription)
{
init();
}
void init()
{
headerItem = new QStandardItem(i18n("Favorites"));
q->appendRow(headerItem);
}
void addFavoriteItem(const QString& url)
{
QStandardItem *item = StandardItemFactory::createItemForUrl(url, displayOrder);
headerItem->appendRow(item);
}
void moveFavoriteItem(int startRow, int destRow)
{
if (destRow == startRow) {
return;
}
QStandardItem *item = headerItem->takeChild(startRow);
headerItem->removeRow(startRow);
headerItem->insertRow(destRow, item);
}
void removeFavoriteItem(const QString& url)
{
QModelIndexList matches = q->match(q->index(0, 0), UrlRole,
url, -1,
Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap | Qt::MatchRecursive));
kDebug() << "Removing item matches" << matches;
foreach (const QModelIndex& index, matches) {
QStandardItem *item = q->itemFromIndex(index);
if (item->parent()) {
item->parent()->removeRow(item->row());
} else {
qDeleteAll(q->takeRow(item->row()));
}
}
}
static void loadFavorites()
{
globalFavoriteList.clear();
globalFavoriteSet.clear();
KConfigGroup favoritesGroup = componentData().config()->group("Favorites");
QList<QString> favoriteList = favoritesGroup.readEntry("FavoriteURLs", QList<QString>());
if (favoriteList.isEmpty()) {
favoriteList = defaultFavorites();
}
foreach (const QString &favorite, favoriteList) {
FavoritesModel::add(favorite);
}
}
static QList<QString> defaultFavorites()
{
QList<QString> applications;
applications << "konsole" << "dolphin" << "systemsettings";
QList<QString> desktopFiles;
foreach (const QString& application, applications) {
KService::Ptr service = KService::serviceByStorageId(application + ".desktop");
if (service) {
desktopFiles << service->entryPath();
}
}
return desktopFiles;
}
static void saveFavorites()
{
KConfigGroup favoritesGroup = componentData().config()->group("Favorites");
favoritesGroup.writeEntry("FavoriteURLs", globalFavoriteList);
favoritesGroup.config()->sync();
}
static QList<QString> globalFavoriteList;
static QSet<QString> globalFavoriteSet;
static QSet<FavoritesModel*> models;
FavoritesModel * const q;
QStandardItem *headerItem;
DisplayOrder displayOrder;
};
QList<QString> FavoritesModel::Private::globalFavoriteList;
QSet<QString> FavoritesModel::Private::globalFavoriteSet;
QSet<FavoritesModel*> FavoritesModel::Private::models;
FavoritesModel::FavoritesModel(QObject *parent)
: KickoffModel(parent)
, d(new Private(this))
{
Private::models << this;
if (Private::models.count() == 1 && Private::globalFavoriteList.isEmpty()) {
Private::loadFavorites();
} else {
foreach (const QString &url, Private::globalFavoriteList) {
d->addFavoriteItem(url);
}
}
}
FavoritesModel::~FavoritesModel()
{
Private::models.remove(this);
if (Private::models.isEmpty()) {
Private::saveFavorites();
}
delete d;
}
void FavoritesModel::add(const QString& url)
{
Private::globalFavoriteList << url;
Private::globalFavoriteSet << url;
foreach (FavoritesModel* model, Private::models) {
model->d->addFavoriteItem(url);
}
// save after each add in case we crash
Private::saveFavorites();
}
void FavoritesModel::move(int startRow, int destRow)
{
// just move the item
Private::globalFavoriteList.move(startRow, destRow);
foreach (FavoritesModel* model, Private::models) {
model->d->moveFavoriteItem(startRow, destRow);
}
// save after each add in case we crash
Private::saveFavorites();
}
void FavoritesModel::remove(const QString& url)
{
Private::globalFavoriteList.removeAll(url);
Private::globalFavoriteSet.remove(url);
foreach (FavoritesModel* model, Private::models) {
model->d->removeFavoriteItem(url);
}
// save after each remove in case of crash or other mishaps
Private::saveFavorites();
}
bool FavoritesModel::isFavorite(const QString& url)
{
return Private::globalFavoriteSet.contains(url);
}
int FavoritesModel::numberOfFavorites()
{
foreach (FavoritesModel* model, Private::models) {
return model->d->headerItem->rowCount() - 1;
}
return 0;
}
void FavoritesModel::sortFavorites(Qt::SortOrder order)
{
if(Private::models.isEmpty()) {
return;
}
foreach (FavoritesModel *model, Private::models) {
model->d->headerItem->sortChildren(0, order);
}
Private::globalFavoriteList.clear();
FavoritesModel *model = *Private::models.begin();
QStandardItem *childData;
for (int i = 0; i <= numberOfFavorites(); i++) {
childData = model->d->headerItem->child(i, 0);
Private::globalFavoriteList.append(childData->data(Kickoff::UrlRole).toString());
}
Private::saveFavorites();
}
void FavoritesModel::sortFavoritesAscending()
{
sortFavorites(Qt::AscendingOrder);
}
void FavoritesModel::sortFavoritesDescending()
{
sortFavorites(Qt::DescendingOrder);
}
bool FavoritesModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex & parent)
{
Q_UNUSED(parent);
if (action == Qt::IgnoreAction) {
return true;
}
if (column > 0) {
return false;
}
if (action == Qt::MoveAction) {
QModelIndex modelIndex;
QStandardItem *startItem;
int startRow = -1;
int destRow = row;
// look for the favorite that was dragged
for (int i = 0; i < d->headerItem->rowCount(); i++) {
startItem = d->headerItem->child(i, 0);
if (QFileInfo(startItem->data(Kickoff::UrlRole).toString()).completeBaseName()
== QFileInfo(data->text()).completeBaseName()) {
startRow = i;
break;
}
}
if (startRow < 0) {
bool dropped = false;
foreach (const QUrl &url, data->urls()) {
if (!url.isValid()) {
continue;
}
const QString path = url.toLocalFile();
if (!KDesktopFile::isDesktopFile(path)) {
continue;
}
KDesktopFile dFile(path);
if (dFile.hasApplicationType() && !dFile.noDisplay()) {
FavoritesModel::add(path);
dropped = true;
}
}
return dropped;
}
if (destRow < 0)
return false;
// now move the item to it's new location
FavoritesModel::move(startRow, destRow);
return true;
}
return true;
}
QVariant FavoritesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal || section != 0) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole:
return i18nc("@title:column", "Favorites");
break;
default:
return QVariant();
}
}
void FavoritesModel::setNameDisplayOrder(DisplayOrder displayOrder)
{
if (d->displayOrder == displayOrder) {
return;
}
d->displayOrder = displayOrder;
foreach (FavoritesModel* model, Private::models) {
model->clear();
model->d->init();
}
Private::loadFavorites();
}
DisplayOrder FavoritesModel::nameDisplayOrder() const
{
return d->displayOrder;
}
#include "moc_favoritesmodel.cpp"

View file

@ -1,72 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef FAVORITESMODEL_H
#define FAVORITESMODEL_H
#include "kickoff_export.h"
#include "core/kickoffmodel.h"
#include "core/models.h"
namespace Kickoff
{
/**
* A model which provides an ordered list of 'favorite' items chosen by the user.
* The items may represent documents, folders, applications, devices or anything else
* identified by a URL.
*
* The information persists between sessions.
*/
class KICKOFF_EXPORT FavoritesModel : public KickoffModel
{
Q_OBJECT
public:
FavoritesModel(QObject *parent);
virtual ~FavoritesModel();
/** Add a new item for @p url to the user's favorites list. */
static void add(const QString& url);
/** Remove the item associated with @p url from the user's favorites list. */
static void remove(const QString& url);
/** Returns true if @p url is in the list of the user's favorite URLs. */
static void move(int startRow, int destRow);
static int numberOfFavorites();
static void sortFavorites(Qt::SortOrder order);
static bool isFavorite(const QString& url);
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex & parent);
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
void setNameDisplayOrder(DisplayOrder displayOrder);
DisplayOrder nameDisplayOrder() const;
public Q_SLOTS:
void sortFavoritesAscending();
void sortFavoritesDescending();
private:
class Private;
Private * const d;
};
}
#endif // FAVORITESMODEL_H

View file

@ -1,174 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "core/itemhandlers.h"
// Qt
#include <QTimer>
// KDE
#include <KDebug>
#include <KJob>
#include <KService>
#include <KRun>
#include <KUrl>
#include <Solid/PowerManagement>
// KDE Base
#include "kworkspace/kworkspace.h"
#include "kworkspace/kdisplaymanager.h"
// Local
#include "core/recentapplications.h"
// DBus
#include "screensaver_interface.h"
#include "ksmserver_interface.h"
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusConnectionInterface>
using namespace Kickoff;
bool ServiceItemHandler::openUrl(const KUrl& url)
{
KService::Ptr service = KService::serviceByDesktopPath(url.pathOrUrl());
if (!service.isNull()) {
RecentApplications::self()->add(service);
} else {
qWarning() << "Failed to find service for" << url;
return false;
}
new KRun(url, 0);
return true;
}
bool LeaveItemHandler::openUrl(const KUrl& url)
{
m_logoutAction = url.path().remove('/');
if (m_logoutAction == "sleep") {
// decouple dbus call, otherwise we'll run into a dead-lock
QTimer::singleShot(0, this, SLOT(suspendRAM()));
return true;
} else if (m_logoutAction == "hibernate") {
// decouple dbus call, otherwise we'll run into a dead-lock
QTimer::singleShot(0, this, SLOT(suspendDisk()));
return true;
} else if (m_logoutAction == "hybrid") {
// decouple dbus call, otherwise we'll run into a dead-lock
QTimer::singleShot(0, this, SLOT(suspendHybrid()));
return true;
} else if (m_logoutAction == "lock") {
// decouple dbus call, otherwise we'll run into a dead-lock
QTimer::singleShot(0, this, SLOT(lock()));
return true;
} else if (m_logoutAction == "switch") {
// decouple dbus call, otherwise we'll run into a dead-lock
QTimer::singleShot(0, this, SLOT(switchUser()));
return true;
} else if (m_logoutAction == "logout" || m_logoutAction == "logoutonly" ||
m_logoutAction == "restart" || m_logoutAction == "shutdown") {
// decouple dbus call, otherwise we'll run into a dead-lock
QTimer::singleShot(0, this, SLOT(logout()));
return true;
} else if (m_logoutAction == "savesession") {
// decouple dbus call, otherwise we'll run into a dead-lock
QTimer::singleShot(0, this, SLOT(saveSession()));
return true;
} else if (m_logoutAction == "suspendram") {
// decouple dbus call, otherwise we'll run into a dead-lock
QTimer::singleShot(0, this, SLOT(suspendRAM()));
return true;
} else if (m_logoutAction == "suspenddisk") {
// decouple dbus call, otherwise we'll run into a dead-lock
QTimer::singleShot(0, this, SLOT(suspendDisk()));
return true;
} else if (m_logoutAction == "suspendhybrid") {
// decouple dbus call, otherwise we'll run into a dead-lock
QTimer::singleShot(0, this, SLOT(suspendHybrid()));
return true;
}
return false;
}
void LeaveItemHandler::logout()
{
KWorkSpace::ShutdownConfirm confirm = KWorkSpace::ShutdownConfirmDefault;
KWorkSpace::ShutdownType type = KWorkSpace::ShutdownTypeNone;
if (m_logoutAction == "logout" || m_logoutAction == "logoutonly") {
type = KWorkSpace::ShutdownTypeNone;
} else if (m_logoutAction == "lock") {
kDebug() << "Locking screen";
} else if (m_logoutAction == "switch") {
kDebug() << "Switching user";
} else if (m_logoutAction == "restart") {
type = KWorkSpace::ShutdownTypeReboot;
} else if (m_logoutAction == "shutdown") {
type = KWorkSpace::ShutdownTypeHalt;
}
KWorkSpace::requestShutDown(confirm, type);
}
void LeaveItemHandler::lock()
{
QString interface("org.freedesktop.ScreenSaver");
org::freedesktop::ScreenSaver screensaver(interface, "/ScreenSaver", QDBusConnection::sessionBus());
screensaver.Lock();
}
void LeaveItemHandler::switchUser()
{
QDBusInterface saveriface(
"org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver"
);
saveriface.call("Lock");
KDisplayManager dm;
dm.newSession();
}
void LeaveItemHandler::saveSession()
{
QString interface("org.kde.ksmserver");
org::kde::KSMServerInterface ksmserver(interface, "/KSMServer", QDBusConnection::sessionBus());
if (ksmserver.isValid()) {
ksmserver.saveCurrentSession();
}
}
void LeaveItemHandler::suspendRAM()
{
Solid::PowerManagement::requestSleep(Solid::PowerManagement::SuspendState);
}
void LeaveItemHandler::suspendDisk()
{
Solid::PowerManagement::requestSleep(Solid::PowerManagement::HibernateState);
}
void LeaveItemHandler::suspendHybrid()
{
Solid::PowerManagement::requestSleep(Solid::PowerManagement::HybridSuspendState);
}

View file

@ -1,58 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef ITEMHANDLERS_H
#define ITEMHANDLERS_H
#include <QtCore/QObject>
#include "kickoff_export.h"
#include "core/urlitemlauncher.h"
namespace Kickoff
{
class KICKOFF_EXPORT ServiceItemHandler : public UrlItemHandler
{
public:
virtual bool openUrl(const KUrl& url);
};
class KICKOFF_EXPORT LeaveItemHandler : public QObject, public UrlItemHandler
{
Q_OBJECT
public:
virtual bool openUrl(const KUrl& url);
private Q_SLOTS:
void logout();
void lock();
void switchUser();
void saveSession();
void suspendRAM();
void suspendDisk();
void suspendHybrid();
private:
QString m_logoutAction;
};
}
#endif // ITEMHANDLERS_H

View file

@ -1,92 +0,0 @@
/*
Copyright 2008 Marco Martin <notmart@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "core/kickoffabstractmodel.h"
// Qt
#include <QMimeData>
// KDE
#include <KUrl>
#include <KDebug>
// Local
#include "core/models.h"
using namespace Kickoff;
KickoffAbstractModel::KickoffAbstractModel(QObject *parent)
: QAbstractItemModel(parent)
{}
KickoffAbstractModel::~KickoffAbstractModel()
{}
Qt::ItemFlags KickoffAbstractModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
if (index.isValid()) {
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
} else {
return 0;
}
}
QMimeData *KickoffAbstractModel::mimeData(const QModelIndexList &indexes) const
{
KUrl::List urls;
QByteArray itemData;
foreach(const QModelIndex &index, indexes) {
KUrl itemUrl = KUrl(data(index, UrlRole).toString());
if (itemUrl.isValid()) {
urls << itemUrl;
}
}
QMimeData *mimeData = new QMimeData();
if (!urls.isEmpty()) {
urls.populateMimeData(mimeData);
}
return mimeData;
}
QStringList KickoffAbstractModel::mimeTypes() const
{
QStringList types;
types << QLatin1String("text/uri-list");
return types;
}
Qt::DropActions KickoffAbstractModel::supportedDropActions() const
{
return 0;
}
Qt::DropActions KickoffAbstractModel::supportedDragActions() const
{
return Qt::CopyAction;
}
#include "moc_kickoffabstractmodel.cpp"

View file

@ -1,55 +0,0 @@
/*
Copyright 2008 Marco Martin <notmart@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef KICKOFFABSTRACTMODEL_H
#define KICKOFFABSTRACTMODEL_H
#include "kickoff_export.h"
// Qt
#include <QAbstractItemModel>
// KDE
namespace Kickoff
{
/**
* Base model for Kickoff models based on QAbstractItemModel, enables drag and drop support
*/
class KICKOFF_EXPORT KickoffAbstractModel : public QAbstractItemModel
{
Q_OBJECT
public:
/** Construct a new KickoffModel with the specified parent. */
KickoffAbstractModel(QObject *parent = 0);
virtual ~KickoffAbstractModel();
//Reimplementations
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
virtual QStringList mimeTypes() const;
virtual Qt::DropActions supportedDropActions() const;
virtual Qt::DropActions supportedDragActions() const;
};
}
#endif // KICKOFFABSTRACTMODEL_H

View file

@ -1,92 +0,0 @@
/*
Copyright 2008 Marco Martin <notmart@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "core/kickoffmodel.h"
// Qt
#include <QMimeData>
// KDE
#include <KUrl>
#include <KDebug>
// Local
#include "core/models.h"
using namespace Kickoff;
KickoffModel::KickoffModel(QObject *parent)
: QStandardItemModel(parent)
{
}
KickoffModel::~KickoffModel()
{}
Qt::ItemFlags KickoffModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QStandardItemModel::flags(index);
if (index.isValid()) {
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
} else {
return 0;
}
}
QMimeData *KickoffModel::mimeData(const QModelIndexList &indexes) const
{
KUrl::List urls;
foreach (const QModelIndex &index, indexes) {
KUrl itemUrl = KUrl(data(index, UrlRole).toString());
if (itemUrl.isValid()) {
urls << itemUrl;
}
}
QMimeData *mimeData = new QMimeData();
if (!urls.isEmpty()) {
urls.populateMimeData(mimeData);
}
return mimeData;
}
QStringList KickoffModel::mimeTypes() const
{
QStringList types;
types << QLatin1String("text/uri-list");
return types;
}
Qt::DropActions KickoffModel::supportedDropActions() const
{
return Qt::MoveAction;
}
Qt::DropActions KickoffModel::supportedDragActions() const
{
return Qt::CopyAction;
}
#include "moc_kickoffmodel.cpp"

View file

@ -1,57 +0,0 @@
/*
Copyright 2008 Marco Martin <notmart@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef KICKOFFMODEL_H
#define KICKOFFMODEL_H
#include "kickoff_export.h"
// Qt
#include <QStandardItemModel>
// KDE
namespace Kickoff
{
/**
* Base model for Kickoff models based on QStandardItemModel, enables drag and drop support
*/
class KICKOFF_EXPORT KickoffModel : public QStandardItemModel
{
Q_OBJECT
public:
/** Construct a new KickoffModel with the specified parent. */
KickoffModel(QObject *parent = 0);
virtual ~KickoffModel();
//Reimplementations
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
virtual QStringList mimeTypes() const;
virtual Qt::DropActions supportedDropActions() const;
virtual Qt::DropActions supportedDragActions() const;
private:
};
}
#endif // KICKOFFMODEL_H

View file

@ -1,92 +0,0 @@
/*
Copyright 2008 Marco Martin <notmart@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "core/kickoffproxymodel.h"
// Qt
#include <QMimeData>
// KDE
#include <KUrl>
#include <KDebug>
// Local
#include "core/models.h"
using namespace Kickoff;
KickoffProxyModel::KickoffProxyModel(QObject *parent)
: QAbstractProxyModel(parent)
{}
KickoffProxyModel::~KickoffProxyModel()
{}
Qt::ItemFlags KickoffProxyModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QAbstractProxyModel::flags(index);
if (index.isValid()) {
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
} else {
return 0;
}
}
QMimeData *KickoffProxyModel::mimeData(const QModelIndexList &indexes) const
{
KUrl::List urls;
QByteArray itemData;
foreach(const QModelIndex &index, indexes) {
KUrl itemUrl = KUrl(data(index, UrlRole).toString());
if (itemUrl.isValid()) {
urls << itemUrl;
}
}
QMimeData *mimeData = new QMimeData();
if (!urls.isEmpty()) {
urls.populateMimeData(mimeData);
}
return mimeData;
}
QStringList KickoffProxyModel::mimeTypes() const
{
QStringList types;
types << QLatin1String("text/uri-list");
return types;
}
Qt::DropActions KickoffProxyModel::supportedDropActions() const
{
return 0;
}
Qt::DropActions KickoffProxyModel::supportedDragActions() const
{
return Qt::CopyAction;
}
#include "moc_kickoffproxymodel.cpp"

View file

@ -1,55 +0,0 @@
/*
Copyright 2008 Marco Martin <notmart@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef KICKOFFPROXYMODEL_H
#define KICKOFFPROXYMODEL_H
#include "kickoff_export.h"
// Qt
#include <QAbstractProxyModel>
// KDE
namespace Kickoff
{
/**
* Base model for Kickoff models based on QAbstractProxyModel, enables drag and drop support
*/
class KICKOFF_EXPORT KickoffProxyModel : public QAbstractProxyModel
{
Q_OBJECT
public:
/** Construct a new KickoffModel with the specified parent. */
KickoffProxyModel(QObject *parent = 0);
virtual ~KickoffProxyModel();
//Reimplementations
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
virtual QStringList mimeTypes() const;
virtual Qt::DropActions supportedDropActions() const;
virtual Qt::DropActions supportedDragActions() const;
};
}
#endif // KICKOFFPROXYMODEL_H

View file

@ -1,229 +0,0 @@
/*
Copyright 2009 Ivan Cukic <ivan.cukic+kde@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "core/krunnermodel.h"
// Qt
#include <QBasicTimer>
#include <QDebug>
#include <QList>
#include <QMimeData>
#include <QString>
#include <QtCore/qcoreevent.h>
// KDE
#include <KUrl>
#include <KService>
#include <KStandardDirs>
#include <Plasma/AbstractRunner>
// Local
#include "core/recentapplications.h"
#define DELAY_TIME 50
using namespace Kickoff;
Plasma::RunnerManager* _runnerManager = nullptr;
static KService::Ptr serviceForUrl(const KUrl &url)
{
QString runner = url.host();
QString id = url.path();
if (id.startsWith(QLatin1Char('/'))) {
id = id.remove(0, 1);
}
if (runner != QLatin1String("services")) {
return KService::Ptr(nullptr);
}
// URL path example: services_kde4-kate.desktop
// or: services_firefox.desktop
id.remove("services_");
return KService::serviceByStorageId(id);
}
bool KRunnerItemHandler::openUrl(const KUrl& url)
{
QString runner = url.host();
QString id = url.path();
if (id.startsWith(QLatin1Char('/'))) {
id = id.remove(0, 1);
}
// Since krunner:// urls can't be added to recent applications,
// we find the local .desktop entry.
KService::Ptr service = serviceForUrl(url);
if (service) {
RecentApplications::self()->add(service);
} else {
qWarning() << "Failed to find service for" << url;
}
KRunnerModel::runnerManager()->run(id);
return true;
}
class KRunnerModel::Private {
public:
QBasicTimer searchDelay;
QString searchQuery;
DisplayOrder displayOrder;
};
KRunnerModel::KRunnerModel(QObject *parent)
: KickoffModel(parent)
, d(new Private())
{
connect(
runnerManager(), SIGNAL(matchesChanged(QList<Plasma::QueryMatch>)),
this, SLOT(matchesChanged(QList<Plasma::QueryMatch>))
);
}
KRunnerModel::~KRunnerModel()
{
delete d;
}
void KRunnerModel::setQuery(const QString& query)
{
runnerManager()->reset();
clear();
d->searchQuery = query.trimmed();
if (d->searchQuery.isEmpty()) {
return;
}
d->searchDelay.start(DELAY_TIME, this);
}
void KRunnerModel::timerEvent(QTimerEvent * event)
{
KickoffModel::timerEvent(event);
if (event->timerId() == d->searchDelay.timerId()) {
d->searchDelay.stop();
runnerManager()->launchQuery(d->searchQuery);
};
}
void KRunnerModel::setNameDisplayOrder(DisplayOrder displayOrder)
{
d->displayOrder = displayOrder;
}
DisplayOrder KRunnerModel::nameDisplayOrder() const
{
return d->displayOrder;
}
void KRunnerModel::matchesChanged(const QList< Plasma::QueryMatch > & m)
{
QList< Plasma::QueryMatch > matches = m;
qSort(matches.begin(), matches.end());
clear();
while (matches.size()) {
Plasma::QueryMatch match = matches.takeLast();
appendRow(
StandardItemFactory::createItem(
match.icon(),
match.text(),
match.subtext(),
QString("krunner://") + match.runner()->id() + "/" + match.id()
)
);
}
}
Qt::ItemFlags KRunnerModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = KickoffModel::flags(index);
if (index.isValid()) {
KUrl url = data(index, UrlRole).toString();
QString host = url.host();
if (host != "services") {
flags &= ~ (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
}
} else {
flags = 0;
}
return flags;
}
QMimeData * KRunnerModel::mimeData(const QModelIndexList &indexes) const
{
KUrl::List urls;
foreach (const QModelIndex &index, indexes) {
KUrl url = data(index, UrlRole).toString();
KService::Ptr service = serviceForUrl(url);
if (service) {
urls << KUrl(service->entryPath());
}
}
QMimeData *mimeData = new QMimeData();
if (!urls.isEmpty()) {
urls.populateMimeData(mimeData);
}
return mimeData;
}
Plasma::RunnerManager* KRunnerModel::runnerManager()
{
if (!_runnerManager) {
KConfigGroup conf = componentData().config()->group("Plugins");
QStringList allowed;
foreach (KPluginInfo plugin, Plasma::RunnerManager::listRunnerInfo()) {
plugin.load(conf);
if (plugin.isPluginEnabled()) {
allowed.append(plugin.pluginName());
}
}
// NOTE: Plasma::RunnerManager uses sub-group named PlasmaRunnerManager
conf = componentData().config()->group("KRunner");
conf.writeEntry("loadAll", false);
conf.sync();
_runnerManager = new Plasma::RunnerManager(conf);
_runnerManager->setAllowedRunners(allowed);
}
return _runnerManager;
}
#include "moc_krunnermodel.cpp"

View file

@ -1,75 +0,0 @@
/*
Copyright 2009 Ivan Cukic <ivan.cukic+kde@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef KRUNNERMODEL_H
#define KRUNNERMODEL_H
#include "kickoff_export.h"
#include "core/models.h"
#include "core/kickoffmodel.h"
#include "core/urlitemlauncher.h"
#include <Plasma/QueryMatch>
#include <Plasma/RunnerManager>
namespace Kickoff {
class KICKOFF_EXPORT KRunnerItemHandler : public UrlItemHandler {
public:
virtual bool openUrl(const KUrl& url);
};
class KICKOFF_EXPORT KRunnerModel : public KickoffModel
{
Q_OBJECT
public:
KRunnerModel(QObject *parent);
virtual ~KRunnerModel();
void setNameDisplayOrder(DisplayOrder displayOrder);
DisplayOrder nameDisplayOrder() const;
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
// virtual QStringList mimeTypes() const;
// virtual Qt::DropActions supportedDropActions() const;
// virtual Qt::DropActions supportedDragActions() const;
static Plasma::RunnerManager* runnerManager();
private:
void timerEvent(QTimerEvent * event);
public Q_SLOTS:
void setQuery(const QString& query);
private Q_SLOTS:
void matchesChanged(const QList< Plasma::QueryMatch > & matches);
Q_SIGNALS:
void resultsAvailable();
private:
class Private;
Private * const d;
};
}
#endif // KRUNNERMODEL_H

View file

@ -1,181 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "core/leavemodel.h"
// Qt
#include <QFileInfo>
// KDE
#include <KConfigGroup>
#include <KDebug>
#include <KIcon>
#include <Solid/PowerManagement>
#include <kworkspace/kworkspace.h>
#include <kworkspace/kdisplaymanager.h>
// Local
#include "core/models.h"
using namespace Kickoff;
QStandardItem* LeaveModel::createStandardItem(const QString& url)
{
//Q_ASSERT(KUrl(url).scheme() == "leave");
QStandardItem *item = new QStandardItem();
const QString basename = QFileInfo(url).baseName();
if (basename == "logoutonly") {
item->setText(i18n("Log out"));
item->setIcon(KIcon("system-log-out"));
item->setData(i18n("End session"), Kickoff::SubTitleRole);
} else if (basename == "lock") {
item->setText(i18n("Lock"));
item->setIcon(KIcon("system-lock-screen"));
item->setData(i18n("Lock screen"), Kickoff::SubTitleRole);
} else if (basename == "switch") {
item->setText(i18n("Switch user"));
item->setIcon(KIcon("system-switch-user"));
item->setData(i18n("Start a parallel session as a different user"), Kickoff::SubTitleRole);
} else if (basename == "shutdown") {
item->setText(i18n("Shut down"));
item->setIcon(KIcon("system-shutdown"));
item->setData(i18n("Turn off computer"), Kickoff::SubTitleRole);
} else if (basename == "restart") {
item->setText(i18nc("Restart computer", "Restart"));
item->setIcon(KIcon("system-reboot"));
item->setData(i18n("Restart computer"), Kickoff::SubTitleRole);
} else if (basename == "savesession") {
item->setText(i18n("Save Session"));
item->setIcon(KIcon("document-save"));
item->setData(i18n("Save current session for next login"), Kickoff::SubTitleRole);
} else if (basename == "suspenddisk") {
item->setText(i18n("Hibernate"));
item->setIcon(KIcon("system-suspend-hibernate"));
item->setData(i18n("Suspend to disk"), Kickoff::SubTitleRole);
} else if (basename == "suspendram") {
item->setText(i18n("Sleep"));
item->setIcon(KIcon("system-suspend"));
item->setData(i18n("Suspend to RAM"), Kickoff::SubTitleRole);
} else if (basename == "suspendhybrid") {
item->setText(i18n("Hybrid Suspend"));
item->setIcon(KIcon("system-suspend"));
item->setData(i18n("Hybrid Suspend"), Kickoff::SubTitleRole);
} else {
item->setText(basename);
item->setData(url, Kickoff::SubTitleRole);
}
item->setData(url, Kickoff::UrlRole);
return item;
}
LeaveModel::LeaveModel(QObject *parent)
: QStandardItemModel(parent)
{
}
QVariant LeaveModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal || section != 0) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole:
return i18n("Leave");
break;
default:
return QVariant();
}
}
void LeaveModel::updateModel()
{
clear();
// Session Options
QStandardItem *sessionOptions = new QStandardItem(i18n("Session"));
// Logout
QStandardItem *logoutOption = createStandardItem("leave:/logoutonly");
sessionOptions->appendRow(logoutOption);
// Lock
QStandardItem *lockOption = createStandardItem("leave:/lock");
sessionOptions->appendRow(lockOption);
// Save Session
KConfigGroup c(KSharedConfig::openConfig("ksmserverrc", KConfig::NoGlobals), "General");
if (c.readEntry("loginMode") == "restoreSavedSession") {
QStandardItem *saveSessionOption = createStandardItem("leave:/savesession");
sessionOptions->appendRow(saveSessionOption);
}
// Switch User
if (KDisplayManager().isSwitchable()) {
QStandardItem *switchUserOption = createStandardItem("leave:/switch");
sessionOptions->appendRow(switchUserOption);
}
// System Options
QStandardItem *systemOptions = new QStandardItem(i18n("System"));
bool addSystemSession = false;
QSet< Solid::PowerManagement::SleepState > spdMethods = Solid::PowerManagement::supportedSleepStates();
if (spdMethods.contains(Solid::PowerManagement::SuspendState)) {
QStandardItem *suspendramOption = createStandardItem("leave:/suspendram");
systemOptions->appendRow(suspendramOption);
addSystemSession = true;
}
if (spdMethods.contains(Solid::PowerManagement::HibernateState)) {
QStandardItem *suspenddiskOption = createStandardItem("leave:/suspenddisk");
systemOptions->appendRow(suspenddiskOption);
addSystemSession = true;
}
if (spdMethods.contains(Solid::PowerManagement::HybridSuspendState)) {
QStandardItem *suspendhybridOption = createStandardItem("leave:/suspendhybrid");
systemOptions->appendRow(suspendhybridOption);
addSystemSession = true;
}
if (KWorkSpace::canShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeReboot)) {
// Restart
QStandardItem *restartOption = createStandardItem("leave:/restart");
systemOptions->appendRow(restartOption);
addSystemSession = true;
}
if (KWorkSpace::canShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeHalt)) {
// Shutdown
QStandardItem *shutDownOption = createStandardItem("leave:/shutdown");
systemOptions->appendRow(shutDownOption);
addSystemSession = true;
}
appendRow(sessionOptions);
if (addSystemSession) {
appendRow(systemOptions);
} else {
delete systemOptions;
}
}
#include "moc_leavemodel.cpp"

View file

@ -1,45 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef LEAVEMODEL_H
#define LEAVEMODEL_H
#include "kickoff_export.h"
#include <QStandardItemModel>
namespace Kickoff
{
class KICKOFF_EXPORT LeaveModel : public QStandardItemModel
{
Q_OBJECT
public:
LeaveModel(QObject *parent);
static QStandardItem* createStandardItem(const QString& url);
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
void updateModel();
};
}
#endif // LEAVEMODEL_H

View file

@ -1,216 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "core/models.h"
#include "core/leavemodel.h"
// Qt
#include <QFileInfo>
#include <QtGui/qstandarditemmodel.h>
#include <QDir>
// KDE
#include <KDebug>
#include <KConfigGroup>
#include <KDesktopFile>
#include <KIcon>
#include <KMimeType>
#include <KUrl>
#include <Solid/Device>
#include <Solid/StorageAccess>
#include <Solid/StorageDrive>
using namespace Kickoff;
namespace Kickoff
{
Q_GLOBAL_STATIC_WITH_ARGS(KUrl, homeUrl, (QDir::homePath()))
Q_GLOBAL_STATIC_WITH_ARGS(KUrl, remoteUrl, ("remote:/"))
K_GLOBAL_STATIC(StandardItemFactoryData, factoryData)
StandardItemFactoryData* deviceFactoryData()
{
return factoryData;
}
} // namespace Kickoff
QStandardItem *StandardItemFactory::createItemForUrl(const QString& urlString, DisplayOrder displayOrder)
{
KUrl url(urlString);
QStandardItem *item = 0;
// Match files ending with ".desktop" and being local or having a relative
// path. For instance applications that still installs .desktop files at
// /usr/share/applnk, like KVirc 3
const bool isDesktopFile = urlString.endsWith(QLatin1String(".desktop"));
if (isDesktopFile && (url.isLocalFile() || url.isRelative())) {
// .desktop files may be services (type field == 'Application' or 'Service')
// or they may be other types such as links.
//
// first look in the KDE service database to see if this file is a service,
// otherwise represent it as a generic .desktop file
KService::Ptr service = KService::serviceByDesktopPath(url.toLocalFile());
if (service) {
return createItemForService(service, displayOrder);
}
item = new QStandardItem();
KDesktopFile desktopFile(url.toLocalFile());
QString urlFileName = QFileInfo(urlString).fileName();
if (isDesktopFile) {
urlFileName = urlFileName.mid(0, urlFileName.size() - 8);
}
item->setText(urlFileName);
item->setIcon(KIcon(desktopFile.readIcon()));
//FIXME: desktopUrl is a hack around borkage in KRecentDocuments which
// stores a path in the URL field!
KUrl desktopUrl(desktopFile.desktopGroup().readPathEntry("URL", QString()));
if (!desktopUrl.url().isEmpty()) {
item->setData(desktopUrl.url(), Kickoff::UrlRole);
} else {
// desktopUrl.url() is empty if the file doesn't exist so set the
// url role to that which was passed so that the item can still be
// manually removed
item->setData(urlString, Kickoff::UrlRole);
}
QString subTitle = desktopUrl.isLocalFile() ? desktopUrl.toLocalFile() : desktopUrl.prettyUrl();
item->setData(subTitle, Kickoff::SubTitleRole);
setSpecialUrlProperties(desktopUrl, item);
} else if (url.scheme() == "leave") {
item = LeaveModel::createStandardItem(urlString);
} else {
item = new QStandardItem();
const QString subTitle = url.isLocalFile() ? url.toLocalFile() : url.prettyUrl();
QString basename = QFileInfo(url.prettyUrl()).fileName();
if (basename.isNull()) {
basename = subTitle;
}
item->setText(basename);
item->setIcon(KIcon(KMimeType::iconNameForUrl(url)));
item->setData(url.url(), Kickoff::UrlRole);
item->setData(subTitle, Kickoff::SubTitleRole);
setSpecialUrlProperties(url, item);
}
return item;
}
void StandardItemFactory::setSpecialUrlProperties(const KUrl& url, QStandardItem *item)
{
// specially handled URLs
if (homeUrl() && url == *homeUrl()) {
item->setText(i18n("Home Folder"));
item->setIcon(KIcon("user-home"));
} else if (remoteUrl() && url == *remoteUrl()) {
item->setText(i18n("Network Folders"));
}
}
QStandardItem *StandardItemFactory::createItem(const QIcon & icon, const QString & title,
const QString & description, const QString & url)
{
QStandardItem *appItem = new QStandardItem;
appItem->setText(title);
appItem->setIcon(icon);
appItem->setData(description, Kickoff::SubTitleRole);
appItem->setData(url, Kickoff::UrlRole);
return appItem;
}
QStandardItem *StandardItemFactory::createItemForService(KService::Ptr service, DisplayOrder displayOrder)
{
QStandardItem *appItem = new QStandardItem;
QString genericName = service->genericName();
QString appName = service->name();
bool nameFirst = displayOrder == NameBeforeDescription;
appItem->setText(nameFirst || genericName.isEmpty() ? appName : genericName);
appItem->setIcon(KIcon(service->icon()));
appItem->setData(service->entryPath(), Kickoff::UrlRole);
if (nameFirst) {
if (!genericName.isEmpty()) {
appItem->setData(genericName, Kickoff::SubTitleRole);
}
} else if (!genericName.isEmpty()) {
// we only set the subtitle to appname if the generic name is empty because if
// the generic name IS empty, then the app name is used as the title role
// and we don't want it repeated twice.
appItem->setData(appName, Kickoff::SubTitleRole);
}
return appItem;
}
bool Kickoff::isLaterVersion(KService::Ptr first , KService::Ptr second)
{
// a very crude heuristic using the .desktop path names
// which only understands kde3 vs kde4
bool firstIsKde4 = first->entryPath().contains("kde4");
bool secondIsKde4 = second->entryPath().contains("kde4");
return firstIsKde4 && !secondIsKde4;
}
QStringList Kickoff::systemApplicationList()
{
KConfigGroup appsGroup = componentData().config()->group("SystemApplications");
QStringList apps;
apps << "systemsettings";
apps = appsGroup.readEntry("DesktopFiles", apps);
return apps;
}
#if 0
void Kickoff::swapModelIndexes(QModelIndex& first, QModelIndex& second)
{
Q_ASSERT(first.isValid());
Q_ASSERT(second.isValid());
QAbstractItemModel *firstModel = const_cast<QAbstractItemModel*>(first.model());
QAbstractItemModel *secondModel = const_cast<QAbstractItemModel*>(second.model());
Q_ASSERT(firstModel && secondModel);
QMap<int, QVariant> firstIndexData = firstModel->itemData(first);
QMap<int, QVariant> secondIndexData = secondModel->itemData(second);
firstModel->setItemData(first, secondIndexData);
secondModel->setItemData(second, firstIndexData);
}
#endif
K_GLOBAL_STATIC_WITH_ARGS(KComponentData, kickoffComponent, ("kickoff", QByteArray(), KComponentData::SkipMainComponentRegistration))
KComponentData Kickoff::componentData()
{
return *kickoffComponent;
}

View file

@ -1,139 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef MODELS_H
#define MODELS_H
#include "kickoff_export.h"
// Katie
#include <QStandardItem>
#include <QModelIndex>
// KDE
#include <KService>
#include <KUrl>
namespace Solid
{
class Device;
}
namespace Kickoff
{
class StandardItemFactoryData
{
public:
QHash<QString, Solid::Device> deviceByUrl;
};
StandardItemFactoryData* deviceFactoryData();
/**
* List of applications contained in the DesktopFiles key in the
* SystemApplications group, used to populate the System model
* @return list of desktop entry names
*/
QStringList systemApplicationList();
/**
* Additional data roles for data which the Kickoff models supply with their items
* for use when rendering the items and launching them.
*/
enum DataRole {
/** A sub title to be displayed below the text from the item's Qt::DisplayRole data */
SubTitleRole = Qt::UserRole + 1,
FirstDataRole = SubTitleRole,
/** The URL to be opened when executing the item. */
UrlRole = Qt::UserRole + 2,
/** The Solid device identifier for items which represent devices. */
DeviceUdiRole = Qt::UserRole + 3,
/** The amount of space (in Kilobytes) used for items which represent storage. */
DiskUsedSpaceRole = Qt::UserRole + 4,
/** The amount of free space (in Kilobytes) for items which represent storage. */
DiskFreeSpaceRole = Qt::UserRole + 5,
SubTitleMandatoryRole = Qt::UserRole + 6,
/** Is item a separator. **/
SeparatorRole = Qt::UserRole + 7,
/** relative path of the item */
RelPathRole = Qt::UserRole + 8,
IconNameRole = Qt::UserRole + 9,
LastDataRole = IconNameRole
};
/**
* This enum describes the policy for displaying
* Name of Application - Description
* Description - Name of Application
*/
enum DisplayOrder {
NameAfterDescription,
NameBeforeDescription
};
/**
* Factory for creating QStandardItems with appropriate text, icons, URL
* and other Kickoff-specific information for a given URL or Service.
*/
class StandardItemFactory
{
public:
static QStandardItem *createItemForUrl(const QString& url, DisplayOrder displayOrder);
static QStandardItem *createItemForService(KService::Ptr service,
DisplayOrder displayOrder);
static QStandardItem *createItem(const QIcon & icon, const QString & title,
const QString & description, const QString & url);
private:
static void setSpecialUrlProperties(const KUrl& url, QStandardItem *item);
};
/**
* Abstract base class for delegates which provide information about a model
* item's state in a particular view.
*/
class ItemStateProvider
{
public:
virtual ~ItemStateProvider() {}
/**
* Returns true if a @p index should be drawn in the view or
* false if it should be hidden.
*/
virtual bool isVisible(const QModelIndex& index) const = 0;
};
// returns true if 'first' represents a more recent version of
// an application than 'second'
//
// eg. isLaterVersion(myapp_kde4,myapp_kde3) returns true
bool isLaterVersion(KService::Ptr first , KService::Ptr second);
#if 0
/** Swaps the data for two indexes in a QAbstractItemModel */
void swapModelIndexes(QModelIndex& first, QModelIndex& second);
#endif
// returns the Kickoff component data, this is mainly used
// to access the Kickoff shared config data
KICKOFF_EXPORT KComponentData componentData();
}
#endif //MODELS_H

View file

@ -1,7 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.kickoff.recent">
<signal name="clearRecentDocumentsAndApplications"/>
</interface>
</node>

View file

@ -1,7 +0,0 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.kickoff">
<signal name="reloadMenu"/>
</interface>
</node>

View file

@ -1,191 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "core/recentapplications.h"
// Qt
#include <QtCore/QHash>
#include <QtCore/QList>
// KDE
#include <KConfigGroup>
#include <KDebug>
// Local
#include "core/models.h"
using namespace Kickoff;
class RecentApplications::Private
{
public:
class ServiceInfo;
Private() : defaultMaxServices(DEFAULT_MAX_SERVICES) {
KConfigGroup recentGroup = componentData().config()->group("RecentlyUsed");
QList<QString> recentApplications = recentGroup.readEntry("Applications", QList<QString>());
defaultMaxServices = maxServices = qMax(0, recentGroup.readEntry("MaxApplications", defaultMaxServices));
// TESTING
// the actual last date/time is not currently recorded, instead we just use
// the current date/time and adjust it by one second after each item is added
// to preserve the order of the applications in the list loaded from the KConfig
// source
QDateTime dateTime = QDateTime::currentDateTime();
foreach(const QString& application, recentApplications) {
ServiceInfo info;
info.storageId = application;
info.startCount = 1;
info.lastStartedTime = dateTime;
addEntry(info.storageId, info);
dateTime = dateTime.addSecs(1);
}
};
~Private() {
KConfigGroup recentGroup = componentData().config()->group("RecentlyUsed");
QList<ServiceInfo> services = serviceInfo.values();
qSort(services.begin(), services.end());
// TESTING
// only the desktop file used is currently recorded, information such
// as start count and date/time of last used is lost
QList<QString> recentApplications;
foreach(const ServiceInfo& info, services) {
recentApplications << info.storageId;
}
recentGroup.writeEntry("Applications", recentApplications);
recentGroup.config()->sync();
}
void addEntry(const QString& id, ServiceInfo& info) {
// if this service is already in the list then remove the existing
// queue entry (so that there are no duplicates in the queue)
if (serviceInfo.contains(id)) {
kDebug() << "Duplicate entry added. Removing existing entry from queue.";
serviceQueue.removeAll(id);
}
serviceQueue.append(id);
serviceInfo.insert(id, info);
}
void removeExpiredEntries() {
// if more than the maximum number of services have been added
// remove the least recently used service
while (serviceQueue.count() > maxServices) {
QString removeId = serviceQueue.takeFirst();
kDebug() << "More than the maximal " << maxServices << " services added. Removing" << removeId << "from queue.";
serviceInfo.remove(removeId);
emit instance.applicationRemoved(KService::serviceByStorageId(removeId));
}
}
class ServiceInfo
{
public:
ServiceInfo() : startCount(0) {}
QString storageId;
int startCount;
QDateTime lastStartedTime;
bool operator<(const ServiceInfo& rhs) const {
return this->lastStartedTime < rhs.lastStartedTime;
}
};
static const int DEFAULT_MAX_SERVICES = 5;
int defaultMaxServices, maxServices;
// queue to keep track of the order in which services have been used
// (most recently used at the back)
QList<QString> serviceQueue;
QHash<QString, ServiceInfo> serviceInfo;
RecentApplications instance;
};
K_GLOBAL_STATIC(RecentApplications::Private, privateSelf)
RecentApplications *RecentApplications::self()
{
return &privateSelf->instance;
}
RecentApplications::RecentApplications()
{
}
QList<KService::Ptr> RecentApplications::recentApplications() const
{
QList<Private::ServiceInfo> services = privateSelf->serviceInfo.values();
qSort(services.begin(), services.end(), qGreater<Private::ServiceInfo>());
QList<KService::Ptr> servicePtrs;
foreach(const Private::ServiceInfo& info, services) {
KService::Ptr s = KService::serviceByStorageId(info.storageId);
if (s) {
servicePtrs << s;
}
}
return servicePtrs;
}
int RecentApplications::startCount(KService::Ptr service) const
{
return privateSelf->serviceInfo[service->storageId()].startCount;
}
QDateTime RecentApplications::lastStartedTime(KService::Ptr service) const
{
return privateSelf->serviceInfo[service->storageId()].lastStartedTime;
}
void RecentApplications::setMaximum(int maximum)
{
Q_ASSERT(maximum >= 0);
privateSelf->maxServices = maximum;
privateSelf->removeExpiredEntries();
}
int RecentApplications::maximum() const
{
return privateSelf->maxServices;
}
int RecentApplications::defaultMaximum() const
{
return privateSelf->defaultMaxServices;
}
void RecentApplications::add(KService::Ptr service)
{
Private::ServiceInfo info = privateSelf->serviceInfo.value(service->storageId());
info.storageId = service->storageId();
info.startCount++;
info.lastStartedTime = QDateTime::currentDateTime();
privateSelf->addEntry(info.storageId, info);
kDebug() << "Recent app added" << info.storageId << info.startCount;
emit applicationAdded(service, info.startCount);
privateSelf->removeExpiredEntries();
}
void RecentApplications::clear()
{
privateSelf->serviceInfo.clear();
emit cleared();
}
#include "moc_recentapplications.cpp"

View file

@ -1,93 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef RECENTAPPLICATIONS_H
#define RECENTAPPLICATIONS_H
#include "kickoff_export.h"
// Qt
#include <QtCore/QObject>
#include <QDateTime>
// KDE
#include <KService>
namespace Kickoff
{
/**
* Singleton class which can be used to keep track of recently started applications
* in the Kickoff launcher.
*
* RecentApplications information persists between sessions.
*/
class KICKOFF_EXPORT RecentApplications : public QObject
{
Q_OBJECT
public:
class Private;
friend class Private;
static RecentApplications *self();
/**
* List of service pointers for recently started applications in the order in which
* they were started, with the most recently used application first.
*/
QList<KService::Ptr> recentApplications() const;
/** Returns the number of times an application represented by @p service has been started. */
int startCount(KService::Ptr service) const;
/** Returns the last time and date with the application represented by @p service was started. */
QDateTime lastStartedTime(KService::Ptr service) const;
/** Sets the maximum number of recently used applications to remember. */
void setMaximum(int max);
/** Returns the maximum number of recently used applications that are remembered. */
int maximum() const;
/** Returns the default maximum number of recently used applications that are remembered as defined
either in the configfile as "MaxApplications" or via the DEFAULT_MAX_SERVICES macro. */
int defaultMaximum() const;
public Q_SLOTS:
/**
* Registers the startup of an application. This should be called whenever a new application
* or service is started.
*/
void add(KService::Ptr service);
/** Clear the list of recent applications. */
void clear();
Q_SIGNALS:
/** Emitted after add() has been called. */
void applicationAdded(KService::Ptr service, int startCount);
/** Emitted after remove() has been called. */
void applicationRemoved(KService::Ptr service);
/** Emitted after clear() has been called. */
void cleared();
private:
RecentApplications();
};
}
#endif // RECENTAPPLICATIONS_H

View file

@ -1,286 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "core/recentlyusedmodel.h"
// Qt
// KDE
#include <KDesktopFile>
#include <KDirWatch>
#include <KRecentDocument>
#include <KUrl>
#include <KDebug>
// Local
#include "core/recentapplications.h"
#include "recentadaptor.h"
using namespace Kickoff;
class RecentlyUsedModel::Private
{
public:
Private(RecentlyUsedModel *parent, RecentType recenttype, int maxRecentApps)
: q(parent),
recenttype(recenttype),
maxRecentApps(maxRecentApps >= 0 ? maxRecentApps : Kickoff::RecentApplications::self()->defaultMaximum()),
recentDocumentItem(0),
recentAppItem(0),
displayOrder(NameAfterDescription)
{
}
void removeExistingItem(const QString& path) {
if (!itemsByPath.contains(path)) {
return;
}
QStandardItem *existingItem = itemsByPath[path];
kDebug() << "Removing existing item" << existingItem;
Q_ASSERT(existingItem->parent());
existingItem->parent()->removeRow(existingItem->row());
itemsByPath.remove(path);
}
void addRecentApplication(KService::Ptr service, bool append) {
// remove existing item if any
removeExistingItem(service->entryPath());
QStandardItem *appItem = StandardItemFactory::createItemForService(service, displayOrder);
itemsByPath.insert(service->entryPath(), appItem);
if (append) {
recentAppItem->appendRow(appItem);
} else {
recentAppItem->insertRow(0, appItem);
}
while (recentAppItem->rowCount() > maxRecentApps) {
QList<QStandardItem*> row = recentAppItem->takeRow(recentAppItem->rowCount() - 1);
//don't leave pending stuff in itemsByPath
if (!row.isEmpty()) {
itemsByPath.remove(row.first()->data(UrlRole).toString());
qDeleteAll(row.begin(), row.end());
}
}
}
void addRecentDocument(const QString& desktopPath, bool append) {
// remove existing item if any
KDesktopFile desktopFile(desktopPath);
KUrl documentUrl = desktopFile.readUrl();
removeExistingItem(documentUrl.url());
QStandardItem *documentItem = StandardItemFactory::createItemForUrl(desktopPath, displayOrder);
documentItem->setData(true, Kickoff::SubTitleMandatoryRole);
itemsByPath.insert(desktopPath, documentItem);
//kDebug() << "Document item" << documentItem << "text" << documentItem->text() << "url" << documentUrl.url();
if (append) {
recentDocumentItem->appendRow(documentItem);
} else {
recentDocumentItem->insertRow(0, documentItem);
}
}
void loadRecentDocuments()
{
// create branch for documents and add existing items
recentDocumentItem = new QStandardItem(i18n("Documents"));
const QStringList documents = KRecentDocument::recentDocuments();
foreach(const QString& document, documents) {
addRecentDocument(document, true);
}
q->appendRow(recentDocumentItem);
}
void loadRecentApplications()
{
recentAppItem = new QStandardItem(i18n("Applications"));
const QList<KService::Ptr> services = RecentApplications::self()->recentApplications();
for(int i = 0; i < maxRecentApps && i < services.count(); ++i) {
addRecentApplication(services[i], true);
}
q->appendRow(recentAppItem);
}
RecentlyUsedModel * const q;
RecentType recenttype;
int maxRecentApps;
QStandardItem *recentDocumentItem;
QStandardItem *recentAppItem;
QHash<QString, QStandardItem*> itemsByPath;
DisplayOrder displayOrder;
};
RecentlyUsedModel::RecentlyUsedModel(QObject *parent, RecentType recenttype, int maxRecentApps)
: KickoffModel(parent),
d(new Private(this, recenttype, maxRecentApps))
{
QDBusConnection dbus = QDBusConnection::sessionBus();
(void)new RecentAdaptor(this);
QDBusConnection::sessionBus().registerObject("/kickoff/RecentAppDoc", this);
dbus.connect(QString(), "/kickoff/RecentAppDoc", "org.kde.plasma", "clearRecentDocumentsAndApplications", this, SLOT(clearRecentDocumentsAndApplications()));
if (recenttype != DocumentsOnly) {
d->loadRecentApplications();
// listen for changes to the list of recent applications
connect(RecentApplications::self(), SIGNAL(applicationAdded(KService::Ptr,int)),
this, SLOT(recentApplicationAdded(KService::Ptr,int)));
connect(RecentApplications::self(), SIGNAL(applicationRemoved(KService::Ptr)),
this, SLOT(recentApplicationRemoved(KService::Ptr)));
connect(RecentApplications::self(), SIGNAL(cleared()),
this, SLOT(recentApplicationsCleared()));
}
if (recenttype != ApplicationsOnly) {
d->loadRecentDocuments();
// listen for changes to the list of recent documents
KDirWatch *recentDocWatch = new KDirWatch(this);
recentDocWatch->addDir(KRecentDocument::recentDocumentDirectory());
connect(recentDocWatch, SIGNAL(dirty(QString)), this, SLOT(recentDocumentDirty(QString)));
}
}
RecentlyUsedModel::~RecentlyUsedModel()
{
delete d;
}
QVariant RecentlyUsedModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal || section != 0) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole:
if (d->recenttype == DocumentsAndApplications) {
return i18n("Recently Used");
} else if (d->recenttype == DocumentsOnly) {
return i18n("Recently Used Documents");
} else if (d->recenttype == ApplicationsOnly) {
return i18n("Recently Used Applications");
}
default:
return QVariant();
}
}
void RecentlyUsedModel::setNameDisplayOrder(DisplayOrder displayOrder)
{
if (d->displayOrder == displayOrder) {
return;
}
d->displayOrder = displayOrder;
d->itemsByPath.clear();
clear();
if (d->recenttype != DocumentsOnly) {
d->loadRecentApplications();
}
if (d->recenttype != ApplicationsOnly) {
d->loadRecentDocuments();
}
}
DisplayOrder RecentlyUsedModel::nameDisplayOrder() const
{
return d->displayOrder;
}
void RecentlyUsedModel::recentDocumentDirty(const QString& path)
{
kDebug() << "Recent document dirty" << path;
d->itemsByPath.clear();
clear();
if (d->recenttype != DocumentsOnly) {
d->loadRecentApplications();
}
if (d->recenttype != ApplicationsOnly) {
d->loadRecentDocuments();
}
}
void RecentlyUsedModel::recentApplicationAdded(KService::Ptr service, int)
{
if (service) {
d->addRecentApplication(service, false);
}
}
void RecentlyUsedModel::recentApplicationRemoved(KService::Ptr service)
{
if (service) {
d->removeExistingItem(service->entryPath());
}
}
void RecentlyUsedModel::recentApplicationsCleared()
{
QSet<QStandardItem*> appItems;
const int rows = d->recentAppItem->rowCount();
for (int i = 0;i < rows;i++) {
appItems << d->recentAppItem->child(i);
}
QMutableHashIterator<QString, QStandardItem*> iter(d->itemsByPath);
while (iter.hasNext()) {
iter.next();
if (appItems.contains(iter.value())) {
iter.remove();
}
}
d->recentAppItem->removeRows(0, d->recentAppItem->rowCount());
}
void RecentlyUsedModel::clearRecentApplications()
{
RecentApplications::self()->clear();
}
void RecentlyUsedModel::clearRecentDocuments()
{
KRecentDocument::clear();
}
void RecentlyUsedModel::clearRecentDocumentsAndApplications()
{
clearRecentDocuments();
clearRecentApplications();
}
#include "moc_recentlyusedmodel.cpp"

View file

@ -1,76 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef RECENTLYUSEDMODEL_H
#define RECENTLYUSEDMODEL_H
#include "kickoff_export.h"
#include "core/kickoffmodel.h"
#include "core/models.h"
// KDE
#include <KService>
namespace Kickoff
{
/**
* Model for the Recently Used view which provides a tree of recently used
* applications and documents.
*/
class KICKOFF_EXPORT RecentlyUsedModel : public KickoffModel
{
Q_OBJECT
public:
/** Defines what the model should display. */
enum RecentType {
DocumentsAndApplications, ///< display recently used documents and applications.
DocumentsOnly, ///< documents only. recently used applications will not be displayed.
ApplicationsOnly ///< applications only, recently used documents will not be displayed.
};
/** Construct a new RecentlyUsedModel with the specified parent. */
explicit RecentlyUsedModel(QObject *parent = 0, RecentType recenttype = DocumentsAndApplications, int maxRecentApps = -1);
virtual ~RecentlyUsedModel();
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
void setNameDisplayOrder(DisplayOrder displayOrder);
DisplayOrder nameDisplayOrder() const;
public Q_SLOTS:
void clearRecentApplications();
void clearRecentDocuments();
void clearRecentDocumentsAndApplications();
private Q_SLOTS:
void recentDocumentDirty(const QString& path);
void recentApplicationAdded(KService::Ptr, int startCount);
void recentApplicationRemoved(KService::Ptr);
void recentApplicationsCleared();
private:
class Private;
Private * const d;
};
}
#endif // RECENTLYUSEDMODEL_H

View file

@ -1,464 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2007 Kevin Ottens <ervin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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 "systemmodel.h"
// Qt
#include <QHash>
#include <QTimer>
// KDE
#include <KDebug>
#include <KDiskFreeSpaceInfo>
#include <KIcon>
#include <KUrl>
#include <KSycoca>
#include <KFilePlacesModel>
#include <Solid/Device>
#include <Solid/DeviceInterface>
#include <Solid/DeviceNotifier>
#include <Solid/StorageAccess>
#include <Solid/StorageDrive>
// Local
#include "core/models.h"
#include "core/systemmodel.h"
using namespace Kickoff;
static const int APPLICATIONS_ROW = 0;
static const int BOOKMARKS_ROW = 1;
static const int REMOVABLE_ROW = 2;
static const int FIXED_ROW = 3;
static const int LAST_ROW = FIXED_ROW;
class SystemModel::Private
{
public:
Private(SystemModel *parent)
: q(parent),
placesModel(new KFilePlacesModel(parent)),
refreshRequested(false)
{
q->setSourceModel(placesModel);
connect(placesModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
q, SLOT(sourceDataChanged(QModelIndex,QModelIndex)));
connect(placesModel, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
q, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int)));
connect(placesModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
q, SLOT(sourceRowsInserted(QModelIndex,int,int)));
connect(placesModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
q, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
connect(placesModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
q, SLOT(sourceRowsRemoved(QModelIndex,int,int)));
topLevelSections << i18n("Applications")
<< i18n("Places")
<< i18n("Removable Storage")
<< i18n("Storage");
connect(KSycoca::self(), SIGNAL(databaseChanged(QStringList)), q, SLOT(reloadApplications()));
}
SystemModel * const q;
KFilePlacesModel *placesModel;
QStringList topLevelSections;
KService::List appsList;
QMap<QString, UsageInfo> usageByMountpoint;
QWeakPointer<UsageFinder> usageFinder;
bool refreshRequested;
};
SystemModel::SystemModel(QObject *parent)
: KickoffProxyModel(parent)
, d(new Private(this))
{
qRegisterMetaType<UsageInfo>("UsageInfo");
reloadApplications();
}
SystemModel::~SystemModel()
{
delete d;
}
QModelIndex SystemModel::mapFromSource(const QModelIndex &sourceIndex) const
{
if (!sourceIndex.isValid()) {
return QModelIndex();
}
QModelIndex parent;
if (!d->placesModel->isDevice(sourceIndex)) {
parent = index(BOOKMARKS_ROW, 0);
} else {
bool isFixedDevice = d->placesModel->data(sourceIndex, KFilePlacesModel::FixedDeviceRole).toBool();
if (!isFixedDevice) {
parent = index(REMOVABLE_ROW, 0);
} else {
parent = index(FIXED_ROW, 0);
}
}
return index(sourceIndex.row(), 0, parent);
}
QModelIndex SystemModel::mapToSource(const QModelIndex &proxyIndex) const
{
if (!proxyIndex.isValid() || !proxyIndex.parent().isValid()) {
return QModelIndex();
}
return d->placesModel->index(proxyIndex.row(), proxyIndex.column());
}
QModelIndex SystemModel::index(int row, int column, const QModelIndex &parent) const
{
if (!parent.isValid()) {
return createIndex(row, column, 0);
}
// We use the row+1 of the parent as internal Id.
return createIndex(row, column, parent.row() + 1);
}
QModelIndex SystemModel::parent(const QModelIndex &item) const
{
if (item.internalId() > 0) {
return index(item.internalId() - 1, 0);
} else {
return QModelIndex();
}
}
int SystemModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid()) {
return LAST_ROW + 1;
} else if (!parent.parent().isValid()) {
switch (parent.row()) {
case APPLICATIONS_ROW:
return d->appsList.size() + 1;
case BOOKMARKS_ROW:
return d->placesModel->rowCount();
case REMOVABLE_ROW:
return d->placesModel->rowCount();
default:
return 0;
}
}
return 0;
}
int SystemModel::columnCount(const QModelIndex &/*parent*/) const
{
return 1;
}
QVariant SystemModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (index.internalId() == 0) {
if (role == Qt::DisplayRole) {
return d->topLevelSections[index.row()];
} else {
return QVariant();
}
}
if (index.internalId() - 1 == APPLICATIONS_ROW) {
if (d->appsList.count() < index.row()) {
return QVariant();
} else if (d->appsList.count() == index.row()) {
// used to be "Run Command"
return QVariant();
}
KService::Ptr service = d->appsList[index.row()];
switch (role) {
case Qt::DisplayRole:
return service->name();
case Qt::DecorationRole:
return KIcon(service->icon());
case SubTitleRole:
return service->genericName();
case UrlRole:
return service->entryPath();
default:
return QVariant();
}
}
if (role == UrlRole && !d->placesModel->isHidden(mapToSource(index))) {
QModelIndex parent = index.parent();
QModelIndex sourceIndex = mapToSource(index);
bool isDevice = d->placesModel->isDevice(sourceIndex);
bool wellPlaced = false;
if (!isDevice && parent.row() == BOOKMARKS_ROW) {
wellPlaced = true;
} else if (isDevice) {
bool fixed = d->placesModel->data(sourceIndex, KFilePlacesModel::FixedDeviceRole).toBool();
if (!fixed && parent.row() == REMOVABLE_ROW) {
wellPlaced = true;
} else if (fixed && parent.row() == FIXED_ROW) {
wellPlaced = true;
}
}
if (wellPlaced) {
return d->placesModel->url(sourceIndex).url();
} else {
return QVariant();
}
} else if (role == DeviceUdiRole) {
QModelIndex sourceIndex = mapToSource(index);
if (d->placesModel->isDevice(sourceIndex)) {
Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
return dev.udi();
} else {
return QVariant();
}
} else if (role == SubTitleRole) {
QModelIndex sourceIndex = mapToSource(index);
if (d->placesModel->isDevice(sourceIndex)) {
Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
if (access) {
return access->filePath();
}
} else if (index.parent().row() != APPLICATIONS_ROW) {
KUrl url = d->placesModel->url(sourceIndex);
return url.isLocalFile() ? url.toLocalFile() : url.prettyUrl();
}
return QVariant();
} else if (role == DiskUsedSpaceRole || role == DiskFreeSpaceRole) {
QModelIndex sourceIndex = mapToSource(index);
QString mp;
if (d->placesModel->isDevice(sourceIndex)) {
Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
if (access) {
mp = access->filePath();
}
}
if (!mp.isEmpty() && d->usageByMountpoint.contains(mp)) {
UsageInfo info = d->usageByMountpoint[mp];
if (role == DiskUsedSpaceRole) {
return info.used;
} else {
return info.available;
}
}
}
return d->placesModel->data(mapToSource(index), role);
}
QVariant SystemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal || section != 0) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole:
return i18n("Computer");
break;
default:
return QVariant();
}
}
UsageFinder::UsageFinder(QObject *parent)
: QThread(parent)
{
}
void UsageFinder::add(int index, const QString &mountPoint)
{
//NOTE: this is not particularly perfect for a threaded object ;)
// but the assumption here is that add is called before run()
m_toCheck.append(qMakePair(index, mountPoint));
}
void UsageFinder::run()
{
typedef QPair<int, QString> CheckPair;
foreach (CheckPair check, m_toCheck) {
KDiskFreeSpaceInfo freeSpace = KDiskFreeSpaceInfo::freeSpaceInfo(check.second);
if (freeSpace.isValid()) {
UsageInfo info;
info.used = freeSpace.used() / 1024;
info.available = freeSpace.available() / 1024;
emit usageInfo(check.first, check.second, info);
}
}
}
void SystemModel::refreshUsageInfo()
{
if (d->usageFinder) {
d->refreshRequested = true;
} else {
QTimer::singleShot(100, this, SLOT(startUsageInfoFetch()));
}
}
void SystemModel::setUsageInfo(int index, const QString &mountPoint, const UsageInfo &usageInfo)
{
QModelIndex sourceIndex = d->placesModel->index(index, 0);
if (sourceIndex.isValid()) {
d->usageByMountpoint[mountPoint] = usageInfo;
QModelIndex index = mapFromSource(sourceIndex);
emit dataChanged(index, index);
}
}
void SystemModel::stopRefreshingUsageInfo()
{
d->refreshRequested = false;
}
void SystemModel::usageFinderFinished()
{
if (d->refreshRequested) {
d->refreshRequested = false;
QTimer::singleShot(100, this, SLOT(startUsageInfoFetch()));
}
}
void SystemModel::startUsageInfoFetch()
{
if (d->usageFinder) {
return;
}
UsageFinder *usageFinder = new UsageFinder(this);
d->usageFinder = usageFinder;
connect(usageFinder, SIGNAL(finished()),
this, SLOT(usageFinderFinished()));
connect(usageFinder, SIGNAL(finished()),
usageFinder, SLOT(deleteLater()));
connect(usageFinder, SIGNAL(usageInfo(int,QString,UsageInfo)),
this, SLOT(setUsageInfo(int,QString,UsageInfo)));
bool hasDevices = false;
for (int i = 0; i < d->placesModel->rowCount(); ++i) {
QModelIndex sourceIndex = d->placesModel->index(i, 0);
if (d->placesModel->isDevice(sourceIndex)) {
Solid::Device dev = d->placesModel->deviceForIndex(sourceIndex);
Solid::StorageAccess *access = dev.as<Solid::StorageAccess>();
if (access && !access->filePath().isEmpty()) {
usageFinder->add(i, access->filePath());
hasDevices = true;
}
}
}
if (hasDevices) {
usageFinder->start();
} else {
delete usageFinder;
}
}
void SystemModel::reloadApplications()
{
const QStringList apps = Kickoff::systemApplicationList();
d->appsList.clear();
foreach (const QString &app, apps) {
KService::Ptr service = KService::serviceByStorageId(app);
if (service) {
d->appsList << service;
}
}
}
void Kickoff::SystemModel::sourceDataChanged(const QModelIndex &start, const QModelIndex &end)
{
if (start.parent().isValid()) return;
for (int row = BOOKMARKS_ROW; row <= LAST_ROW; ++row) {
QModelIndex section = index(row, 0);
QModelIndex new_start = index(start.row(), start.column(), section);
QModelIndex new_end = index(end.row(), end.column(), section);
emit dataChanged(new_start, new_end);
}
}
void Kickoff::SystemModel::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
{
if (parent.isValid()) return;
for (int row = BOOKMARKS_ROW; row <= LAST_ROW; ++row) {
QModelIndex section = index(row, 0);
beginInsertRows(section, start, end);
}
}
void Kickoff::SystemModel::sourceRowsInserted(const QModelIndex &parent, int /*start*/, int /*end*/)
{
if (parent.isValid()) return;
endInsertRows();
}
void Kickoff::SystemModel::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{
if (parent.isValid()) return;
for (int row = BOOKMARKS_ROW; row <= LAST_ROW; ++row) {
QModelIndex section = index(row, 0);
beginRemoveRows(section, start, end);
}
}
void Kickoff::SystemModel::sourceRowsRemoved(const QModelIndex &parent, int /*start*/, int /*end*/)
{
if (parent.isValid()) return;
endRemoveRows();
}
#include "moc_systemmodel.cpp"

View file

@ -1,103 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2007 Kevin Ottens <ervin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef SYSTEMMODEL_H
#define SYSTEMMODEL_H
#include "kickoff_export.h"
#include "core/kickoffproxymodel.h"
#include <QThread>
namespace Kickoff
{
struct UsageInfo {
UsageInfo()
: used(0),
available(0)
{}
quint64 used;
quint64 available;
};
class UsageFinder : public QThread
{
Q_OBJECT
public:
UsageFinder(QObject *parent);
void add(int index, const QString &mountPoint);
Q_SIGNALS:
void usageInfo(int index, const QString &mountPoint, const UsageInfo &usageInfo);
protected:
void run();
private:
QList<QPair<int, QString> > m_toCheck;
};
/**
* Model which provides a tree of items for important system setup tools (eg. System Settings) ,
* folders (eg. the user's home folder and the local network) and fixed and removable storage.
*/
class KICKOFF_EXPORT SystemModel : public KickoffProxyModel
{
Q_OBJECT
public:
/** Constructs a new SystemModel with the specified parent. */
SystemModel(QObject *parent = 0);
virtual ~SystemModel();
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
virtual QModelIndex parent(const QModelIndex &item) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
void refreshUsageInfo();
void stopRefreshingUsageInfo();
private Q_SLOTS:
void startUsageInfoFetch();
void reloadApplications();
void sourceDataChanged(const QModelIndex &start, const QModelIndex &end);
void sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
void sourceRowsInserted(const QModelIndex &parent, int start, int end);
void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
void sourceRowsRemoved(const QModelIndex &parent, int start, int end);
void setUsageInfo(int index, const QString &mountPoint, const UsageInfo &usageInfo);
void usageFinderFinished();
private:
class Private;
Private * const d;
};
}
#endif // SYSTEMMODEL_H

View file

@ -1,154 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2007 Kevin Ottens <ervin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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 "urlitemlauncher.h"
// Qt
#include <QFileInfo>
#include <QHash>
#include <QtCore/qabstractitemmodel.h>
// KDE
#include <KDebug>
#include <KRun>
#include <KUrl>
#include <Solid/Device>
#include <Solid/StorageAccess>
// Local
#include "core/models.h"
#include "core/urlitemlauncher.h"
using namespace Kickoff;
class HandlerInfo
{
public:
HandlerInfo() : type(UrlItemLauncher::ProtocolHandler) , handler(0) {}
UrlItemLauncher::HandlerType type;
UrlItemHandler *handler;
};
class GenericItemHandler : public UrlItemHandler
{
public:
virtual bool openUrl(const KUrl& url) {
new KRun(url, nullptr);
return true;
}
};
class UrlItemLauncher::Private
{
public:
static QHash<QString, HandlerInfo> globalHandlers;
static GenericItemHandler genericHandler;
static bool openUrl(const QString &urlString) {
kDebug() << "Opening item with URL" << urlString;
KUrl url(urlString);
HandlerInfo protocolHandler = globalHandlers[url.scheme()];
if (protocolHandler.type == ProtocolHandler && protocolHandler.handler != 0) {
return protocolHandler.handler->openUrl(url);
}
QString extension = QFileInfo(url.path()).suffix();
HandlerInfo extensionHandler = globalHandlers[extension];
if (extensionHandler.type == ExtensionHandler && extensionHandler.handler != 0) {
return extensionHandler.handler->openUrl(url);
}
return genericHandler.openUrl(url);
}
};
QHash<QString, HandlerInfo> UrlItemLauncher::Private::globalHandlers;
GenericItemHandler UrlItemLauncher::Private::genericHandler;
UrlItemLauncher::UrlItemLauncher(QObject *parent)
: QObject(parent)
, d(new Private)
{
}
UrlItemLauncher::~UrlItemLauncher()
{
delete d;
}
bool UrlItemLauncher::openItem(const QModelIndex& index)
{
QString urlString = index.data(UrlRole).value<QString>();
if (urlString.isEmpty()) {
QString udi = index.data(DeviceUdiRole).toString();
if (!udi.isEmpty()) {
Solid::Device device(udi);
Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
if (access && !access->isAccessible()) {
connect(access, SIGNAL(setupDone(Solid::ErrorType,QString,QString)),
this, SLOT(onSetupDone(Solid::ErrorType,QString,QString)));
access->setup();
return true;
}
}
kDebug() << "Item" << index.data(Qt::DisplayRole) << "has no URL to open.";
return false;
}
return Private::openUrl(urlString);
}
bool UrlItemLauncher::openUrl(const QString& url)
{
return Private::openUrl(url);
}
void UrlItemLauncher::onSetupDone(Solid::ErrorType error, const QString &errorData, const QString &udi)
{
Q_UNUSED(errorData);
if (error != Solid::NoError) {
return;
}
Solid::Device device(udi);
Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
Q_ASSERT(access);
QString urlString = "file://" + access->filePath();
Private::openUrl(urlString);
}
// FIXME: the handlers are leaked, as they are added with each new Kickoff instance,
// but never deleted.
void UrlItemLauncher::addGlobalHandler(HandlerType type, const QString& name, UrlItemHandler *handler)
{
HandlerInfo info;
info.type = type;
info.handler = handler;
Private::globalHandlers.insert(name, info);
}
#include "moc_urlitemlauncher.cpp"

View file

@ -1,90 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2007 Kevin Ottens <ervin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef URLITEMLAUNCHER_H
#define URLITEMLAUNCHER_H
#include "kickoff_export.h"
#include <QObject>
#include <QModelIndex>
#include <solid/storageaccess.h>
class KUrl;
namespace Kickoff
{
/**
* UrlItemHandler is an abstract base class for handlers which can open particular
* types of URL.
*
* @see UrlItemLauncher
*/
class UrlItemHandler
{
public:
virtual ~UrlItemHandler() {}
virtual bool openUrl(const KUrl& url) = 0;
};
/**
* UrlItemLauncher provides facilities to open a item from a Kickoff model based on its UrlRole
* data.
*
* By default, a UrlItemLauncher opens all URLs using the KRun class. Additional handlers can be created
* to handle URLs with particular protocols or extensions differently. Handlers can be
* registered using the static addGlobalHandler() method.
*/
class KICKOFF_EXPORT UrlItemLauncher : public QObject
{
Q_OBJECT
public:
UrlItemLauncher(QObject *parent = 0);
virtual ~UrlItemLauncher();
enum HandlerType {
ProtocolHandler,
ExtensionHandler
};
static void addGlobalHandler(HandlerType type,
const QString& name,
UrlItemHandler *handler);
public Q_SLOTS:
/** Open the specified @p index from a Kickoff model. */
bool openItem(const QModelIndex& index);
/** Open the specified @p url */
bool openUrl(const QString& url);
private Q_SLOTS:
void onSetupDone(Solid::ErrorType error, const QString &errorData, const QString &udi);
private:
class Private;
Private * const d;
};
}
#endif // URLITEMLAUNCHER_H

View file

@ -1,48 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// KDE
#include <KAboutData>
#include <KApplication>
#include <KCmdLineArgs>
// Local
#include "ui/launcher.h"
#define KICKOFF_VERSION "1.9.3"
int main(int argc, char** argv)
{
KAboutData about("kickoff", 0, ki18n("Kickoff"), KICKOFF_VERSION,
ki18n("Application Launcher"),
KAboutData::License_GPL_V2);
KCmdLineArgs::init(argc, argv, &about);
KApplication app;
Kickoff::Launcher *launcher = new Kickoff::Launcher((QWidget*)0);
launcher->setWindowTitle("Kickoff KDE 4 Test Application");
// ensure launcher gets deleted when the app closes so that favorites,
// recent applications etc. are saved
launcher->setAttribute(Qt::WA_DeleteOnClose);
launcher->show();
return app.exec();
}

View file

@ -1,563 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2008-2009 Sebastian Sauer <mail@dipe.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "menuview.h"
// Qt
#include <QtCore/QAbstractItemModel>
#include <QtCore/qabstractitemmodel.h>
#include <QtCore/QStack>
#include <QtGui/QApplication>
#include <QtGui/qevent.h>
#include <QtGui/qstandarditemmodel.h>
#include <QtGui/qstyleoption.h>
#include <QtGui/QPainter>
#include <QtGui/QToolTip>
// KDE
#include <KDebug>
#include <KUrl>
#include <KIconLoader>
// Local
#include "core/models.h"
#include "core/itemhandlers.h"
#define MAX_NAME_SIZE 50
Q_DECLARE_METATYPE(QPersistentModelIndex)
Q_DECLARE_METATYPE(QAction*)
using namespace Kickoff;
/// @internal d-pointer class
class MenuView::Private
{
public:
enum { ActionRole = Qt::UserRole + 52 };
Private(MenuView *q) : q(q), column(0), launcher(new UrlItemLauncher(q)), formattype(MenuView::DescriptionName) {}
~Private()
{
qDeleteAll(items);
}
QAction *createActionForIndex(QAbstractItemModel *model, const QModelIndex& index, QMenu *parent)
{
Q_ASSERT(index.isValid());
QAction *action = 0;
if (model->hasChildren(index)) {
KMenu *childMenu = new KMenu(parent);
childMenu->installEventFilter(q);
childMenu->setContextMenuPolicy(Qt::CustomContextMenu);
connect(childMenu, SIGNAL(customContextMenuRequested(QPoint)),
q, SLOT(contextMenuRequested(QPoint)));
action = childMenu->menuAction();
buildBranch(childMenu, model, index);
} else {
action = q->createLeafAction(index, parent);
}
q->updateAction(model, action, index);
return action;
}
QString trunctuateName(QString name, int maxSize)
{
if (name.length() <= maxSize) {
return name;
}
maxSize -= 2; // remove the 3 placeholder points
const int start = maxSize / 3; //use one third of the chars for the start of the name
const int end = maxSize - start;
return name.left(start) + ".." + name.right(end);
}
void buildBranch(KMenu *menu, QAbstractItemModel *model, const QModelIndex& parent)
{
if (model->canFetchMore(parent)) {
model->fetchMore(parent);
}
const int rowCount = model->rowCount(parent);
for (int i = 0; i < rowCount; i++) {
QAction *action = createActionForIndex(model, model->index(i, column, parent), menu);
action->setText(trunctuateName(action->text(), MAX_NAME_SIZE));
menu->addAction(action);
}
}
QModelIndex findByRelPath(QAbstractItemModel * model, const QModelIndex & parent, const QString & relPath)
{
QModelIndex newRoot;
if (model->canFetchMore(parent)) {
model->fetchMore(parent);
}
const int rowCount = model->rowCount(parent);
for (int row = 0; row < rowCount; row++) {
QModelIndex index = model->index(row, 0, parent);
Q_ASSERT(index.isValid());
if (index.data(Kickoff::RelPathRole).isValid()) {
QString indexRelPath = index.data(Kickoff::RelPathRole).toString();
if (relPath == indexRelPath) {
newRoot = index;
break;
}
if (!indexRelPath.isEmpty() && relPath.startsWith(indexRelPath)) {
newRoot = findByRelPath(model, index, relPath);
break;
}
}
}
return newRoot;
}
MenuView * const q;
int column;
UrlItemLauncher *launcher;
MenuView::FormatType formattype;
QPoint mousePressPos;
QList<QStandardItem*> items;
QHash<QAbstractItemModel*, QAction*> modelsHeader;
QList<QWeakPointer<QAbstractItemModel> > models;
};
MenuView::MenuView(QWidget *parent, const QString &title, const QIcon &icon)
: KMenu(parent)
, d(new Private(this))
{
if (! title.isNull())
setTitle(title);
if (! icon.isNull())
setIcon(icon);
installEventFilter(this);
connect(this, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(contextMenuRequested(QPoint)));
}
MenuView::~MenuView()
{
QListIterator<QWeakPointer<QAbstractItemModel> > it(d->models);
while (it.hasNext()) {
QAbstractItemModel *model = it.next().data();
if (model) {
model->disconnect(this);
}
}
delete d;
}
QAction *MenuView::createLeafAction(const QModelIndex &index, QObject *parent)
{
Q_UNUSED(index);
return new QAction(parent);
}
void MenuView::updateAction(QAbstractItemModel *model, QAction *action, const QModelIndex& index)
{
bool isSeparator = index.data(Kickoff::SeparatorRole).value<bool>();
// if Description or DescriptionName -> displayOrder = Kickoff::NameAfterDescription
// Qt::DisplayRole returns genericName, the generic name e.g. "Advanced Text Editor" or "Spreadsheet" or just "" (right, it's a mess too)
// Kickoff::SubTitleRole returns appName, the name e.g. "Kate" or "OpenOffice.org Calc" (right, sometimes the text is also used for the generic app-name)
//
// if Name or NameDescription or NameDashDescription -> displayOrder = Kickoff::NameBeforeDescription
// Qt::DisplayRole returns appName,
// Kickoff::SubTitleRole returns genericName.
QString mainText = index.data(Qt::DisplayRole).value<QString>().replace('&', "&&");
QString altText = index.data(Kickoff::SubTitleRole).value<QString>().replace('&', "&&");
if (action->menu() != 0) { // if it is an item with sub-menuitems, we probably like to thread them another way...
action->setText(mainText);
} else {
switch (d->formattype) {
case Name:
case Description: {
action->setText(mainText);
action->setToolTip(altText);
} break;
case NameDescription: // fall through
case NameDashDescription: // fall through
case DescriptionName: {
if (!mainText.isEmpty()) { // seems we have a program, but some of them don't define a name at all
if (mainText.contains(altText, Qt::CaseInsensitive)) { // sometimes the description contains also the name
action->setText(mainText);
} else if (altText.contains(mainText, Qt::CaseInsensitive)) { // and sometimes the name also contains the description
action->setText(altText);
} else { // seems we have a perfect desktop-file (likely a KDE one, heh) and name+description are clear separated
if (d->formattype == NameDashDescription) {
action->setText(QString("%1 - %2").arg(mainText).arg(altText));
} else {
action->setText(QString("%1 (%2)").arg(mainText).arg(altText));
}
}
} else { // if there is no name, let's just use the describing text
action->setText(altText);
}
} break;
}
}
action->setSeparator(isSeparator);
if (!isSeparator) {
action->setIcon(index.data(Qt::DecorationRole).value<QIcon>());
}
// we map modelindex and action together
action->setData(qVariantFromValue(QPersistentModelIndex(index)));
// don't emit the dataChanged-signal cause else we may end in a infinite loop
model->blockSignals(true);
model->setData(index, qVariantFromValue(action), Private::ActionRole);
model->blockSignals(false);
}
bool MenuView::eventFilter(QObject *watched, QEvent *event)
{
switch(event->type()) {
case QEvent::MouseMove: {
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
const int mousePressDistance = !d->mousePressPos.isNull() ? (mouseEvent->pos() - d->mousePressPos).manhattanLength() : 0;
if (watchedMenu && mouseEvent->buttons() & Qt::LeftButton
&& mousePressDistance >= QApplication::startDragDistance()) {
QAction *action = watchedMenu->actionAt(mouseEvent->pos());
if (!action) {
return KMenu::eventFilter(watched, event);
}
QPersistentModelIndex index = action->data().value<QPersistentModelIndex>();
if (!index.isValid()) {
return KMenu::eventFilter(watched, event);
}
QUrl url = index.data(UrlRole).toUrl();
if (url.isEmpty()) {
return KMenu::eventFilter(watched, event);
}
QMimeData *mimeData = new QMimeData();
mimeData->setUrls(QList<QUrl>() << url);
mimeData->setText(url.toString());
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
QIcon icon = action->icon();
drag->setPixmap(icon.pixmap(IconSize(KIconLoader::Desktop)));
d->mousePressPos = QPoint();
Qt::DropAction dropAction = drag->exec();
Q_UNUSED(dropAction);
return true;
}
break;
}
case QEvent::MouseButtonPress: {
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
if (watchedMenu) {
d->mousePressPos = mouseEvent->pos();
}
break;
}
case QEvent::MouseButtonRelease: {
QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
if (watchedMenu) {
d->mousePressPos = QPoint();
}
break;
}
case QEvent::Hide: {
emit afterBeingHidden();
break;
}
case QEvent::ToolTip: {
bool hide = true;
if ((d->formattype == Name) || (d->formattype == Description)) {
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
if (watchedMenu && watchedMenu->activeAction()) {
QString toolTip = watchedMenu->activeAction()->toolTip();
if ((toolTip != watchedMenu->activeAction()->text()) && (watchedMenu->activeAction()->menu() == 0)) {
QToolTip::showText(helpEvent->globalPos(), toolTip);
hide = false ;
}
}
}
if (hide) {
QToolTip::hideText();
event->ignore();
}
break;
}
default:
break;
}
return KMenu::eventFilter(watched, event);
}
void MenuView::addModel(QAbstractItemModel *model, MenuView::ModelOptions options, const QString & relativePath)
{
QString title = model->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString();
QAction *header = addTitle(title);
header->setVisible(false);
d->modelsHeader.insert(model, header);
d->models.append(model);
if (options & MergeFirstLevel) {
const int count = model->rowCount();
for (int row = 0; row < count; ++row) {
QModelIndex index = model->index(row, 0, QModelIndex());
Q_ASSERT(index.isValid());
const QString title = index.data(Qt::DisplayRole).value<QString>();
if (count > 1 && ! title.isEmpty() && model->rowCount(index) > 0) {
addTitle(title);
}
model->blockSignals(true);
model->setData(index, qVariantFromValue(this->menuAction()), Private::ActionRole);
model->blockSignals(false);
d->buildBranch(this, model, index);
}
} else {
QModelIndex root;
if (!relativePath.isEmpty()) {
root = d->findByRelPath(model, root, relativePath);
}
d->buildBranch(this, model, root);
}
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)));
connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)));
connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
connect(model, SIGNAL(modelReset()), this, SLOT(modelReset()));
}
void MenuView::addItem(QStandardItem *item)
{
QAction *action = new QAction(item->icon(), item->text(), this);
KUrl url(item->data(Kickoff::UrlRole).toString());
Q_ASSERT(url.isValid());
action->setData(url);
addAction(action);
d->items << item;
}
UrlItemLauncher *MenuView::launcher() const
{
return d->launcher;
}
QModelIndex MenuView::indexForAction(QAction *action) const
{
Q_ASSERT(action != 0);
QPersistentModelIndex index = action->data().value<QPersistentModelIndex>();
return index;
}
QAction *MenuView::actionForIndex(const QModelIndex& index) const
{
if (!index.isValid()) {
return this->menuAction();
}
const QAbstractItemModel *model = index.model();
Q_ASSERT(model);
QVariant v = model->data(index, Private::ActionRole);
Q_ASSERT(v.isValid());
QAction* a = v.value<QAction*>();
Q_ASSERT(a);
return a;
}
bool MenuView::isValidIndex(const QModelIndex& index) const
{
QVariant v = (index.isValid() && index.model()) ? index.model()->data(index, Private::ActionRole) : QVariant();
return v.isValid() && v.value<QAction*>();
}
void MenuView::rowsInserted(const QModelIndex& parent, int start, int end)
{
kDebug() << start << end;
Q_ASSERT(parent.isValid());
Q_ASSERT(parent.model());
//Q_ASSERT( ! isValidIndex(parent) );
QMenu *menu = isValidIndex(parent) ? actionForIndex(parent)->menu() : this;
QAbstractItemModel *model = const_cast<QAbstractItemModel*>(parent.model());
if (!model) {
return;
}
QList<QAction*> newActions;
for (int row = start; row <= end; row++) {
QModelIndex index = model->index(row, d->column, parent);
QAction *newAction = d->createActionForIndex(model, index, menu);
kDebug()<<"new action="<<newAction->text();
newActions << newAction;
}
int lastidx = -1, offset = -1;
for (int i = 0; i < menu->actions().count(); ++i) {
QAction *action = menu->actions()[i];
Q_ASSERT(action);
QModelIndex index = indexForAction(action);
if (index.isValid() && index.model() == model) {
lastidx = i;
if(++offset == start)
break;
}
}
if (lastidx < 0 && d->modelsHeader.contains(model)) {
QAction *header = d->modelsHeader[model];
lastidx = menu->actions().indexOf(header);
}
if (lastidx >= 0) {
if (offset < start) {
lastidx++; // insert action the item right after the last valid index
}
if (lastidx < menu->actions().count()) {
menu->insertActions(menu->actions()[lastidx], newActions);
} else {
lastidx = -1;
}
}
if (lastidx < 0) {
// just append the action
menu->addActions(newActions);
}
}
void MenuView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
{
kDebug() << start << end;
Q_UNUSED(parent);
Q_UNUSED(start);
Q_UNUSED(end);
modelReset();
}
void MenuView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
QAbstractItemModel *model = const_cast<QAbstractItemModel*>(topLeft.model());
Q_ASSERT(model);
//Q_ASSERT( ! isValidIndex(topLeft) );
QMenu *menu = isValidIndex(topLeft) ? actionForIndex(topLeft)->menu() : this;
QList<QAction*> actions = menu->actions();
Q_ASSERT(bottomRight.row() < actions.count());
for (int row = topLeft.row(); row <= bottomRight.row() && row < actions.count(); ++row) {
QModelIndex index = model->index(row, d->column, topLeft.parent());
kDebug()<<row<<index.data(Qt::DisplayRole).value<QString>();
updateAction(model, actions[row], index);
}
}
void MenuView::modelReset()
{
kDebug();
deleteLater(); // we need to force clearance of the menu and rebuild from scratch
}
void MenuView::setColumn(int column)
{
d->column = column;
modelReset();
}
int MenuView::column() const
{
return d->column;
}
MenuView::FormatType MenuView::formatType() const
{
return d->formattype;
}
void MenuView::setFormatType(MenuView::FormatType formattype)
{
d->formattype = formattype;
}
void MenuView::setModelTitleVisible(QAbstractItemModel *model, bool visible)
{
if (d->modelsHeader.contains(model)) {
QAction *header = d->modelsHeader[model];
header->setVisible(visible);
}
}
void MenuView::actionTriggered(QAction *action)
{
KUrl url = action->data().value<KUrl>();
if (url.isValid()) {
d->launcher->openUrl(url.url());
} else {
QModelIndex index = indexForAction(action);
if (index.isValid()) {
d->launcher->openItem(index);
} else {
kWarning()<<"Invalid action objectName="<<action->objectName()<<"text="<<action->text()<<"parent="<<(action->parent()?action->parent()->metaObject()->className():"NULL");
}
}
}
void MenuView::contextMenuRequested(const QPoint &pos)
{
KMenu *menu = qobject_cast<KMenu *>(sender());
emit customContextMenuRequested(menu, pos);
}
#include "moc_menuview.cpp"

View file

@ -1,179 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2008-2009 Sebastian Sauer <mail@dipe.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef MENUVIEW_H
#define MENUVIEW_H
// Qt
#include <QtCore/qabstractitemmodel.h>
// KDE
#include <KMenu>
#include <QAbstractItemModel>
#include <QStandardItem>
namespace Kickoff
{
class UrlItemLauncher;
/**
* A view for a QAbstractItemModel which displays the model (set with setModel())
* as a hierarchical menu.
*
* When the menu is executed and an item is triggered, the model index associated with the
* chosen item can be found by calling indexForAction() with the triggered action. The action
* associated with a particular model index can be found using actionForIndex().
*
* MenuView creates actions for parts of the model on demand as the user explores the menu.
* The type of action created for leaf items in the tree can be changed by re-implementing
* createLeafAction(). When a new action is created or if the corresponding model
* index's data changes, updateAction() is called to set the action's properties. This
* can be reimplemented in sub-classes to change the appearance of the actions.
*/
class MenuView : public KMenu
{
Q_OBJECT
public:
/** Constructs a new menu with the specified @p parent */
MenuView(QWidget *parent = 0, const QString &title = QString(), const QIcon &icon = QIcon());
/** Destructor */
virtual ~MenuView();
/// Options for a model.
enum ModelOptions {
None, ///< no options.
MergeFirstLevel ///< merge the first both levels of items within the model into one hirachy in the menuview.
};
/** Adds a model to display within this menu. */
void addModel(QAbstractItemModel *model, ModelOptions options = None, const QString & relativePath = QString());
/** Adds a QStandardItem to display within this menu. This menu will take over the ownership of the item. */
void addItem(QStandardItem *item);
/** Returns the UrlItemLauncher used to handle launching of urls. */
UrlItemLauncher *launcher() const;
/** Maps an action in the menu to its corresponding index in model() */
QModelIndex indexForAction(QAction *action) const;
/**
* Maps an index in the model to its corresponding action in the menu.
* If @p index is invalid then menuAction() will be returned. If @p index
* is in a part of the tree which the user has not yet explored then 0 will
* be returned because the menu hierarchy is constructed on-demand as the user
* explores the menu.
*/
QAction *actionForIndex(const QModelIndex& index) const;
/**
* Returns true if the passed \p index is a valid QModelIndex and does
* represent a QAction. This method is equal to the actionForIndex() method
* above except allowing to explicit ask if the QModelIndex is valid and
* to indicate that way, that it may the case that the QModelIndex went
* out of scope already.
*/
bool isValidIndex(const QModelIndex& index) const;
/** Sets the column from the model which is used to construct the actions in the menu. */
void setColumn(int column);
/** Returns the column from the model which is used to construct the actions in the menu. */
int column() const;
/** The format type enumeration. */
enum FormatType {
Name = 0, ///< Name only
Description, ///< Description only
NameDescription, ///< Name (Description)
DescriptionName, ///< Description (Name)
NameDashDescription ///< Name - Description
};
/** \return the format type. */
FormatType formatType() const;
/** Set the format type. */
void setFormatType(FormatType formattype);
/** Set visibility of model title on menu. */
void setModelTitleVisible(QAbstractItemModel *model, bool visible);
protected:
/**
* Creates a new action to represent a leaf index in the tree. A leaf index
* is one which does not have children. The default implementation creates a new
* QAction with no properties set. updateAction() is immediately called on the
* return action to set its text and icon.
*
* @param index The index in the model for which an action should be created
* @param parent The object which should be set as the parent of the new action
*/
virtual QAction *createLeafAction(const QModelIndex& index, QObject *parent);
/**
* Sets the text, icon and other properties of @p action using the data
* associated with @p index in the model(). This is called whenever the data for
* a range of indexes in the tree is altered.
*
* The default implementation sets the action's text to the index's Qt::DisplayRole data
* and the action's icon to the index's Qt::DecorationRole data.
*/
virtual void updateAction(QAbstractItemModel *model, QAction *action, const QModelIndex& index);
// reimplemented
virtual bool eventFilter(QObject *watched, QEvent *event);
public Q_SLOTS:
/**
* An item in the menu got triggered.
*/
void actionTriggered(QAction* action);
void contextMenuRequested(const QPoint& pos);
Q_SIGNALS:
/**
* Compared to the aboutToShow() this signal will be emitted after the menu
* got hidden.
*/
void afterBeingHidden();
void customContextMenuRequested(QMenu* menu, const QPoint& pos);
private Q_SLOTS:
/// new items have been inserted into the model
void rowsInserted(const QModelIndex& parent, int start, int end);
/// existing items are about to be removed from the model
void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end);
/// data within an item of the model change
void dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
/// the model did reset itself and all items are invalid
void modelReset();
private:
class Private;
Private * const d;
};
}
#endif // MENUVIEW_H

View file

@ -1,164 +0,0 @@
[Desktop Entry]
Name=Application Launcher Menu
Name[af]=Programlanseerder-kieslys
Name[ar]=قائمة مطلق التطبيقات
Name[ast]=Menú del llanzador d'aplicaciones
Name[be@latin]=Menu dla ŭklučeńnia aplikacyjaŭ
Name[bg]=Стартиране на програми с меню
Name[bn]=ি
Name[bs]=meni za pokretanje programa
Name[ca]=Menú del llançador d'aplicacions
Name[ca@valencia]=Menú del llançador d'aplicacions
Name[cs]=Nabídka spouštěče aplikací
Name[csb]=Menu zrëszôcza programów
Name[da]=Startmenu
Name[de]=K-Menü (traditionell)
Name[el]=Μενού εκτέλεσης εφαρμογών
Name[en_GB]=Application Launcher Menu
Name[eo]=Aplikaĵolanĉila menuo
Name[es]=Menú del lanzador de aplicaciones
Name[et]=Rakenduste käivitaja menüü
Name[eu]=Aplikazio-abiarazlearen menua
Name[fi]=Sovellusten käynnistysvalikko
Name[fr]=Menu de lancement des applications
Name[fy]=Applikaasje úteinsetter menu
Name[ga]=Roghchlár Tosaitheora Feidhmchlár
Name[gl]=Menú iniciador de programas
Name[gu]=
Name[he]=תפריט משגר יישומים
Name[hi]=
Name[hne]=
Name[hr]=Izbornik pokretača aplikacija
Name[hsb]=Meni startowarja aplikacijow
Name[hu]=Programindító menü
Name[ia]=Menu de Lanceator de application
Name[id]=Menu Peluncur Aplikasi
Name[is]=Valmynd forritaræsingar
Name[ja]=
Name[kk]=Қолданбаны жегу мәзірі
Name[km]=
Name[kn]= () ಿಿಿ
Name[ko]=
Name[lt]=Programų paleidiklio meniu
Name[lv]=Programmu palaidēja izvēlne
Name[ml]=ിിി
Name[mr]=
Name[nb]=Meny for programstarter
Name[nds]=Programmoproopmenü
Name[nl]=Programmastartermenu
Name[nn]=Programstartmeny
Name[or]= ି
Name[pa]=
Name[pl]=Menu aktywatora programów
Name[pt]=Menu do Lançador de Aplicações
Name[pt_BR]=Menu do lançador de aplicativos
Name[ro]=Meniu pentru lansatorul de aplicații
Name[ru]=Классическое меню запуска приложений
Name[se]=Prográmmaálggahanfállu
Name[si]=
Name[sk]=Spúšťač aplikácií (menu)
Name[sl]=Meni zaganjalnika programov
Name[sr]=мени за покретање програма
Name[sr@ijekavian]=мени за покретање програма
Name[sr@ijekavianlatin]=meni za pokretanje programa
Name[sr@latin]=meni za pokretanje programa
Name[sv]=Programstartmeny
Name[ta]=
Name[te]=
Name[tg]=Менюи оғози барномаҳо
Name[th]=
Name[tr]=Uygulama Başlatma Menüsü
Name[ug]=پروگرامما ئىجرا قىلغۇچ تىزىملىكى
Name[uk]=Інструмент запуску програм з меню
Name[vi]=Trình đơn chy ng dng
Name[wa]=Dressêye d' enondeu di programe
Name[x-test]=xxApplication Launcher Menuxx
Name[zh_CN]=
Name[zh_TW]=
Comment=Traditional menu based application launcher
Comment[af]=Tradisionele kieslys as programlanseerder
Comment[ar]=مطلق تطبيقات تقليدي على شكل قائمة
Comment[ast]=Llanzador d'aplicaciones tradicional basáu en menú
Comment[be@latin]=Tradycyjnaje menu dla ŭklučeńnia aplikacyjaŭ
Comment[bg]=Традиционно стартиране на програми с меню
Comment[bs]=Tradicionalno pokretanje programa pomoću menija
Comment[ca]=Llançador d'aplicacions basat en el menú tradicional
Comment[ca@valencia]=Llançador d'aplicacions basat en el menú tradicional
Comment[cs]=Tradiční spouštěč aplikací založený na nabídce
Comment[da]=Traditionel menubaseret startmenu
Comment[de]=Programmstarter mit traditionellen Menüs
Comment[el]=Παραδοσιακό μενού εκτέλεσης εφαρμογών
Comment[en_GB]=Traditional menu based application launcher
Comment[eo]=La kutima menua bazita aplikaĵolanĉilo
Comment[es]=Lanzador de aplicaciones tradicional basado en menú
Comment[et]=Rakenduste käivitajale tuginev traditsiooniline menüü
Comment[eu]=Menuetan oinarritutako aplikazio-abiarazle tradizionala
Comment[fi]=Perinteinen valikkopohjainen sovellusten käynnistin
Comment[fr]=Lanceur d'applications utilisant un menu traditionnel
Comment[fy]=Tradisjoneel menu bassearre applikaasje úteinsetter
Comment[ga]=Tosaitheoir traidisiúnta feidhmchlár bunaithe ar roghchlár
Comment[gl]=Un iniciador de programas baseado no menú tradicional
Comment[gu]= િ
Comment[he]=משגר יישומים מסורתי מבוסס תפריטים
Comment[hi]=ि ि
Comment[hne]= ि
Comment[hr]=Tradicionalni pokretač baziran na izbornicima
Comment[hsb]=Tradicionalny, na meniju bazowacy startowar aplikacijow
Comment[hu]=Hagyományos programindító menü
Comment[ia]=Lanceator de application basate sur menu traditional
Comment[id]=Menu tradisional berbasis peluncur aplikasi
Comment[is]=Venjuleg valmyndabyggð forritaræsing
Comment[ja]=Kickoff
Comment[kk]=Қолданба жеккішті негіздейтін дәстүрлі мәзір
Comment[km]=
Comment[kn]=ಿ ಿಿಿ ಿ ()
Comment[ko]=
Comment[lt]=Tradicinio meniu programų paleidiklis
Comment[lv]=Tradicionālais uz izvēlnēm bāzētais programmu palaidējs
Comment[mk]=Традиционален стартувач на апликации базиран на мени
Comment[ml]= ിി ിിി
Comment[mr]=ि ि
Comment[nb]=Tradisjonell menybasert programstarter
Comment[nds]=Begäng Programmoproper mit Menüs
Comment[nl]=Traditioneel menu voor het starten van programma's
Comment[nn]=Tradisjonell menybasert programstartar
Comment[or]= ି ି
Comment[pa]= ਿ
Comment[pl]=Tradycyjny, oparty o menu, aktywator programów
Comment[pt]=Lançador de aplicações baseado num menu tradicional
Comment[pt_BR]=Lançador de aplicativos baseado no menu tradicional
Comment[ro]=Lansator de aplicații bazat pe meniu tradițional
Comment[ru]=Традиционное меню запуска приложений
Comment[se]=Árbevirolaš fállovuođđoduvvun prográmmaálggaheaddji
Comment[si]=
Comment[sk]=Tradičný spúšťač aplikácií založený na menu
Comment[sl]=Zaganjalnik programov, ki temelji na tradicionalnem meniju
Comment[sr]=Традиционално покретање програма помоћу менија
Comment[sr@ijekavian]=Традиционално покретање програма помоћу менија
Comment[sr@ijekavianlatin]=Tradicionalno pokretanje programa pomoću menija
Comment[sr@latin]=Tradicionalno pokretanje programa pomoću menija
Comment[sv]=Traditionell menybaserad programstart
Comment[ta]=Traditional menu based application launcher
Comment[te]= ి
Comment[tg]=Традиционное средство запуска приложений на основе меню
Comment[th]=
Comment[tr]=Geleneksel menü temelli uygulama başlatıcı
Comment[ug]=تىزىملىك ئاساسىدىكى ئەنئەنىۋى پروگرامما ئىجرا قىلغۇچ
Comment[uk]=Інструмент запуску програм з традиційним меню
Comment[wa]=Enondeu d' programe båzé sol tradicionele dressêye
Comment[x-test]=xxTraditional menu based application launcherxx
Comment[zh_CN]=
Comment[zh_TW]=
Icon=start-here-kde
Type=Service
X-KDE-ServiceTypes=Plasma/Applet
X-KDE-Library=plasma_applet_simplelauncher
X-KDE-PluginInfo-Author=Robert Knight
X-KDE-PluginInfo-Email=robertknight@gmail.com
X-KDE-PluginInfo-Name=simplelauncher
X-KDE-PluginInfo-Version=1.0
X-KDE-PluginInfo-Website=
X-KDE-PluginInfo-Category=Application Launchers
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=false

View file

@ -1,892 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2008-2009 Sebastian Sauer <mail@dipe.org>
Copyright 2009 Christian Loose <christian.loose@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "simpleapplet/simpleapplet.h"
#include "simpleapplet/menuview.h"
// Katie
#include <QtCore/QMetaObject>
#include <QtCore/QSharedPointer>
#include <QtGui/QLabel>
#include <QtGui/QCheckBox>
#include <QtGui/QSpinBox>
#include <QtGui/QGridLayout>
#include <QtGui/QGraphicsView>
#include <QtGui/QGraphicsLinearLayout>
#include <QtGui/QSpacerItem>
#include <QtGui/QListWidget>
// KDE Libs
#include <KActionCollection>
#include <KBookmarkMenu>
#include <KCModuleInfo>
#include <KComboBox>
#include <KConfigDialog>
#include <KIcon>
#include <KIconButton>
#include <KIconLoader>
#include <KMenu>
#include <KRun>
#include <KServiceTypeTrader>
#include <KToolInvocation>
#include <Solid/PowerManagement>
// KDE Base
#include <kworkspace/kworkspace.h>
// Plasma
#include <Plasma/IconWidget>
#include <Plasma/Containment>
#include <Plasma/ToolTipManager>
// Local
#include "core/itemhandlers.h"
#include "core/models.h"
#include "core/applicationmodel.h"
#include "core/favoritesmodel.h"
#include "core/systemmodel.h"
#include "core/recentlyusedmodel.h"
#include "core/recentapplications.h"
#include "core/leavemodel.h"
#include "core/urlitemlauncher.h"
#include "ui/contextmenufactory.h"
Q_DECLARE_METATYPE(QPersistentModelIndex)
/// @internal KBookmarkOwner specialization
class BookmarkOwner : public KBookmarkOwner
{
public:
BookmarkOwner() : KBookmarkOwner() {}
virtual bool enableOption(BookmarkOption) const {
return false;
}
virtual bool supportsTabs() const {
return false;
}
virtual void openBookmark(const KBookmark& b, Qt::MouseButtons, Qt::KeyboardModifiers) {
new KRun(b.url(), (QWidget*)0);
}
};
/// @internal d-pointer class
class MenuLauncherApplet::Private
{
public:
MenuLauncherApplet *q;
QWeakPointer<Kickoff::MenuView> menuview;
Plasma::IconWidget *icon;
QString iconname;
QWeakPointer<Kickoff::UrlItemLauncher> launcher;
KActionCollection* bookmarkcollection;
BookmarkOwner* bookmarkowner;
KBookmarkMenu* bookmarkmenu;
QStringList viewtypes;//QList<MenuLauncherApplet::ViewType>
QString relativePath;
MenuLauncherApplet::FormatType formattype;
int maxRecentApps;
bool showMenuTitles;
bool showRecentlyInstalled;
QListWidget *view;
KIconButton *iconButton;
KComboBox *formatComboBox;
QSpinBox *recentApplicationsSpinBox;
QCheckBox *showMenuTitlesCheckBox;
QCheckBox *showRecentlyInstalledCheckBox;
QList<QAction*> actions;
QAction* switcher;
Kickoff::ContextMenuFactory *contextMenuFactory;
bool delayedConfigLoad;
explicit Private(MenuLauncherApplet *q)
: q(q),
icon(0),
bookmarkcollection(0),
bookmarkowner(0),
bookmarkmenu(0),
view(0),
iconButton(0),
formatComboBox(0),
showMenuTitlesCheckBox(0),
showRecentlyInstalledCheckBox(0),
switcher(0),
contextMenuFactory(0)
{}
~Private()
{
delete bookmarkmenu;
delete bookmarkowner;
delete menuview.data();
}
void setMaxRecentApps(int num) {
maxRecentApps = qMax(0, num);
if (maxRecentApps > Kickoff::RecentApplications::self()->maximum()) {
Kickoff::RecentApplications::self()->setMaximum(maxRecentApps);
}
}
void addItem(KComboBox* combo, const QString& caption, int index, const QString& icon = QString())
{
if (icon.isEmpty()) {
combo->addItem(caption, index);
} else {
combo->addItem(KIcon(icon), caption, index);
}
}
void setCurrentItem(KComboBox* combo, int currentIndex)
{
for (int i = combo->count() - 1; i >= 0; --i) {
if (combo->itemData(i).toInt() == currentIndex) {
combo->setCurrentIndex(i);
return;
}
}
if (combo->count() > 0)
combo->setCurrentIndex(0);
}
void addModel(QAbstractItemModel *model, ViewType viewtype, Kickoff::MenuView::ModelOptions options = Kickoff::MenuView::MergeFirstLevel, int formattype = -1)
{
Kickoff::MenuView* mainView = menuview.data();
Kickoff::MenuView* m = mainView;
if (viewtypes.count() > 1 || !m) {
m = new Kickoff::MenuView(mainView, viewText(viewtype), KIcon(viewIcon(viewtype)));
m->setFormatType((formattype >= 0 || !mainView) ? (Kickoff::MenuView::FormatType)formattype : mainView->formatType());
mainView->addMenu(m);
}
m->addModel(model, options);
}
QString viewText(MenuLauncherApplet::ViewType vt) const {
switch (vt) {
case Favorites: return i18n("Favorites");
case Bookmarks: return i18n("Bookmarks");
case Applications: return i18n("Applications");
case Computer: return i18n("Computer");
case RecentlyUsed: return i18n("Recently Used");
case RecentlyUsedApplications: return i18n("Recently Used Applications");
case RecentlyUsedDocuments: return i18n("Recently Used Documents");
case Settings: return i18n("System Settings");
case SwitchUser: return i18n("Switch User");
case SaveSession: return i18n("Save Session");
case LockScreen: return i18n("Lock Screen");
case SuspendDisk: return i18n("Hibernate");
case SuspendRAM: return i18n("Sleep");
case HybridSuspend: return i18n("Hybrid Suspend");
case Restart: return i18nc("Restart Computer", "Restart");
case Shutdown: return i18n("Shut down");
case Logout: return i18n("Log out");
case Leave: return i18n("Leave");
}
return QString();
}
QString viewIcon(MenuLauncherApplet::ViewType vt) const {
switch (vt) {
case Favorites: return "bookmarks";
case Bookmarks: return "folder-bookmarks";
case Applications: return "applications-other";
case Computer: return "computer";
case RecentlyUsed: return "document-open-recent";
case RecentlyUsedApplications: return "document-open-recent";
case RecentlyUsedDocuments: return "document-open-recent";
case Settings: return "preferences-system";
case SwitchUser: return "system-switch-user";
case SaveSession: return "document-save";
case LockScreen: return "system-lock-screen";
case SuspendDisk: return "system-suspend-hibernate";
case SuspendRAM: return "system-suspend-hibernate";
case HybridSuspend: return "system-suspend";
case Restart: return "system-reboot";
case Shutdown: return "system-shutdown";
case Logout: return "system-log-out";
case Leave: return "system-shutdown";
}
return QString();
}
MenuLauncherApplet::ViewType viewType(const QByteArray& type) const {
QMetaEnum e = q->metaObject()->enumerator(q->metaObject()->indexOfEnumerator("ViewType"));
return (MenuLauncherApplet::ViewType) e.keyToValue(type);
}
/*
QByteArray viewType(MenuLauncherApplet::ViewType type) const {
QMetaEnum e = q->metaObject()->enumerator(q->metaObject()->indexOfEnumerator("ViewType"));
return e.valueToKey(types);
}
QList<MenuLauncherApplet::ViewType> viewTypes(const QStringList &types) const {
QList<MenuLauncherApplet::ViewType> l;
foreach(QString t, types) l << viewType(t.toUtf());
return l;
}
QStringList viewTypes(const QList<MenuLauncherApplet::ViewType> &types) const {
QStringList l;
foreach(MenuLauncherApplet::ViewType t, types) l << viewType(t);
return l;
}
*/
void updateTooltip() {
QStringList names;
foreach(const QString &vtname, viewtypes)
names << viewText(viewType(vtname.toUtf8()));
Plasma::ToolTipContent data(i18n("Application Launcher Menu"), names.join(", "), icon->icon());
Plasma::ToolTipManager::self()->setContent(q, data);
}
};
MenuLauncherApplet::MenuLauncherApplet(QObject *parent, const QVariantList &args)
: Plasma::Applet(parent, args),
d(new Private(this))
{
KGlobal::locale()->insertCatalog("plasma_applet_launcher");
setAspectRatioMode(Plasma::ConstrainedSquare);
setHasConfigurationInterface(true);
setBackgroundHints(NoBackground);
resize(IconSize(KIconLoader::Desktop) * 2, IconSize(KIconLoader::Desktop) * 2);
d->icon = new Plasma::IconWidget(QString(), this);
d->icon->setFlag(ItemIsMovable, false);
connect(d->icon, SIGNAL(pressed(bool)), this, SLOT(showMenu(bool)));
connect(this, SIGNAL(activate()), this, SLOT(toggleMenu()));
d->delayedConfigLoad = false;
switch(args.count()) {
case 2: { //Use submenu paths
d->viewtypes << "Applications";
d->relativePath = args.value(0).toString();
d->iconname = args.value(1).toString();
break;
}
case 1: { //Check for delayed config (switch from Kickoff)
d->delayedConfigLoad = true;
//Do not "break;", as we may need the default views configuration
//(if SimpleLauncher was never used before)
}
default: { //Default configuration
d->viewtypes << "RecentlyUsedApplications" << "Applications" << "Favorites";
d->viewtypes << "Leave";
d->iconname = "start-here-kde";
}
}
d->formattype = NameDescription;
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
layout->addItem(d->icon);
d->contextMenuFactory = new Kickoff::ContextMenuFactory(this);
d->contextMenuFactory->setApplet(this);
}
MenuLauncherApplet::~MenuLauncherApplet()
{
delete d;
}
void MenuLauncherApplet::init()
{
bool receivedArgs = false;
if (!d->relativePath.isEmpty()) {
receivedArgs = true;
}
configChanged();
Kickoff::UrlItemLauncher::addGlobalHandler(Kickoff::UrlItemLauncher::ExtensionHandler, "desktop", new Kickoff::ServiceItemHandler);
Kickoff::UrlItemLauncher::addGlobalHandler(Kickoff::UrlItemLauncher::ProtocolHandler, "leave", new Kickoff::LeaveItemHandler);
if (KService::serviceByStorageId("kmenuedit.desktop")) {
QAction* menueditor = new QAction(i18n("Edit Applications..."), this);
d->actions.append(menueditor);
connect(menueditor, SIGNAL(triggered(bool)), this, SLOT(startMenuEditor()));
}
Q_ASSERT(! d->switcher);
d->switcher = new QAction(i18n("Switch to Application Launcher Style"), this);
d->actions.append(d->switcher);
connect(d->switcher, SIGNAL(triggered(bool)), this, SLOT(switchMenuStyle()));
if (receivedArgs) {
KConfigGroup cg = config();
cg.writeEntry("relativePath", d->relativePath);
cg.writeEntry("icon", d->iconname);
emit configNeedsSaving();
}
connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)),
this, SLOT(iconSizeChanged(int)));
}
void MenuLauncherApplet::constraintsEvent(Plasma::Constraints constraints)
{
setBackgroundHints(NoBackground);
if (constraints & Plasma::FormFactorConstraint) {
if (formFactor() == Plasma::Planar || formFactor() == Plasma::MediaCenter) {
//FIXME set correct minimum size
//setMinimumContentSize(d->icon->sizeFromIconSize(IconSize(KIconLoader::Desktop)));
} else {
//setMinimumContentSize(d->icon->sizeFromIconSize(IconSize(KIconLoader::Small)));
}
}
if ((constraints & Plasma::ImmutableConstraint) && d->switcher) {
d->switcher->setVisible(immutability() == Plasma::Mutable);
}
}
void MenuLauncherApplet::switchMenuStyle()
{
if (containment()) {
Plasma::Applet *launcher = containment()->addApplet("launcher", QVariantList(), geometry());
//Copy all the config items to the simple launcher
QMetaObject::invokeMethod(launcher, "saveConfigurationFromSimpleLauncher",
Qt::DirectConnection, Q_ARG(KConfigGroup, config()),
Q_ARG(KConfigGroup, globalConfig()));
//Switch shortcuts with the new launcher to avoid losing it
KShortcut currentShortcut = globalShortcut();
setGlobalShortcut(KShortcut());
launcher->setGlobalShortcut(currentShortcut);
destroy();
}
}
void MenuLauncherApplet::startMenuEditor()
{
KToolInvocation::kdeinitExec("kmenuedit");
}
void MenuLauncherApplet::customContextMenuRequested(QMenu* menu, const QPoint& pos)
{
if (!menu) {
return;
}
// there are 2 possible cases
// 1) An an actual action is active - menu is the menu containing the action wanted
// 2) either a submenu is active, but none of its actions are active. menu is the submenu,
// not the menu containing the submenu
QAction* menuAction = menu->activeAction();
if (menuAction) { // case 1)
const QPersistentModelIndex index = menuAction->data().value<QPersistentModelIndex>();
d->contextMenuFactory->showContextMenu(0, index, pos);
} else { // case 2)
menuAction = menu->menuAction();
if (menuAction) {
const QPersistentModelIndex index = menuAction->data().value<QPersistentModelIndex>();
d->contextMenuFactory->showContextMenu(0, index, pos);
}
}
}
void MenuLauncherApplet::createConfigurationInterface(KConfigDialog *parent)
{
d->view = 0;
d->recentApplicationsSpinBox = 0;
if (d->relativePath.isEmpty()) {
QWidget *viewpage = new QWidget(parent);
QVBoxLayout *l = new QVBoxLayout(viewpage);
l->setMargin(0);
viewpage->setLayout(l);
d->view = new QListWidget(viewpage);
d->view->resize(300,500);
l->addWidget(d->view);
QMetaEnum vte = metaObject()->enumerator(metaObject()->indexOfEnumerator("ViewType"));
for(int i = 0; i < vte.keyCount(); ++i) {
ViewType vt = (ViewType) vte.value(i);
const QByteArray vtname = vte.key(i);
QListWidgetItem *item = new QListWidgetItem(KIcon(d->viewIcon(vt)), d->viewText(vt), d->view);
item->setCheckState(d->viewtypes.contains(vtname) ? Qt::Checked : Qt::Unchecked);
item->setData(Qt::UserRole, vtname);
d->view->addItem(item);
}
parent->addPage(viewpage, i18n("View"), "view-list-details");
}
QWidget *p = new QWidget(parent);
QGridLayout *grid = new QGridLayout(p);
grid->setMargin(0);
QLabel *iconLabel = new QLabel(i18n("Icon:"), p);
grid->addWidget(iconLabel, 0, 0, Qt::AlignRight);
d->iconButton = new KIconButton(p);
d->iconButton->setIcon(d->icon->icon());
iconLabel->setBuddy(d->iconButton);
grid->addWidget(d->iconButton, 0, 1);
QLabel *formatLabel = new QLabel(i18nc("@label:listbox How to present applications in a KMenu-like menu", "Format:"), p);
grid->addWidget(formatLabel, 1, 0, Qt::AlignRight);
d->formatComboBox = new KComboBox(p);
formatLabel->setBuddy(d->formatComboBox);
d->addItem(d->formatComboBox, i18nc("@item:inlistbox Format:", "Name Only"), MenuLauncherApplet::Name);
d->addItem(d->formatComboBox, i18nc("@item:inlistbox Format:", "Description Only"), MenuLauncherApplet::Description);
d->addItem(d->formatComboBox, i18nc("@item:inlistbox Format:", "Name (Description)"), MenuLauncherApplet::NameDescription);
d->addItem(d->formatComboBox, i18nc("@item:inlistbox Format:", "Description (Name)"), MenuLauncherApplet::DescriptionName);
d->addItem(d->formatComboBox, i18nc("@item:inlistbox Format:", "Name - Description"), MenuLauncherApplet::NameDashDescription);
d->setCurrentItem(d->formatComboBox, d->formattype);
grid->addWidget(d->formatComboBox, 1, 1);
if (d->relativePath.isEmpty()) {
QLabel *recentLabel = new QLabel(i18n("Recently used applications:"), p);
grid->addWidget(recentLabel, 2, 0, Qt::AlignRight);
d->recentApplicationsSpinBox = new QSpinBox(p);
d->recentApplicationsSpinBox->setMaximum(10);
d->recentApplicationsSpinBox->setMinimum(0);
d->recentApplicationsSpinBox->setValue(d->maxRecentApps);
recentLabel->setBuddy(d->recentApplicationsSpinBox);
grid->addWidget(d->recentApplicationsSpinBox, 2, 1);
}
QLabel *showMenuTitlesLabel = new QLabel(i18n("Show menu titles:"), p);
grid->addWidget(showMenuTitlesLabel, 3, 0, Qt::AlignRight);
d->showMenuTitlesCheckBox = new QCheckBox(p);
d->showMenuTitlesCheckBox->setChecked(d->showMenuTitles);
grid->addWidget(d->showMenuTitlesCheckBox, 3, 1);
QLabel *showMenuRecentlyInstalledLabel = new QLabel(i18n("Show 'Recently Installed':"), p);
grid->addWidget(showMenuRecentlyInstalledLabel, 4, 0, Qt::AlignRight);
d->showRecentlyInstalledCheckBox = new QCheckBox(p);
d->showRecentlyInstalledCheckBox->setChecked(d->showRecentlyInstalled);
grid->addWidget(d->showRecentlyInstalledCheckBox, 4, 1);
grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 5, 0, 1, 3);
parent->addPage(p, i18n("Options"), "configure");
connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
connect(d->iconButton, SIGNAL(iconChanged(QString)), parent, SLOT(settingsModified()));
connect(d->formatComboBox, SIGNAL(currentIndexChanged(QString)), parent, SLOT(settingsModified()));
connect(d->recentApplicationsSpinBox, SIGNAL(valueChanged(int)), parent, SLOT(settingsModified()));
connect(d->showMenuTitlesCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
connect(d->showRecentlyInstalledCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
connect(d->view, SIGNAL(currentTextChanged(QString)), parent, SLOT(settingsModified()));
}
void MenuLauncherApplet::configAccepted()
{
bool needssaving = false;
KConfigGroup cg = config();
if (d->view) {
QStringList viewtypes;
for(int i = 0; i < d->view->count(); ++i) {
QListWidgetItem *item = d->view->item(i);
QByteArray vtname = item->data(Qt::UserRole).toByteArray();
if(item->checkState() == Qt::Checked)
viewtypes << vtname;
if( !needssaving && ((item->checkState() == Qt::Checked && ! d->viewtypes.contains(vtname)) || (item->checkState() == Qt::Unchecked && d->viewtypes.contains(vtname))) )
needssaving = true;
}
if(needssaving) {
d->viewtypes = viewtypes;
d->updateTooltip();
cg.writeEntry("views", d->viewtypes);
cg.deleteEntry("view"); // "view" was from <KDE4.3, we are using "views" now
}
}
const QString iconname = d->iconButton->icon();
if (! iconname.isEmpty()) {
needssaving = true;
d->icon->setIcon(KIcon(iconname));
d->updateTooltip();
cg.writeEntry("icon", iconname);
}
const int ft = d->formatComboBox->itemData(d->formatComboBox->currentIndex()).toInt();
if (ft != d->formattype) {
needssaving = true;
d->formattype = (MenuLauncherApplet::FormatType) ft;
QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("FormatType"));
cg.writeEntry("format", QByteArray(e.valueToKey(d->formattype)));
}
if (d->recentApplicationsSpinBox) {
const int maxRecentApps = d->recentApplicationsSpinBox->value();
if (maxRecentApps != d->maxRecentApps) {
needssaving = true;
d->setMaxRecentApps(maxRecentApps);
cg.writeEntry("maxRecentApps", maxRecentApps);
}
}
const bool showMenuTitles = d->showMenuTitlesCheckBox->isChecked();
if (showMenuTitles != d->showMenuTitles) {
needssaving = true;
d->showMenuTitles = showMenuTitles;
cg.writeEntry("showMenuTitles", showMenuTitles);
}
const bool showRecentlyInstalled = d->showRecentlyInstalledCheckBox->isChecked();
if (showRecentlyInstalled != d->showRecentlyInstalled) {
needssaving = true;
d->showRecentlyInstalled = showRecentlyInstalled;
cg.writeEntry("showRecentlyInstalled", showRecentlyInstalled);
}
if (needssaving) {
d->updateTooltip();
emit configNeedsSaving();
if (d->menuview) {
d->menuview.data()->deleteLater();
}
}
}
inline int weightOfService( const KService::Ptr service )
{
QVariant tmp = service->property("X-KDE-Weight", QVariant::Int);
return (tmp.isValid() ? tmp.toInt() : 100);
}
inline bool sortServiceItemsByWeight(const KService::Ptr left, const KService::Ptr right)
{
return weightOfService(left) < weightOfService(right);
}
KService::List sortServices(KService::List list)
{
qSort(list.begin(), list.end(), sortServiceItemsByWeight);
return list;
}
void MenuLauncherApplet::toggleMenu()
{
showMenu(!d->menuview || !d->menuview.data()->isVisible());
}
void MenuLauncherApplet::showMenu(bool pressed)
{
if (!pressed || d->viewtypes.count()<=0) {
if (d->menuview) {
d->menuview.data()->deleteLater();
d->menuview.clear();
}
return;
}
d->icon->setPressed();
if (!d->menuview) {
Kickoff::MenuView *menuview = new Kickoff::MenuView();
d->menuview = menuview;
menuview->setAttribute(Qt::WA_DeleteOnClose);
menuview->setFormatType( (Kickoff::MenuView::FormatType) d->formattype );
menuview->setContextMenuPolicy(Qt::CustomContextMenu);
connect(menuview, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*)));
connect(menuview, SIGNAL(aboutToHide()), this, SLOT(menuHiding()));
connect(menuview, SIGNAL(customContextMenuRequested(QMenu*,QPoint)),
this, SLOT(customContextMenuRequested(QMenu*,QPoint)));
//connect(menuview, SIGNAL(afterBeingHidden()), menuview, SLOT(deleteLater()));
//Kickoff::MenuView::ModelOptions options = d->viewtypes.count() < 2 ? Kickoff::MenuView::MergeFirstLevel : Kickoff::MenuView::None;
foreach(const QString &vtname, d->viewtypes) {
if(vtname == "Applications") {
Kickoff::ApplicationModel *appModel = new Kickoff::ApplicationModel(menuview, true /*allow separators*/);
appModel->setApplet(this);
appModel->setDuplicatePolicy(Kickoff::ApplicationModel::ShowLatestOnlyPolicy);
if (d->formattype == Name || d->formattype == NameDescription || d->formattype == NameDashDescription) {
appModel->setNameDisplayOrder(Kickoff::NameBeforeDescription);
appModel->setPrimaryNamePolicy(Kickoff::ApplicationModel::AppNamePrimary);
}
appModel->setSystemApplicationPolicy(Kickoff::ApplicationModel::ShowApplicationAndSystemPolicy);
appModel->setShowRecentlyInstalled(d->showRecentlyInstalled);
menuview->addModel(appModel, Kickoff::MenuView::None, d->relativePath);
if (d->relativePath.isEmpty()) {
if (d->showMenuTitles) {
menuview->setModelTitleVisible(appModel, true);
menuview->addTitle(i18n("Actions"));
} else {
menuview->addSeparator();
}
}
} else if(vtname == "Favorites") {
Kickoff::FavoritesModel *favoritesModel = new Kickoff::FavoritesModel(menuview);
if (d->formattype == Name || d->formattype == NameDescription || d->formattype == NameDashDescription) {
favoritesModel->setNameDisplayOrder(Kickoff::NameBeforeDescription);
}
d->addModel(favoritesModel, Favorites);
} else if(vtname == "Computer") {
d->addModel(new Kickoff::SystemModel(menuview), Computer);
} else if(vtname == "RecentlyUsed") {
Kickoff::RecentlyUsedModel *recentModel = new Kickoff::RecentlyUsedModel(menuview);
if (d->formattype == Name || d->formattype == NameDescription || d->formattype == NameDashDescription) {
recentModel->setNameDisplayOrder(Kickoff::NameBeforeDescription);
}
d->addModel(recentModel, RecentlyUsed);
} else if(vtname == "RecentlyUsedApplications") {
if (d->maxRecentApps > 0) {
Kickoff::RecentlyUsedModel *recentModel = new Kickoff::RecentlyUsedModel(menuview, Kickoff::RecentlyUsedModel::ApplicationsOnly, d->maxRecentApps);
if (d->formattype == Name || d->formattype == NameDescription || d->formattype == NameDashDescription) {
recentModel->setNameDisplayOrder(Kickoff::NameBeforeDescription);
}
menuview->addModel(recentModel, Kickoff::MenuView::MergeFirstLevel);
if (d->showMenuTitles) {
menuview->setModelTitleVisible(recentModel, true);
} else {
menuview->addSeparator();
}
}
} else if(vtname == "RecentlyUsedDocuments") {
Kickoff::RecentlyUsedModel *recentModel = new Kickoff::RecentlyUsedModel(menuview, Kickoff::RecentlyUsedModel::DocumentsOnly);
menuview->addModel(recentModel, Kickoff::MenuView::MergeFirstLevel);
if (d->showMenuTitles) {
menuview->setModelTitleVisible(recentModel, true);
} else {
menuview->addSeparator();
}
} else if(vtname == "Bookmarks") {
KMenu* menu = menuview;
if(d->viewtypes.count() > 1) {
menu = new KMenu(d->viewText(Bookmarks), menuview);
menu->setIcon(KIcon(d->viewIcon(Bookmarks)));
menuview->addMenu(menu);
}
KBookmarkManager* mgr = KBookmarkManager::userBookmarksManager();
if (! d->bookmarkcollection) {
d->bookmarkcollection = new KActionCollection(this);
d->bookmarkowner = new BookmarkOwner();
}
delete d->bookmarkmenu;
d->bookmarkmenu = new KBookmarkMenu(mgr, d->bookmarkowner, menu, d->bookmarkcollection);
} else if(vtname == "Settings") {
KMenu* parentmenu = menuview;
if(d->viewtypes.count() > 1) {
parentmenu = new KMenu(d->viewText(Settings), menuview);
parentmenu->setIcon(KIcon(d->viewIcon(Settings)));
menuview->addMenu(parentmenu);
}
QMap<QString, KMenu*> menus;
foreach(const KService::Ptr &rootentry, sortServices(KServiceTypeTrader::self()->query("SystemSettingsCategory", "(not exist [X-KDE-System-Settings-Parent-Category]) or [X-KDE-System-Settings-Parent-Category]==''"))) {
parentmenu->addTitle(rootentry->name().replace('&',"&&"));
const QString rootcategory = rootentry->property("X-KDE-System-Settings-Category").toString();
foreach(const KService::Ptr &entry, sortServices(KServiceTypeTrader::self()->query("SystemSettingsCategory", QString("[X-KDE-System-Settings-Parent-Category]=='%1'").arg(rootcategory)))) {
KMenu* menu = new KMenu(entry->name().replace('&',"&&"), parentmenu);
menu->setIcon(KIcon(entry->icon()));
parentmenu->addMenu(menu);
const QString category = entry->property("X-KDE-System-Settings-Category").toString();
menus[category] = menu;
}
}
QMap<QString, QList<KService::Ptr> > modules;
foreach(const KService::Ptr &entry, sortServices(KServiceTypeTrader::self()->query("KCModule"))) {
const QString category = entry->property("X-KDE-System-Settings-Parent-Category").toString();
if(! category.isEmpty() && ! entry->noDisplay())
modules[category] << entry;
}
foreach(const QString& category, modules.keys()) {
QString menucategory = category;
KService::Ptr subcategory = KService::Ptr();
while(! menucategory.isEmpty() && ! menus.contains(menucategory)) {
KService::List services = KServiceTypeTrader::self()->query("SystemSettingsCategory", QString("[X-KDE-System-Settings-Category]=='%1'").arg(menucategory));
//Q_ASSERT(services.count() > 0); //if that happens then we miss the desktop-file defining the category
//Q_ASSERT(services.count() < 2); //if that happens then we have more then one desktop-file defining the same category
if(services.count() < 1) {
menucategory.clear();
} else {
subcategory = services[0];
menucategory = subcategory->property("X-KDE-System-Settings-Parent-Category").toString();
}
}
if(menucategory.isEmpty()) continue; //skip the category
KMenu* m = menus[menucategory];
if(! subcategory.isNull())
m->addTitle(subcategory->name().replace('&',"&&"));
foreach(const KService::Ptr &entry, modules[category]) {
KCModuleInfo module(entry->entryPath());
m->addAction(KIcon(module.icon()), module.moduleName().replace('&',"&&"))->setData(KUrl("kcm:/" + entry->entryPath()));
}
}
} else if(vtname == "SwitchUser") {
menuview->addAction(KIcon(d->viewIcon(SwitchUser)), d->viewText(SwitchUser))->setData(KUrl("leave:/switch"));
} else if(vtname == "SaveSession") {
KConfigGroup c(KSharedConfig::openConfig("ksmserverrc", KConfig::NoGlobals), "General");
if (c.readEntry("loginMode") == "restoreSavedSession")
menuview->addAction(KIcon(d->viewIcon(SaveSession)), d->viewText(SaveSession))->setData(KUrl("leave:/savesession"));
} else if(vtname == "LockScreen") {
menuview->addAction(KIcon(d->viewIcon(LockScreen)), d->viewText(LockScreen))->setData(KUrl("leave:/lock"));
} else if(vtname == "Logout") {
menuview->addAction(KIcon(d->viewIcon(Logout)), d->viewText(Logout))->setData(KUrl("leave:/logout"));
} else if(vtname == "Leave") {
Kickoff::LeaveModel *leavemodel = new Kickoff::LeaveModel(menuview);
leavemodel->updateModel();
d->addModel(leavemodel, Leave, Kickoff::MenuView::MergeFirstLevel, Kickoff::MenuView::Name);
} else {
QSet< Solid::PowerManagement::SleepState > spdMethods = Solid::PowerManagement::supportedSleepStates();
if(vtname == "SuspendDisk") {
if (spdMethods.contains(Solid::PowerManagement::HibernateState))
menuview->addAction(KIcon(d->viewIcon(SuspendDisk)), d->viewText(SuspendDisk))->setData(KUrl("leave:/suspenddisk"));
} else if(vtname == "SuspendRAM") {
if (spdMethods.contains(Solid::PowerManagement::SuspendState))
menuview->addAction(KIcon(d->viewIcon(SuspendRAM)), d->viewText(SuspendRAM))->setData(KUrl("leave:/suspendram"));
} else if (vtname == "HybridSuspend") {
if (spdMethods.contains(Solid::PowerManagement::HybridSuspendState))
menuview->addAction(KIcon(d->viewIcon(HybridSuspend)), d->viewText(HybridSuspend))->setData(KUrl("leave:/suspendhybrid"));
} else if(vtname == "Restart") {
if (KWorkSpace::canShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeReboot))
menuview->addAction(KIcon(d->viewIcon(Restart)), d->viewText(Restart))->setData(KUrl("leave:/restart"));
} else if(vtname == "Shutdown") {
if (KWorkSpace::canShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeHalt))
menuview->addAction(KIcon(d->viewIcon(Shutdown)), d->viewText(Shutdown))->setData(KUrl("leave:/shutdown"));
}
}
}
}
Plasma::ToolTipManager::self()->hide(this);
setStatus(Plasma::NeedsAttentionStatus);
d->menuview.data()->popup(popupPosition(d->menuview.data()->sizeHint()));
}
void MenuLauncherApplet::menuHiding()
{
d->icon->setUnpressed();
setStatus(Plasma::PassiveStatus);
}
void MenuLauncherApplet::actionTriggered(QAction *action)
{
const KUrl url = action->data().value<KUrl>();
if (url.scheme() == "leave") {
if (!d->launcher) {
d->launcher = new Kickoff::UrlItemLauncher(d->menuview.data());
}
d->launcher.data()->openUrl(url.url());
return;
}
if (url.scheme() == "kcm") {
KToolInvocation::kdeinitExec("kcmshell4", QStringList() << url.fileName());
return;
}
for (QWidget* w = action->parentWidget(); w; w = w->parentWidget()) {
if (Kickoff::MenuView *view = qobject_cast<Kickoff::MenuView*>(w)) {
view->actionTriggered(action);
break;
}
}
}
QList<QAction*> MenuLauncherApplet::contextualActions()
{
return d->actions;
}
void MenuLauncherApplet::iconSizeChanged(int group)
{
if (group == KIconLoader::Desktop || group == KIconLoader::Panel) {
updateGeometry();
}
}
QSizeF MenuLauncherApplet::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
{
if (which == Qt::PreferredSize) {
int iconSize = -1;
switch (formFactor()) {
case Plasma::Planar:
case Plasma::MediaCenter:
iconSize = IconSize(KIconLoader::Desktop);
break;
case Plasma::Horizontal:
case Plasma::Vertical:
iconSize = IconSize(KIconLoader::Panel);
break;
}
return QSizeF(iconSize, iconSize);
}
return Plasma::Applet::sizeHint(which, constraint);
}
void MenuLauncherApplet::saveConfigurationFromKickoff(const KConfigGroup & configGroup, const KConfigGroup & globalConfigGroup)
{
//Copy configuration values
KConfigGroup cg = config();
configGroup.copyTo(&cg);
KConfigGroup gcg = globalConfig();
globalConfigGroup.copyTo(&gcg);
configChanged();
emit configNeedsSaving();
}
void MenuLauncherApplet::configChanged()
{
KConfigGroup cg = config();
const QStringList viewtypes = cg.readEntry("views", QStringList());
if(viewtypes.isEmpty()) { // backward-compatibility to <KDE4.3
QByteArray oldview = cg.readEntry("view", QByteArray());
if (!oldview.isEmpty() && oldview != "Combined") {
d->viewtypes = QStringList() << oldview;
d->iconname = d->viewIcon(d->viewType(oldview));
} // else we use the default d->viewtypes
} else {
d->viewtypes = viewtypes;
}
QMetaEnum fte = metaObject()->enumerator(metaObject()->indexOfEnumerator("FormatType"));
QByteArray ftb = cg.readEntry("format", QByteArray(fte.valueToKey(d->formattype)));
d->formattype = (MenuLauncherApplet::FormatType) fte.keyToValue(ftb);
d->setMaxRecentApps(cg.readEntry("maxRecentApps", qMin(5, Kickoff::RecentApplications::self()->maximum())));
d->showMenuTitles = cg.readEntry("showMenuTitles", false);
d->showRecentlyInstalled = cg.readEntry("showRecentlyInstalled", true);
d->icon->setIcon(KIcon(cg.readEntry("icon", d->iconname)));
d->relativePath = cg.readEntry("relativePath", d->relativePath);
if (!d->relativePath.isEmpty()) {
d->viewtypes.clear();
d->viewtypes << "Applications";
}
d->updateTooltip();
constraintsEvent(Plasma::ImmutableConstraint);
}
#include "moc_simpleapplet.cpp"

View file

@ -1,163 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2008-2009 Sebastian Sauer <mail@dipe.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef SIMPLEAPPLET_H
#define SIMPLEAPPLET_H
// Plasma
#include <Plasma/Applet>
#include <QAction>
/**
* The MenuLauncherApplet class implements an applet that provides the traditional
* aka classic KDE3 like KMenu application launcher using the Kickoff functionality.
*/
class MenuLauncherApplet : public Plasma::Applet
{
Q_OBJECT
Q_ENUMS(ViewType)
Q_ENUMS(FormatType)
public:
/**
* The content we like to display within the menu.
*/
enum ViewType {
RecentlyUsedApplications, ///< Recently Used Applications Menu
RecentlyUsedDocuments, ///< Recently Used Documents Menu
Applications, ///< Applications Menu
Favorites, ///< Favorites Menu
Bookmarks, ///< Bookmarks Menu
Computer, ///< Computer Menu
RecentlyUsed, ///< Recently Used Menu
Settings, ///< Settings Menu
SwitchUser, ///< Switch User Action
SaveSession, ///< Save Session Action (only enabled if restoreSavedSession is enabled)
LockScreen, ///< Lock Screen Action
SuspendDisk, ///< Suspend to Disk Action
SuspendRAM, ///< Suspend to RAM Action
HybridSuspend, ///< Hybrid Suspend Action
Restart, ///< Restart Action
Shutdown, ///< Shutdown Action
Logout, ///< Logout Action
Leave ///< Leave Menu
};
/**
* How the text of the menuitems got formatted.
*/
enum FormatType {
Name = 0, ///< Name only
Description, ///< Description only
NameDescription, ///< Name Description
DescriptionName, ///< Description (Name)
NameDashDescription ///< Name - Description
};
/**
* Constructor.
*
* \param parent The parent QObject.
* \param args The optional list of arguments.
*/
MenuLauncherApplet(QObject *parent, const QVariantList &args);
/**
* Destructor.
*/
virtual ~MenuLauncherApplet();
/**
* This method is called once the applet is loaded and added to a Corona.
**/
void init();
/**
* Called when any of the geometry constraints have been updated.
*
* @param constraints the type of constraints that were updated
*/
void constraintsEvent(Plasma::Constraints constraints);
/**
* Returns a list of context-related QAction instances.
*/
virtual QList<QAction*> contextualActions();
public Q_SLOTS:
/**
* Switch the menu style from the traditional aka classic KDE3 like
* KMenu to the new Kickoff menu.
*/
void switchMenuStyle();
/**
* Start the menu editor by launching kmenuedit.
*/
void startMenuEditor();
/**
* Show a custom context menu for the selected action.
*/
void customContextMenuRequested(QMenu* menu, const QPoint& pos);
/**
* Save config values stored on Kickoff after a menu switch
*/
void saveConfigurationFromKickoff(const KConfigGroup & configGroup,
const KConfigGroup & globalConfigGroup);
/**
* Reload configuration values
* Useful to load previously stored configurations after a menu switch
*/
void configChanged();
protected:
/**
* Create a configuration dialog.
*/
void createConfigurationInterface(KConfigDialog *parent);
QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF()) const;
private Q_SLOTS:
/// Configuration-dialog accepted.
void configAccepted();
/// The menu got toggled or activated.
void toggleMenu();
/// Shows the menu due to being toggled / activated.
void showMenu(bool pressed);
/// An action within the menu got triggered.
void actionTriggered(QAction *action);
/// Icon size setting changed
void iconSizeChanged(int group);
/// Menu is hidden, reset the UI
void menuHiding();
private:
class Private;
Private * const d;
};
K_EXPORT_PLASMA_APPLET(menulauncher, MenuLauncherApplet)
#endif

View file

@ -1,58 +0,0 @@
/*
Copyright 2008 Andrew Lake <jamboarder@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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 "contentareacap.h"
#include <QPainter>
#include <QPen>
ContentAreaCap::ContentAreaCap(QWidget *parent, bool flip)
:QWidget(parent)
{
setMaximumHeight(3);
setMinimumHeight(3);
sizePolicy().setVerticalPolicy(QSizePolicy::Fixed);
flipCap = flip;
parent->setCursor(Qt::ArrowCursor);
}
void ContentAreaCap::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
QPainterPath path;
QRect r = rect();
if (!flipCap) {
path.moveTo(r.topLeft() + QPoint(0,3));
path.quadTo(r.topLeft(), r.topLeft() + QPoint(3,0));
path.lineTo(r.topRight() + QPoint(-2,0));
path.quadTo(r.topRight() + QPoint(1,0), r.topRight() + QPoint(1,3));
} else {
path.moveTo(r.topLeft());
path.quadTo(r.topLeft() + QPoint(0,3), r.topLeft() + QPoint(3,3));
path.lineTo(r.topRight() + QPoint(-2,3));
path.quadTo(r.topRight() + QPoint(1,3), r.topRight() + QPoint(1,0));
}
painter.setPen(QPen(palette().base(), 1));
painter.setRenderHint(QPainter::Antialiasing);
painter.fillPath(path, palette().base());
painter.end();
}

View file

@ -1,311 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2007 Kevin Ottens <ervin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "ui/contextmenufactory.h"
// Qt
#include <QAbstractItemView>
#include <QDebug>
#include <QMap>
#include <QDBusMessage>
#include <QDBusConnection>
// KDE
#include <KIcon>
#include <KMenu>
#include <KActionCollection>
#include <KFileItem>
#include <KBookmarkManager>
#include <Solid/Device>
#include <Solid/StorageAccess>
#include <Solid/OpticalDrive>
#include <Solid/OpticalDisc>
#include <KUrl>
#include <KStandardDirs>
#include <KWindowSystem>
// Plasma
#include <Plasma/Containment>
#include <Plasma/Corona>
// Local
#include "core/favoritesmodel.h"
#include "core/models.h"
Q_DECLARE_METATYPE(QPersistentModelIndex)
using namespace Kickoff;
class ContextMenuFactory::Private
{
public:
Private()
: applet(0) {
}
QMap<QAbstractItemView*, QList<QAction*> > viewActions;
Plasma::Applet *applet;
};
ContextMenuFactory::ContextMenuFactory(QObject *parent)
: QObject(parent)
, d(new Private)
{
}
ContextMenuFactory::~ContextMenuFactory()
{
delete d;
}
void ContextMenuFactory::showContextMenu(QAbstractItemView *view,
const QPersistentModelIndex& index, const QPoint& pos)
{
Q_UNUSED(pos);
if (!index.isValid()) {
return;
}
QString url = index.data(UrlRole).value<QString>();
qDebug() << "ContextMenuFactory::showContextMenu: " << url;
if (url.isEmpty()) {
return;
}
// ivan: The url handling is dirty - instead of handling it in
// the source data models (that define them), we are handling
// them here. So, we need to make urls from KRunner model
// to behave properly
if (url.startsWith(QLatin1String("krunner://"))) {
url = url.remove("krunner://");
qDebug() << "ContextMenuFactory::showContextMenu: 1 " << url;
if (url.startsWith(QLatin1String("services/services_"))) {
url = url.remove("services/services_");
} else {
return;
}
KService::Ptr service = KService::serviceByStorageId(url);
if(!service) {
return;
}
url = service->entryPath();
qDebug() << "ContextMenuFactory::showContextMenu: " << "KRunner service runner: " << url;
}
const bool isFavorite = FavoritesModel::isFavorite(url);
QList<QAction*> actions;
QAction *favoriteAction = 0;
if (url.endsWith(QLatin1String(".desktop"))) {
// add to / remove from favorites
favoriteAction = new QAction(this);
if (isFavorite) {
favoriteAction->setText(i18n("Remove From Favorites"));
favoriteAction->setIcon(KIcon("list-remove"));
actions << favoriteAction;
//exclude stuff in the leave tab
} else if (KUrl(url).protocol() != "leave") {
favoriteAction->setText(i18n("Add to Favorites"));
favoriteAction->setIcon(KIcon("bookmark-new"));
actions << favoriteAction;
}
}
// add to desktop
QAction *addToDesktopAction = new QAction(this);
// add to main panel
QAction *addToPanelAction = new QAction(this);
//### FIXME : icons in leave-view are not properly based on a .desktop file
//so you cant put them in desktop or panel
//### TODO : do not forget to remove (kurl.scheme() != "leave") and kurl declaration
//when proper action for such case will be provided
KUrl kurl(url);
if ((d->applet) && (kurl.scheme() != "leave")) {
Plasma::Containment *containment = d->applet->containment();
// There might be relative paths for .desktop installed in
// /usr/shar/applnk, we need to locate them
bool urlFound = true;
if (kurl.isRelative() && kurl.url().endsWith(QLatin1String(".desktop"))) {
kurl = KStandardDirs::locate("apps", url);
urlFound = !kurl.isEmpty();
}
if (urlFound && containment && containment->corona()) {
Plasma::Containment *desktop = containment->corona()->containmentForScreen(containment->screen());
if (desktop && desktop->immutability() == Plasma::Mutable) {
addToDesktopAction->setText(i18n("Add to Desktop"));
actions << addToDesktopAction;
}
}
if (urlFound && containment && containment->immutability() == Plasma::Mutable &&
(containment->containmentType() == Plasma::Containment::PanelContainment ||
containment->containmentType() == Plasma::Containment::CustomPanelContainment)) {
addToPanelAction->setText(i18n("Add to Panel"));
actions << addToPanelAction;
}
}
QAction *advancedSeparator = new QAction(this);
if (actions.count() > 0) {
// advanced item actions
advancedSeparator->setSeparator(true);
actions << advancedSeparator;
}
// device actions
const QString udi = index.data(DeviceUdiRole).toString();
Solid::Device device(udi);
Solid::StorageAccess *access = device.as<Solid::StorageAccess>();
QAction *ejectAction = 0;
if (device.isValid() && access) {
ejectAction = new QAction(this);
if (device.is<Solid::OpticalDisc>()) {
ejectAction->setText(i18n("Eject"));
} else {
ejectAction->setText(i18n("Safely Remove"));
}
actions << ejectAction;
}
// add view specific actions
QAction *viewSeparator = new QAction(this);
if (view) {
if (actions.count() > 0) {
viewSeparator->setSeparator(true);
actions << viewSeparator;
}
actions << viewActions(view);
}
//return if we added just a separator so far
if (actions.count() < 2) {
return;
}
// display menu
KMenu menu;
menu.addTitle(index.data(Qt::DisplayRole).value<QString>());
foreach(QAction* action, actions) {
menu.addAction(action);
}
QAction *result = menu.exec(QCursor::pos());
if (favoriteAction && result == favoriteAction) {
if (isFavorite) {
FavoritesModel::remove(url);
} else {
FavoritesModel::add(url);
}
} else if (ejectAction && result == ejectAction) {
if (device.is<Solid::OpticalDisc>()) {
Solid::OpticalDrive *drive = device.as<Solid::OpticalDrive>();
drive->eject();
} else {
access->teardown();
}
} else if (addToDesktopAction && result == addToDesktopAction) {
if (d->applet) {
Plasma::Containment *containment = d->applet->containment();
if (containment) {
Plasma::Corona *corona = containment->corona();
if (corona) {
int vdesk = KWindowSystem::currentDesktop() - 1;
Plasma::Containment *desktop = corona->containmentForScreen(containment->screen(), vdesk);
//PVDA disabled?
if (!desktop) {
desktop = corona->containmentForScreen(containment->screen(), -1);
}
if (desktop) {
QVariantList args;
args << kurl.url() << index.data(Kickoff::IconNameRole);
if (kurl.scheme() == "applications") { // it's a service group
desktop->addApplet("simplelauncher", args);
} else if (desktop->metaObject()->indexOfSlot("addUrls(KUrl::List)") != -1) {
QMetaObject::invokeMethod(desktop, "addUrls",
Qt::DirectConnection, Q_ARG(KUrl::List, KUrl::List(kurl)));
} else {
desktop->addApplet("icon", args);
}
}
}
}
}
}else if (addToPanelAction && result == addToPanelAction) {
if (d->applet) {
// we assume that the panel is the same containment where the kickoff is located
Plasma::Containment *panel = d->applet->containment();
if (panel) {
QVariantList args;
args << kurl.url() << index.data(Kickoff::IconNameRole);
// move it to the middle of the panel
QRectF rect(panel->geometry().width() / 2, 0, 150, panel->boundingRect().height());
if (kurl.scheme() == "applications") { // it's a service group
panel->addApplet("simplelauncher", args);
} else {
panel->addApplet("icon", args, rect);
}
}
}
}
delete favoriteAction;
delete addToDesktopAction;
delete addToPanelAction;
delete advancedSeparator;
delete viewSeparator;
delete ejectAction;
}
void ContextMenuFactory::setViewActions(QAbstractItemView *view, const QList<QAction*>& actions)
{
if (actions.isEmpty()) {
d->viewActions.remove(view);
} else {
d->viewActions.insert(view, actions);
}
}
QList<QAction*> ContextMenuFactory::viewActions(QAbstractItemView *view) const
{
return d->viewActions[view];
}
void ContextMenuFactory::setApplet(Plasma::Applet *applet)
{
d->applet = applet;
}
#include "moc_contextmenufactory.cpp"

View file

@ -1,58 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef CONTEXTMENUFACTORY_H
#define CONTEXTMENUFACTORY_H
// Qt
#include <QAction>
#include <QtCore/qabstractitemmodel.h>
// Plasma
#include <Plasma/Applet>
#include <QAbstractItemView>
namespace Kickoff
{
class ContextMenuFactory : public QObject
{
Q_OBJECT
public:
ContextMenuFactory(QObject *parent = 0);
~ContextMenuFactory();
void setViewActions(QAbstractItemView *view, const QList<QAction*>& actions);
QList<QAction*> viewActions(QAbstractItemView *view) const;
void setApplet(Plasma::Applet *applet);
public Q_SLOTS:
void showContextMenu(QAbstractItemView *view,
const QPersistentModelIndex& index, const QPoint& pos);
private:
class Private;
Private * const d;
};
}
#endif // CONTEXTMENUFACTORY_H

View file

@ -1,567 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "ui/flipscrollview.h"
// Qt
#include <QCoreApplication>
#include <QtGui/qevent.h>
#include <QtGui/qevent.h>
#include <QPainter>
#include <QScrollBar>
#include <QStack>
#include <QTimeLine>
// KDE
#include <KDebug>
#include <KGlobalSettings>
#include <KIconLoader>
#include <KColorScheme>
#include "ui/itemdelegate.h"
using namespace Kickoff;
class FlipScrollView::Private
{
public:
Private(FlipScrollView *view)
: q(view)
, flipAnimTimeLine(new QTimeLine())
, animLeftToRight(true)
, itemHeight(-1) {
}
~Private()
{
delete flipAnimTimeLine;
}
QModelIndex currentRoot() const
{
if (currentRootIndex.isValid()) {
return currentRootIndex;
} else {
return q->rootIndex();
}
}
QModelIndex previousRoot() const
{
if (previousRootIndices.isEmpty()) {
return QModelIndex();
}
return previousRootIndices.top();
}
void setCurrentRoot(const QModelIndex& index)
{
if (previousRootIndices.isEmpty() || previousRootIndices.top() != index) {
// we're entering into a submenu
//kDebug() << "pushing" << currentRootIndex.data(Qt::DisplayRole).value<QString>();
animLeftToRight = true;
hoveredIndex = QModelIndex();
previousRootIndices.push(currentRootIndex);
currentRootIndex = index;
previousVerticalOffsets.append(q->verticalOffset());
updateScrollBarRange();
q->verticalScrollBar()->setValue(0);
} else {
// we're exiting to the parent menu
//kDebug() << "popping" << previousRootIndices.top().data(Qt::DisplayRole).value<QString>();
animLeftToRight = false;
hoveredIndex = currentRootIndex;
previousRootIndices.pop();
//if (!previousRootIndices.isEmpty()) {
// kDebug() << "now the previos root is" << previousRootIndices.top().data(Qt::DisplayRole).value<QString>();
//}
currentRootIndex = index;
updateScrollBarRange();
q->verticalScrollBar()->setValue(previousVerticalOffsets.pop());
}
emit q->currentRootChanged(index);
if (q->viewOptions().direction == Qt::RightToLeft) {
animLeftToRight = !animLeftToRight;
}
flipAnimTimeLine->setCurrentTime(0);
q->update();
}
int previousVerticalOffset()
{
return previousVerticalOffsets.isEmpty() ? 0 : previousVerticalOffsets.top();
}
int treeDepth(const QModelIndex& headerIndex) const
{
int depth = 0;
QModelIndex index = headerIndex;
while (index.isValid()) {
index = index.parent();
depth++;
}
return depth;
}
QPainterPath trianglePath(qreal width = 5, qreal height = 10) {
QPainterPath path(QPointF(-width / 2, 0.0));
path.lineTo(width, -height / 2);
path.lineTo(width, height / 2);
path.lineTo(-width / 2, 0.0);
return path;
}
void updateScrollBarRange()
{
int childCount = q->model()->rowCount(currentRootIndex);
int pageSize = q->height();
int itemH = q->sizeHintForIndex(q->model()->index(0, 0)).height();
q->verticalScrollBar()->setRange(0, (childCount * itemH) - pageSize);
q->verticalScrollBar()->setPageStep(pageSize);
q->verticalScrollBar()->setSingleStep(itemH);
}
FlipScrollView * const q;
QPersistentModelIndex hoveredIndex;
QPersistentModelIndex watchedIndexForDrag;
QTimeLine *flipAnimTimeLine;
bool animLeftToRight;
int itemHeight;
static const int FLIP_ANIM_DURATION = 200;
private:
QPersistentModelIndex currentRootIndex;
QStack<QPersistentModelIndex> previousRootIndices;
QStack<int> previousVerticalOffsets;
};
FlipScrollView::FlipScrollView(QWidget *parent)
: QAbstractItemView(parent)
, d(new Private(this))
{
connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(openItem(QModelIndex)));
connect(d->flipAnimTimeLine, SIGNAL(valueChanged(qreal)), this, SLOT(updateFlipAnimation(qreal)));
d->flipAnimTimeLine->setDuration(Private::FLIP_ANIM_DURATION);
d->flipAnimTimeLine->setCurrentTime(Private::FLIP_ANIM_DURATION);
setIconSize(QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium));
setMouseTracking(true);
setAutoScroll(true);
QPalette viewPalette(palette());
viewPalette.setColor(QPalette::Window, palette().color(QPalette::Active, QPalette::Base));
setPalette(viewPalette);
setAutoFillBackground(true);
}
FlipScrollView::~FlipScrollView()
{
delete d;
}
void FlipScrollView::setCurrentRoot(const QModelIndex &index)
{
d->setCurrentRoot(index);
}
void FlipScrollView::viewRoot()
{
QModelIndex index;
while (d->currentRoot().isValid()) {
index = d->currentRoot();
d->setCurrentRoot(d->currentRoot().parent());
setCurrentIndex(index);
}
update(d->hoveredIndex);
d->hoveredIndex = index;
}
QModelIndex FlipScrollView::indexAt(const QPoint& point) const
{
const int items = model()->rowCount(d->currentRoot());
const int rowIndex = (point.y() + verticalOffset()) / itemHeight();
if (rowIndex < items) {
return model()->index(rowIndex, 0, d->currentRoot());
} else {
return QModelIndex();
}
}
int FlipScrollView::itemHeight() const
{
//TODO: reset on font change
if (d->itemHeight < 1) {
QModelIndex index = model()->index(0, 0, d->currentRoot());
d->itemHeight = sizeHintForIndex(index).height();
}
return d->itemHeight;
}
void FlipScrollView::scrollTo(const QModelIndex& index, ScrollHint hint)
{
if (!index.isValid()) {
return;
}
const QRect itemRect = visualRect(index);
if (itemRect.isValid() && hint == EnsureVisible) {
if (itemRect.top() < 0) {
verticalScrollBar()->setValue(verticalScrollBar()->value() +
itemRect.top());
} else if (itemRect.bottom() > height()) {
verticalScrollBar()->setValue(verticalScrollBar()->value() +
(itemRect.bottom() - height()));
}
update(itemRect);
}
}
bool FlipScrollView::isIndexHidden(const QModelIndex&) const
{
return false;
}
QRect FlipScrollView::visualRect(const QModelIndex& index) const
{
const int leftOffset = ItemDelegate::ITEM_LEFT_MARGIN;
if (index.parent() != d->currentRoot() &&
index.parent() != d->previousRoot() &&
index.parent() != (QModelIndex)d->hoveredIndex) {
return QRect();
}
const bool parentIsPreviousRoot = d->previousRoot().isValid() && index.parent() == d->previousRoot();
if (parentIsPreviousRoot && d->flipAnimTimeLine->state() == QTimeLine::NotRunning) {
return QRect();
}
const int scrollBarWidth = verticalScrollBar()->isVisible() ? verticalScrollBar()->width() : 0;
QRect itemRect(leftOffset, index.row() * itemHeight() - verticalOffset() ,
width() - leftOffset - scrollBarWidth, itemHeight());
const qreal timeValue = d->flipAnimTimeLine->currentValue();
if (index.parent() == d->currentRoot()) {
if (d->animLeftToRight) {
itemRect.translate(itemRect.width() * (1 - timeValue), 0);
} else {
itemRect.translate(-itemRect.width() * (1 - timeValue), 0);
}
} else {
if (d->animLeftToRight) {
itemRect.translate((-timeValue*itemRect.width()), 0);
} else {
itemRect.translate((timeValue*itemRect.width()), 0);
}
}
return itemRect;
}
int FlipScrollView::horizontalOffset() const
{
return 0;
}
int FlipScrollView::verticalOffset() const
{
return verticalScrollBar()->value();
}
QRegion FlipScrollView::visualRegionForSelection(const QItemSelection& selection) const
{
QRegion region;
foreach(const QModelIndex& index , selection.indexes()) {
region |= visualRect(index);
}
return region;
}
QModelIndex FlipScrollView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers)
{
QModelIndex index = currentIndex();
// kDebug() << "Moving cursor with current index" << index.data(Qt::DisplayRole);
switch (cursorAction) {
case MoveUp:
if (!currentIndex().isValid()) {
index = model()->index(model()->rowCount(d->currentRoot()) - 1, 0, d->currentRoot());
} else if (currentIndex().row() > 0) {
index = currentIndex().sibling(currentIndex().row() - 1,
currentIndex().column());
}
break;
case MoveDown:
if (!currentIndex().isValid()) {
index = model()->index(0, 0, d->currentRoot());
} else if (currentIndex().row() <
model()->rowCount(currentIndex().parent()) - 1) {
index = currentIndex().sibling(currentIndex().row() + 1,
currentIndex().column());
}
break;
case MoveLeft:
if (d->currentRoot().isValid()) {
index = d->currentRoot();
d->setCurrentRoot(d->currentRoot().parent());
setCurrentIndex(index);
}
break;
case MoveRight:
if (model()->hasChildren(currentIndex())) {
openItem(currentIndex());
// return the new current index set by openItem()
index = currentIndex();
}
break;
default:
// Do nothing
break;
}
// clear the hovered index
update(d->hoveredIndex);
d->hoveredIndex = index;
//kDebug() << "New index after move" << index.data(Qt::DisplayRole);
return index;
}
void FlipScrollView::setSelection(const QRect& rect , QItemSelectionModel::SelectionFlags flags)
{
QItemSelection selection;
selection.select(indexAt(rect.topLeft()), indexAt(rect.bottomRight()));
selectionModel()->select(selection, flags);
}
void FlipScrollView::openItem(const QModelIndex& index)
{
if (model()->canFetchMore(index)) {
model()->fetchMore(index);
}
const bool hasChildren = model()->hasChildren(index);
if (hasChildren) {
d->setCurrentRoot(index);
setCurrentIndex(model()->index(0, 0, index));
} else {
//TODO Emit a signal to open/execute the item
}
}
void FlipScrollView::resizeEvent(QResizeEvent*)
{
d->updateScrollBarRange();
}
void FlipScrollView::mousePressEvent(QMouseEvent *event)
{
d->watchedIndexForDrag = indexAt(event->pos());
QAbstractItemView::mousePressEvent(event);
}
void FlipScrollView::mouseReleaseEvent(QMouseEvent *event)
{
d->watchedIndexForDrag = QModelIndex();
QAbstractItemView::mouseReleaseEvent(event);
}
void FlipScrollView::mouseMoveEvent(QMouseEvent *event)
{
const QModelIndex itemUnderMouse = indexAt(event->pos());
if (itemUnderMouse != d->hoveredIndex) {
update(itemUnderMouse);
update(d->hoveredIndex);
d->hoveredIndex = itemUnderMouse;
setCurrentIndex(d->hoveredIndex);
}
QAbstractItemView::mouseMoveEvent(event);
}
void FlipScrollView::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Enter ||
event->key() == Qt::Key_Return ||
event->key() == Qt::Key_Right) {
moveCursor(MoveRight, event->modifiers());
event->accept();
return;
}
if (event->key() == Qt::Key_Escape || event->key() == Qt::Key_Left) {
if (d->currentRoot().isValid()) {
moveCursor(MoveLeft, event->modifiers());
event->accept();
return;
} else {
// we are already in the leftmost column.
kDebug() << "we are in Left-Most column, processing Key_Left";
event->accept();
emit focusNextViewLeft();
return;
}
}
QAbstractItemView::keyPressEvent(event);
}
void FlipScrollView::leaveEvent(QEvent *event)
{
Q_UNUSED(event);
d->hoveredIndex = QModelIndex();
setCurrentIndex(QModelIndex());
}
void FlipScrollView::paintItems(QPainter &painter, QPaintEvent *event, QModelIndex &root)
{
const int rows = model()->rowCount(root);
//kDebug() << "painting" << rows << "items";
for (int i = 0; i < rows; ++i) {
QModelIndex index = model()->index(i, 0, root);
QStyleOptionViewItem option = viewOptions();
option.rect = visualRect(index);
// only draw items intersecting the region of the widget
// being updated
if (!event->rect().intersects(option.rect)) {
continue;
}
if (selectionModel()->isSelected(index)) {
option.state |= QStyle::State_Selected;
}
if (index == d->hoveredIndex) {
option.state |= QStyle::State_MouseOver;
}
if (index == currentIndex()) {
option.state |= QStyle::State_HasFocus;
}
itemDelegate(index)->paint(&painter, option, index);
if (model()->hasChildren(index)) {
painter.save();
painter.setPen(Qt::NoPen);
// there is an assumption made here that the delegate will fill the background
// with the selected color or some similar color which contrasts well with the
// highlighted text color
if (option.state & QStyle::State_MouseOver) {
painter.setBrush(palette().highlight());
} else {
painter.setBrush(palette().text());
}
QRect triRect = option.rect;
QPainterPath tPath = d->trianglePath();
if (option.direction == Qt::LeftToRight) {
triRect.setLeft(triRect.right() - ItemDelegate::ITEM_RIGHT_MARGIN);
painter.translate(triRect.center().x() - 6, triRect.y() + (option.rect.height() / 2));
} else {
triRect.setRight(triRect.left() + ItemDelegate::ITEM_RIGHT_MARGIN);
painter.translate(triRect.center().x() + 6, triRect.y() + (option.rect.height() / 2));
}
if (option.direction == Qt::LeftToRight) {
painter.rotate(180);
}
painter.drawPath(tPath);
painter.resetTransform();
painter.restore();
}
}
}
void FlipScrollView::paintEvent(QPaintEvent * event)
{
QPalette viewPalette(palette());
viewPalette.setColor(QPalette::Window, palette().color(QPalette::Active, QPalette::Base));
setPalette(viewPalette);
setAutoFillBackground(true);
QPainter painter(viewport());
painter.setRenderHint(QPainter::Antialiasing);
// draw items
QModelIndex currentRoot = d->currentRoot();
QModelIndex previousRoot = d->animLeftToRight ? d->previousRoot() : (QModelIndex)d->hoveredIndex;
//kDebug() << "current root is" << currentRoot.data(Qt::DisplayRole).value<QString>();
paintItems(painter, event, currentRoot);
const qreal timerValue = d->flipAnimTimeLine->currentValue();
if (timerValue < 1.0) {
//kDebug() << "previous root is" << previousRoot.data(Qt::DisplayRole).value<QString>();
paintItems(painter, event, previousRoot);
if (d->flipAnimTimeLine->state() != QTimeLine::Running) {
d->flipAnimTimeLine->start();
}
}
// draw navigation
QStyle::State state = 0;
if (currentRoot.isValid()) {
state |= QStyle::State_Enabled;
}
}
void FlipScrollView::startDrag(Qt::DropActions supportedActions)
{
kDebug() << "Starting UrlItemView drag with actions" << supportedActions;
if (!d->watchedIndexForDrag.isValid()) {
return;
}
QDrag *drag = new QDrag(this);
QMimeData *mimeData = model()->mimeData(selectionModel()->selectedIndexes());
if (mimeData->text().isNull()) {
return;
}
drag->setMimeData(mimeData);
QModelIndex idx = selectionModel()->selectedIndexes().first();
QIcon icon = idx.data(Qt::DecorationRole).value<QIcon>();
drag->setPixmap(icon.pixmap(IconSize(KIconLoader::Desktop)));
drag->exec();
}
void FlipScrollView::updateFlipAnimation(qreal)
{
setDirtyRegion(rect());
}
#include "moc_flipscrollview.cpp"

View file

@ -1,101 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef FLIPSCROLLVIEW_H
#define FLIPSCROLLVIEW_H
// Qt
#include <QAbstractItemView>
namespace Kickoff
{
/**
* An "iPod-style" item view for single-column tree and list data models which
* displays items in pages (one per tree branch).
*
* Clicking on an item which has children (eg. a folder in a directory model)
* scrolls the whole contents of the view to show the items children. A large back
* arrow is displayed on the left of the display if the current item has a valid parent,
* when clicked on this scrolls the whole contents of the view to show the parent item's
* children.
*
* The view assumes that the item delegate will fill the background with the current palette's
* highlight color when the user hovers over items with the mouse. Item delegates should check
* for the QStyle::State_MouseOver or QStyle::State_Selected flags in the state
* field of the QStyleOptionViewItem passed to the QAbstractItemDelegate::paint() method.
*/
class FlipScrollView : public QAbstractItemView
{
Q_OBJECT
public:
/** Construct a new FlipScrollView with the specified parent. */
FlipScrollView(QWidget *parent = 0);
virtual ~FlipScrollView();
void setCurrentRoot(const QModelIndex &index);
/** Go to the root item. */
void viewRoot();
// reimplemented from QAbstractItemView
virtual QModelIndex indexAt(const QPoint& point) const;
virtual void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible);
virtual QRect visualRect(const QModelIndex& index) const;
int itemHeight() const;
Q_SIGNALS:
void currentRootChanged(const QModelIndex &index);
void focusNextViewLeft();
protected:
// reimplemented from QWidget
virtual void paintEvent(QPaintEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void resizeEvent(QResizeEvent *event);
virtual void keyPressEvent(QKeyEvent *event);
virtual void leaveEvent(QEvent *event);
// reimplemented from QAbstractItemView
virtual bool isIndexHidden(const QModelIndex& index) const;
virtual int horizontalOffset() const;
virtual int verticalOffset() const;
virtual QRegion visualRegionForSelection(const QItemSelection& selection) const;
virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
virtual void setSelection(const QRect& rect , QItemSelectionModel::SelectionFlags flags);
virtual void startDrag(Qt::DropActions supportedActions);
private Q_SLOTS:
void openItem(const QModelIndex& index);
void updateFlipAnimation(qreal value);
private:
void paintItems(QPainter &painter, QPaintEvent *event, QModelIndex &index);
class Private;
Private * const d;
};
}
#endif // FLIPSCROLLVIEW_H

View file

@ -1,114 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2007 Kevin Ottens <ervin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "ui/itemdelegate.h"
// Qt
#include <QtCore/qabstractitemmodel.h>
#include <QPainter>
#include <QStyle>
#include <QtGui/qstyleoption.h>
// KDE
#include <KDebug>
#include <KGlobal>
#include <kcapacitybar.h>
// plasma
#include <Plasma/Plasma>
using namespace Kickoff;
ItemDelegate::ItemDelegate(QObject *parent)
: Plasma::Delegate(parent)
{
}
void ItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
Plasma::Delegate::paint(painter, option, index);
qreal freeSpace = -1;
qreal usedSpace = -1;
if (!index.data(DiskFreeSpaceRole).isNull()) {
freeSpace = index.data(DiskFreeSpaceRole).value<int>() / 1024.0 / 1024.0;
usedSpace = index.data(DiskUsedSpaceRole).value<int>() / 1024.0 / 1024.0;
}
// draw free space information (for drive icons)
if (usedSpace >= 0) {
painter->save();
QRect emptyRect = rectAfterTitle(option, index);
QSize barSize = QSize(qMin(emptyRect.width(), option.rect.width() / 3), emptyRect.height());
if (barSize.width() > 0) {
// if the item view is gradually resized smaller or larger, make the bar fade out/in
// as enough space for it becomes available
if (barSize.width() < 20.0) {
painter->setOpacity(barSize.width() / 20.0);
}
QRect spaceRect = QStyle::alignedRect(option.direction,
Qt::AlignRight, barSize, emptyRect);
if (!(option.state & (QStyle::State_Selected | QStyle::State_MouseOver | QStyle::State_HasFocus))) {
painter->setOpacity(painter->opacity() / 2.5);
} else {
}
KCapacityBar capacityBar(KCapacityBar::DrawTextInline);
capacityBar.setValue((usedSpace / (freeSpace + usedSpace))*100);
capacityBar.drawCapacityBar(painter, spaceRect);
// -- Removed the free space text because it added too much 'visual noise' to the item
//
// some precision is lost here, but it is acceptible given that the disk-free bar
// is only shown as a guide
// QString freeSpaceString = KGlobal::locale()->formatByteSize(freeSpace*1024*1024*1024);
// painter->drawText(spaceRect,Qt::AlignCenter,i18n("%1 free",freeSpaceString));
}
painter->restore();
}
}
bool ItemDelegate::isVisible(const QModelIndex& index) const
{
if (!index.isValid()) return false;
if (index.model()->hasChildren(index)) {
const int childCount = index.model()->rowCount(index);
for (int i = 0; i < childCount; ++i) {
QModelIndex child = index.model()->index(i, 0, index);
if (!child.data(UrlRole).isNull()) {
return true;
}
}
return false;
}
return !index.data(UrlRole).isNull();
}

View file

@ -1,65 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef ITEMDELEGATE_H
#define ITEMDELEGATE_H
// Qt
#include <QtGui/QAbstractItemDelegate>
//Plasma
#include <Plasma/Delegate>
// Local
#include "core/models.h"
namespace Kickoff
{
/**
* Item delegate for rendering items in the Kickoff launcher's views.
*
* The delegate makes use of the various additional Kickoff item data roles
* to draw the item. For example, if the DiskFreeSpaceRole and DiskUsedSpaceRole item
* data is valid then a bar chart showing the amount of free space available on the disk
* will be drawn.
*/
class ItemDelegate : public Plasma::Delegate , public ItemStateProvider
{
public:
ItemDelegate(QObject *parent = 0);
virtual void paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
// reimplemented from ItemStateProvider
virtual bool isVisible(const QModelIndex& index) const;
static const int HEADER_LEFT_MARGIN = 5;
static const int HEADER_TOP_MARGIN = 15;
static const int HEADER_BOTTOM_MARGIN = 4;
static const int HEADER_HEIGHT = 35;
static const int FIRST_HEADER_HEIGHT = 20;
static const int ITEM_LEFT_MARGIN = 12;
static const int ITEM_RIGHT_MARGIN = 7;
static const int TOP_OFFSET = 5;
};
}
#endif // ITEMDELEGATE_H

File diff suppressed because it is too large Load diff

View file

@ -1,115 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef LAUNCHER_H
#define LAUNCHER_H
// Qt
#include <QtCore/qabstractitemmodel.h>
#include <QWidget>
// Plasma
#include <Plasma/Applet>
namespace Kickoff
{
/**
* The main window class for the Kickoff launcher. This class is responsible
* for creating the various tabs, views and models which make up the launcher's
* user interface.
*/
class Launcher : public QWidget
{
Q_OBJECT
public:
/** Construct a new Launcher with the specified parent. */
explicit Launcher(QWidget *parent = 0);
/** Construct a new Launcher associated with the specified Plasma::Applet. */
explicit Launcher(Plasma::Applet *applet = 0);
~Launcher();
/** Specifies whether the launcher should hide itself when an item is activated. */
void setAutoHide(bool autoHide);
bool autoHide() const;
/** Specifies whether the application names in the launcher should be displayed *
before or after the description */
void setShowAppsByName(bool showAppByName);
bool showAppsByName() const;
/** Specifies whether hovering switches between tabs or if a click is required to switch the tabs. */
void setSwitchTabsOnHover(bool switchOnHover);
bool switchTabsOnHover() const;
/** Specifies the number of visible items used to determinate the visible height. */
void setVisibleItemCount(int count);
int visibleItemCount() const;
/** Specifies the plasma applet the launcher is working on. */
void setApplet(Plasma::Applet *applet);
/** Specifies the direction the launcher is popping up in relative to its icon */
void setLauncherOrigin(const Plasma::PopupPlacement placement, Plasma::Location location);
void setAppViewIsReceivingKeyEvents(bool isReceiving);
bool appViewIsReceivingKeyEvents() const;
// reimplemented
virtual bool eventFilter(QObject *object, QEvent *event);
virtual QSize minimumSizeHint() const;
virtual QSize sizeHint() const;
/** Reset the launcher. This is called e.g. by the Kickoff-applet before shown to be sure
we don't display old searches and switch back to the favorite-view. */
void reset();
/** Specifies whether 'Recently Installed' hierarchy shall be shown in application view */
void setShowRecentlyInstalled(bool showRecentlyInstalled);
bool showRecentlyInstalled() const;
signals:
void aboutToHide();
void configNeedsSaving();
protected:
virtual void keyPressEvent(QKeyEvent *event);
virtual void showEvent(QShowEvent *event);
virtual void hideEvent(QHideEvent *event);
private Q_SLOTS:
void focusSearchView(const QString& query);
void showViewContextMenu(const QPoint& pos);
void focusFavoritesView();
void resultsAvailable();
void updateThemedPalette();
void fillBreadcrumbs(const QModelIndex &index);
void breadcrumbActivated();
void moveViewToLeft();
private:
void addBreadcrumb(const QModelIndex &index, bool isLeaf);
void init();
class Private;
Private * const d;
};
}
#endif // LAUNCHER_H

View file

@ -1,150 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "ui/searchbar.h"
#include "ui/itemdelegate.h"
// Katie
#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
#include <QHBoxLayout>
#include <QtGui/qevent.h>
#include <QLabel>
#include <QTimer>
// KDE
#include <KIcon>
#include <KIconLoader>
#include <KLineEdit>
//Plasma
#include <Plasma/Theme>
using namespace Kickoff;
class SearchBar::Private
{
public:
Private() : editWidget(0), timer(0) {}
KLineEdit *editWidget;
QLabel *searchLabel;
QTimer *timer;
};
SearchBar::SearchBar(QWidget *parent)
: QWidget(parent)
, d(new Private)
{
// timer for buffered updates
d->timer = new QTimer(this);
d->timer->setInterval(300);
d->timer->setSingleShot(true);
connect(d->timer, SIGNAL(timeout()), this, SLOT(updateTimerExpired()));
connect(this, SIGNAL(startUpdateTimer()), d->timer, SLOT(start()));
// setup UI
QHBoxLayout *layout = new QHBoxLayout;
layout->setMargin(3);
layout->setSpacing(0); // we do the spacing manually to line up with the views below
d->searchLabel = new QLabel(i18nc("Label of the search bar textedit", "Search:"), this);
QLabel *searchIcon = new QLabel(this);
const QFileInfo fi(QDir(QDir::homePath()), ".face.icon");
if (fi.exists()) {
searchIcon->setPixmap(QPixmap(fi.absoluteFilePath()).scaled(KIconLoader::SizeMedium, KIconLoader::SizeMedium, Qt::KeepAspectRatio));
} else {
searchIcon->setPixmap(KIcon("system-search").pixmap(KIconLoader::SizeMedium, KIconLoader::SizeMedium));
}
d->editWidget = new KLineEdit(this);
d->editWidget->installEventFilter(this);
d->editWidget->setClearButtonShown(true);
connect(d->editWidget, SIGNAL(textChanged(QString)), this, SIGNAL(startUpdateTimer()));
//add arbitrary spacing
layout->addSpacing(2);
layout->addWidget(searchIcon);
layout->addSpacing(5);
layout->addWidget(d->searchLabel);
layout->addSpacing(5);
layout->addWidget(d->editWidget);
setLayout(layout);
setFocusProxy(d->editWidget);
updateThemedPalette();
connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()),
this, SLOT(updateThemedPalette()));
}
void SearchBar::updateThemedPalette()
{
QColor color = Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor);
QPalette p = d->searchLabel->palette();
p.setColor(QPalette::Normal, QPalette::WindowText, color);
p.setColor(QPalette::Inactive, QPalette::WindowText, color);
d->searchLabel->setPalette(p);
}
void SearchBar::updateTimerExpired()
{
emit queryChanged(d->editWidget->text());
}
SearchBar::~SearchBar()
{
delete d;
}
bool SearchBar::eventFilter(QObject *watched, QEvent *event)
{
if (watched == d->editWidget && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
// Left and right arrow key presses in the search edit when the
// edit is empty are propagated up to the 'launcher'. This allows
// the launcher to navigate among views (switch among the Tabs)
//while the search bar still has the focus.
if ((keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_Right) &&
d->editWidget->text().isEmpty()) {
QCoreApplication::sendEvent(this, event);
return true;
}
// Up and Down arrows, as well as Tab, propagate up to 'launcher'.
// It will recognize them as a request to enter the currently-visible Tab View.
if (keyEvent->key() == Qt::Key_Down ||
keyEvent->key() == Qt::Key_Up ||
keyEvent->key() == Qt::Key_Tab) {
QCoreApplication::sendEvent(this, event);
return true;
}
}
return false;
}
void SearchBar::clear()
{
d->editWidget->clear();
}
#include "moc_searchbar.cpp"

View file

@ -1,58 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef SEARCHBAR_H
#define SEARCHBAR_H
// Qt
#include <QWidget>
namespace Kickoff
{
class SearchBar : public QWidget
{
Q_OBJECT
public:
SearchBar(QWidget *parent);
virtual ~SearchBar();
bool eventFilter(QObject *watched, QEvent *event);
void clear();
Q_SIGNALS:
void queryChanged(const QString& newQuery);
// internal
void startUpdateTimer();
private Q_SLOTS:
void updateTimerExpired();
void updateThemedPalette();
private:
class Private;
Private * const d;
};
}
#endif // SEARCHBAR_H

View file

@ -1,440 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "ui/tabbar.h"
// KDE
#include <KGlobalSettings>
#include <KColorScheme>
// Qt
#include <QtGui/qevent.h>
#include <QPainter>
#include <QIcon>
#include <QEasingCurve>
#include <QPropertyAnimation>
#include <Plasma/Plasma>
#include <Plasma/Theme>
#include <Plasma/FrameSvg>
using namespace Kickoff;
TabBar::TabBar(QWidget *parent)
: KTabBar(parent),
m_hoveredTabIndex(-1),
m_switchOnHover(true),
m_animateSwitch(true),
m_animProgress(1.0)
{
m_lastIndex[0] = -1;
connect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
m_tabSwitchTimer.setSingleShot(true);
connect(&m_tabSwitchTimer, SIGNAL(timeout()), this, SLOT(switchToHoveredTab()));
setAcceptDrops(true);
setMouseTracking(true);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
setUsesScrollButtons(false);
background = new Plasma::FrameSvg(this);
background->setImagePath("dialogs/kickoff");
background->setEnabledBorders(
Plasma::FrameSvg::BottomBorder |
Plasma::FrameSvg::LeftBorder |
Plasma::FrameSvg::RightBorder
);
background->resizeFrame(size());
background->setElementPrefix("plain");
connect(background, SIGNAL(repaintNeeded()), this, SLOT(update()));
}
void TabBar::setShape(Shape shape)
{
resize(0, 0); // This is required, so that the custom implementation of tabSizeHint,
// which expands the tabs to the full width of the widget does not pick up
// the previous width, e.g. if the panel is moved from the bottom to the left
KTabBar::setShape(shape);
resize(sizeHint());
}
void TabBar::setCurrentIndexWithoutAnimation(int index)
{
disconnect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
setCurrentIndex(index);
storeLastIndex();
connect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
animationFinished();
}
void TabBar::setSwitchTabsOnHover(bool switchOnHover)
{
m_switchOnHover = switchOnHover;
}
bool TabBar::switchTabsOnHover() const
{
return m_switchOnHover;
}
void TabBar::setAnimateSwitch(bool animateSwitch)
{
m_animateSwitch = animateSwitch;
}
bool TabBar::animateSwitch() const
{
return m_animateSwitch;
}
QSize TabBar::tabSize(int index) const
{
QSize hint;
const QFontMetrics metrics(KGlobalSettings::smallestReadableFont());
const QSize textSize = metrics.size(Qt::TextHideMnemonic, tabText(index));
hint.rwidth() = qMax(iconSize().width(), textSize.width());
hint.rheight() = iconSize().height() + textSize.height();
hint.rwidth() += 4 * TAB_CONTENTS_MARGIN;
hint.rheight() += 2 * TAB_CONTENTS_MARGIN;
return hint;
}
void TabBar::storeLastIndex()
{
// if first run
if (m_lastIndex[0] == -1) {
m_lastIndex[1] = currentIndex();
}
m_lastIndex[0] = m_lastIndex[1];
m_lastIndex[1] = currentIndex();
}
int TabBar::lastIndex() const
{
return m_lastIndex[0];
}
QSize TabBar::tabSizeHint(int index) const
{
QSize hint = tabSize(index);
int minwidth = 0;
int minheight = 0;
Shape s = shape();
switch (s) {
case RoundedSouth:
case TriangularSouth:
case RoundedNorth:
case TriangularNorth:
if (count() > 0) {
for (int i = count() - 1; i >= 0; i--) {
minwidth += tabSize(i).width();
}
if (minwidth < width()) {
hint.rwidth() += (width() - minwidth) / count();
}
}
break;
case RoundedWest:
case TriangularWest:
case RoundedEast:
case TriangularEast:
if (count() > 0) {
for (int i = count() - 1; i >= 0; i--) {
minheight += tabSize(i).height();
}
if (minheight < height()) {
hint.rheight() += (height() - minheight) / count();
}
}
hint.rwidth() = qMax(hint.width(), width());
break;
}
return hint;
}
QSize TabBar::sizeHint() const
{
int width = 0;
int height = 0;
if (isVertical()) {
for (int i = count() - 1; i >= 0; i--) {
height += tabSize(i).height();
}
width = tabSize(0).width();
} else {
for (int i = count() - 1; i >= 0; i--) {
width += tabSize(i).width();
}
height = tabSize(0).height();
}
return QSize(width, height);
}
QPainterPath TabBar::tabPath(const QRectF &_r)
{
const qreal radius = 6;
Shape s = shape();
QPainterPath path;
QRectF r = _r;
switch (s) {
case RoundedSouth:
case TriangularSouth:
r.adjust(0, 0, 0, -3);
path.moveTo(r.topLeft());
// Top left corner
path.quadTo(r.topLeft() + QPointF(radius, 0), r.topLeft() + QPointF(radius, radius));
path.lineTo(r.bottomLeft() + QPointF(radius, -radius));
// Bottom left corner
path.quadTo(r.bottomLeft() + QPointF(radius, 0), r.bottomLeft() + QPointF(radius * 2, 0));
path.lineTo(r.bottomRight() + QPoint(-radius * 2, 0));
// Bottom right corner
path.quadTo(r.bottomRight() + QPointF(-radius, 0), r.bottomRight() + QPointF(-radius, -radius));
path.lineTo(r.topRight() + QPointF(-radius, radius));
// Top right corner
path.quadTo(r.topRight() + QPointF(-radius, 0), r.topRight());
break;
case RoundedNorth:
case TriangularNorth:
r.adjust(0, 3, 0, 1);
path.moveTo(r.bottomLeft());
// Bottom left corner
path.quadTo(r.bottomLeft() + QPointF(radius, 0), r.bottomLeft() + QPointF(radius, -radius));
// Top left corner
path.lineTo(r.topLeft() + QPointF(radius, radius));
path.quadTo(r.topLeft() + QPoint(radius, 0), r.topLeft() + QPointF(radius * 2, 0));
// Top right corner
path.lineTo(r.topRight() + QPointF(-radius * 2, 0));
path.quadTo(r.topRight() + QPointF(-radius, 0), r.topRight() + QPointF(-radius, radius));
// Bottom right corner
path.lineTo(r.bottomRight() + QPointF(-radius, -radius));
path.quadTo(r.bottomRight() + QPointF(-radius, 0), r.bottomRight());
break;
case RoundedWest:
case TriangularWest:
r.adjust(3, 0, 1, 0);
path.moveTo(r.topRight());
// Top right corner
path.lineTo(r.topRight());
path.quadTo(r.topRight() + QPointF(0, radius), r.topRight() + QPointF(-radius, radius));
// Top left corner
path.lineTo(r.topLeft() + QPointF(radius, radius));
path.quadTo(r.topLeft() + QPointF(0, radius), r.topLeft() + QPointF(0, radius * 2));
// Bottom left corner
path.lineTo(r.bottomLeft() + QPointF(0, -radius * 2));
path.quadTo(r.bottomLeft() + QPointF(0, -radius), r.bottomLeft() + QPointF(radius, -radius));
// Bottom right corner
path.lineTo(r.bottomRight() + QPointF(-radius, -radius));
path.quadTo(r.bottomRight() + QPointF(0, -radius), r.bottomRight());
break;
case RoundedEast:
case TriangularEast:
r.adjust(0, 0, -3, 0);
path.moveTo(r.topLeft());
// Top left corner
path.quadTo(r.topLeft() + QPointF(0, radius), r.topLeft() + QPointF(radius, radius));
// Top right corner
path.lineTo(r.topRight() + QPointF(-radius, radius));
path.quadTo(r.topRight() + QPointF(0, radius), r.topRight() + QPointF(0, radius * 2));
// Bottom right corner
path.lineTo(r.bottomRight() + QPointF(0, -radius * 2));
path.quadTo(r.bottomRight() + QPointF(0, -radius), r.bottomRight() + QPointF(-radius, -radius));
// Bottom left corner
path.lineTo(r.bottomLeft() + QPointF(radius, -radius));
path.quadTo(r.bottomLeft() + QPointF(0, -radius), r.bottomLeft());
break;
}
return path;
}
void TabBar::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter painter(this);
//int numTabs = count();
int currentTab = currentIndex();
background->paintFrame(&painter);
//bool ltr = painter.layoutDirection() == Qt::LeftToRight; // Not yet used
painter.setFont(KGlobalSettings::smallestReadableFont());
// Drawing Tabborders
QRectF movingRect;
if (m_currentAnimRect.isNull()) {
movingRect = tabRect(currentIndex());
} else {
movingRect = m_currentAnimRect;
}
QPainterPath path = tabPath(movingRect);
painter.save();
painter.setPen(QPen(palette().base(), 1));
painter.setRenderHint(QPainter::Antialiasing);
painter.fillPath(path, palette().base());
painter.restore();
QFontMetrics metrics(painter.font());
int textHeight = metrics.height();
for (int i = 0; i < count(); i++) {
QRect rect = tabRect(i).adjusted(TAB_CONTENTS_MARGIN, TAB_CONTENTS_MARGIN,
-TAB_CONTENTS_MARGIN, -TAB_CONTENTS_MARGIN);
// draw tab icon
QRectF iconRect = rect;
iconRect.setBottom(iconRect.bottom() - textHeight);
iconRect.adjust(0, (isVertical() ? 1 : 0) * TAB_CONTENTS_MARGIN + 3, 0, 0);
tabIcon(i).paint(&painter, iconRect.toRect());
// draw tab text
if (i != currentTab || m_animProgress < 0.9) {
//painter.setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText), 0));
painter.setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
} else {
painter.setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText), 0));
}
QRect textRect = rect;
textRect.setTop(textRect.bottom() - textHeight);
painter.drawText(textRect, Qt::AlignCenter | Qt::TextHideMnemonic, tabText(i));
}
}
void TabBar::leaveEvent(QEvent *event)
{
Q_UNUSED(event)
m_hoveredTabIndex = -1;
}
void TabBar::mouseMoveEvent(QMouseEvent *event)
{
m_hoveredTabIndex = tabAt(event->pos());
if (m_switchOnHover && m_hoveredTabIndex > -1 && m_hoveredTabIndex != currentIndex()) {
m_tabSwitchTimer.stop();
m_tabSwitchTimer.start(50);
}
}
void TabBar::resizeEvent(QResizeEvent* event)
{
KTabBar::resizeEvent(event);
m_currentAnimRect = tabRect(currentIndex());
background->resizeFrame(event->size());
update();
}
void TabBar::dragEnterEvent(QDragEnterEvent *event)
{
m_hoveredTabIndex = tabAt(event->pos());
m_tabSwitchTimer.stop();
m_tabSwitchTimer.start(50);
event->ignore();
}
void TabBar::switchToHoveredTab()
{
if (m_hoveredTabIndex < 0 || m_hoveredTabIndex == currentIndex()) {
return;
}
if (m_animateSwitch) {
setCurrentIndex(m_hoveredTabIndex);
} else {
setCurrentIndexWithoutAnimation(m_hoveredTabIndex);
}
}
void TabBar::startAnimation()
{
storeLastIndex();
QPropertyAnimation *animation = m_animation.data();
if (animation) {
animation->pause();
} else {
animation = new QPropertyAnimation(this, "animValue");
animation->setEasingCurve(QEasingCurve::OutQuad);
animation->setDuration(150);
animation->setStartValue(0.0);
animation->setEndValue(1.0);
}
animation->start(QAbstractAnimation::DeleteWhenStopped);
}
qreal TabBar::animValue() const
{
return m_animProgress;
}
void TabBar::setAnimValue(qreal value)
{
if ((m_animProgress = value) == 1.0) {
animationFinished();
return;
}
// animation rect
QRect rect = tabRect(currentIndex());
QRect lastRect = tabRect(lastIndex());
int x = isHorizontal() ? (int)(lastRect.x() - value * (lastRect.x() - rect.x())) : rect.x();
int y = isHorizontal() ? rect.y() : (int)(lastRect.y() - value * (lastRect.y() - rect.y()));
QSizeF sz = lastRect.size() - value * (lastRect.size() - rect.size());
m_currentAnimRect = QRect(x, y, (int)(sz.width()), (int)(sz.height()));
update();
}
void TabBar::animationFinished()
{
m_currentAnimRect = QRect();
update();
}
bool TabBar::isVertical() const
{
Shape s = shape();
if (s == RoundedWest ||
s == RoundedEast ||
s == TriangularWest ||
s == TriangularEast) {
return true;
}
return false;
}
bool TabBar::isHorizontal() const
{
return !isVertical();
}
#include "moc_tabbar.cpp"

View file

@ -1,99 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef TABBAR_H
#define TABBAR_H
#include <KTabBar>
#include <QTimer>
#include <QtCore/qsharedpointer.h>
#include <QPropertyAnimation>
namespace Plasma
{
class FrameSvg;
}
namespace Kickoff
{
class TabBar : public KTabBar
{
Q_OBJECT
Q_PROPERTY(qreal animValue READ animValue WRITE setAnimValue)
public:
TabBar(QWidget *parent);
QSize sizeHint() const;
/** Like the setCurrentIndex() method but switches the tab without using any
animation. This is used e.g. within Launcher::reset() to switch back to the
favorite tab before Kickoff got shown. */
void setCurrentIndexWithoutAnimation(int index);
/** Specifies whether hovering switches between tabs or if a click is required to switch the tabs. */
void setSwitchTabsOnHover(bool switchOnHover);
bool switchTabsOnHover() const;
void setAnimateSwitch(bool animateSwitch);
bool animateSwitch() const ;
void setShape(Shape shape);
qreal animValue() const;
protected:
int lastIndex() const;
// reimplemented from KTabBar
virtual QSize tabSizeHint(int index) const;
virtual void paintEvent(QPaintEvent *event);
virtual void leaveEvent(QEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void resizeEvent(QResizeEvent* event);
virtual void dragEnterEvent(QDragEnterEvent *event);
bool isHorizontal() const;
bool isVertical() const;
protected slots:
void switchToHoveredTab();
void animationFinished();
void startAnimation();
void setAnimValue(qreal value);
private:
QPainterPath tabPath(const QRectF &r);
static const int TAB_CONTENTS_MARGIN = 6;
int m_hoveredTabIndex;
QTimer m_tabSwitchTimer;
bool m_switchOnHover;
bool m_animateSwitch;
QRectF m_currentAnimRect;
int m_lastIndex[2];
QWeakPointer<QPropertyAnimation> m_animation;
qreal m_animProgress;
Plasma::FrameSvg *background;
QSize tabSize(int index) const;
void storeLastIndex();
};
}
#endif // TABBAR_H

View file

@ -1,715 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
// Own
#include "ui/urlitemview.h"
// Qt
#include <QtCore/QHash>
#include <QtCore/qabstractitemmodel.h>
#include <QtCore/QUrl>
#include <QtGui/QPen>
#include <QtGui/qevent.h>
#include <QtGui/QPainter>
#include <QtGui/QScrollBar>
#include <QtGui/QToolTip>
// KDE
#include <KDebug>
#include <KGlobalSettings>
#include <KIconLoader>
#include <KColorScheme>
// Local
#include "core/models.h"
#include "core/kickoffmodel.h"
#include "ui/itemdelegate.h"
using namespace Kickoff;
class UrlItemView::Private
{
public:
Private(UrlItemView *parent)
: q(parent)
, contentsHeight(0)
, itemStateProvider(0) {
}
void doLayout() {
// clear existing layout information
itemRects.clear();
visualOrder.clear();
if (!q->model()) {
return;
}
int verticalOffset = ItemDelegate::TOP_OFFSET;
int horizontalOffset = 0;
int row = 0;
int visualColumn = 0;
QModelIndex branch = currentRootIndex;
while (true) {
if (itemChildOffsets[branch] + row >= q->model()->rowCount(branch) ||
(branch != currentRootIndex && row > MAX_CHILD_ROWS)) {
if (branch.isValid()) {
row = branch.row() + 1;
branch = branch.parent();
continue;
} else {
break;
}
}
QModelIndex child = q->model()->index(row + itemChildOffsets[branch], 0, branch);
if (q->model()->hasChildren(child)) {
QSize childSize = calculateHeaderSize(child);
QRect rect(QPoint(ItemDelegate::HEADER_LEFT_MARGIN, verticalOffset), childSize);
//kDebug() << "header rect for" << child.data(Qt::DisplayRole) << "is" << rect;
itemRects.insert(child, rect);
if (childSize.isValid()) {
// don't subtract 1
verticalOffset += childSize.height();
}
horizontalOffset = 0;
branch = child;
row = 0;
visualColumn = 0;
} else {
QSize childSize = calculateItemSize(child);
//kDebug() << "item rect for" << child.data(Qt::DisplayRole) << "is" << QRect(QPoint(horizontalOffset,verticalOffset), childSize);
itemRects.insert(child, QRect(QPoint(horizontalOffset, verticalOffset),
childSize));
if (childSize.isValid()) {
visualOrder << child;
}
horizontalOffset += contentWidth() / MAX_COLUMNS;
visualColumn++;
row++;
bool wasLastRow = row + itemChildOffsets[branch] >= q->model()->rowCount(branch);
bool nextItemIsBranch = false;
if (!wasLastRow) {
QModelIndex nextIndex = q->model()->index(row + itemChildOffsets[branch], 0, branch);
nextItemIsBranch = q->model()->hasChildren(nextIndex);
}
if (visualColumn >= MAX_COLUMNS || wasLastRow || nextItemIsBranch) {
horizontalOffset = 0;
visualColumn = 0;
}
if (childSize.isValid()) {
// don't subtract 1
verticalOffset += childSize.height();
}
}
}
contentsHeight = verticalOffset;
updateScrollBarRange();
}
void drawHeader(QPainter *painter,
const QModelIndex& index,
const QStyleOptionViewItem& option) {
const bool first = isFirstHeader(index);
const int rightMargin = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent) + 6;
const int dy = (first ? 4 : ItemDelegate::HEADER_TOP_MARGIN);
painter->save();
painter->setRenderHint(QPainter::Antialiasing, false);
if (!first) {
QLinearGradient gradient(option.rect.topLeft(), option.rect.topRight());
gradient.setColorAt(0.0, Qt::transparent);
gradient.setColorAt(0.1, option.palette.midlight().color());
gradient.setColorAt(0.5, option.palette.mid().color());
gradient.setColorAt(0.9, option.palette.midlight().color());
gradient.setColorAt(1.0, Qt::transparent);
painter->setPen(QPen(gradient, 1));
painter->drawLine(option.rect.x() + 6, option.rect.y() + dy + 2,
option.rect.right() - rightMargin , option.rect.y() + dy + 2);
}
painter->setFont(KGlobalSettings::smallestReadableFont());
painter->setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText), 0));
QString text = index.data(Qt::DisplayRole).value<QString>();
painter->drawText(option.rect.adjusted(0, dy, -rightMargin, 0),
Qt::AlignVCenter | Qt::AlignRight, text);
painter->restore();
}
void updateScrollBarRange() {
const int pageSize = q->height();
q->verticalScrollBar()->setRange(0, contentsHeight - pageSize);
q->verticalScrollBar()->setPageStep(pageSize);
q->verticalScrollBar()->setSingleStep(q->sizeHintForRow(0));
}
int contentWidth() const {
return q->width() - q->style()->pixelMetric(QStyle::PM_ScrollBarExtent) + 2;
}
QSize calculateItemSize(const QModelIndex& index) const {
if (itemStateProvider && !itemStateProvider->isVisible(index)) {
return QSize();
} else {
return QSize(contentWidth() / MAX_COLUMNS, q->sizeHintForIndex(index).height());
}
}
bool isFirstHeader(const QModelIndex &index) const {
if (index.row() == 0) {
return q->model()->hasChildren(index);
}
QModelIndex prevHeader = index.sibling(index.row() - 1, index.column());
while (prevHeader.isValid()) {
//kDebug() << "checking" << prevHeader.data(Qt::DisplayRole).value<QString>();
if (q->model()->hasChildren(prevHeader)) {
//kDebug() << "it has children";
return false;
}
prevHeader = prevHeader.sibling(prevHeader.row() - 1, prevHeader.column());
}
return true;
}
bool insertAbove(const QRect &itemRect, const QPoint &pos) const {
return pos.y() < itemRect.top() + (itemRect.height() / 2);
}
bool insertBelow(const QRect &itemRect, const QPoint &pos) const {
return pos.y() >= itemRect.top() + (itemRect.height() / 2);
}
QSize calculateHeaderSize(const QModelIndex& index) const {
const QFontMetrics fm(KGlobalSettings::smallestReadableFont());
int minHeight = ItemDelegate::HEADER_HEIGHT;
const bool isFirst = isFirstHeader(index);
if (itemStateProvider && !itemStateProvider->isVisible(index)) {
return QSize();
} else if (isFirst) {
minHeight = ItemDelegate::FIRST_HEADER_HEIGHT;
}
return QSize(q->width() - ItemDelegate::HEADER_LEFT_MARGIN,
qMax(fm.height() + (isFirst ? 4 : ItemDelegate::HEADER_TOP_MARGIN), minHeight)
+ ItemDelegate::HEADER_BOTTOM_MARGIN) ;
}
QPoint mapFromViewport(const QPoint& point) const {
return point + QPoint(0, q->verticalOffset());
}
QPoint mapToViewport(const QPoint& point) const {
return point - QPoint(0, q->verticalOffset());
}
UrlItemView * const q;
QPersistentModelIndex currentRootIndex;
QPersistentModelIndex hoveredIndex;
QPersistentModelIndex watchedIndexForDrag;
QHash<QModelIndex, int> itemChildOffsets;
QHash<QModelIndex, QRect> itemRects;
QList<QModelIndex> visualOrder;
QRect dropRect;
int draggedRow;
bool dragging;
int contentsHeight;
ItemStateProvider *itemStateProvider;
static const int MAX_COLUMNS = 1;
// TODO Eventually it will be possible to restrict each branch to only showing
// a given number of children, with Next/Previous arrows to view more children
//
// eg.
//
// Recent Documents [1-10 of 100] Next
// Recent Applications [10-20 of 30] Previous|Next
//
static const int MAX_CHILD_ROWS = 1000;
};
UrlItemView::UrlItemView(QWidget *parent)
: QAbstractItemView(parent)
, d(new Private(this))
{
d->dragging = false;
setIconSize(QSize(KIconLoader::SizeMedium, KIconLoader::SizeMedium));
setMouseTracking(true);
QPalette viewPalette(palette());
viewPalette.setColor(QPalette::Window, palette().color(QPalette::Active, QPalette::Base));
setPalette(viewPalette);
setAutoFillBackground(true);
}
UrlItemView::~UrlItemView()
{
delete d;
}
QModelIndex UrlItemView::indexAt(const QPoint& point) const
{
// simple linear search through the item rects, this will
// be inefficient when the viewport is large
QHashIterator<QModelIndex, QRect> iter(d->itemRects);
while (iter.hasNext()) {
iter.next();
if (iter.value().contains(d->mapFromViewport(point))) {
return iter.key();
}
}
return QModelIndex();
}
bool UrlItemView::initializeSelection()
{
if (!selectionModel()
|| selectionModel()->hasSelection()
|| d->itemRects.size() == 0
) {
return false;
}
// searching for the first item - the item whose rectangle is the
// top one
QHashIterator<QModelIndex, QRect> iter(d->itemRects);
// There is at least one item
iter.next();
int y = iter.value().top();
QModelIndex index = iter.key();
while (iter.hasNext()) {
iter.next();
if (y > iter.value().top()) {
y = iter.value().top();
index = iter.key();
}
}
setCurrentIndex(index);
return selectionModel()->hasSelection();
}
void UrlItemView::setModel(QAbstractItemModel *model)
{
QAbstractItemView::setModel(model);
if (model) {
connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(updateLayout()));
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(updateLayout()));
connect(model, SIGNAL(modelReset()), this, SLOT(updateLayout()));
}
d->currentRootIndex = QModelIndex();
d->itemChildOffsets.clear();
updateLayout();
}
void UrlItemView::updateLayout()
{
d->doLayout();
if (viewport()->isVisible()) {
viewport()->update();
}
}
void UrlItemView::scrollTo(const QModelIndex& index, ScrollHint hint)
{
QRect itemRect = d->itemRects[index];
QRect viewedRect = QRect(d->mapFromViewport(QPoint(0, 0)),
size());
int topDifference = viewedRect.top() - itemRect.top();
int bottomDifference = viewedRect.bottom() - itemRect.bottom();
QScrollBar *scrollBar = verticalScrollBar();
if (!itemRect.isValid())
return;
switch (hint) {
case EnsureVisible: {
if (!viewedRect.contains(itemRect)) {
if (topDifference < 0) {
// scroll view down
scrollBar->setValue(scrollBar->value() - bottomDifference);
} else {
// scroll view up
scrollBar->setValue(scrollBar->value() - topDifference);
}
}
}
break;
case PositionAtTop: {
//d->viewportOffset = itemRect.top();
}
default:
Q_ASSERT(false); // Not implemented
}
}
QRect UrlItemView::visualRect(const QModelIndex& index) const
{
QRect itemRect = d->itemRects[index];
if (!itemRect.isValid()) {
return itemRect;
}
itemRect.moveTopLeft(d->mapToViewport(itemRect.topLeft()));
return itemRect;
}
int UrlItemView::horizontalOffset() const
{
return 0;
}
bool UrlItemView::isIndexHidden(const QModelIndex&) const
{
return false;
}
QModelIndex UrlItemView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers)
{
QModelIndex index = currentIndex();
int visualIndex = d->visualOrder.indexOf(index);
switch (cursorAction) {
case MoveUp:
if (!currentIndex().isValid()) {
const QModelIndex root = model()->index(0, 0);
index = model()->index(model()->rowCount(root) - 1, 0, root);
} else {
visualIndex = qMax(0, visualIndex - 1);
}
break;
case MoveDown:
if (!currentIndex().isValid()) {
const QModelIndex root = model()->index(0, 0);
index = model()->index(0, 0, root);
} else {
visualIndex = qMin(d->visualOrder.count() - 1, visualIndex + 1);
}
break;
default:
// Do nothing
break;
}
d->hoveredIndex = QModelIndex();
return currentIndex().isValid() ? d->visualOrder.value(visualIndex, QModelIndex())
: index;
}
void UrlItemView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags)
{
QItemSelection selection;
selection.select(indexAt(rect.topLeft()), indexAt(rect.bottomRight()));
selectionModel()->select(selection, flags);
}
int UrlItemView::verticalOffset() const
{
return verticalScrollBar()->value();
}
QRegion UrlItemView::visualRegionForSelection(const QItemSelection& selection) const
{
QRegion region;
foreach(const QModelIndex& index, selection.indexes()) {
region |= visualRect(index);
}
return region;
}
void UrlItemView::paintEvent(QPaintEvent *event)
{
if (!model()) {
return;
}
QPalette viewPalette(palette());
viewPalette.setColor(QPalette::Window, palette().color(QPalette::Active, QPalette::Base));
setPalette(viewPalette);
setAutoFillBackground(true);
QPainter painter(viewport());
painter.setRenderHint(QPainter::Antialiasing);
if (d->dragging && dragDropMode() == QAbstractItemView::DragDrop) {
const int y = (d->dropRect.top() + d->dropRect.bottom()) / 2;
painter.save();
QLinearGradient gr(d->dropRect.left(), y, d->dropRect.right(), y);
gr.setColorAt(0, palette().base().color());
gr.setColorAt(.35, palette().windowText().color());
gr.setColorAt(.65, palette().windowText().color());
gr.setColorAt(1, palette().base().color());
painter.setPen(QPen(gr, 1));
painter.drawLine(d->dropRect.left(), y, d->dropRect.right(), y);
painter.restore();
}
QHashIterator<QModelIndex, QRect> indexIter(d->itemRects);
while (indexIter.hasNext()) {
indexIter.next();
const QRect itemRect = visualRect(indexIter.key());
const QModelIndex index = indexIter.key();
if (event->region().contains(itemRect)) {
QStyleOptionViewItem option = viewOptions();
option.rect = itemRect;
if (selectionModel()->isSelected(index)) {
option.state |= QStyle::State_Selected;
}
if (index == d->hoveredIndex) {
option.state |= QStyle::State_MouseOver;
}
if (index == currentIndex()) {
option.state |= QStyle::State_HasFocus;
}
if (model()->hasChildren(index)) {
d->drawHeader(&painter, index, option);
} else {
if (option.rect.left() == 0) {
option.rect.setLeft(option.rect.left() + ItemDelegate::ITEM_LEFT_MARGIN);
option.rect.setRight(option.rect.right() - ItemDelegate::ITEM_RIGHT_MARGIN);
}
itemDelegate(index)->paint(&painter, option, index);
}
}
}
}
void UrlItemView::resizeEvent(QResizeEvent *)
{
updateLayout();
}
void UrlItemView::mouseMoveEvent(QMouseEvent *event)
{
const QModelIndex itemUnderMouse = indexAt(event->pos());
if (itemUnderMouse != d->hoveredIndex && state() == NoState) {
update(itemUnderMouse);
update(d->hoveredIndex);
d->hoveredIndex = itemUnderMouse;
setCurrentIndex(d->hoveredIndex);
}
Plasma::Delegate *hoveredItemDelegate =
static_cast<Plasma::Delegate*>(itemDelegate(d->hoveredIndex));
if (hoveredItemDelegate->showToolTip()) {
QModelIndex index = d->hoveredIndex;
QString titleText = index.data(Qt::DisplayRole).toString();
QString subTitleText = index.data(Plasma::Delegate::SubTitleRole).toString();
setToolTip(titleText + '\n' + subTitleText);
} else {
setToolTip(QString());
}
QAbstractItemView::mouseMoveEvent(event);
}
void UrlItemView::mousePressEvent(QMouseEvent *event)
{
d->watchedIndexForDrag = indexAt(event->pos());
QAbstractItemView::mousePressEvent(event);
}
void UrlItemView::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event)
d->watchedIndexForDrag = QModelIndex();
}
void UrlItemView::setItemStateProvider(ItemStateProvider *provider)
{
d->itemStateProvider = provider;
}
void UrlItemView::dragEnterEvent(QDragEnterEvent *event)
{
if (dragDropMode() != QAbstractItemView::DragDrop) {
return;
}
d->dragging = true;
setDirtyRegion(d->dropRect);
event->accept();
}
void UrlItemView::dragLeaveEvent(QDragLeaveEvent *event)
{
if (dragDropMode() != QAbstractItemView::DragDrop) {
return;
}
d->dragging = false;
setDirtyRegion(d->dropRect);
event->accept();
}
void UrlItemView::dragMoveEvent(QDragMoveEvent *event)
{
QAbstractItemView::dragMoveEvent(event);
const QPoint pos = event->pos();
const QModelIndex index = indexAt(pos);
setDirtyRegion(d->dropRect);
// check to see if it's the header
if (d->isFirstHeader(index) && index.row() == 0) {
event->ignore();
return;
}
if (index.isValid()) {
const QRect rect = visualRect(index);
const int gap = d->contentsHeight;
if (d->insertAbove(rect, pos)) {
d->dropRect = QRect(rect.left(), rect.top() - gap / 2,
rect.width(), gap);
} else if (d->insertBelow(rect, pos)) {
d->dropRect = QRect(rect.left(), rect.bottom() + 1 - gap / 2,
rect.width(), gap);
} else {
d->dropRect = rect;
}
}
setDirtyRegion(d->dropRect);
}
void UrlItemView::startDrag(Qt::DropActions supportedActions)
{
Q_UNUSED(supportedActions)
//kDebug() << "Starting UrlItemView drag with actions" << supportedActions;
if (!d->watchedIndexForDrag.isValid()) {
return;
}
QMimeData *mimeData = model()->mimeData(selectionModel()->selectedIndexes());
if (!mimeData || mimeData->text().isNull()) {
return;
}
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
QModelIndex idx = selectionModel()->selectedIndexes().first();
QIcon icon = idx.data(Qt::DecorationRole).value<QIcon>();
d->draggedRow = idx.row();
drag->setPixmap(icon.pixmap(IconSize(KIconLoader::Desktop)));
d->dropRect = QRect();
drag->exec(Qt::CopyAction|Qt::MoveAction|Qt::LinkAction);
}
void UrlItemView::dropEvent(QDropEvent *event)
{
if (!d->dragging) {
return;
}
// This special code is necessary in order to be able to
// inidcate to the model WHERE the item should be dropped,
// Since the model cannot tell where the user dropped the
// item. The model itself handles the actual moving of the
// item.
if (dragDropMode() == QAbstractItemView::DragDrop) {
int row;
QPoint pos = event->pos();
QModelIndex parent = indexAt(pos);
if (!parent.isValid()) {
return;
}
const QRect rect = visualRect(parent);
row = parent.row();
if(d->insertBelow(rect, pos) && d->draggedRow > row) {
row++;
} else if(d->insertAbove(rect, pos) && d->draggedRow < row) {
row--;
}
model()->dropMimeData(event->mimeData(), event->dropAction(),
row, 0, parent);
d->dragging = false;
event->accept();
}
}
void UrlItemView::leaveEvent(QEvent *event)
{
Q_UNUSED(event)
//kDebug() << "UrlItemView leave event";
d->hoveredIndex = QModelIndex();
setCurrentIndex(QModelIndex());
}
ItemStateProvider *UrlItemView::itemStateProvider() const
{
return d->itemStateProvider;
}
#include "moc_urlitemview.cpp"

View file

@ -1,87 +0,0 @@
/*
Copyright 2007 Robert Knight <robertknight@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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.
*/
#ifndef URLITEMVIEW_H
#define URLITEMVIEW_H
// Qt
#include <QAbstractItemView>
namespace Kickoff
{
class ItemStateProvider;
class UrlItemView : public QAbstractItemView
{
Q_OBJECT
public:
UrlItemView(QWidget *parent = 0);
virtual ~UrlItemView();
void setItemStateProvider(ItemStateProvider *provider);
ItemStateProvider *itemStateProvider() const;
// reimplemented from QAbstractItemView
virtual QModelIndex indexAt(const QPoint& point) const;
virtual void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible);
virtual QRect visualRect(const QModelIndex& index) const;
virtual void setModel(QAbstractItemModel *model);
/* if nothing is selected, the first item is
* @returns whether the selection has changed
*/
bool initializeSelection();
protected:
// reimplemented from QAbstractItemView
virtual int horizontalOffset() const;
virtual bool isIndexHidden(const QModelIndex& index) const;
virtual QModelIndex moveCursor(CursorAction action, Qt::KeyboardModifiers modifiers);
virtual void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags);
virtual int verticalOffset() const;
virtual QRegion visualRegionForSelection(const QItemSelection& selection) const;
virtual void startDrag(Qt::DropActions supportedActions);
virtual void dragEnterEvent(QDragEnterEvent *event);
virtual void dragLeaveEvent(QDragLeaveEvent *event);
virtual void dragMoveEvent(QDragMoveEvent *event);
// reimplemented from QWidget
virtual void paintEvent(QPaintEvent *event);
virtual void resizeEvent(QResizeEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void dropEvent(QDropEvent *event);
virtual void leaveEvent(QEvent *event);
private Q_SLOTS:
// lays out all items in the view and sets the current index to the first
// selectable item
void updateLayout();
private:
class Private;
Private * const d;
};
}
#endif // URLITEMVIEW_H

View file

@ -0,0 +1,25 @@
project(plasma-applet-launcher)
set(launcher_SRCS
launcher.cpp
)
kde4_add_plugin(plasma_applet_launcher ${launcher_SRCS})
target_link_libraries(plasma_applet_launcher
KDE4::plasma
KDE4::kio
KDE4::solid
KDE4::kcmutils
${QT_QTNETWORK_LIBRARY}
kworkspace
)
install(
TARGETS plasma_applet_launcher
DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}
)
install(
FILES plasma-applet-launcher.desktop
DESTINATION ${KDE4_SERVICES_INSTALL_DIR}
)

View file

@ -1,3 +1,2 @@
#!/bin/bash
$EXTRACTRC `find . -name \*.ui` >> rc.cpp
$XGETTEXT `find . -name \*.cpp` -o $podir/plasma_applet_launcher.pot

View file

@ -0,0 +1,976 @@
/*
This file is part of the KDE project
Copyright (C) 2024 Ivailo Monev <xakepa10@gmail.com>
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 "launcher.h"
#include "kworkspace/kworkspace.h"
#include "kworkspace/kdisplaymanager.h"
#include <QMutex>
#include <QGraphicsLinearLayout>
#include <QGraphicsGridLayout>
#include <QHostInfo>
#include <QDBusInterface>
#include <KUser>
#include <kdbusconnectionpool.h>
#include <KIcon>
#include <KIconLoader>
#include <KStandardDirs>
#include <KSycoca>
#include <KRun>
#include <KBookmarkManager>
#include <KServiceGroup>
#include <KDirWatch>
#include <KDesktopFile>
#include <KRecentDocument>
#include <Solid/PowerManagement>
#include <Plasma/IconWidget>
#include <Plasma/Separator>
#include <Plasma/Label>
#include <Plasma/LineEdit>
#include <Plasma/TabBar>
#include <Plasma/ScrollWidget>
#include <Plasma/RunnerManager>
#include <KDebug>
static const QString s_defaultpopupicon = QString::fromLatin1("start-here-kde");
static const QSizeF s_minimumsize = QSizeF(450, 350);
static const QString s_firsttimeaddress = QString::fromLatin1("_k_firsttime");
static const QStringList s_firsttimeservices = QStringList()
<< QString::fromLatin1("konsole")
<< QString::fromLatin1("dolphin")
<< QString::fromLatin1("systemsettings");
static QSizeF kIconSize()
{
const int iconsize = KIconLoader::global()->currentSize(KIconLoader::Desktop);
return QSizeF(iconsize, iconsize);
}
// TODO: custom widget to catch hover events from anywhere in the widget rect?
static Plasma::IconWidget* kMakeIconWidget(QGraphicsWidget *parent,
const QSizeF &iconsize,
const QString &text,
const QString &infotext,
const QString &icon,
const QString &url)
{
Plasma::IconWidget* iconwidget = new Plasma::IconWidget(parent);
iconwidget->setOrientation(Qt::Horizontal);
iconwidget->setMinimumIconSize(iconsize);
iconwidget->setMaximumIconSize(iconsize);
iconwidget->setText(text);
iconwidget->setInfoText(infotext);
iconwidget->setIcon(icon);
iconwidget->setProperty("_k_url", url);
return iconwidget;
}
static bool kCanLockScreen()
{
return KDBusConnectionPool::isServiceRegistered("org.freedesktop.ScreenSaver", QDBusConnection::sessionBus());
}
static void kLockScreen()
{
QDBusInterface screensaver(
"org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver",
QDBusConnection::sessionBus()
);
screensaver.call("Lock");
}
class LauncherSearch : public QGraphicsWidget
{
Q_OBJECT
public:
LauncherSearch(QGraphicsWidget *parent);
void prepare();
void query(const QString &text);
void finish();
private Q_SLOTS:
void slotUpdateLayout(const QList<Plasma::QueryMatch> &matches);
void slotActivated();
void slotTriggered();
private:
QMutex m_mutex;
QGraphicsLinearLayout* m_layout;
QList<Plasma::IconWidget*> m_iconwidgets;
Plasma::Label* m_label;
Plasma::RunnerManager* m_runnermanager;
};
LauncherSearch::LauncherSearch(QGraphicsWidget *parent)
: QGraphicsWidget(parent),
m_layout(nullptr),
m_label(nullptr),
m_runnermanager(nullptr)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_layout = new QGraphicsLinearLayout(Qt::Vertical, this);
setLayout(m_layout);
m_label = new Plasma::Label(this);
m_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_label->setAlignment(Qt::AlignCenter);
m_label->setText(i18n("No matches found"));
m_layout->addItem(m_label);
m_runnermanager = new Plasma::RunnerManager(this);
connect(
m_runnermanager, SIGNAL(matchesChanged(QList<Plasma::QueryMatch>)),
this, SLOT(slotUpdateLayout(QList<Plasma::QueryMatch>))
);
// TODO: option to disable and enable runners
// m_runnermanager->setAllowedRunners(s_allowedrunners);
}
void LauncherSearch::prepare()
{
// qDebug() << Q_FUNC_INFO;
QMutexLocker locker(&m_mutex);
foreach (Plasma::IconWidget* iconwidget, m_iconwidgets) {
m_layout->removeItem(iconwidget);
}
qDeleteAll(m_iconwidgets);
m_iconwidgets.clear();
m_label->setVisible(true);
adjustSize();
m_runnermanager->reset();
}
void LauncherSearch::query(const QString &text)
{
// qDebug() << Q_FUNC_INFO << text;
m_runnermanager->launchQuery(text);
}
void LauncherSearch::finish()
{
// qDebug() << Q_FUNC_INFO;
m_runnermanager->matchSessionComplete();
}
void LauncherSearch::slotUpdateLayout(const QList<Plasma::QueryMatch> &matches)
{
// qDebug() << Q_FUNC_INFO;
QMutexLocker locker(&m_mutex);
m_label->setVisible(false);
adjustSize();
const QSizeF iconsize = kIconSize();
foreach (const Plasma::QueryMatch &match, matches) {
// qDebug() << Q_FUNC_INFO << match.text() << match.subtext();
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, match.text(), match.subtext(), QString(), match.id()
);
iconwidget->setIcon(match.icon());
if (match.type() == Plasma::QueryMatch::InformationalMatch) {
iconwidget->setAcceptHoverEvents(false);
iconwidget->setAcceptedMouseButtons(Qt::NoButton);
}
int counter = 1;
if (match.hasConfigurationInterface()) {
// TODO: action is set but where is it?
QAction* matchconfigaction = new QAction(iconwidget);
matchconfigaction->setText(i18n("Configure"));
matchconfigaction->setIcon(KIcon("system-preferences"));
matchconfigaction->setProperty("_k_id", match.id());
connect(
matchconfigaction, SIGNAL(triggered()),
this, SLOT(slotTriggered())
);
iconwidget->addIconAction(matchconfigaction);
counter++;
qDebug() << Q_FUNC_INFO << match.id();
}
foreach (QAction* action, m_runnermanager->actionsForMatch(match)) {
iconwidget->addIconAction(action);
if (counter >= 4) {
// the limit of Plasma::IconWidget
break;
}
counter++;
}
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
}
}
void LauncherSearch::slotActivated()
{
Plasma::IconWidget* iconwidget = qobject_cast<Plasma::IconWidget*>(sender());
const QString iconwidgeturl = iconwidget->property("_k_url").toString();
m_runnermanager->run(iconwidgeturl);
}
void LauncherSearch::slotTriggered()
{
QAction* matchconfigaction = qobject_cast<QAction*>(sender());
const QString matchconfigid = matchconfigaction->property("_k_id").toString();
Plasma::AbstractRunner* runner = m_runnermanager->runner(m_runnermanager->runnerName(matchconfigid));
if (!runner) {
kWarning() << "no runner for" << matchconfigid;
return;
}
// TODO: implement
qDebug() << Q_FUNC_INFO << matchconfigid;
}
class LauncherFavorites : public QGraphicsWidget
{
Q_OBJECT
public:
LauncherFavorites(QGraphicsWidget *parent);
private Q_SLOTS:
void slotUpdateLayout();
void slotActivated();
private:
QMutex m_mutex;
QGraphicsLinearLayout* m_layout;
QList<Plasma::IconWidget*> m_iconwidgets;
KBookmarkManager* m_bookmarkmanager;
};
// TODO: context menu to remove
LauncherFavorites::LauncherFavorites(QGraphicsWidget *parent)
: QGraphicsWidget(parent),
m_layout(nullptr),
m_bookmarkmanager(nullptr)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_layout = new QGraphicsLinearLayout(Qt::Vertical, this);
setLayout(m_layout);
const QString bookmarfile = KStandardDirs::locateLocal("data", "plasma/bookmarks.xml");
m_bookmarkmanager = KBookmarkManager::managerForFile(bookmarfile, "launcher");
// m_bookmarkmanager->slotEditBookmarks();
slotUpdateLayout();
connect(
m_bookmarkmanager, SIGNAL(changed(QString,QString)),
this, SLOT(slotUpdateLayout())
);
connect(
KSycoca::self(), SIGNAL(databaseChanged(QStringList)),
this, SLOT(slotUpdateLayout())
);
}
void LauncherFavorites::slotUpdateLayout()
{
QMutexLocker locker(&m_mutex);
foreach (Plasma::IconWidget* iconwidget, m_iconwidgets) {
m_layout->removeItem(iconwidget);
}
qDeleteAll(m_iconwidgets);
m_iconwidgets.clear();
adjustSize();
bool isfirsttime = true;
KBookmarkGroup bookmarkgroup = m_bookmarkmanager->root();
// first time gets a special treatment
KBookmark bookmark = bookmarkgroup.first();
while (!bookmark.isNull()) {
if (bookmark.url().url() == s_firsttimeaddress) {
isfirsttime = false;
break;
}
bookmark = bookmarkgroup.next(bookmark);
}
if (isfirsttime) {
bookmark = bookmarkgroup.createNewSeparator();
bookmark.setUrl(s_firsttimeaddress);
bookmark.setDescription("internal bookmark");
foreach (const QString &name, s_firsttimeservices) {
KService::Ptr service = KService::serviceByDesktopName(name);
if (!service.isNull()) {
bookmarkgroup.addBookmark(service->desktopEntryName(), KUrl(service->entryPath()), service->icon());
} else {
kWarning() << "invalid first-time serivce" << name;
}
}
m_bookmarkmanager->emitChanged(bookmarkgroup);
}
const QSizeF iconsize = kIconSize();
bookmark = bookmarkgroup.first();
while (!bookmark.isNull()) {
// qDebug() << Q_FUNC_INFO << bookmark.text() << bookmark.description() << bookmark.icon() << bookmark.url();
if (bookmark.isSeparator()) {
bookmark = bookmarkgroup.next(bookmark);
continue;
}
const QString serviceentrypath = bookmark.url().url();
KService::Ptr service = KService::serviceByDesktopPath(serviceentrypath);
if (service.isNull()) {
service = KService::serviceByDesktopName(bookmark.text());
}
if (service.isNull()) {
kWarning() << "could not find service for" << serviceentrypath;
bookmark = bookmarkgroup.next(bookmark);
continue;
}
// qDebug() << Q_FUNC_INFO << service->entryPath() << service->name() << service->comment();
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, service->name(), service->genericName(), service->icon(), service->entryPath()
);
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
bookmark = bookmarkgroup.next(bookmark);
}
}
void LauncherFavorites::slotActivated()
{
Plasma::IconWidget* iconwidget = qobject_cast<Plasma::IconWidget*>(sender());
const QString iconwidgeturl = iconwidget->property("_k_url").toString();
(void)new KRun(KUrl(iconwidgeturl), nullptr);
}
class LauncherServiceWidget : public QGraphicsWidget
{
Q_OBJECT
public:
LauncherServiceWidget(QGraphicsWidget *parent, Plasma::TabBar *tabbar, const int tabindex);
void appendGroup(Plasma::IconWidget* iconwidget, LauncherServiceWidget* servicewidget);
void appendApp(Plasma::IconWidget* iconwidget);
int serviceCount() const;
int tabIndex() const;
public Q_SLOTS:
void slotGroupActivated();
void slotAppActivated();
private:
Plasma::TabBar* m_tabbar;
int m_tabindex;
QGraphicsLinearLayout* m_layout;
QList<Plasma::IconWidget*> m_iconwidgets;
};
LauncherServiceWidget::LauncherServiceWidget(QGraphicsWidget *parent, Plasma::TabBar *tabbar, const int tabindex)
: QGraphicsWidget(parent),
m_tabbar(tabbar),
m_tabindex(tabindex),
m_layout(nullptr)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_layout = new QGraphicsLinearLayout(Qt::Vertical, this);
setLayout(m_layout);
}
int LauncherServiceWidget::serviceCount() const
{
return m_iconwidgets.size();
}
int LauncherServiceWidget::tabIndex() const
{
return m_tabindex;
}
void LauncherServiceWidget::appendGroup(Plasma::IconWidget* iconwidget, LauncherServiceWidget* servicewidget)
{
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
servicewidget, SLOT(slotGroupActivated())
);
}
void LauncherServiceWidget::appendApp(Plasma::IconWidget* iconwidget)
{
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotAppActivated())
);
}
void LauncherServiceWidget::slotGroupActivated()
{
m_tabbar->setCurrentIndex(m_tabindex);
}
void LauncherServiceWidget::slotAppActivated()
{
Plasma::IconWidget* iconwidget = qobject_cast<Plasma::IconWidget*>(sender());
const QString iconwidgeturl = iconwidget->property("_k_url").toString();
(void)new KRun(KUrl(iconwidgeturl), nullptr);
}
class LauncherApplications : public Plasma::TabBar
{
Q_OBJECT
public:
LauncherApplications(QGraphicsWidget *parent);
private Q_SLOTS:
void slotUpdateLayout();
private:
void addGroup(LauncherServiceWidget *servicewidget, KServiceGroup::Ptr group);
QMutex m_mutex;
Plasma::ScrollWidget* m_rootscrollwidget;
LauncherServiceWidget* m_root;
QList<Plasma::ScrollWidget*> m_tabscrollwidgets;
QList<LauncherServiceWidget*> m_tabwidgets;
};
LauncherApplications::LauncherApplications(QGraphicsWidget *parent)
: Plasma::TabBar(parent),
m_rootscrollwidget(nullptr),
m_root(nullptr)
{
// TODO: navigation bar instead
// setTabBarShown(false);
slotUpdateLayout();
connect(
KSycoca::self(), SIGNAL(databaseChanged(QStringList)),
this, SLOT(slotUpdateLayout())
);
}
void LauncherApplications::slotUpdateLayout()
{
QMutexLocker locker(&m_mutex);
qDeleteAll(m_tabwidgets);
m_tabwidgets.clear();
delete m_root;
m_root = nullptr;
delete m_rootscrollwidget;
m_rootscrollwidget = nullptr;
m_rootscrollwidget = new Plasma::ScrollWidget(this);
m_rootscrollwidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_root = new LauncherServiceWidget(m_rootscrollwidget, this, 0);
m_rootscrollwidget->setWidget(m_root);
addTab(KIcon("applications-other"), "root", m_rootscrollwidget);
KServiceGroup::Ptr group = KServiceGroup::root();
if (group && group->isValid()) {
addGroup(m_root, group);
}
}
void LauncherApplications::addGroup(LauncherServiceWidget *servicewidget, KServiceGroup::Ptr group)
{
const QString name = group->name();
if (name.isEmpty() || group->noDisplay()) {
kDebug() << "hidden or invalid group" << name;
return;
}
const QSizeF iconsize = kIconSize();
foreach (const KServiceGroup::Ptr subgroup, group->groupEntries(KServiceGroup::NoOptions)) {
Plasma::ScrollWidget* scrollwidget = new Plasma::ScrollWidget(this);
scrollwidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
LauncherServiceWidget* subgroupwidget = new LauncherServiceWidget(scrollwidget, this, servicewidget->tabIndex() + 1);
addGroup(subgroupwidget, subgroup);
if (subgroupwidget->serviceCount() < 1) {
delete subgroupwidget;
delete scrollwidget;
} else {
scrollwidget->setWidget(subgroupwidget);
m_tabscrollwidgets.append(scrollwidget);
Plasma::IconWidget* subgroupiconwidget = kMakeIconWidget(
servicewidget,
iconsize, subgroup->caption(), subgroup->comment(), subgroup->icon(), QString()
);
servicewidget->appendGroup(subgroupiconwidget, subgroupwidget);
addTab(KIcon("applications-other"), subgroup->caption(), scrollwidget);
}
}
foreach (const KService::Ptr app, group->serviceEntries(KServiceGroup::NoOptions)) {
if (app->noDisplay()) {
kDebug() << "hidden entry" << app->name();
continue;
}
Plasma::IconWidget* appiconwidget = kMakeIconWidget(
servicewidget,
iconsize, app->name(), app->comment(), app->icon(), app->entryPath()
);
servicewidget->appendApp(appiconwidget);
}
}
class LauncherRecent : public QGraphicsWidget
{
Q_OBJECT
public:
LauncherRecent(QGraphicsWidget *parent);
private Q_SLOTS:
void slotUpdateLayout();
void slotActivated();
private:
QMutex m_mutex;
QGraphicsLinearLayout* m_layout;
QList<Plasma::IconWidget*> m_iconwidgets;
KDirWatch* m_dirwatch;
};
LauncherRecent::LauncherRecent(QGraphicsWidget *parent)
: QGraphicsWidget(parent),
m_layout(nullptr),
m_dirwatch(nullptr)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_layout = new QGraphicsLinearLayout(Qt::Vertical, this);
setLayout(m_layout);
m_dirwatch = new KDirWatch(this);
m_dirwatch->addDir(KRecentDocument::recentDocumentDirectory());
slotUpdateLayout();
connect(
m_dirwatch, SIGNAL(dirty(QString)),
this, SLOT(slotUpdateLayout())
);
}
void LauncherRecent::slotUpdateLayout()
{
QMutexLocker locker(&m_mutex);
foreach (Plasma::IconWidget* iconwidget, m_iconwidgets) {
m_layout->removeItem(iconwidget);
}
qDeleteAll(m_iconwidgets);
m_iconwidgets.clear();
adjustSize();
const QSizeF iconsize = kIconSize();
foreach (const QString &recent, KRecentDocument::recentDocuments()) {
KDesktopFile recentfile(recent);
// qDebug() << Q_FUNC_INFO << recentfile.readName() << recentfile.readComment();
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, recentfile.readName(), recentfile.readComment(), recentfile.readIcon(), recentfile.readUrl()
);
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
}
}
void LauncherRecent::slotActivated()
{
Plasma::IconWidget* iconwidget = qobject_cast<Plasma::IconWidget*>(sender());
const QString iconwidgeturl = iconwidget->property("_k_url").toString();
(void)new KRun(KUrl(iconwidgeturl), nullptr);
}
class LauncherLeave : public QGraphicsWidget
{
Q_OBJECT
public:
LauncherLeave(QGraphicsWidget *parent);
private Q_SLOTS:
void slotUpdateLayout();
void slotActivated();
private:
QMutex m_mutex;
QGraphicsLinearLayout* m_layout;
QList<Plasma::IconWidget*> m_iconwidgets;
Plasma::Separator* m_systemseparator;
};
// TODO: check for lock and switchable state changes
LauncherLeave::LauncherLeave(QGraphicsWidget *parent)
: QGraphicsWidget(parent),
m_systemseparator(nullptr)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_layout = new QGraphicsLinearLayout(Qt::Vertical, this);
setLayout(m_layout);
slotUpdateLayout();
connect(
Solid::PowerManagement::notifier(), SIGNAL(supportedSleepStatesChanged()),
this, SLOT(slotUpdateLayout())
);
}
void LauncherLeave::slotUpdateLayout()
{
QMutexLocker locker(&m_mutex);
foreach (Plasma::IconWidget* iconwidget, m_iconwidgets) {
m_layout->removeItem(iconwidget);
}
qDeleteAll(m_iconwidgets);
m_iconwidgets.clear();
if (m_systemseparator) {
m_layout->removeItem(m_systemseparator);
delete m_systemseparator;
m_systemseparator = nullptr;
}
adjustSize();
const QSizeF iconsize = kIconSize();
bool hassessionicon = false;
if (kCanLockScreen()) {
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, i18n("Lock"), i18n("Lock screen"), "system-lock-screen", "lock"
);
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
hassessionicon = true;
}
if (KDisplayManager().isSwitchable()) {
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, i18n("Switch user"), i18n("Start a parallel session as a different user"), "system-switch-user", "switch"
);
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
hassessionicon = true;
}
if (hassessionicon) {
m_systemseparator = new Plasma::Separator(this);
m_systemseparator->setOrientation(Qt::Horizontal);
m_layout->addItem(m_systemseparator);
}
const QSet<Solid::PowerManagement::SleepState> sleepsates = Solid::PowerManagement::supportedSleepStates();
if (sleepsates.contains(Solid::PowerManagement::SuspendState)) {
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, i18n("Sleep"), i18n("Suspend to RAM"), "system-suspend", "suspendram"
);
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
}
if (sleepsates.contains(Solid::PowerManagement::HibernateState)) {
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, i18n("Hibernate"), i18n("Suspend to disk"), "system-suspend-hibernate", "suspenddisk"
);
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
}
if (sleepsates.contains(Solid::PowerManagement::HybridSuspendState)) {
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, i18n("Hybrid Suspend"), i18n("Hybrid Suspend"), "system-suspend", "suspendhybrid"
);
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
}
if (KWorkSpace::canShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeReboot)) {
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, i18nc("Restart computer", "Restart"), i18n("Restart computer"), "system-reboot", "restart"
);
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
}
if (KWorkSpace::canShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeHalt)) {
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, i18n("Shut down"), i18n("Turn off computer"), "system-shutdown", "shutdown"
);
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
}
Plasma::IconWidget* iconwidget = kMakeIconWidget(
this,
iconsize, i18n("Log out"), i18n("End session"), "system-log-out", "logout"
);
m_iconwidgets.append(iconwidget);
m_layout->addItem(iconwidget);
connect(
iconwidget, SIGNAL(activated()),
this, SLOT(slotActivated())
);
}
void LauncherLeave::slotActivated()
{
Plasma::IconWidget* iconwidget = qobject_cast<Plasma::IconWidget*>(sender());
const QString iconwidgeturl = iconwidget->property("_k_url").toString();
if (iconwidgeturl == QLatin1String("lock")) {
kLockScreen();
} else if (iconwidgeturl == QLatin1String("switch")) {
kLockScreen();
KDisplayManager().newSession();
} else if (iconwidgeturl == QLatin1String("suspendram")) {
Solid::PowerManagement::requestSleep(Solid::PowerManagement::SuspendState);
} else if (iconwidgeturl == QLatin1String("suspenddisk")) {
Solid::PowerManagement::requestSleep(Solid::PowerManagement::HibernateState);
} else if (iconwidgeturl == QLatin1String("suspendhybrid")) {
Solid::PowerManagement::requestSleep(Solid::PowerManagement::HybridSuspendState);
} else if (iconwidgeturl == QLatin1String("restart")) {
KWorkSpace::requestShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeReboot);
} else if (iconwidgeturl == QLatin1String("shutdown")) {
KWorkSpace::requestShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeHalt);
} else if (iconwidgeturl == QLatin1String("logout")) {
KWorkSpace::requestShutDown(KWorkSpace::ShutdownConfirmDefault, KWorkSpace::ShutdownTypeNone);
} else {
Q_ASSERT(false);
kWarning() << "invalid url" << iconwidgeturl;
}
}
class LauncherAppletWidget : public QGraphicsWidget
{
Q_OBJECT
public:
LauncherAppletWidget(LauncherApplet* auncherapplet);
private Q_SLOTS:
void slotSearch(const QString &text);
void slotTimeout();
private:
LauncherApplet* m_launcherapplet;
QGraphicsGridLayout* m_layout;
Plasma::IconWidget* m_iconwidget;
Plasma::Label* m_label;
Plasma::LineEdit* m_lineedit;
Plasma::TabBar* m_tabbar;
Plasma::ScrollWidget* m_favoritesscrollwidget;
LauncherFavorites* m_favoriteswidget;
LauncherApplications* m_applicationswidget;
Plasma::ScrollWidget* m_recentscrollwidget;
LauncherRecent* m_recentwidget;
Plasma::ScrollWidget* m_leavecrollwidget;
LauncherLeave* m_leavewidget;
Plasma::ScrollWidget* m_searchscrollwidget;
LauncherSearch* m_searchwidget;
QTimer* m_timer;
};
LauncherAppletWidget::LauncherAppletWidget(LauncherApplet* auncherapplet)
: QGraphicsWidget(auncherapplet),
m_launcherapplet(auncherapplet),
m_layout(nullptr),
m_iconwidget(nullptr),
m_label(nullptr),
m_lineedit(nullptr),
m_tabbar(nullptr),
m_favoritesscrollwidget(nullptr),
m_favoriteswidget(nullptr),
m_applicationswidget(nullptr),
m_recentscrollwidget(nullptr),
m_recentwidget(nullptr),
m_leavecrollwidget(nullptr),
m_leavewidget(nullptr),
m_searchscrollwidget(nullptr),
m_searchwidget(nullptr),
m_timer(nullptr)
{
m_layout = new QGraphicsGridLayout(this);
const QString hostname = QHostInfo::localHostName();
KUser user(KUser::UseEffectiveUID);
QString usericon = user.faceIconPath();
if (usericon.isEmpty()) {
usericon = QLatin1String("system-search");
}
m_iconwidget = new Plasma::IconWidget(this);
const int iconsize = KIconLoader::global()->currentSize(KIconLoader::Toolbar);
const QSizeF iconsizef = QSizeF(iconsize, iconsize);
m_iconwidget->setMinimumIconSize(iconsizef);
m_iconwidget->setMaximumIconSize(iconsizef);
m_iconwidget->setAcceptHoverEvents(false);
m_iconwidget->setAcceptedMouseButtons(Qt::NoButton);
m_iconwidget->setIcon(usericon);
m_layout->addItem(m_iconwidget, 0, 0);
QString usertext;
QString fullusername = user.property(KUser::FullName);
if (fullusername.isEmpty()) {
usertext = i18nc("login name, hostname", "User <b>%1</b> on <b>%2</b>", user.loginName(), hostname);
} else {
usertext = i18nc("full name, login name, hostname", "<b>%1 (%2)</b> on <b>%3</b>", fullusername, user.loginName(), hostname);
}
m_label = new Plasma::Label(this);
m_label->setWordWrap(false);
m_label->setText(usertext);
m_layout->addItem(m_label, 0, 1);
m_lineedit = new Plasma::LineEdit(this);
m_lineedit->setClickMessage(i18n("Search"));
m_layout->addItem(m_lineedit, 0, 2);
setFocusProxy(m_lineedit);
m_tabbar = new Plasma::TabBar(this);
// has not effect..
// m_tabbar->nativeWidget()->setShape(QTabBar::RoundedSouth);
m_favoritesscrollwidget = new Plasma::ScrollWidget(m_tabbar);
m_favoritesscrollwidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_favoritesscrollwidget->setMinimumSize(s_minimumsize);
m_favoriteswidget = new LauncherFavorites(m_favoritesscrollwidget);
m_favoritesscrollwidget->setWidget(m_favoriteswidget);
m_tabbar->addTab(KIcon("bookmarks"), i18n("Favorites"), m_favoritesscrollwidget);
m_applicationswidget = new LauncherApplications(m_tabbar);
m_applicationswidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_applicationswidget->setMinimumSize(s_minimumsize);
m_tabbar->addTab(KIcon("applications-other"), i18n("Applications"), m_applicationswidget);
m_recentscrollwidget = new Plasma::ScrollWidget(m_tabbar);
m_recentscrollwidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_recentscrollwidget->setMinimumSize(s_minimumsize);
m_recentwidget = new LauncherRecent(m_recentscrollwidget);
m_recentscrollwidget->setWidget(m_recentwidget);
m_tabbar->addTab(KIcon("document-open-recent"), i18n("Recently User"), m_recentscrollwidget);
m_leavecrollwidget = new Plasma::ScrollWidget(m_tabbar);
m_leavecrollwidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_leavecrollwidget->setMinimumSize(s_minimumsize);
m_leavewidget = new LauncherLeave(m_leavecrollwidget);
m_leavecrollwidget->setWidget(m_leavewidget);
m_tabbar->addTab(KIcon("system-shutdown"), i18n("Leave"), m_leavecrollwidget);
m_layout->addItem(m_tabbar, 1, 0, 1, 3);
m_searchscrollwidget = new Plasma::ScrollWidget(this);
m_searchscrollwidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_searchscrollwidget->setMinimumSize(s_minimumsize);
m_searchscrollwidget->setVisible(false);
m_searchwidget = new LauncherSearch(m_searchscrollwidget);
m_searchscrollwidget->setWidget(m_searchwidget);
m_layout->addItem(m_searchscrollwidget, 2, 0, 1, 3);
connect(
m_lineedit, SIGNAL(textChanged(QString)),
this, SLOT(slotSearch(QString))
);
m_timer = new QTimer(this);
m_timer->setSingleShot(true);
m_timer->setInterval(500);
connect(
m_timer, SIGNAL(timeout()),
this, SLOT(slotTimeout())
);
setLayout(m_layout);
m_layout->setColumnStretchFactor(2, 100);
}
void LauncherAppletWidget::slotSearch(const QString &text)
{
const QString query = text.trimmed();
if (query.isEmpty()) {
m_timer->stop();
m_searchwidget->finish();
m_searchscrollwidget->setVisible(false);
m_tabbar->setVisible(true);
return;
}
m_searchwidget->prepare();
m_timer->start();
}
void LauncherAppletWidget::slotTimeout()
{
m_searchwidget->query(m_lineedit->text());
m_tabbar->setVisible(false);
m_searchscrollwidget->setVisible(true);
}
LauncherApplet::LauncherApplet(QObject *parent, const QVariantList &args)
: Plasma::PopupApplet(parent, args),
m_launcherwidget(nullptr)
{
KGlobal::locale()->insertCatalog("plasma_applet_launcher");
setPopupIcon(s_defaultpopupicon);
setAspectRatioMode(Plasma::IgnoreAspectRatio);
m_launcherwidget = new LauncherAppletWidget(this);
}
QGraphicsWidget *LauncherApplet::graphicsWidget()
{
return m_launcherwidget;
}
void LauncherApplet::popupEvent(bool show)
{
if (show) {
m_launcherwidget->setFocus();
}
}
#include "launcher.moc"
#include "moc_launcher.cpp"

View file

@ -1,10 +1,10 @@
/*
Copyright 2008 Andrew Lake <jamboarder@yahoo.com>
This file is part of the KDE project
Copyright (C) 2024 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
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
@ -17,22 +17,30 @@
Boston, MA 02110-1301, USA.
*/
#ifndef CONTENTAREACAP_H
#define CONTENTAREACAP_H
#ifndef LAUNCHER_H
#define LAUNCHER_H
#include <QWidget>
#include <Plasma/PopupApplet>
class ContentAreaCap: public QWidget
class LauncherAppletWidget;
class LauncherApplet : public Plasma::PopupApplet
{
Q_OBJECT
public:
ContentAreaCap(QWidget *parent, bool flip = false);
LauncherApplet(QObject *parent, const QVariantList &args);
// Plasma::PopupApplet reimplementation
QGraphicsWidget* graphicsWidget() final;
protected:
virtual void paintEvent(QPaintEvent *event);
bool flipCap;
void popupEvent(bool show) final;
private:
friend LauncherAppletWidget;
LauncherAppletWidget *m_launcherwidget;
};
#endif
K_EXPORT_PLASMA_APPLET(launcher, LauncherApplet)
#endif // LAUNCHER_H

View file

@ -169,10 +169,10 @@ Type=Service
X-KDE-ServiceTypes=Plasma/Applet
X-KDE-Library=plasma_applet_launcher
X-KDE-PluginInfo-Author=Robert Knight
X-KDE-PluginInfo-Email=robertknight@gmail.com
X-KDE-PluginInfo-Author=Ivailo Monev
X-KDE-PluginInfo-Email=xakepa10@gmail.com
X-KDE-PluginInfo-Name=launcher
X-KDE-PluginInfo-Version=1.0
X-KDE-PluginInfo-Version=2.0
X-KDE-PluginInfo-Website=
X-KDE-PluginInfo-Category=Application Launchers
X-KDE-PluginInfo-Depends=