1
/*****************************************************************
3
Copyright (c) 1996-2000 the kicker authors. See file AUTHORS.
5
Permission is hereby granted, free of charge, to any person obtaining a copy
6
of this software and associated documentation files (the "Software"), to deal
7
in the Software without restriction, including without limitation the rights
8
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
copies of the Software, and to permit persons to whom the Software is
10
furnished to do so, subject to the following conditions:
12
The above copyright notice and this permission notice shall be included in
13
all copies or substantial portions of the Software.
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
******************************************************************/
25
#include <sys/types.h>
28
#include <QtDBus/QtDBus>
30
#include <QDesktopWidget>
34
#include <QPaintEvent>
35
#include <QResizeEvent>
38
#include <QMouseEvent>
40
#include <QStyleOptionFrame>
41
#include <kapplication.h>
42
#include <kactioncollection.h>
43
#include <kbookmarkmenu.h>
47
#include <kglobalsettings.h>
48
#include <kiconloader.h>
50
#include <kmessagebox.h>
51
#include <kstandarddirs.h>
52
#include <kwindowsystem.h>
53
#include <kauthorized.h>
54
#include <kworkspace.h>
58
#include "container_base.h"
60
#include "kickerSettings.h"
61
#include "konqbookmarkmanager.h"
63
#include "quickbrowser_mnu.h"
64
#include "recentapps.h"
67
#include "krunner_interface.h"
68
#include "screensaver_interface.h"
71
PanelKMenu::PanelKMenu()
72
: PanelServiceMenu(QString(), QString(), 0, "KMenu")
75
// set the first client id to some arbitrarily large value.
77
// Don't automatically clear the main menu.
79
actionCollection = new KActionCollection(this);
80
setWindowTitle(i18n("K Menu"));
81
connect(Kicker::self(), SIGNAL(configurationChanged()),
82
this, SLOT(configChanged()));
84
/* FIXME: we need a proper way to file recent app/doc usage
85
DCOPClient *dcopClient = KApplication::dcopClient();
86
dcopClient->connectDCOPSignal(0, "appLauncher",
87
"serviceStartedByStorageId(QString,QString)",
89
"slotServiceStartedByStorageId(QString,QString)",
94
PanelKMenu::~PanelKMenu()
100
void PanelKMenu::slotServiceStartedByStorageId(QString starter,
103
if (starter != "kmenu")
105
kDebug() << "KMenu - updating recently used applications: " <<
107
KService::Ptr service = KService::serviceByStorageId(storageId);
108
RecentlyLaunchedApps::self().updateRecentlyUsedApps(service);
113
bool PanelKMenu::loadSidePixmap()
115
if (!KickerSettings::useSidePixmap())
120
QString sideName = KickerSettings::sidePixmapName();
121
QString sideTileName = KickerSettings::sideTileName();
124
image.load(KStandardDirs::locate("data", "kicker/pics/" + sideName));
128
kDebug(1210) << "Can't find a side pixmap";
132
Plasma::colorize(image);
133
sidePixmap = QPixmap::fromImage(image);
135
image.load(KStandardDirs::locate("data", "kicker/pics/" + sideTileName));
139
kDebug(1210) << "Can't find a side tile pixmap";
143
Plasma::colorize(image);
144
sideTilePixmap = QPixmap::fromImage(image);
146
if (sidePixmap.width() != sideTilePixmap.width())
148
kDebug(1210) << "Pixmaps have to be the same size";
152
// pretile the pixmap to a height of at least 100 pixels
153
if (sideTilePixmap.height() < 100)
155
int tiles = (int)(100 / sideTilePixmap.height()) + 1;
156
QPixmap preTiledPixmap(sideTilePixmap.width(), sideTilePixmap.height() * tiles);
157
QPainter p(&preTiledPixmap);
158
p.drawTiledPixmap(preTiledPixmap.rect(), sideTilePixmap);
159
sideTilePixmap = preTiledPixmap;
165
void PanelKMenu::paletteChanged()
167
if (!loadSidePixmap())
169
sidePixmap = sideTilePixmap = QPixmap();
170
setMinimumSize( sizeHint() );
174
void PanelKMenu::initialize()
176
// kDebug(1210) << "PanelKMenu::initialize()";
184
if (loadSidePixmap())
186
// in case we've been through here before, let's disconnect
187
disconnect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
188
this, SLOT(paletteChanged()));
189
connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()),
190
this, SLOT(paletteChanged()));
194
sidePixmap = sideTilePixmap = QPixmap();
198
PanelServiceMenu::initialize();
201
FIXME: no more insertTitle! now what?
202
if (KickerSettings::showMenuTitles())
205
id = insertTitle(i18n("All Applications"), -1, 0);
206
setItemEnabled( id, false );
207
id = insertTitle(i18n("Actions"), -1 , -1);
208
setItemEnabled( id, false );
212
// create recent menu section
213
createRecentMenuItems();
215
bool need_separator = false;
218
if (KickerSettings::useBookmarks() && KAuthorized::authorizeKAction("bookmarks"))
220
// Need to create a new popup each time, it's deleted by subMenus.clear()
221
KMenu * bookmarkParent = new KMenu(this);
222
bookmarkParent->setObjectName("bookmarks" );
223
delete bookmarkMenu; // can't reuse old one, the popup has been deleted
224
bookmarkMenu = new KBookmarkMenu( KonqBookmarkManager::self(), 0, bookmarkParent, actionCollection );
226
insertItem(Plasma::menuIconSet("bookmark"),
227
i18n("Bookmarks"), bookmarkParent);
229
subMenus.append(bookmarkParent);
230
need_separator = true;
233
// insert quickbrowser
234
if (KickerSettings::useBrowser())
236
PanelQuickBrowser *browserMnu = new PanelQuickBrowser(this);
237
browserMnu->initialize();
239
insertItem(Plasma::menuIconSet("kdisknav"),
240
i18n("Quick Browser"),
241
Plasma::reduceMenu(browserMnu));
242
subMenus.append(browserMnu);
243
need_separator = true;
246
// insert dynamic menus
247
QStringList menu_ext = KickerSettings::menuExtensions();
248
if (!menu_ext.isEmpty())
250
for (QStringList::ConstIterator it=menu_ext.begin(); it!=menu_ext.end(); ++it)
256
KPanelMenu *menu = info.load();
259
insertItem(Plasma::menuIconSet(info.icon()), info.name(), menu);
260
dynamicSubMenus.append(menu);
261
need_separator = true;
270
if (KAuthorized::authorizeKAction("run_command"))
272
insertItem(Plasma::menuIconSet("system-run"),
273
i18n("Run Command..."),
275
SLOT( slotRunCommand()));
279
if (DM().isSwitchable() && KAuthorized::authorizeKAction("switch_user"))
281
sessionsMenu = new QMenu( this );
282
insertItem(Plasma::menuIconSet("switchuser"),
283
i18n("Switch User"), sessionsMenu);
284
connect( sessionsMenu, SIGNAL(aboutToShow()), SLOT(slotPopulateSessions()) );
285
connect( sessionsMenu, SIGNAL(activated(int)), SLOT(slotSessionActivated(int)) );
289
If the user configured ksmserver to
291
KConfig ksmserver("ksmserverrc", KConfig::NoGlobals);
292
if (ksmserver.group("General").readEntry( "loginMode" ) == "restoreSavedSession")
294
insertItem(Plasma::menuIconSet("document-save"),
295
i18n("Save Session"), this, SLOT(slotSaveSession()));
298
if (KAuthorized::authorizeKAction("lock_screen"))
300
insertItem(Plasma::menuIconSet("system-lock-screen"),
301
i18n("Lock Session"), this, SLOT(slotLock()));
304
if (KAuthorized::authorizeKAction("logout"))
306
insertItem(Plasma::menuIconSet("application-exit"),
307
i18n("Log Out..."), this, SLOT(slotLogout()));
311
// WABA: tear off handles don't work together with dynamically updated
312
// menus. We can't update the menu while torn off, and we don't know
313
// when it is torn off.
314
if (KGlobalSettings::insertTearOffHandle())
315
insertTearOffHandle();
318
setInitialized(true);
321
extern int kicker_screen_number;
323
void PanelKMenu::slotLock()
325
QString interface( "org.freedesktop.ScreenSaver" );
326
org::freedesktop::ScreenSaver screenSaverInterface( interface, "/ScreenSaver", QDBusConnection::sessionBus() );
327
if ( screenSaverInterface.isValid() )
328
screenSaverInterface.Lock();
331
void PanelKMenu::slotLogout()
333
KWorkSpace::requestShutDown();
336
void PanelKMenu::slotPopulateSessions()
341
sessionsMenu->clear();
342
if (KAuthorized::authorizeKAction("start_new_session") && (p = dm.numReserve()) >= 0)
344
if (KAuthorized::authorizeKAction("lock_screen"))
345
sessionsMenu->insertItem(/*KIcon("lockfork"),*/ i18n("Lock Current && Start New Session"), 100 );
346
sessionsMenu->insertItem(KIcon("fork"), i18n("Start New Session"), 101 );
348
sessionsMenu->setItemEnabled( 100, false );
349
sessionsMenu->setItemEnabled( 101, false );
351
sessionsMenu->addSeparator();
354
if (dm.localSessions( sess ))
355
for (SessList::ConstIterator it = sess.begin(); it != sess.end(); ++it) {
356
int id = sessionsMenu->insertItem( DM::sess2Str( *it ), (*it).vt );
358
sessionsMenu->setItemEnabled( id, false );
360
sessionsMenu->setItemChecked( id, true );
364
void PanelKMenu::slotSessionActivated( int ent )
367
doNewSession( true );
369
doNewSession( false );
370
else if (!sessionsMenu->isItemChecked( ent ))
371
DM().lockSwitchVT( ent );
374
void PanelKMenu::doNewSession( bool lock )
376
int result = KMessageBox::warningContinueCancel(
377
kapp->desktop()->screen(kapp->desktop()->screenNumber(this)),
378
i18n("<p>You have chosen to open another desktop session.<br>"
379
"The current session will be hidden "
380
"and a new login screen will be displayed.<br>"
381
"An F-key is assigned to each session; "
382
"F%1 is usually assigned to the first session, "
383
"F%2 to the second session and so on. "
384
"You can switch between sessions by pressing "
385
"Ctrl, Alt and the appropriate F-key at the same time. "
386
"Additionally, the KDE Panel and Desktop menus have "
387
"actions for switching between sessions.</p>",
389
i18n("Warning - New Session"),
390
KGuiItem(i18n("&Start New Session"), "fork"),
391
KStandardGuiItem::cancel(),
392
":confirmNewSession",
393
KMessageBox::PlainCaption | KMessageBox::Notify);
395
if (result==KMessageBox::Cancel)
404
void PanelKMenu::slotSaveSession()
406
QDBusInterface ksmserver("org.kde.ksmserver", "/ksmserver", "org.kde.KSMServerInterface");
407
ksmserver.call("saveCurrentSession");
410
void PanelKMenu::slotRunCommand()
412
QString interface( "org.kde.krunner" );
413
org::kde::krunner::Interface desktopInterface( interface, "/Interface", QDBusConnection::sessionBus() );
414
desktopInterface.display();
417
void PanelKMenu::slotEditUserContact()
421
void PanelKMenu::setMinimumSize(const QSize & s)
423
KPanelMenu::setMinimumSize(s.width() + sidePixmap.width(), s.height());
426
void PanelKMenu::setMaximumSize(const QSize & s)
428
KPanelMenu::setMaximumSize(s.width() + sidePixmap.width(), s.height());
431
void PanelKMenu::setMinimumSize(int w, int h)
433
KPanelMenu::setMinimumSize(w + sidePixmap.width(), h);
436
void PanelKMenu::setMaximumSize(int w, int h)
438
KPanelMenu::setMaximumSize(w + sidePixmap.width(), h);
441
QRect PanelKMenu::sideImageRect()
443
return QStyle::visualRect( layoutDirection(), rect(), QRect( frameWidth(), frameWidth(), sidePixmap.width(),
444
height() - 2*frameWidth() ) );
447
void PanelKMenu::resizeEvent(QResizeEvent * e)
449
// kDebug(1210) << "PanelKMenu::resizeEvent():";
450
// kDebug(1210) << geometry().width() << ", " << geometry().height();
452
PanelServiceMenu::resizeEvent(e);
454
#warning "KDE4: Qt4 doesn't seem to provide a way of doing this, will need different impl. for side image"
456
// setFrameRect( QStyle::visualRect( layoutDirection(), rect(), QRect( sidePixmap.width(), 0,
457
// width() - sidePixmap.width(), height() ) ) );
460
//Workaround Qt3.3.x sizing bug, by ensuring we're always wide enough.
461
void PanelKMenu::resize(int width, int height)
463
width = qMax(width, maximumSize().width());
464
PanelServiceMenu::resize(width, height);
467
QSize PanelKMenu::sizeHint() const
469
QSize s = PanelServiceMenu::sizeHint();
470
// kDebug(1210) << "PanelKMenu::sizeHint()";
471
// kDebug(1210) << s.width() << ", " << s.height();
475
void PanelKMenu::paintEvent(QPaintEvent * e)
477
if (sidePixmap.isNull()) {
478
PanelServiceMenu::paintEvent(e);
483
p.setClipRegion(e->region());
485
QStyleOptionFrame frOpt;
487
frOpt.lineWidth = frameWidth();
488
frOpt.midLineWidth = 0;
489
style()->drawPrimitive( QStyle::PE_FrameMenu, &frOpt, &p, this);
491
QRect r = sideImageRect();
492
r.setBottom( r.bottom() - sidePixmap.height() );
493
if ( r.intersects( e->rect() ) )
495
p.drawTiledPixmap( r, sideTilePixmap );
499
r.setTop( r.bottom() - sidePixmap.height() );
500
if ( r.intersects( e->rect() ) )
502
QRect drawRect = r.intersect( e->rect() );
503
QRect pixRect = drawRect;
504
pixRect.translate( -r.left(), -r.top() );
505
p.drawPixmap( drawRect.topLeft(), sidePixmap, pixRect );
508
PanelServiceMenu::paintEvent( e );
511
QMouseEvent PanelKMenu::translateMouseEvent( QMouseEvent* e )
513
QRect side = sideImageRect();
515
if ( !side.contains( e->pos() ) )
518
QPoint newpos( e->pos() );
519
QApplication::isRightToLeft() ?
520
newpos.setX( newpos.x() - side.width() ) :
521
newpos.setX( newpos.x() + side.width() );
522
QPoint newglobal( e->globalPos() );
523
QApplication::isRightToLeft() ?
524
newglobal.setX( newpos.x() - side.width() ) :
525
newglobal.setX( newpos.x() + side.width() );
527
return QMouseEvent( e->type(), newpos, newglobal, e->button(), e->state() );
530
void PanelKMenu::mousePressEvent(QMouseEvent * e)
532
QMouseEvent newEvent = translateMouseEvent(e);
533
PanelServiceMenu::mousePressEvent( &newEvent );
536
void PanelKMenu::mouseReleaseEvent(QMouseEvent *e)
538
QMouseEvent newEvent = translateMouseEvent(e);
539
PanelServiceMenu::mouseReleaseEvent( &newEvent );
542
void PanelKMenu::mouseMoveEvent(QMouseEvent *e)
544
QMouseEvent newEvent = translateMouseEvent(e);
545
PanelServiceMenu::mouseMoveEvent( &newEvent );
548
void PanelKMenu::configChanged()
550
RecentlyLaunchedApps::self().m_bNeedToUpdate = false;
551
RecentlyLaunchedApps::self().configChanged();
552
PanelServiceMenu::configChanged();
555
// create and fill "recent" section at first
556
void PanelKMenu::createRecentMenuItems()
558
RecentlyLaunchedApps::self().init();
559
RecentlyLaunchedApps::self().m_nNumMenuItems = 0;
561
QStringList RecentApps;
562
RecentlyLaunchedApps::self().getRecentApps(RecentApps);
564
if (RecentApps.count() > 0)
566
bool bSeparator = KickerSettings::showMenuTitles();
567
int nId = serviceMenuEndId() + 1;
568
int nIndex = KickerSettings::showMenuTitles() ? 1 : 0;
570
for (QList<QString>::iterator it =
571
RecentApps.fromLast(); /*nop*/; --it)
573
KService::Ptr s = KService::serviceByDesktopPath(*it);
576
RecentlyLaunchedApps::self().removeItem(*it);
584
FIXME: no more titles!
585
int id = insertTitle(
586
RecentlyLaunchedApps::self().caption(),
587
serviceMenuEndId(), 0);
588
setItemEnabled( id, false );
592
insertMenuItem(s, nId++, nIndex);
593
RecentlyLaunchedApps::self().m_nNumMenuItems++;
596
if (it == RecentApps.begin())
602
if (!KickerSettings::showMenuTitles())
604
insertSeparator(RecentlyLaunchedApps::self().m_nNumMenuItems);
609
void PanelKMenu::clearSubmenus()
611
// we don't need to delete these on the way out since the libloader
612
// handles them for us
613
if (QApplication::closingDown())
618
for (PopupMenuList::const_iterator it = dynamicSubMenus.constBegin();
619
it != dynamicSubMenus.constEnd();
624
dynamicSubMenus.clear();
626
PanelServiceMenu::clearSubmenus();
629
void PanelKMenu::updateRecent()
631
if (!RecentlyLaunchedApps::self().m_bNeedToUpdate)
636
RecentlyLaunchedApps::self().m_bNeedToUpdate = false;
638
int nId = serviceMenuEndId() + 1;
640
// remove previous items
641
if (RecentlyLaunchedApps::self().m_nNumMenuItems > 0)
644
int i = KickerSettings::showMenuTitles() ? -1 : 0;
645
for (; i < RecentlyLaunchedApps::self().m_nNumMenuItems; i++)
648
entryMap_.remove(nId + i);
650
RecentlyLaunchedApps::self().m_nNumMenuItems = 0;
652
if (!KickerSettings::showMenuTitles())
659
QStringList RecentApps;
660
RecentlyLaunchedApps::self().getRecentApps(RecentApps);
662
if (RecentApps.count() > 0)
664
bool bNeedSeparator = KickerSettings::showMenuTitles();
665
for (QList<QString>::iterator it = RecentApps.fromLast();
668
KService::Ptr s = KService::serviceByDesktopPath(*it);
671
RecentlyLaunchedApps::self().removeItem(*it);
677
bNeedSeparator = false;
679
/* FIXME: no more titles!
680
int id = insertTitle(
681
RecentlyLaunchedApps::self().caption(),
683
setItemEnabled( id, false );
686
insertMenuItem(s, nId++, KickerSettings::showMenuTitles() ?
688
RecentlyLaunchedApps::self().m_nNumMenuItems++;
691
if (it == RecentApps.begin())
695
if (!KickerSettings::showMenuTitles())
697
insertSeparator(RecentlyLaunchedApps::self().m_nNumMenuItems);
702
void PanelKMenu::clearRecentMenuItems()
704
RecentlyLaunchedApps::self().clearRecentApps();
705
RecentlyLaunchedApps::self().save();
706
RecentlyLaunchedApps::self().m_bNeedToUpdate = true;