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 "shutdowndlg.h"
27 #include "plasma/framesvg.h"
28 #include "plasma/theme.h"
31 #include <QDesktopWidget>
37 #include <QPaintEvent>
38 #include <QVBoxLayout>
39 #include <QHBoxLayout>
41 #include <KApplication>
43 #include <kiconloader.h>
46 #include <solid/control/powermanager.h>
47 #include <kwindowsystem.h>
51 #include <kxerrorhandler.h>
53 #include <kdisplaymanager.h>
55 #include <config-workspace.h>
57 #include "logouteffect.h"
58 #include "shutdowndlg.moc"
62 #define FONTCOLOR "#bfbfbf"
64 KSMShutdownFeedback
* KSMShutdownFeedback::s_pSelf
= 0L;
66 KSMShutdownFeedback::KSMShutdownFeedback()
67 : QWidget( 0L, Qt::Popup
),
68 m_currentY( 0 ), initialized( false )
70 setObjectName( "feedbackwidget" );
71 setAttribute( Qt::WA_NoSystemBackground
);
72 setAttribute( Qt::WA_PaintOnScreen
);
73 setGeometry( QApplication::desktop()->geometry() );
74 m_pixmap
= QPixmap( size() );
75 QTimer::singleShot( 10, this, SLOT( slotPaintEffect() ) );
79 void KSMShutdownFeedback::paintEvent( QPaintEvent
* )
84 QPainter
painter( this );
85 painter
.setCompositionMode( QPainter::CompositionMode_Source
);
86 painter
.drawPixmap( 0, 0, m_pixmap
);
89 void KSMShutdownFeedback::slotPaintEffect()
91 effect
= LogoutEffect::create(this, &m_pixmap
);
92 connect(effect
, SIGNAL(initialized()),
93 this, SLOT (slotPaintEffectInitialized()));
98 void KSMShutdownFeedback::slotPaintEffectInitialized()
103 void KSMShutdownFeedback::start()
105 if( KWindowSystem::compositingActive()) {
107 Display
* dpy
= QX11Info::display();
108 char net_wm_cm_name
[ 100 ];
109 sprintf( net_wm_cm_name
, "_NET_WM_CM_S%d", DefaultScreen( dpy
));
110 Atom net_wm_cm
= XInternAtom( dpy
, net_wm_cm_name
, False
);
111 Window sel
= XGetSelectionOwner( dpy
, net_wm_cm
);
112 Atom hack
= XInternAtom( dpy
, "_KWIN_LOGOUT_EFFECT", False
);
113 bool wmsupport
= false;
115 KXErrorHandler handler
;
117 Atom
* props
= XListProperties( dpy
, sel
, &cnt
);
118 if( !handler
.error( false ) && props
!= NULL
&& qFind( props
, props
+ cnt
, hack
) != props
+ cnt
)
126 s_pSelf
= new KSMShutdownFeedback();
130 void KSMShutdownFeedback::stop()
138 KSMPushButton::KSMPushButton( const QString
&text
, QWidget
*parent
, bool smallButton
)
139 : QPushButton( text
, parent
), m_highlight( false ), m_text( text
), m_popupMenu(0), m_popupTimer(0),
140 m_glowOpacity( 0.0 ), m_smallButton( smallButton
)
142 setAttribute(Qt::WA_Hover
, true);
147 void KSMPushButton::init()
150 setMinimumSize(88, 22);
151 setFixedHeight(22); // workaround: force correct height
153 setMinimumSize(165, 38);
154 setSizePolicy(QSizePolicy::MinimumExpanding
, QSizePolicy::MinimumExpanding
);
157 connect( this, SIGNAL(pressed()), SLOT(slotPressed()) );
158 connect( this, SIGNAL(released()), SLOT(slotReleased()) );
160 m_glowSvg
= new Plasma::Svg(this);
161 m_glowSvg
->setImagePath("dialogs/shutdowndialog");
162 connect( m_glowSvg
, SIGNAL(repaintNeeded()), this, SLOT(update()) );
164 m_glowTimeLine
= new QTimeLine( 150, this );
165 connect( m_glowTimeLine
, SIGNAL(valueChanged(qreal
)),
166 this, SLOT(animateGlow(qreal
)) );
169 fnt
.setPixelSize(12);
171 // Calculate the width of the text when splitted on two lines and
172 // properly resize the button.
173 if (QFontMetrics(fnt
).width(m_text
) > width() - 4 - (m_smallButton
? 16 : 32) ||
174 (2 * QFontMetrics(fnt
).lineSpacing() > height() && !m_smallButton
) ) {
176 int i
= m_text
.length()/2;
179 while( i
&& i
< m_text
.length() && m_text
[i
] != ' ' ) {
180 i
= i
+ (diff
* fac
);
184 QString upper
= m_text
.left( i
);
185 QString lower
= m_text
.right( m_text
.length() - i
);
187 w
= qMax(QFontMetrics(fnt
).width(upper
) + 18 + (m_smallButton
? 16 : 32),
188 QFontMetrics(fnt
).width(lower
) + 18 + (m_smallButton
? 16 : 32));
189 w
= qMax(w
, width());
190 h
= qMax(height(), ((upper
.isEmpty() || lower
.isEmpty()) ? 1 : 2) * QFontMetrics(fnt
).lineSpacing());
191 if (w
> width() || h
> height()) {
192 setMinimumSize(w
, h
);
200 void KSMPushButton::paintEvent( QPaintEvent
* e
)
203 p
.setClipRect( e
->rect() );
204 p
.setRenderHints(QPainter::Antialiasing
| QPainter::TextAntialiasing
);
207 QColor fntColor
= Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor
);
208 fnt
.setPixelSize(12);
210 p
.setCompositionMode(QPainter::CompositionMode_SourceOver
);
214 if (m_glowOpacity
> 0) {
215 p
.setOpacity(m_glowOpacity
); // fade in
216 m_glowSvg
->paint(&p
, QRect(0, 0, width(), height()), m_smallButton
? "button-small-hover" : "button-hover");
217 p
.setOpacity(1.0 - m_glowOpacity
); // fade normal background out
218 m_glowSvg
->paint(&p
, QRect(0, 0, width(), height()), m_smallButton
? "button-small-normal" : "button-normal");
222 m_glowSvg
->paint(&p
, QRect(0, 0, width(), height()), m_smallButton
? "button-small-normal" : "button-normal");
227 p
.setRenderHints( QPainter::Antialiasing
, false);
228 p
.drawPixmap(width() - (m_smallButton
? 16 : 32) - 4, height() / 2 - (m_smallButton
? 8 : 16), m_pixmap
);
232 p
.drawText(10, 0, width() - (m_smallButton
? 16 : 32) - 8, height(),
233 Qt::AlignVCenter
| Qt::AlignLeft
| Qt::TextWordWrap
| Qt::TextShowMnemonic
, m_text
);
238 p
.setBrush(fntColor
);
239 pen
.setColor(QColor(fntColor
));
242 QPoint(width() - 10 - 34, height() - 7),
243 QPoint(width() - 4 - 34, height() - 7),
244 QPoint(width() - 7 - 34, height() - 4) };
245 p
.drawPolygon(points
, 3); // TODO: use QStyle
250 void KSMPushButton::resizeEvent(QResizeEvent
*e
)
252 m_glowSvg
->resize( e
->size() );
253 QPushButton::resizeEvent( e
);
256 void KSMPushButton::animateGlow( qreal value
)
258 m_glowOpacity
= value
;
262 void KSMPushButton::setPixmap( const QPixmap
&p
)
265 int size
= m_smallButton
? 16 : 32;
266 if (m_pixmap
.size().width() != size
|| m_pixmap
.size().height() != size
)
267 m_pixmap
= m_pixmap
.scaled(size
, size
);
271 void KSMPushButton::setPopupMenu( QMenu
*m
)
274 if( !m_popupTimer
) {
275 m_popupTimer
= new QTimer( this );
276 connect( m_popupTimer
, SIGNAL(timeout()), this, SLOT(slotTimeout()));
280 void KSMPushButton::slotPressed()
283 m_popupTimer
->start( QApplication::startDragTime() );
286 void KSMPushButton::slotReleased()
289 m_popupTimer
->stop();
292 void KSMPushButton::slotTimeout()
294 m_popupTimer
->stop();
296 m_popupMenu
->popup( mapToGlobal(rect().bottomLeft()) );
302 bool KSMPushButton::event( QEvent
*e
)
304 if (e
->type() == QEvent::HoverEnter
|| e
->type() == QEvent::FocusIn
)
306 if (m_glowOpacity
> 0) // already hovered
309 m_glowTimeLine
->setDirection( QTimeLine::Forward
);
310 if (m_glowTimeLine
->state() == QTimeLine::Running
)
311 m_glowTimeLine
->stop();
312 m_glowTimeLine
->start();
316 else if (e
->type() == QEvent::HoverLeave
|| e
->type() == QEvent::FocusOut
)
321 m_glowTimeLine
->setDirection( QTimeLine::Backward
);
322 if (m_glowTimeLine
->state() == QTimeLine::Running
)
323 m_glowTimeLine
->stop();
324 m_glowTimeLine
->start();
329 return QPushButton::event( e
);
334 Q_DECLARE_METATYPE(Solid::Control::PowerManager::SuspendMethod
)
336 KSMShutdownDlg::KSMShutdownDlg( QWidget
* parent
,
337 bool maysd
, KWorkSpace::ShutdownType sdtype
)
338 : QDialog( parent
, Qt::Popup
), //krazy:exclude=qclasses
342 m_automaticallyDoSeconds(30),
344 // this is a WType_Popup on purpose. Do not change that! Not
345 // having a popup here has severe side effects.
347 winId(); // workaround for Qt4.3 setWindowRole() assert
348 setWindowRole( "logoutdialog" );
349 //#if !(QT_VERSION >= QT_VERSION_CHECK(4, 3, 3) || defined(QT_KDE_QT_COPY))
350 // Qt doesn't set this on unmanaged windows
351 QByteArray appName
= qAppName().toLatin1();
352 XClassHint class_hint
;
353 class_hint
.res_name
= appName
.data(); // application name
354 class_hint
.res_class
= const_cast<char *>(QX11Info::appClass()); // application class
355 XSetWMProperties( QX11Info::display(), winId(), NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &class_hint
);
356 XChangeProperty( QX11Info::display(), winId(),
357 XInternAtom( QX11Info::display(), "WM_WINDOW_ROLE", False
), XA_STRING
, 8, PropModeReplace
,
358 (unsigned char *)"logoutdialog", strlen( "logoutdialog" ));
361 m_svg
= new Plasma::FrameSvg(this);
362 m_svg
->setImagePath("dialogs/shutdowndialog");
363 connect( m_svg
, SIGNAL(repaintNeeded()), this, SLOT(update()) );
366 QVBoxLayout
*mainLayout
= new QVBoxLayout();
368 mainLayout
->setContentsMargins(12, 9, 12, 7);
370 QVBoxLayout
*buttonLayout
= new QVBoxLayout();
371 QHBoxLayout
*buttonMainLayout
= new QHBoxLayout();
373 m_automaticallyDoLabel
= new QLabel(this);
374 mainLayout
->addWidget(m_automaticallyDoLabel
, 0, Qt::AlignRight
);
376 buttonMainLayout
->addLayout(buttonLayout
);
378 QHBoxLayout
*bottomLayout
= new QHBoxLayout();
381 fnt
.setPixelSize(16);
382 QColor fntColor
= Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor
);
384 palette
.setColor(QPalette::WindowText
, fntColor
);
386 m_btnLogout
= new KSMPushButton( i18n("&Logout"), this );
387 m_btnLogout
->setPixmap(KIconLoader::global()->loadIcon("system-log-out", KIconLoader::NoGroup
, 32));
388 if ( sdtype
== KWorkSpace::ShutdownTypeLogout
)
389 m_btnLogout
->setFocus();
390 connect(m_btnLogout
, SIGNAL(clicked()), SLOT(slotLogout()));
391 buttonLayout
->addWidget(m_btnLogout
, Qt::AlignRight
| Qt::AlignTop
);
395 m_btnHalt
= new KSMPushButton( i18n("&Turn Off Computer"), this );
396 m_btnHalt
->setPixmap(KIconLoader::global()->loadIcon("system-shutdown", KIconLoader::NoGroup
, 32));
397 buttonLayout
->addWidget(m_btnHalt
, Qt::AlignTop
| Qt::AlignRight
);
398 connect(m_btnHalt
, SIGNAL(clicked()), SLOT(slotHalt()));
399 if ( sdtype
== KWorkSpace::ShutdownTypeHalt
)
400 m_btnHalt
->setFocus();
402 QMenu
*shutdownMenu
= new QMenu( m_btnHalt
);
403 QActionGroup
* spdActionGroup
= new QActionGroup(shutdownMenu
);
404 connect( spdActionGroup
, SIGNAL(triggered(QAction
*)), SLOT(slotSuspend(QAction
*)) );
405 m_btnHalt
->setPopupMenu( shutdownMenu
);
406 Solid::Control::PowerManager::SuspendMethods spdMethods
= Solid::Control::PowerManager::supportedSuspendMethods();
407 if( spdMethods
& Solid::Control::PowerManager::Standby
) {
408 QAction
* action
= new QAction(i18n("&Standby"), spdActionGroup
);
409 action
->setData(QVariant::fromValue(Solid::Control::PowerManager::Standby
));
411 if( spdMethods
& Solid::Control::PowerManager::ToRam
) {
412 QAction
* action
= new QAction(i18n("Suspend to &RAM"), spdActionGroup
);
413 action
->setData(QVariant::fromValue(Solid::Control::PowerManager::ToRam
));
415 if( spdMethods
& Solid::Control::PowerManager::ToDisk
) {
416 QAction
* action
= new QAction(i18n("Suspend to &Disk"), spdActionGroup
);
417 action
->setData(QVariant::fromValue(Solid::Control::PowerManager::ToDisk
));
419 shutdownMenu
->addActions(spdActionGroup
->actions());
422 m_btnReboot
= new KSMPushButton( i18n("&Restart Computer"), this );
423 m_btnReboot
->setPixmap(KIconLoader::global()->loadIcon("system-restart", KIconLoader::NoGroup
, 32));
424 connect(m_btnReboot
, SIGNAL(clicked()), SLOT(slotReboot()));
425 buttonLayout
->addWidget(m_btnReboot
, Qt::AlignTop
| Qt::AlignRight
);
426 if ( sdtype
== KWorkSpace::ShutdownTypeReboot
)
427 m_btnReboot
->setFocus();
430 if ( KDisplayManager().bootOptions( rebootOptions
, def
, cur
) ) {
434 QMenu
*rebootMenu
= new QMenu( m_btnReboot
);
435 QActionGroup
* rebootActionGroup
= new QActionGroup(rebootMenu
);
436 connect( rebootActionGroup
, SIGNAL(triggered(QAction
*)), SLOT(slotReboot(QAction
*)) );
437 m_btnReboot
->setPopupMenu( rebootMenu
);
440 for (QStringList::ConstIterator it
= rebootOptions
.constBegin(); it
!= rebootOptions
.constEnd(); ++it
, ++index
) {
441 QString label
= (*it
);
442 label
=label
.replace('&',"&&");
443 QAction
* action
= new QAction(label
, rebootActionGroup
);
444 action
->setData(index
);
446 action
->setText( label
+ i18nc("current option in boot loader", " (current)") );
449 rebootMenu
->addActions(rebootActionGroup
->actions());
453 if ( sdtype
== KWorkSpace::ShutdownTypeLogout
) {
454 m_btnReboot
->setHidden(true);
455 m_btnHalt
->setHidden(true);
457 else if ( sdtype
== KWorkSpace::ShutdownTypeHalt
) {
458 m_btnReboot
->setHidden(true);
459 m_btnLogout
->setHidden(true);
461 else if ( sdtype
== KWorkSpace::ShutdownTypeReboot
) {
462 m_btnHalt
->setHidden(true);
463 m_btnLogout
->setHidden(true);
466 btnBack
= new KSMPushButton(i18n("&Cancel"), this, true);
467 btnBack
->setPixmap(KIconLoader::global()->loadIcon( "dialog-cancel", KIconLoader::NoGroup
, 16));
469 m_automaticallyDoLabel
->setPalette(palette
);
470 fnt
.setPixelSize(11);
471 m_automaticallyDoLabel
->setFont(fnt
);
472 automaticallyDoTimeout();
474 QTimer
*automaticallyDoTimer
= new QTimer(this);
475 connect(automaticallyDoTimer
, SIGNAL(timeout()), this, SLOT(automaticallyDoTimeout()));
476 automaticallyDoTimer
->start(1000);
478 bottomLayout
->addStretch();
479 bottomLayout
->addWidget(btnBack
);
480 connect(btnBack
, SIGNAL(clicked()), SLOT(reject()));
482 mainLayout
->addLayout(buttonMainLayout
);
483 mainLayout
->addSpacing(9);
484 mainLayout
->addLayout(bottomLayout
);
486 setLayout(mainLayout
);
488 if (m_svg
->hasElement("picture")) {
489 QRect pictRect
= m_svg
->elementRect("picture").toRect();
491 if (pictRect
.height() < 1 || pictRect
.width() < 1) {
493 } else if (height() > width()) {
494 m_pictureWidth
= width();
497 m_pictureWidth
= mainLayout
->sizeHint().height() * (pictRect
.width() / pictRect
.height());
498 //kDebug() << "blurk!" << buttonMainLayout->sizeHint().height() << pictRect;
501 //kDebug() << width() << m_pictureWidth;
502 //FIXME: this spaces will be taken from framesvg borders
503 if (m_pictureWidth
> 0) {
504 const int extraSpace
= 18;
505 buttonMainLayout
->insertSpacing(0, m_pictureWidth
+ extraSpace
);
507 //resize(width() + m_pictureWidth, height());
508 //kDebug() << width();
513 KDialog::centerOnScreen(this);
516 void KSMShutdownDlg::automaticallyDoTimeout()
518 QPushButton
*focusedButton
= qobject_cast
<QPushButton
*>(focusWidget());
520 if (m_automaticallyDoSeconds
<= 0) { // timeout is at 0, do selected action
521 focusedButton
->click();
522 // following code is required to provide a clean way to translate strings
523 } else if (focusedButton
== m_btnLogout
) {
524 m_automaticallyDoLabel
->setText(i18np("Logging out in 1 second.",
525 "Logging out in %1 seconds.", m_automaticallyDoSeconds
));
526 } else if (focusedButton
== m_btnHalt
) {
527 m_automaticallyDoLabel
->setText(i18np("Turning off computer in 1 second.",
528 "Turning off computer in %1 seconds.", m_automaticallyDoSeconds
));
529 } else if (focusedButton
== m_btnReboot
) {
530 m_automaticallyDoLabel
->setText(i18np("Restarting computer in 1 second.",
531 "Restarting computer in %1 seconds.", m_automaticallyDoSeconds
));
533 m_automaticallyDoLabel
->setText(QString());
536 if (m_automaticallyDoLabel
> 0) {
537 --m_automaticallyDoSeconds
;
542 void KSMShutdownDlg::paintEvent(QPaintEvent
*e
)
546 p
.setRenderHints(QPainter::Antialiasing
| QPainter::SmoothPixmapTransform
);
547 p
.setCompositionMode( QPainter::CompositionMode_Source
);
548 p
.setClipRect(e
->rect());
550 p
.fillRect(QRect(0, 0, width(), height()), Qt::transparent
);
552 if (m_svg
->hasElement("center")) {
553 m_svg
->resizeFrame(size());
554 m_svg
->paintFrame(&p
);
556 m_svg
->paint(&p
, QRect(0, 0, width(), height()), "background");
559 if (m_pictureWidth
> 0) { // implies hasElement("picture")
560 QRect r
= layout()->geometry();
561 r
.setWidth(m_pictureWidth
);
563 m_svg
->resize(m_svg
->elementRect("picture").size());
564 QPixmap picture
= m_svg
->pixmap("picture");
567 //kDebug() << 1 << r << picture.size();
568 if (r
.width() < picture
.width()) {
569 picture
= picture
.scaledToWidth(r
.width(), Qt::SmoothTransformation
);
572 if (r
.height() < picture
.height()) {
573 picture
= picture
.scaledToHeight(r
.height(), Qt::SmoothTransformation
);
577 int left
= (r
.height() - picture
.height())/2;
578 if (QApplication::isLeftToRight()) {
581 r
.moveRight(layout()->geometry().width() - left
);
584 //kDebug() << 2 << r << picture.size();
585 QRect dest
= picture
.rect();
586 dest
.moveCenter(r
.center());
587 p
.setCompositionMode( QPainter::CompositionMode_SourceOver
);
588 p
.drawPixmap(dest
, picture
, picture
.rect());
592 void KSMShutdownDlg::resizeEvent(QResizeEvent
*e
)
594 QDialog::resizeEvent( e
);
596 if( KWindowSystem::compositingActive()) {
599 QBitmap
mask(size());
600 mask
.fill(Qt::color0
);
603 m_svg
->resize(size());
604 m_svg
->paint(&p
, QRect(0, 0, width(), height()), "background");
609 void KSMShutdownDlg::slotLogout()
611 m_shutdownType
= KWorkSpace::ShutdownTypeNone
;
615 void KSMShutdownDlg::slotReboot()
617 // no boot option selected -> current
618 m_bootOption
.clear();
619 m_shutdownType
= KWorkSpace::ShutdownTypeReboot
;
623 void KSMShutdownDlg::slotReboot(QAction
* action
)
625 int opt
= action
->data().toInt();
626 if (int(rebootOptions
.size()) > opt
)
627 m_bootOption
= rebootOptions
[opt
];
628 m_shutdownType
= KWorkSpace::ShutdownTypeReboot
;
633 void KSMShutdownDlg::slotHalt()
635 m_bootOption
.clear();
636 m_shutdownType
= KWorkSpace::ShutdownTypeHalt
;
641 void KSMShutdownDlg::slotSuspend(QAction
* action
)
643 m_bootOption
.clear();
644 Solid::Control::PowerManager::SuspendMethod spdMethod
= action
->data().value
<Solid::Control::PowerManager::SuspendMethod
>();
645 KJob
*job
= Solid::Control::PowerManager::suspend( spdMethod
);
651 bool KSMShutdownDlg::confirmShutdown( bool maysd
, KWorkSpace::ShutdownType
& sdtype
, QString
& bootOption
)
653 KSMShutdownDlg
* l
= new KSMShutdownDlg( 0,
654 //KSMShutdownFeedback::self(),
656 XClassHint classHint
;
657 classHint
.res_name
= const_cast<char*>("ksmserver");
658 classHint
.res_class
= const_cast<char*>("ksmserver");
660 XSetClassHint(QX11Info::display(), l
->winId(), &classHint
);
661 bool result
= l
->exec();
662 sdtype
= l
->m_shutdownType
;
663 bootOption
= l
->m_bootOption
;