Merge branch 'central-widget'
[krunner.git] / startupid.cpp
blob4f6790ee773a6b64b9b908a7dc9b08828e9e392a
1 /* This file is part of the KDE project
2 Copyright (C) 2001 Lubos Lunak <l.lunak@kde.org>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "startupid.h"
22 #include <config-X11.h>
24 #include "klaunchsettings.h"
26 #include <kiconloader.h>
27 #include <QCursor>
28 #include <kapplication.h>
29 #include <QImage>
30 #include <QBitmap>
31 //Added by qt3to4:
32 #include <QPixmap>
33 #include <QPainter>
34 #include <kconfig.h>
35 #include <X11/Xlib.h>
36 #include <QX11Info>
38 #define KDE_STARTUP_ICON "kmenu"
40 #ifdef HAVE_XCURSOR
41 #include <X11/Xcursor/Xcursor.h>
42 #endif
44 enum kde_startup_status_enum { StartupPre, StartupIn, StartupDone };
45 static kde_startup_status_enum kde_startup_status = StartupPre;
46 static Atom kde_splash_progress;
48 StartupId::StartupId( QWidget* parent, const char* name )
49 : QWidget( parent ),
50 startup_info( KStartupInfo::CleanOnCantDetect ),
51 startup_widget( NULL ),
52 blinking( true ),
53 bouncing( false )
55 setObjectName( name );
56 hide(); // is QWidget only because of x11Event()
57 if( kde_startup_status == StartupPre )
59 kde_splash_progress = XInternAtom( QX11Info::display(), "_KDE_SPLASH_PROGRESS", False );
60 XWindowAttributes attrs;
61 XGetWindowAttributes( QX11Info::display(), QX11Info::appRootWindow(), &attrs);
62 XSelectInput( QX11Info::display(), QX11Info::appRootWindow(), attrs.your_event_mask | SubstructureNotifyMask);
63 kapp->installX11EventFilter( this );
65 update_timer.setSingleShot( true );
66 connect( &update_timer, SIGNAL( timeout()), SLOT( update_startupid()));
67 connect( &startup_info,
68 SIGNAL( gotNewStartup( const KStartupInfoId&, const KStartupInfoData& )),
69 SLOT( gotNewStartup( const KStartupInfoId&, const KStartupInfoData& )));
70 connect( &startup_info,
71 SIGNAL( gotStartupChange( const KStartupInfoId&, const KStartupInfoData& )),
72 SLOT( gotStartupChange( const KStartupInfoId&, const KStartupInfoData& )));
73 connect( &startup_info,
74 SIGNAL( gotRemoveStartup( const KStartupInfoId&, const KStartupInfoData& )),
75 SLOT( gotRemoveStartup( const KStartupInfoId& )));
78 StartupId::~StartupId()
80 stop_startupid();
83 void StartupId::configure()
85 startup_info.setTimeout( KLaunchSettings::timeout());
86 blinking = KLaunchSettings::blinking();
87 bouncing = KLaunchSettings::bouncing();
90 void StartupId::gotNewStartup( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
92 QString icon = data_P.findIcon();
93 current_startup = id_P;
94 startups[ id_P ] = icon;
95 start_startupid( icon );
98 void StartupId::gotStartupChange( const KStartupInfoId& id_P, const KStartupInfoData& data_P )
100 if( current_startup == id_P )
102 QString icon = data_P.findIcon();
103 if( !icon.isEmpty() && icon != startups[ current_startup ] )
105 startups[ id_P ] = icon;
106 start_startupid( icon );
111 void StartupId::gotRemoveStartup( const KStartupInfoId& id_P )
113 startups.remove( id_P );
114 if( startups.count() == 0 )
116 current_startup = KStartupInfoId(); // null
117 if( kde_startup_status == StartupIn )
118 start_startupid( KDE_STARTUP_ICON );
119 else
120 stop_startupid();
121 return;
123 current_startup = startups.begin().key();
124 start_startupid( startups[ current_startup ] );
127 bool StartupId::x11Event( XEvent* e )
129 if( e->type == ClientMessage && e->xclient.window == QX11Info::appRootWindow()
130 && e->xclient.message_type == kde_splash_progress )
132 const char* s = e->xclient.data.b;
133 if( strcmp( s, "kicker" ) == 0 && kde_startup_status == StartupPre )
135 kde_startup_status = StartupIn;
136 if( startups.count() == 0 )
137 start_startupid( KDE_STARTUP_ICON );
138 // 60(?) sec timeout - shouldn't be hopefully needed anyway, ksmserver should have it too
139 QTimer::singleShot( 60000, this, SLOT( finishKDEStartup()));
141 else if( strcmp( s, "session ready" ) == 0 && kde_startup_status < StartupDone )
142 QTimer::singleShot( 2000, this, SLOT( finishKDEStartup()));
144 return false;
147 void StartupId::finishKDEStartup()
149 kde_startup_status = StartupDone;
150 kapp->removeX11EventFilter( this );
151 if( startups.count() == 0 )
152 stop_startupid();
155 void StartupId::stop_startupid()
157 delete startup_widget;
158 startup_widget = NULL;
159 if( blinking )
160 for( int i = 0;
161 i < NUM_BLINKING_PIXMAPS;
162 ++i )
163 pixmaps[ i ] = QPixmap(); // null
164 update_timer.stop();
167 static QPixmap scalePixmap( const QPixmap& pm, int w, int h )
169 QImage scaled = pm.toImage().scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
170 if (scaled.format() != QImage::Format_ARGB32_Premultiplied && scaled.format() != QImage::Format_ARGB32)
171 scaled = scaled.convertToFormat(QImage::Format_ARGB32_Premultiplied);
173 QImage result(20, 20, QImage::Format_ARGB32_Premultiplied);
174 QPainter p(&result);
175 p.setCompositionMode(QPainter::CompositionMode_Source);
176 p.fillRect(result.rect(), Qt::transparent);
177 p.drawImage((20 - w) / 2, (20 - h) / 2, scaled, 0, 0, w, h);
178 return QPixmap::fromImage(result);
181 void StartupId::start_startupid( const QString& icon_P )
184 const QColor startup_colors[ StartupId::NUM_BLINKING_PIXMAPS ]
185 = { Qt::black, Qt::darkGray, Qt::lightGray, Qt::white, Qt::white };
188 QPixmap icon_pixmap = KIconLoader::global()->loadIcon( icon_P, K3Icon::Small, 0,
189 K3Icon::DefaultState, QStringList(), 0, true ); // return null pixmap if not found
190 if( icon_pixmap.isNull())
191 icon_pixmap = SmallIcon( "exec" );
192 if( startup_widget == NULL )
194 startup_widget = new QWidget( 0, Qt::X11BypassWindowManagerHint );
195 XSetWindowAttributes attr;
196 attr.save_under = True; // useful saveunder if possible to avoid redrawing
197 XChangeWindowAttributes( QX11Info::display(), startup_widget->winId(), CWSaveUnder, &attr );
199 startup_widget->resize( icon_pixmap.width(), icon_pixmap.height());
200 if( blinking )
202 startup_widget->clearMask();
203 int window_w = icon_pixmap.width();
204 int window_h = icon_pixmap.height();
205 for( int i = 0;
206 i < NUM_BLINKING_PIXMAPS;
207 ++i )
209 pixmaps[ i ] = QPixmap( window_w, window_h );
210 pixmaps[ i ].fill( startup_colors[ i ] );
211 QPainter p( &pixmaps[ i ] );
212 p.drawPixmap( 0, 0, icon_pixmap );
213 p.end();
215 color_index = 0;
217 else if( bouncing )
219 startup_widget->resize( 20, 20 );
220 pixmaps[ 0 ] = scalePixmap( icon_pixmap, 16, 16 );
221 pixmaps[ 1 ] = scalePixmap( icon_pixmap, 14, 18 );
222 pixmaps[ 2 ] = scalePixmap( icon_pixmap, 12, 20 );
223 pixmaps[ 3 ] = scalePixmap( icon_pixmap, 18, 14 );
224 pixmaps[ 4 ] = scalePixmap( icon_pixmap, 20, 12 );
225 frame = 0;
227 else
229 if( !icon_pixmap.mask().isNull() )
230 startup_widget->setMask( icon_pixmap.mask() );
231 else
232 startup_widget->clearMask();
234 QPalette palette;
235 palette.setBrush( startup_widget->backgroundRole(), QBrush( icon_pixmap ) );
236 startup_widget->setPalette( palette );
237 startup_widget->update();
239 update_startupid();
242 namespace
244 const int X_DIFF = 15;
245 const int Y_DIFF = 15;
246 const int color_to_pixmap[] = { 0, 1, 2, 3, 2, 1 };
247 const int frame_to_yoffset[] =
249 -5, -1, 2, 5, 8, 10, 12, 13, 15, 15, 15, 15, 14, 12, 10, 8, 5, 2, -1, -5
251 const int frame_to_pixmap[] =
253 0, 0, 0, 1, 2, 2, 1, 0, 3, 4, 4, 3, 0, 1, 2, 2, 1, 0, 0, 0
257 void StartupId::update_startupid()
259 int yoffset = 0;
260 if( blinking )
262 QPalette palette;
263 palette.setBrush( startup_widget->backgroundRole(), QBrush( pixmaps[ color_to_pixmap[ color_index ]] ) );
264 startup_widget->setPalette( palette );
265 if( ++color_index >= ( sizeof( color_to_pixmap ) / sizeof( color_to_pixmap[ 0 ] )))
266 color_index = 0;
268 else if( bouncing )
270 yoffset = frame_to_yoffset[ frame ];
271 QPixmap pm = pixmaps[ frame_to_pixmap[ frame ] ];
272 QPalette palette;
273 palette.setBrush( startup_widget->backgroundRole(), QBrush( pm ) );
274 startup_widget->setPalette( palette );
275 if ( !pm.mask().isNull() )
276 startup_widget->setMask( pm.mask() );
277 else
278 startup_widget->clearMask();
279 if ( ++frame >= ( sizeof( frame_to_yoffset ) / sizeof( frame_to_yoffset[ 0 ] ) ) )
280 frame = 0;
282 Window dummy1, dummy2;
283 int x, y;
284 int dummy3, dummy4;
285 unsigned int dummy5;
286 if( !XQueryPointer( QX11Info::display(), QX11Info::appRootWindow(), &dummy1, &dummy2, &x, &y, &dummy3, &dummy4, &dummy5 ))
288 startup_widget->hide();
289 update_timer.start( 100 );
290 return;
292 QPoint c_pos( x, y );
293 int cursor_size = 0;
294 #ifdef HAVE_XCURSOR
295 cursor_size = XcursorGetDefaultSize( QX11Info::display());
296 #endif
297 int X_DIFF;
298 if( cursor_size <= 16 )
299 X_DIFF = 8 + 7;
300 else if( cursor_size <= 32 )
301 X_DIFF = 16 + 7;
302 else if( cursor_size <= 48 )
303 X_DIFF = 24 + 7;
304 else
305 X_DIFF = 32 + 7;
306 int Y_DIFF = X_DIFF;
307 if( startup_widget->x() != c_pos.x() + X_DIFF
308 || startup_widget->y() != c_pos.y() + Y_DIFF + yoffset )
309 startup_widget->move( c_pos.x() + X_DIFF, c_pos.y() + Y_DIFF + yoffset );
310 startup_widget->show();
311 XRaiseWindow( QX11Info::display(), startup_widget->winId());
312 update_timer.start( bouncing ? 30 : 100 );
313 QApplication::flush();
316 #include "startupid.moc"