fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kdeui / util / kcursor.cpp
blob087d227b9d90eb2af52568aff0c4761e3071e12a
1 /* This file is part of the KDE libraries
2 Copyright (C) 1998 Kurt Granroth (granroth@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 version 2 as published by the Free Software Foundation.
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
19 #ifdef KDE_USE_FINAL
20 #ifdef KeyRelease
21 #undef KeyRelease
22 #endif
23 #endif
25 #include "kcursor.h"
26 #include "kcursor_p.h"
27 #include <kdebug.h>
29 #include <QBitmap>
30 #include <QCursor>
31 #include <QEvent>
32 #include <QAbstractScrollArea>
33 #include <QTimer>
34 #include <QWidget>
35 #include <QFile>
37 #include <kglobal.h>
38 #include <ksharedconfig.h>
39 #include <kconfiggroup.h>
41 #include <config.h>
43 #ifdef Q_WS_X11
44 #include <QX11Info>
46 #include <X11/Xlib.h>
47 #include <X11/cursorfont.h>
49 #ifdef HAVE_XCURSOR
50 # include <X11/Xcursor/Xcursor.h>
51 #endif
53 #ifdef HAVE_XFIXES
54 # include <X11/extensions/Xfixes.h>
55 #endif
57 #include <fixx11h.h>
60 namespace
62 // Borrowed from xc/lib/Xcursor/library.c
63 static const char * const standard_names[] = {
64 /* 0 */
65 "X_cursor", "arrow", "based_arrow_down", "based_arrow_up",
66 "boat", "bogosity", "bottom_left_corner", "bottom_right_corner",
67 "bottom_side", "bottom_tee", "box_spiral", "center_ptr",
68 "circle", "clock", "coffee_mug", "cross",
70 /* 32 */
71 "cross_reverse", "crosshair", "diamond_cross", "dot",
72 "dotbox", "double_arrow", "draft_large", "draft_small",
73 "draped_box", "exchange", "fleur", "gobbler",
74 "gumby", "hand1", "hand2", "heart",
76 /* 64 */
77 "icon", "iron_cross", "left_ptr", "left_side",
78 "left_tee", "leftbutton", "ll_angle", "lr_angle",
79 "man", "middlebutton", "mouse", "pencil",
80 "pirate", "plus", "question_arrow", "right_ptr",
82 /* 96 */
83 "right_side", "right_tee", "rightbutton", "rtl_logo",
84 "sailboat", "sb_down_arrow", "sb_h_double_arrow", "sb_left_arrow",
85 "sb_right_arrow", "sb_up_arrow", "sb_v_double_arrow", "shuttle",
86 "sizing", "spider", "spraycan", "star",
88 /* 128 */
89 "target", "tcross", "top_left_arrow", "top_left_corner",
90 "top_right_corner", "top_side", "top_tee", "trek",
91 "ul_angle", "umbrella", "ur_angle", "watch",
92 "xterm",
95 static Qt::HANDLE x11LoadXcursor(const QString &name)
97 #ifdef HAVE_XCURSOR
98 return XcursorLibraryLoadCursor(QX11Info::display(), QFile::encodeName(name));
99 #else
100 return 0;
101 #endif
104 static int x11CursorShape(const QString &name)
106 static QHash<QString, int> shapes;
108 // A font cursor is created from two glyphs; a shape glyph and a mask glyph
109 // stored in pairs in the font, with the shape glyph first. There's only one
110 // name for each pair. This function always returns the index for the
111 // shape glyph.
112 if (shapes.isEmpty())
114 int num = XC_num_glyphs / 2;
115 shapes.reserve(num + 5);
117 for (int i = 0; i < num; ++i)
118 shapes.insert(standard_names[i], i << 1);
120 // Qt uses alternative names for some core cursors
121 shapes.insert("size_all", XC_fleur);
122 shapes.insert("up_arrow", XC_center_ptr);
123 shapes.insert("ibeam", XC_xterm);
124 shapes.insert("wait", XC_watch);
125 shapes.insert("pointing_hand", XC_hand2);
128 return shapes.value(name, -1);
131 static Qt::HANDLE x11LoadFontCursor(const QString &name)
133 int shape = x11CursorShape(name);
135 if (shape != -1)
136 return XCreateFontCursor(QX11Info::display(), shape);
138 return 0;
141 bool x11HaveXfixes()
143 bool result = false;
145 #ifdef HAVE_XFIXES
146 int event_base, error_base;
147 if (XFixesQueryExtension(QX11Info::display(), &event_base, &error_base))
149 int major, minor;
150 XFixesQueryVersion(QX11Info::display(), &major, &minor);
151 result = (major >= 2);
153 #endif
154 return result;
157 static void x11SetCursorName(Qt::HANDLE handle, const QString &name)
159 #ifdef HAVE_XFIXES
160 static bool haveXfixes = x11HaveXfixes();
162 if (haveXfixes)
163 XFixesSetCursorName(QX11Info::display(), handle, QFile::encodeName(name));
164 #endif
167 #endif // Q_WS_X11
170 KCursor::KCursor( const QString& name, Qt::CursorShape fallback )
171 : QCursor( fallback ),
172 d( 0 )
174 #ifdef Q_WS_X11
175 Qt::HANDLE handle = x11LoadXcursor(name);
177 if (!handle)
178 handle = x11LoadFontCursor(name);
180 // Unfortunately QCursor doesn't have a setHandle()
181 if (handle)
182 *this = KCursor(handle);
184 x11SetCursorName(QCursor::handle(), name);
185 #else
186 Q_UNUSED( name )
187 #endif
191 KCursor::KCursor( const QCursor &cursor )
192 : QCursor( cursor ), d( 0 )
196 KCursor &KCursor::operator=( const KCursor &cursor )
198 QCursor::operator=( cursor );
199 return *this;
202 void KCursor::setAutoHideCursor( QWidget *w, bool enable,
203 bool customEventFilter )
205 KCursorPrivate::self()->setAutoHideCursor( w, enable, customEventFilter );
208 void KCursor::autoHideEventFilter( QObject *o, QEvent *e )
210 KCursorPrivate::self()->eventFilter( o, e );
213 void KCursor::setHideCursorDelay( int ms )
215 KCursorPrivate::self()->hideCursorDelay = ms;
218 int KCursor::hideCursorDelay()
220 return KCursorPrivate::self()->hideCursorDelay;
223 // **************************************************************************
225 KCursorPrivateAutoHideEventFilter::KCursorPrivateAutoHideEventFilter( QWidget* widget )
226 : m_widget( widget )
227 , m_wasMouseTracking( m_widget->hasMouseTracking() )
228 , m_isCursorHidden( false )
229 , m_isOwnCursor( false )
231 mouseWidget()->setMouseTracking( true );
232 connect( &m_autoHideTimer, SIGNAL( timeout() ),
233 this, SLOT( hideCursor() ) );
236 KCursorPrivateAutoHideEventFilter::~KCursorPrivateAutoHideEventFilter()
238 if( m_widget != NULL )
239 mouseWidget()->setMouseTracking( m_wasMouseTracking );
242 void KCursorPrivateAutoHideEventFilter::resetWidget()
244 m_widget = NULL;
247 void KCursorPrivateAutoHideEventFilter::hideCursor()
249 m_autoHideTimer.stop();
251 if ( m_isCursorHidden )
252 return;
254 m_isCursorHidden = true;
256 QWidget* w = mouseWidget();
258 m_isOwnCursor = w->testAttribute(Qt::WA_SetCursor);
259 if ( m_isOwnCursor )
260 m_oldCursor = w->cursor();
262 w->setCursor( QCursor( Qt::BlankCursor ) );
265 void KCursorPrivateAutoHideEventFilter::unhideCursor()
267 m_autoHideTimer.stop();
269 if ( !m_isCursorHidden )
270 return;
272 m_isCursorHidden = false;
274 QWidget* w = mouseWidget();
276 if ( w->cursor().shape() != Qt::BlankCursor ) // someone messed with the cursor already
277 return;
279 if ( m_isOwnCursor )
280 w->setCursor( m_oldCursor );
281 else
282 w->unsetCursor();
285 // The widget which gets mouse events, and that shows the cursor
286 // (that is the viewport, for a QAbstractScrollArea)
287 QWidget* KCursorPrivateAutoHideEventFilter::mouseWidget() const
289 QWidget* w = m_widget;
291 // Is w a QAbstractScrollArea ? Call setCursor on the viewport in that case.
292 QAbstractScrollArea * sv = qobject_cast<QAbstractScrollArea *>( w );
293 if ( sv )
294 w = sv->viewport();
296 return w;
299 bool KCursorPrivateAutoHideEventFilter::eventFilter( QObject *o, QEvent *e )
301 Q_UNUSED(o);
302 // o is m_widget or its viewport
303 //Q_ASSERT( o == m_widget );
305 switch ( e->type() )
307 case QEvent::Leave:
308 case QEvent::FocusOut:
309 case QEvent::WindowDeactivate:
310 unhideCursor();
311 break;
312 case QEvent::KeyPress:
313 case QEvent::ShortcutOverride:
314 hideCursor();
315 break;
316 case QEvent::Enter:
317 case QEvent::FocusIn:
318 case QEvent::MouseButtonPress:
319 case QEvent::MouseButtonRelease:
320 case QEvent::MouseButtonDblClick:
321 case QEvent::MouseMove:
322 case QEvent::Show:
323 case QEvent::Hide:
324 case QEvent::Wheel:
325 unhideCursor();
326 if ( m_widget->hasFocus() )
328 m_autoHideTimer.setSingleShot( true );
329 m_autoHideTimer.start( KCursorPrivate::self()->hideCursorDelay );
331 break;
332 default:
333 break;
336 return false;
339 KCursorPrivate * KCursorPrivate::s_self = 0L;
341 KCursorPrivate * KCursorPrivate::self()
343 if ( !s_self )
344 s_self = new KCursorPrivate;
345 // WABA: Don't delete KCursorPrivate, it serves no real purpose.
346 // Even worse it causes crashes because it seems to get deleted
347 // during ~QApplication and ~QApplication doesn't seem to like it
348 // when we delete a QCursor. No idea if that is a bug itself.
350 return s_self;
353 KCursorPrivate::KCursorPrivate()
355 hideCursorDelay = 5000; // 5s default value
357 KConfigGroup cg( KGlobal::config(), QLatin1String("KDE") );
358 enabled = cg.readEntry( QLatin1String("Autohiding cursor enabled"), true);
361 KCursorPrivate::~KCursorPrivate()
365 void KCursorPrivate::setAutoHideCursor( QWidget *w, bool enable, bool customEventFilter )
367 if ( !w || !enabled )
368 return;
370 QWidget* viewport = 0;
371 QAbstractScrollArea * sv = qobject_cast<QAbstractScrollArea *>( w );
372 if ( sv )
373 viewport = sv->viewport();
375 if ( enable )
377 if ( m_eventFilters.contains( w ) )
378 return;
379 KCursorPrivateAutoHideEventFilter* filter = new KCursorPrivateAutoHideEventFilter( w );
380 m_eventFilters.insert( w, filter );
381 if (viewport) {
382 m_eventFilters.insert( viewport, filter );
383 connect(viewport, SIGNAL(destroyed(QObject *)), this, SLOT(slotViewportDestroyed(QObject *)));
385 if ( !customEventFilter ) {
386 w->installEventFilter( filter ); // for key events
387 if (viewport)
388 viewport->installEventFilter( filter ); // for mouse events
390 connect( w, SIGNAL( destroyed(QObject*) ),
391 this, SLOT( slotWidgetDestroyed(QObject*) ) );
393 else
395 KCursorPrivateAutoHideEventFilter* filter = m_eventFilters.take( w );
396 if ( filter == 0 )
397 return;
398 w->removeEventFilter( filter );
399 if (viewport) {
400 m_eventFilters.remove( viewport );
401 disconnect(viewport, SIGNAL(destroyed(QObject *)), this, SLOT(slotViewportDestroyed(QObject *)));
402 viewport->removeEventFilter( filter );
404 delete filter;
405 disconnect( w, SIGNAL( destroyed(QObject*) ),
406 this, SLOT( slotWidgetDestroyed(QObject*) ) );
410 bool KCursorPrivate::eventFilter( QObject *o, QEvent *e )
412 if ( !enabled )
413 return false;
415 KCursorPrivateAutoHideEventFilter* filter = m_eventFilters.value( o );
417 Q_ASSERT( filter != 0 );
418 if ( filter == 0 )
419 return false;
421 return filter->eventFilter( o, e );
424 void KCursorPrivate::slotViewportDestroyed(QObject *o)
426 m_eventFilters.remove(o);
429 void KCursorPrivate::slotWidgetDestroyed( QObject* o )
431 KCursorPrivateAutoHideEventFilter* filter = m_eventFilters.take( o );
433 Q_ASSERT( filter != 0 );
435 filter->resetWidget(); // so that dtor doesn't access it
436 delete filter;
439 #include "kcursor_p.moc"