2 Copyright 2007 Robert Knight <robertknight@gmail.com>
3 Copyright 2008 Sebastian Sauer <mail@dipe.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 #include "simpleapplet/simpleapplet.h"
23 #include "simpleapplet/menuview.h"
26 #include <QtGui/QLabel>
27 #include <QtGui/QComboBox>
28 #include <QtGui/QSpinBox>
29 #include <QtGui/QGridLayout>
30 #include <QtGui/QGraphicsView>
31 #include <QtCore/QMetaObject>
32 #include <QtCore/QMetaEnum>
33 #include <QtCore/QPointer>
34 #include <QtGui/QGraphicsLinearLayout>
35 #include <QtGui/QSpacerItem>
39 #include <KConfigDialog>
42 #include <KActionCollection>
43 #include <KBookmarkMenu>
47 #include <Plasma/IconWidget>
48 #include <Plasma/Containment>
49 #include <Plasma/ToolTipManager>
52 #include "core/itemhandlers.h"
53 #include "core/models.h"
54 #include "core/applicationmodel.h"
55 #include "core/favoritesmodel.h"
56 #include "core/systemmodel.h"
57 #include "core/recentlyusedmodel.h"
58 #include "core/recentapplications.h"
59 #include "core/leavemodel.h"
60 #include "core/urlitemlauncher.h"
62 class BookmarkOwner
: public KBookmarkOwner
65 BookmarkOwner() : KBookmarkOwner() {}
66 virtual bool enableOption(BookmarkOption
) const {
69 virtual bool supportsTabs() const {
72 virtual void openBookmark(const KBookmark
& b
, Qt::MouseButtons
, Qt::KeyboardModifiers
) {
73 new KRun(b
.url(), (QWidget
*)0);
77 /// @internal d-pointer class
78 class MenuLauncherApplet::Private
81 QPointer
<Kickoff::MenuView
> menuview
;
82 Plasma::IconWidget
*icon
;
83 QPointer
<Kickoff::UrlItemLauncher
> launcher
;
85 KActionCollection
* collection
;
86 BookmarkOwner
* bookmarkowner
;
87 KBookmarkMenu
* bookmarkmenu
;
89 MenuLauncherApplet::ViewType viewtype
;
90 MenuLauncherApplet::FormatType formattype
;
93 QComboBox
*viewComboBox
;
94 QComboBox
*formatComboBox
;
95 QSpinBox
*recentApplicationsSpinBox
;
97 QList
<QAction
*> actions
;
112 delete bookmarkowner
;
116 void setMaxRecentApps(int num
) {
117 maxRecentApps
= qMax(0, num
);
118 if (maxRecentApps
> Kickoff::RecentApplications::self()->maximum()) {
119 Kickoff::RecentApplications::self()->setMaximum(maxRecentApps
);
123 void addItem(QComboBox
* combo
, const QString
& caption
, int index
, const QString
& icon
= QString()) {
124 if (icon
.isEmpty()) {
125 combo
->addItem(caption
, index
);
127 combo
->addItem(KIcon(icon
), caption
, index
);
131 void setCurrentItem(QComboBox
* combo
, int currentIndex
) {
132 for (int i
= combo
->count() - 1; i
>= 0; --i
) {
133 if (combo
->itemData(i
).toInt() == currentIndex
) {
134 combo
->setCurrentIndex(i
);
138 if (combo
->count() > 0) {
139 combo
->setCurrentIndex(0);
143 Kickoff::MenuView
*createMenuView(QAbstractItemModel
*model
= 0) {
144 Kickoff::MenuView
*view
= new Kickoff::MenuView(menuview
);
145 view
->setFormatType((Kickoff::MenuView::FormatType
) formattype
);
147 view
->setModel(model
);
152 void addMenu(Kickoff::MenuView
*view
, bool mergeFirstLevel
) {
153 QList
<QAction
*> actions
= view
->actions();
154 foreach(QAction
*action
, actions
) {
155 if (action
->menu() && mergeFirstLevel
) {
156 QMetaObject::invokeMethod(action
->menu(), "aboutToShow"); //fetch the children
157 if (actions
.count() > 1 && action
->menu()->actions().count() > 0) {
158 menuview
->addTitle(action
->text());
160 foreach(QAction
*a
, action
->menu()->actions()) {
161 a
->setVisible(a
->menu() || ! view
->indexForAction(a
).data(Kickoff::UrlRole
).isNull());
162 menuview
->addAction(a
);
165 action
->setVisible(action
->menu() || ! view
->indexForAction(action
).data(Kickoff::UrlRole
).isNull());
166 menuview
->addAction(action
);
170 // if the model asks us for a reset we can't do much except to invalidate our
171 // menuview to be able to rebuild it what is needed to prevent dealing with
173 // the problem here is, that if the menu is currently displayed, it will just
174 // close itself what is evil++ but still better than crashes. anyway, the
175 // right(TM) solution would be to introduce logic to update the content of the
176 // menu even on a reset.
177 connect(view
->model(), SIGNAL(modelReset()), menuview
, SLOT(deleteLater()));
183 return "start-here-kde";
187 return "folder-bookmarks";
189 return "applications-other";
193 return "document-open-recent";
195 return "application-exit";
203 MenuLauncherApplet::MenuLauncherApplet(QObject
*parent
, const QVariantList
&args
)
204 : Plasma::Applet(parent
, args
),
207 KGlobal::locale()->insertCatalog("plasma_applet_launcher");
209 setHasConfigurationInterface(true);
210 setBackgroundHints(NoBackground
);
212 resize(IconSize(KIconLoader::Desktop
) * 2, IconSize(KIconLoader::Desktop
) * 2);
214 d
->icon
= new Plasma::IconWidget(QString(), this);
215 d
->icon
->setFlag(ItemIsMovable
, false);
216 connect(d
->icon
, SIGNAL(pressed(bool)), this, SLOT(toggleMenu(bool)));
217 connect(this, SIGNAL(activate()), this, SLOT(toggleMenu()));
219 d
->viewtype
= Combined
;
220 d
->formattype
= NameDescription
;
223 MenuLauncherApplet::~MenuLauncherApplet()
228 void MenuLauncherApplet::init()
230 QGraphicsLinearLayout
*layout
= new QGraphicsLinearLayout(this);
231 layout
->setContentsMargins(0, 0, 0, 0);
232 layout
->setSpacing(0);
234 layout
->addItem(d
->icon
);
236 KConfigGroup cg
= config();
238 QMetaEnum vte
= metaObject()->enumerator(metaObject()->indexOfEnumerator("ViewType"));
239 QByteArray vtb
= cg
.readEntry("view", QByteArray(vte
.valueToKey(d
->viewtype
)));
240 d
->viewtype
= (MenuLauncherApplet::ViewType
) vte
.keyToValue(vtb
);
242 QMetaEnum fte
= metaObject()->enumerator(metaObject()->indexOfEnumerator("FormatType"));
243 QByteArray ftb
= cg
.readEntry("format", QByteArray(fte
.valueToKey(d
->formattype
)));
244 d
->formattype
= (MenuLauncherApplet::FormatType
) fte
.keyToValue(ftb
);
246 d
->setMaxRecentApps(cg
.readEntry("maxRecentApps", qMin(5, Kickoff::RecentApplications::self()->maximum())));
248 d
->icon
->setIcon(KIcon(d
->viewIcon()));
249 //d->icon->setIcon(KIcon(cg.readEntry("icon","start-here-kde")));
250 //setMinimumContentSize(d->icon->iconSize()); //setSize(d->icon->iconSize())
252 setAspectRatioMode(Plasma::ConstrainedSquare
);
254 Kickoff::UrlItemLauncher::addGlobalHandler(Kickoff::UrlItemLauncher::ExtensionHandler
, "desktop", new Kickoff::ServiceItemHandler
);
255 Kickoff::UrlItemLauncher::addGlobalHandler(Kickoff::UrlItemLauncher::ProtocolHandler
, "leave", new Kickoff::LeaveItemHandler
);
257 if (KService::serviceByStorageId("kde4-kmenuedit.desktop")) {
258 QAction
* menueditor
= new QAction(i18n("Menu Editor"), this);
259 d
->actions
.append(menueditor
);
260 connect(menueditor
, SIGNAL(triggered(bool)), this, SLOT(startMenuEditor()));
263 Q_ASSERT(! d
->switcher
);
264 d
->switcher
= new QAction(i18n("Switch to Kickoff Menu Style"), this);
265 d
->actions
.append(d
->switcher
);
266 connect(d
->switcher
, SIGNAL(triggered(bool)), this, SLOT(switchMenuStyle()));
268 constraintsEvent(Plasma::ImmutableConstraint
);
271 void MenuLauncherApplet::constraintsEvent(Plasma::Constraints constraints
)
273 setBackgroundHints(NoBackground
);
274 if (constraints
& Plasma::FormFactorConstraint
) {
275 if (formFactor() == Plasma::Planar
||
276 formFactor() == Plasma::MediaCenter
) {
277 //FIXME set correct minimum size
278 //setMinimumContentSize(d->icon->sizeFromIconSize(IconSize(KIconLoader::Desktop)));
280 //setMinimumContentSize(d->icon->sizeFromIconSize(IconSize(KIconLoader::Small)));
284 if ((constraints
& Plasma::ImmutableConstraint
) && d
->switcher
) {
285 d
->switcher
->setVisible(immutability() == Plasma::Mutable
);
289 void MenuLauncherApplet::switchMenuStyle()
292 containment()->addApplet("launcher", QVariantList(), geometry());
297 void MenuLauncherApplet::startMenuEditor()
299 KProcess::execute("kmenuedit");
302 void MenuLauncherApplet::createConfigurationInterface(KConfigDialog
*parent
)
304 QWidget
*p
= new QWidget(parent
);
305 QGridLayout
*l
= new QGridLayout(p
);
308 QLabel
*viewLabel
= new QLabel(i18nc("@label:listbox Which category of items to view in a KMenu-like menu", "View:"), p
);
309 l
->addWidget(viewLabel
, 0, 0, Qt::AlignRight
);
310 d
->viewComboBox
= new QComboBox(p
);
311 viewLabel
->setBuddy(d
->viewComboBox
);
312 d
->addItem(d
->viewComboBox
, i18nc("@item:inlistbox View:", "Standard"), MenuLauncherApplet::Combined
, "start-here-kde");
313 d
->addItem(d
->viewComboBox
, i18nc("@item:inlistbox View:", "Favorites"), MenuLauncherApplet::Favorites
, "bookmarks");
314 d
->addItem(d
->viewComboBox
, i18nc("@item:inlistbox View:", "Bookmarks"), MenuLauncherApplet::Bookmarks
, "folder-bookmarks");
315 d
->addItem(d
->viewComboBox
, i18nc("@item:inlistbox View:", "Applications"), MenuLauncherApplet::Applications
, "applications-other");
316 d
->addItem(d
->viewComboBox
, i18nc("@item:inlistbox View:", "Computer"), MenuLauncherApplet::Computer
, "computer");
317 d
->addItem(d
->viewComboBox
, i18nc("@item:inlistbox View:", "Recently Used"), MenuLauncherApplet::RecentlyUsed
, "document-open-recent");
318 d
->addItem(d
->viewComboBox
, i18nc("@item:inlistbox View:", "Leave"), MenuLauncherApplet::Leave
, "application-exit");
319 l
->addWidget(d
->viewComboBox
, 0, 1);
321 QLabel
*formatLabel
= new QLabel(i18nc("@label:listbox How to present applications in a KMenu-like menu", "Format:"), p
);
322 l
->addWidget(formatLabel
, 1, 0, Qt::AlignRight
);
323 d
->formatComboBox
= new QComboBox(p
);
324 formatLabel
->setBuddy(d
->formatComboBox
);
325 d
->addItem(d
->formatComboBox
, i18nc("@item:inlistbox Format:", "Name Only"), MenuLauncherApplet::Name
);
326 d
->addItem(d
->formatComboBox
, i18nc("@item:inlistbox Format:", "Description Only"), MenuLauncherApplet::Description
);
327 d
->addItem(d
->formatComboBox
, i18nc("@item:inlistbox Format:", "Name Description"), MenuLauncherApplet::NameDescription
);
328 d
->addItem(d
->formatComboBox
, i18nc("@item:inlistbox Format:", "Description (Name)"), MenuLauncherApplet::DescriptionName
);
329 d
->addItem(d
->formatComboBox
, i18nc("@item:inlistbox Format:", "Name - Description"), MenuLauncherApplet::NameDashDescription
);
330 l
->addWidget(d
->formatComboBox
, 1, 1);
332 QLabel
*recentLabel
= new QLabel(i18n("Recent Applications:"), p
);
333 l
->addWidget(recentLabel
, 2, 0, Qt::AlignRight
);
334 d
->recentApplicationsSpinBox
= new QSpinBox(p
);
335 d
->recentApplicationsSpinBox
->setMaximum(10);
336 d
->recentApplicationsSpinBox
->setMinimum(0);
337 d
->recentApplicationsSpinBox
->setValue(d
->maxRecentApps
);
338 recentLabel
->setBuddy(d
->recentApplicationsSpinBox
);
339 l
->addWidget(d
->recentApplicationsSpinBox
, 2, 1);
341 l
->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum
, QSizePolicy::Expanding
), 3, 0, 1, 2);
342 l
->setColumnStretch(1, 1);
344 d
->setCurrentItem(d
->viewComboBox
, d
->viewtype
);
345 d
->setCurrentItem(d
->formatComboBox
, d
->formattype
);
347 connect(parent
, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
348 connect(parent
, SIGNAL(okClicked()), this, SLOT(configAccepted()));
349 parent
->addPage(p
, i18n("General"), icon());
352 void MenuLauncherApplet::configAccepted()
354 bool needssaving
= false;
355 KConfigGroup cg
= config();
357 const int vt
= d
->viewComboBox
->itemData(d
->viewComboBox
->currentIndex()).toInt();
358 if (vt
!= d
->viewtype
) {
359 d
->viewtype
= (MenuLauncherApplet::ViewType
) vt
;
362 QMetaEnum e
= metaObject()->enumerator(metaObject()->indexOfEnumerator("ViewType"));
363 cg
.writeEntry("view", QByteArray(e
.valueToKey(d
->viewtype
)));
365 d
->icon
->setIcon(KIcon(d
->viewIcon()));
369 const int ft
= d
->formatComboBox
->itemData(d
->formatComboBox
->currentIndex()).toInt();
370 if (ft
!= d
->formattype
) {
371 d
->formattype
= (MenuLauncherApplet::FormatType
) ft
;
374 QMetaEnum e
= metaObject()->enumerator(metaObject()->indexOfEnumerator("FormatType"));
375 cg
.writeEntry("format", QByteArray(e
.valueToKey(d
->formattype
)));
378 const int maxRecentApps
= d
->recentApplicationsSpinBox
->value();
379 if (maxRecentApps
!= d
->maxRecentApps
) {
381 d
->setMaxRecentApps(maxRecentApps
);
382 cg
.writeEntry("maxRecentApps", maxRecentApps
);
386 emit
configNeedsSaving();
393 void MenuLauncherApplet::toggleMenu(bool pressed
)
400 void MenuLauncherApplet::toggleMenu()
403 d
->menuview
= new Kickoff::MenuView();
404 connect(d
->menuview
, SIGNAL(triggered(QAction
*)), this, SLOT(actionTriggered(QAction
*)));
405 connect(d
->menuview
, SIGNAL(aboutToHide()), d
->icon
, SLOT(setUnpressed()));
406 connect(d
->menuview
, SIGNAL(aboutToHide()), d
->menuview
, SLOT(deleteLater()));
408 switch (d
->viewtype
) {
410 //if (Kickoff::RecentApplications::self()->recentApplications().size() > 0) {
411 if (d
->maxRecentApps
> 0) {
412 d
->menuview
->addTitle(i18n("Recently Used Applications"));
413 Kickoff::RecentlyUsedModel
* recentlymodel
= new Kickoff::RecentlyUsedModel(d
->menuview
, Kickoff::RecentlyUsedModel::ApplicationsOnly
, d
->maxRecentApps
);
414 Kickoff::MenuView
*recentlyview
= d
->createMenuView(recentlymodel
);
415 d
->addMenu(recentlyview
, true);
418 d
->menuview
->addTitle(i18n("All Applications"));
419 Kickoff::ApplicationModel
*appModel
= new Kickoff::ApplicationModel(d
->menuview
);
420 appModel
->setDuplicatePolicy(Kickoff::ApplicationModel::ShowLatestOnlyPolicy
);
421 appModel
->setSystemApplicationPolicy(Kickoff::ApplicationModel::ShowApplicationAndSystemPolicy
);
422 Kickoff::MenuView
*appview
= d
->createMenuView(appModel
);
423 d
->addMenu(appview
, false);
425 d
->menuview
->addSeparator();
426 Kickoff::MenuView
*favview
= d
->createMenuView(new Kickoff::FavoritesModel(d
->menuview
));
427 d
->addMenu(favview
, false);
429 d
->menuview
->addTitle(i18n("Actions"));
430 QAction
*runaction
= d
->menuview
->addAction(KIcon("system-run"), i18n("Run Command..."));
431 runaction
->setData(KUrl("leave:/run"));
432 d
->menuview
->addSeparator();
433 QAction
*switchaction
= d
->menuview
->addAction(KIcon("system-switch-user"), i18n("Switch User"));
434 switchaction
->setData(KUrl("leave:/switch"));
435 QAction
*lockaction
= d
->menuview
->addAction(KIcon("system-lock-screen"), i18n("Lock Screen"));
436 lockaction
->setData(KUrl("leave:/lock"));
437 QAction
*logoutaction
= d
->menuview
->addAction(KIcon("system-shutdown"), i18n("Leave..."));
438 logoutaction
->setData(KUrl("leave:/logout"));
442 Kickoff::MenuView
*favview
= d
->createMenuView(new Kickoff::FavoritesModel(d
->menuview
));
443 d
->addMenu(favview
, true);
447 Kickoff::ApplicationModel
*appModel
= new Kickoff::ApplicationModel(d
->menuview
);
448 appModel
->setDuplicatePolicy(Kickoff::ApplicationModel::ShowLatestOnlyPolicy
);
449 Kickoff::MenuView
*appview
= d
->createMenuView(appModel
);
450 d
->addMenu(appview
, false);
454 Kickoff::MenuView
*systemview
= d
->createMenuView(new Kickoff::SystemModel(d
->menuview
));
455 d
->addMenu(systemview
, true);
459 Kickoff::MenuView
*recentlyview
= d
->createMenuView(new Kickoff::RecentlyUsedModel(d
->menuview
));
460 d
->addMenu(recentlyview
, true);
464 KBookmarkManager
* mgr
= KBookmarkManager::userBookmarksManager();
465 if (! d
->collection
) {
466 d
->collection
= new KActionCollection(this);
467 d
->bookmarkowner
= new BookmarkOwner();
469 delete d
->bookmarkmenu
;
470 d
->bookmarkmenu
= new KBookmarkMenu(mgr
, d
->bookmarkowner
, d
->menuview
, d
->collection
);
474 Kickoff::MenuView
*leaveview
= d
->createMenuView(new Kickoff::LeaveModel(d
->menuview
));
475 d
->addMenu(leaveview
, true);
481 d
->menuview
->setAttribute(Qt::WA_DeleteOnClose
);
482 d
->menuview
->popup(popupPosition(d
->menuview
->sizeHint()));
483 d
->icon
->setPressed();
486 void MenuLauncherApplet::actionTriggered(QAction
*action
)
488 KUrl url
= action
->data().value
<KUrl
>();
489 if (url
.scheme() == "leave") {
491 d
->launcher
= new Kickoff::UrlItemLauncher(d
->menuview
);
493 d
->launcher
->openUrl(url
.url());
496 for (QWidget
* w
= action
->parentWidget(); w
; w
= w
->parentWidget()) {
497 if (Kickoff::MenuView
*view
= dynamic_cast<Kickoff::MenuView
*>(w
)) {
498 view
->actionTriggered(action
);
504 QList
<QAction
*> MenuLauncherApplet::contextualActions()
510 #include "simpleapplet.moc"