1 /*****************************************************************
2 ksmserver - the KDE session management server
4 Copyright 2000 Matthias Ettrich <ettrich@kde.org>
5 Copyright 2007 Urs Wolfer <uwolfer @ kde.org>
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 ******************************************************************/
26 #include <config-workspace.h>
28 #include "shutdowndlg.h"
29 #include "plasma/svg.h"
32 #include <QDesktopWidget>
38 #include <QPaintEvent>
39 #include <QVBoxLayout>
40 #include <QHBoxLayout>
42 #include <qimageblitz.h>
44 #include <KApplication>
46 #include <kiconloader.h>
49 #include <solid/control/powermanager.h>
50 #include <kwindowsystem.h>
54 #include <kxerrorhandler.h>
56 #include <kdisplaymanager.h>
58 #include "shutdowndlg.moc"
60 #define FONTCOLOR "#bfbfbf"
62 KSMShutdownFeedback
* KSMShutdownFeedback::s_pSelf
= 0L;
64 KSMShutdownFeedback::KSMShutdownFeedback()
65 : QWidget( 0L, Qt::Popup
),
68 setObjectName( "feedbackwidget" );
69 setAttribute( Qt::WA_NoSystemBackground
);
70 setAttribute( Qt::WA_PaintOnScreen
);
71 setGeometry( QApplication::desktop()->geometry() );
72 m_pixmap
= QPixmap( size() );
73 m_pixmap
.fill( Qt::transparent
);
74 QTimer::singleShot( 10, this, SLOT( slotPaintEffect() ) );
78 void KSMShutdownFeedback::paintEvent( QPaintEvent
* )
80 if ( m_currentY
>= height() ) {
81 QPainter
painter( this );
82 painter
.drawPixmap( 0, 0, m_pixmap
);
86 QPainter
painter( this );
87 painter
.drawPixmap( 0, 0, m_pixmap
, 0, 0, width(), m_currentY
);
90 void KSMShutdownFeedback::slotPaintEffect()
92 if ( m_currentY
>= height() )
95 QImage image
= QPixmap::grabWindow( QApplication::desktop()->winId(), 0, m_currentY
, width(), 10 ).toImage();
96 Blitz::intensity( image
, -0.4 );
97 Blitz::grayscale( image
);
99 QPainter
painter( &m_pixmap
);
100 painter
.drawImage( 0, m_currentY
, image
);
104 update( 0, 0, width(), m_currentY
);
106 QTimer::singleShot( 5, this, SLOT( slotPaintEffect() ) );
109 void KSMShutdownFeedback::start()
111 if( KWindowSystem::compositingActive()) {
113 Display
* dpy
= QX11Info::display();
114 char net_wm_cm_name
[ 100 ];
115 sprintf( net_wm_cm_name
, "_NET_WM_CM_S%d", DefaultScreen( dpy
));
116 Atom net_wm_cm
= XInternAtom( dpy
, net_wm_cm_name
, False
);
117 Window sel
= XGetSelectionOwner( dpy
, net_wm_cm
);
118 Atom hack
= XInternAtom( dpy
, "_KWIN_LOGOUT_EFFECT", False
);
119 bool wmsupport
= false;
121 KXErrorHandler handler
;
123 Atom
* props
= XListProperties( dpy
, sel
, &cnt
);
124 if( !handler
.error( false ) && props
!= NULL
&& qFind( props
, props
+ cnt
, hack
) != props
+ cnt
)
132 s_pSelf
= new KSMShutdownFeedback();
136 void KSMShutdownFeedback::stop()
144 KSMPushButton::KSMPushButton( const QString
&text
, QWidget
*parent
, bool smallButton
)
145 : QPushButton( parent
), m_highlight( false ), m_text( text
), m_popupMenu(0), m_popupTimer(0),
146 m_glowOpacity( 0.0 ), m_smallButton( smallButton
)
148 setAttribute(Qt::WA_Hover
, true);
153 void KSMPushButton::init()
156 setMinimumSize(88, 22);
157 setFixedHeight(22); // workaround: force correct height
159 setMinimumSize(165, 38);
162 connect( this, SIGNAL(pressed()), SLOT(slotPressed()) );
163 connect( this, SIGNAL(released()), SLOT(slotReleased()) );
165 m_glowSvg
= new Plasma::Svg("dialogs/shutdowndialog", this);
166 connect( m_glowSvg
, SIGNAL(repaintNeeded()), this, SLOT(update()) );
168 m_glowTimeLine
= new QTimeLine( 150, this );
169 connect( m_glowTimeLine
, SIGNAL(valueChanged(qreal
)),
170 this, SLOT(animateGlow(qreal
)) );
173 fnt
.setPixelSize(12);
175 // Calculate the width of the text when splitted on two lines and
176 // properly resize the button.
177 if (QFontMetrics(fnt
).width(m_text
) > width() - 4 - (m_smallButton
? 16 : 32) ||
178 (2 * QFontMetrics(fnt
).lineSpacing() > height() && !m_smallButton
) ) {
180 int i
= m_text
.length()/2;
183 while( i
&& i
< m_text
.length() && m_text
[i
] != ' ' ) {
184 i
= i
+ (diff
* fac
);
188 QString upper
= m_text
.left( i
);
189 QString lower
= m_text
.right( m_text
.length() - i
);
191 w
= qMax(QFontMetrics(fnt
).width(upper
) + 18 + (m_smallButton
? 16 : 32),
192 QFontMetrics(fnt
).width(lower
) + 18 + (m_smallButton
? 16 : 32));
193 w
= qMax(w
, width());
194 h
= qMax(height(), ((upper
.isEmpty() || lower
.isEmpty()) ? 1 : 2) * QFontMetrics(fnt
).lineSpacing());
195 if (w
> width() || h
> height()) {
196 setMinimumSize(w
, h
);
204 void KSMPushButton::paintEvent( QPaintEvent
* e
)
207 p
.setClipRect( e
->rect() );
208 p
.setRenderHints(QPainter::Antialiasing
| QPainter::TextAntialiasing
);
211 fnt
.setPixelSize(12);
213 p
.setCompositionMode(QPainter::CompositionMode_SourceOver
);
217 if (m_glowOpacity
> 0) {
218 p
.setOpacity(m_glowOpacity
); // fade in
219 m_glowSvg
->paint(&p
, QRect(0, 0, width(), height()), m_smallButton
? "button-small-hover" : "button-hover");
220 p
.setOpacity(1.0 - m_glowOpacity
); // fade normal background out
221 m_glowSvg
->paint(&p
, QRect(0, 0, width(), height()), m_smallButton
? "button-small-normal" : "button-normal");
225 m_glowSvg
->paint(&p
, QRect(0, 0, width(), height()), m_smallButton
? "button-small-normal" : "button-normal");
230 p
.setRenderHints( QPainter::Antialiasing
, false);
231 p
.drawPixmap(width() - (m_smallButton
? 16 : 32) - 4, height() / 2 - (m_smallButton
? 8 : 16), m_pixmap
);
234 p
.setPen(QPen(QColor(FONTCOLOR
)));
235 p
.drawText(10, 0, width() - (m_smallButton
? 16 : 32) - 8, height(),
236 Qt::AlignVCenter
| Qt::AlignLeft
| Qt::TextWordWrap
| Qt::TextShowMnemonic
, m_text
);
241 p
.setBrush(QColor(FONTCOLOR
));
242 pen
.setColor(QColor(FONTCOLOR
));
245 QPoint(width() - 10 - 34, height() - 7),
246 QPoint(width() - 4 - 34, height() - 7),
247 QPoint(width() - 7 - 34, height() - 4) };
248 p
.drawPolygon(points
, 3); // TODO: use QStyle
253 void KSMPushButton::resizeEvent(QResizeEvent
*e
)
255 m_glowSvg
->resize( e
->size() );
256 QPushButton::resizeEvent( e
);
259 void KSMPushButton::animateGlow( qreal value
)
261 m_glowOpacity
= value
;
265 void KSMPushButton::setPixmap( const QPixmap
&p
)
268 int size
= m_smallButton
? 16 : 32;
269 if (m_pixmap
.size().width() != size
|| m_pixmap
.size().height() != size
)
270 m_pixmap
= m_pixmap
.scaled(size
, size
);
274 void KSMPushButton::setPopupMenu( QMenu
*m
)
277 if( !m_popupTimer
) {
278 m_popupTimer
= new QTimer( this );
279 connect( m_popupTimer
, SIGNAL(timeout()), this, SLOT(slotTimeout()));
283 void KSMPushButton::slotPressed()
286 m_popupTimer
->start( QApplication::startDragTime() );
289 void KSMPushButton::slotReleased()
292 m_popupTimer
->stop();
295 void KSMPushButton::slotTimeout()
297 m_popupTimer
->stop();
299 m_popupMenu
->popup( mapToGlobal(rect().bottomLeft()) );
305 bool KSMPushButton::event( QEvent
*e
)
307 if (e
->type() == QEvent::HoverEnter
|| e
->type() == QEvent::FocusIn
)
309 if (m_glowOpacity
> 0) // already hovered
312 m_glowTimeLine
->setDirection( QTimeLine::Forward
);
313 if (m_glowTimeLine
->state() == QTimeLine::Running
)
314 m_glowTimeLine
->stop();
315 m_glowTimeLine
->start();
319 else if (e
->type() == QEvent::HoverLeave
|| e
->type() == QEvent::FocusOut
)
324 m_glowTimeLine
->setDirection( QTimeLine::Backward
);
325 if (m_glowTimeLine
->state() == QTimeLine::Running
)
326 m_glowTimeLine
->stop();
327 m_glowTimeLine
->start();
332 return QPushButton::event( e
);
337 Q_DECLARE_METATYPE(Solid::Control::PowerManager::SuspendMethod
)
339 KSMShutdownDlg::KSMShutdownDlg( QWidget
* parent
,
340 bool maysd
, KWorkSpace::ShutdownType sdtype
)
341 : QDialog( parent
, Qt::Popup
)
342 // this is a WType_Popup on purpose. Do not change that! Not
343 // having a popup here has severe side effects.
345 winId(); // workaround for Qt4.3 setWindowRole() assert
346 setWindowRole( "logoutdialog" );
347 //#if !(QT_VERSION >= QT_VERSION_CHECK(4, 3, 3) || defined(QT_KDE_QT_COPY))
348 // Qt doesn't set this on unmanaged windows
349 QByteArray appName
= qAppName().toLatin1();
350 XClassHint class_hint
;
351 class_hint
.res_name
= appName
.data(); // application name
352 class_hint
.res_class
= const_cast<char *>(QX11Info::appClass()); // application class
353 XSetWMProperties( QX11Info::display(), winId(), NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &class_hint
);
354 XChangeProperty( QX11Info::display(), winId(),
355 XInternAtom( QX11Info::display(), "WM_WINDOW_ROLE", False
), XA_STRING
, 8, PropModeReplace
,
356 (unsigned char *)"logoutdialog", strlen( "logoutdialog" ));
359 m_svg
= new Plasma::Svg("dialogs/shutdowndialog", this);
360 connect( m_svg
, SIGNAL(repaintNeeded()), this, SLOT(update()) );
363 KDialog::centerOnScreen(this);
365 QVBoxLayout
*mainLayout
= new QVBoxLayout();
367 mainLayout
->setContentsMargins(9, 9, 12, 7);
368 QVBoxLayout
*buttonLayout
= new QVBoxLayout();
369 buttonLayout
->addStretch();
370 QHBoxLayout
*buttonMainLayout
= new QHBoxLayout();
371 buttonMainLayout
->addStretch();
372 buttonMainLayout
->addLayout(buttonLayout
);
374 QHBoxLayout
*topLayout
= new QHBoxLayout();
376 QHBoxLayout
*bottomLayout
= new QHBoxLayout();
379 fnt
.setPixelSize(16);
381 QLabel
*versionLabel
= new QLabel(this);
383 int vmajor
= KDE_VERSION_MAJOR
;
384 int vminor
= KDE_VERSION_MINOR
;
385 int vbugfix
= KDE_VERSION_RELEASE
;
389 // Placeholders tagged <numid> to avoid treatment as amounts.
390 vcomposed
= i18nc("@label In corner of the logout dialog",
391 "KDE <numid>%1.%2.%3</numid>", vmajor
, vminor
, vbugfix
);
394 vcomposed
= i18nc("@label In corner of the logout dialog",
395 "KDE <numid>%1.%2</numid>", vmajor
, vminor
);
397 versionLabel
->setText("<font color='" + QString(FONTCOLOR
) + "'>" + vcomposed
+ "</font>");
398 versionLabel
->setFont(fnt
);
400 KUser userInformation
;
402 QString userName
= userInformation
.property( KUser::FullName
).toString();
403 QString loginName
= userInformation
.loginName();
405 if (userName
.isEmpty()) {
406 logoutMsg
= i18n( "End Session for %1", loginName
);
408 logoutMsg
= i18n( "End Session for %1 (%2)", userName
, loginName
);
411 QLabel
*logoutMessageLabel
= new QLabel(this);
412 // FIXME Made the color picked from the user's one
413 logoutMessageLabel
->setText("<font color='" + QString(FONTCOLOR
) + "'>" + logoutMsg
+ "</font>");
414 logoutMessageLabel
->setAlignment(Qt::AlignRight
);
415 fnt
.setPixelSize(12);
416 logoutMessageLabel
->setFont(fnt
);
417 logoutMessageLabel
->setWordWrap(true);
419 topLayout
->addWidget(versionLabel
, 0, Qt::AlignTop
); // correct possition if logoutMessageLabel has multiple lines
420 topLayout
->addWidget(logoutMessageLabel
, 1, Qt::AlignBottom
);
422 KSMPushButton
* btnLogout
= new KSMPushButton( i18n("Logout"), this );
423 btnLogout
->setPixmap(KIconLoader::global()->loadIcon("system-log-out", KIconLoader::NoGroup
, 32));
424 btnLogout
->setFocus();
425 connect(btnLogout
, SIGNAL(clicked()), SLOT(slotLogout()));
426 buttonLayout
->addWidget(btnLogout
);
427 buttonLayout
->addStretch();
431 KSMPushButton
* btnHalt
= new KSMPushButton( i18n("Turn Off Computer"), this );
432 btnHalt
->setPixmap(KIconLoader::global()->loadIcon("system-shutdown", KIconLoader::NoGroup
, 32));
433 buttonLayout
->addWidget(btnHalt
);
434 buttonLayout
->addStretch();
435 connect(btnHalt
, SIGNAL(clicked()), SLOT(slotHalt()));
436 if ( sdtype
== KWorkSpace::ShutdownTypeHalt
)
439 QMenu
*shutdownMenu
= new QMenu( btnHalt
);
440 QActionGroup
* spdActionGroup
= new QActionGroup(shutdownMenu
);
441 connect( spdActionGroup
, SIGNAL(triggered(QAction
*)), SLOT(slotSuspend(QAction
*)) );
442 btnHalt
->setPopupMenu( shutdownMenu
);
443 Solid::Control::PowerManager::SuspendMethods spdMethods
= Solid::Control::PowerManager::supportedSuspendMethods();
444 if( spdMethods
& Solid::Control::PowerManager::Standby
) {
445 QAction
* action
= new QAction(i18n("Standby"), spdActionGroup
);
446 action
->setData(QVariant::fromValue(Solid::Control::PowerManager::Standby
));
448 if( spdMethods
& Solid::Control::PowerManager::ToRam
) {
449 QAction
* action
= new QAction(i18n("Suspend to RAM"), spdActionGroup
);
450 action
->setData(QVariant::fromValue(Solid::Control::PowerManager::ToRam
));
452 if( spdMethods
& Solid::Control::PowerManager::ToDisk
) {
453 QAction
* action
= new QAction(i18n("Suspend to Disk"), spdActionGroup
);
454 action
->setData(QVariant::fromValue(Solid::Control::PowerManager::ToDisk
));
456 shutdownMenu
->addActions(spdActionGroup
->actions());
459 KSMPushButton
* btnReboot
= new KSMPushButton( i18n("Restart Computer"), this );
460 btnReboot
->setPixmap(KIconLoader::global()->loadIcon("system-restart", KIconLoader::NoGroup
, 32));
461 connect(btnReboot
, SIGNAL(clicked()), SLOT(slotReboot()));
462 buttonLayout
->addWidget(btnReboot
);
463 buttonLayout
->addStretch();
464 if ( sdtype
== KWorkSpace::ShutdownTypeReboot
)
465 btnReboot
->setFocus();
468 if ( KDisplayManager().bootOptions( rebootOptions
, def
, cur
) ) {
472 QMenu
*rebootMenu
= new QMenu( btnReboot
);
473 QActionGroup
* rebootActionGroup
= new QActionGroup(rebootMenu
);
474 connect( rebootActionGroup
, SIGNAL(triggered(QAction
*)), SLOT(slotReboot(QAction
*)) );
475 btnReboot
->setPopupMenu( rebootMenu
);
478 for (QStringList::ConstIterator it
= rebootOptions
.begin(); it
!= rebootOptions
.end(); ++it
, ++index
) {
479 QString label
= (*it
);
480 label
=label
.replace('&',"&&");
481 QAction
* action
= new QAction(label
, rebootActionGroup
);
482 action
->setData(index
);
484 action
->setText( label
+ i18nc("current option in boot loader", " (current)") );
487 rebootMenu
->addActions(rebootActionGroup
->actions());
491 KSMPushButton
* btnBack
= new KSMPushButton(i18n("Cancel"), this, true);
492 btnBack
->setPixmap(KIconLoader::global()->loadIcon( "dialog-cancel", KIconLoader::NoGroup
, 16));
493 bottomLayout
->addStretch();
494 bottomLayout
->addWidget(btnBack
);
495 connect(btnBack
, SIGNAL(clicked()), SLOT(reject()));
497 mainLayout
->addLayout(topLayout
);
498 mainLayout
->addLayout(buttonMainLayout
);
499 mainLayout
->addLayout(bottomLayout
);
501 setLayout( mainLayout
);
504 void KSMShutdownDlg::paintEvent(QPaintEvent
*e
)
508 p
.setRenderHints(QPainter::Antialiasing
| QPainter::SmoothPixmapTransform
);
509 m_svg
->paint(&p
, QRect(0, 0, width(), height()), "background");
512 void KSMShutdownDlg::resizeEvent(QResizeEvent
*e
)
514 QDialog::resizeEvent( e
);
516 QBitmap
mask(size());
517 mask
.fill(Qt::color0
);
520 m_svg
->resize(size());
521 m_svg
->paint(&p
, QRect(0, 0, width(), height()), "background");
525 void KSMShutdownDlg::slotLogout()
527 m_shutdownType
= KWorkSpace::ShutdownTypeNone
;
531 void KSMShutdownDlg::slotReboot()
533 // no boot option selected -> current
534 m_bootOption
.clear();
535 m_shutdownType
= KWorkSpace::ShutdownTypeReboot
;
539 void KSMShutdownDlg::slotReboot(QAction
* action
)
541 int opt
= action
->data().toInt();
542 if (int(rebootOptions
.size()) > opt
)
543 m_bootOption
= rebootOptions
[opt
];
544 m_shutdownType
= KWorkSpace::ShutdownTypeReboot
;
549 void KSMShutdownDlg::slotHalt()
551 m_bootOption
.clear();
552 m_shutdownType
= KWorkSpace::ShutdownTypeHalt
;
557 void KSMShutdownDlg::slotSuspend(QAction
* action
)
559 m_bootOption
.clear();
560 Solid::Control::PowerManager::SuspendMethod spdMethod
= action
->data().value
<Solid::Control::PowerManager::SuspendMethod
>();
561 Solid::Control::PowerManager::suspend( spdMethod
);
565 bool KSMShutdownDlg::confirmShutdown( bool maysd
, KWorkSpace::ShutdownType
& sdtype
, QString
& bootOption
)
567 KSMShutdownDlg
* l
= new KSMShutdownDlg( 0,
568 //KSMShutdownFeedback::self(),
570 bool result
= l
->exec();
571 sdtype
= l
->m_shutdownType
;
572 bootOption
= l
->m_bootOption
;