KDEUI
kcursor.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 1998 Kurt Granroth (granroth@kde.org) 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License version 2 as published by the Free Software Foundation. 00007 00008 This library is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 Library General Public License for more details. 00012 00013 You should have received a copy of the GNU Library General Public License 00014 along with this library; see the file COPYING.LIB. If not, write to 00015 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00016 Boston, MA 02110-1301, USA. 00017 */ 00018 00019 #ifdef KDE_USE_FINAL 00020 #ifdef KeyRelease 00021 #undef KeyRelease 00022 #endif 00023 #endif 00024 00025 #include "kcursor.h" 00026 #include "kcursor_p.h" 00027 #include <kdebug.h> 00028 00029 #include <QBitmap> 00030 #include <QCursor> 00031 #include <QEvent> 00032 #include <QAbstractScrollArea> 00033 #include <QTimer> 00034 #include <QWidget> 00035 #include <QFile> 00036 00037 #include <kglobal.h> 00038 #include <ksharedconfig.h> 00039 #include <kconfiggroup.h> 00040 00041 #include <config.h> 00042 00043 #ifdef Q_WS_X11 00044 #include <QX11Info> 00045 00046 #include <X11/Xlib.h> 00047 #include <X11/cursorfont.h> 00048 00049 #ifdef HAVE_XCURSOR 00050 # include <X11/Xcursor/Xcursor.h> 00051 #endif 00052 00053 #ifdef HAVE_XFIXES 00054 # include <X11/extensions/Xfixes.h> 00055 #endif 00056 00057 #include <fixx11h.h> 00058 00059 00060 namespace 00061 { 00062 // Borrowed from xc/lib/Xcursor/library.c 00063 static const char * const standard_names[] = { 00064 /* 0 */ 00065 "X_cursor", "arrow", "based_arrow_down", "based_arrow_up", 00066 "boat", "bogosity", "bottom_left_corner", "bottom_right_corner", 00067 "bottom_side", "bottom_tee", "box_spiral", "center_ptr", 00068 "circle", "clock", "coffee_mug", "cross", 00069 00070 /* 32 */ 00071 "cross_reverse", "crosshair", "diamond_cross", "dot", 00072 "dotbox", "double_arrow", "draft_large", "draft_small", 00073 "draped_box", "exchange", "fleur", "gobbler", 00074 "gumby", "hand1", "hand2", "heart", 00075 00076 /* 64 */ 00077 "icon", "iron_cross", "left_ptr", "left_side", 00078 "left_tee", "leftbutton", "ll_angle", "lr_angle", 00079 "man", "middlebutton", "mouse", "pencil", 00080 "pirate", "plus", "question_arrow", "right_ptr", 00081 00082 /* 96 */ 00083 "right_side", "right_tee", "rightbutton", "rtl_logo", 00084 "sailboat", "sb_down_arrow", "sb_h_double_arrow", "sb_left_arrow", 00085 "sb_right_arrow", "sb_up_arrow", "sb_v_double_arrow", "shuttle", 00086 "sizing", "spider", "spraycan", "star", 00087 00088 /* 128 */ 00089 "target", "tcross", "top_left_arrow", "top_left_corner", 00090 "top_right_corner", "top_side", "top_tee", "trek", 00091 "ul_angle", "umbrella", "ur_angle", "watch", 00092 "xterm", 00093 }; 00094 00095 static Qt::HANDLE x11LoadXcursor(const QString &name) 00096 { 00097 #ifdef HAVE_XCURSOR 00098 return XcursorLibraryLoadCursor(QX11Info::display(), QFile::encodeName(name)); 00099 #else 00100 return 0; 00101 #endif 00102 } 00103 00104 static int x11CursorShape(const QString &name) 00105 { 00106 static QHash<QString, int> shapes; 00107 00108 // A font cursor is created from two glyphs; a shape glyph and a mask glyph 00109 // stored in pairs in the font, with the shape glyph first. There's only one 00110 // name for each pair. This function always returns the index for the 00111 // shape glyph. 00112 if (shapes.isEmpty()) 00113 { 00114 int num = XC_num_glyphs / 2; 00115 shapes.reserve(num + 5); 00116 00117 for (int i = 0; i < num; ++i) 00118 shapes.insert(standard_names[i], i << 1); 00119 00120 // Qt uses alternative names for some core cursors 00121 shapes.insert("size_all", XC_fleur); 00122 shapes.insert("up_arrow", XC_center_ptr); 00123 shapes.insert("ibeam", XC_xterm); 00124 shapes.insert("wait", XC_watch); 00125 shapes.insert("pointing_hand", XC_hand2); 00126 } 00127 00128 return shapes.value(name, -1); 00129 } 00130 00131 static Qt::HANDLE x11LoadFontCursor(const QString &name) 00132 { 00133 int shape = x11CursorShape(name); 00134 00135 if (shape != -1) 00136 return XCreateFontCursor(QX11Info::display(), shape); 00137 00138 return 0; 00139 } 00140 00141 bool x11HaveXfixes() 00142 { 00143 bool result = false; 00144 00145 #ifdef HAVE_XFIXES 00146 int event_base, error_base; 00147 if (XFixesQueryExtension(QX11Info::display(), &event_base, &error_base)) 00148 { 00149 int major, minor; 00150 XFixesQueryVersion(QX11Info::display(), &major, &minor); 00151 result = (major >= 2); 00152 } 00153 #endif 00154 return result; 00155 } 00156 00157 static void x11SetCursorName(Qt::HANDLE handle, const QString &name) 00158 { 00159 #ifdef HAVE_XFIXES 00160 static bool haveXfixes = x11HaveXfixes(); 00161 00162 if (haveXfixes) 00163 XFixesSetCursorName(QX11Info::display(), handle, QFile::encodeName(name)); 00164 #endif 00165 } 00166 } 00167 #endif // Q_WS_X11 00168 00169 00170 KCursor::KCursor( const QString& name, Qt::CursorShape fallback ) 00171 : QCursor( fallback ), 00172 d( 0 ) 00173 { 00174 #ifdef Q_WS_X11 00175 Qt::HANDLE handle = x11LoadXcursor(name); 00176 00177 if (!handle) 00178 handle = x11LoadFontCursor(name); 00179 00180 // Unfortunately QCursor doesn't have a setHandle() 00181 if (handle) 00182 *this = KCursor(handle); 00183 00184 x11SetCursorName(QCursor::handle(), name); 00185 #else 00186 Q_UNUSED( name ) 00187 #endif 00188 } 00189 00190 00191 KCursor::KCursor( const QCursor &cursor ) 00192 : QCursor( cursor ), d( 0 ) 00193 { 00194 } 00195 00196 KCursor &KCursor::operator=( const KCursor &cursor ) 00197 { 00198 QCursor::operator=( cursor ); 00199 return *this; 00200 } 00201 00202 void KCursor::setAutoHideCursor( QWidget *w, bool enable, 00203 bool customEventFilter ) 00204 { 00205 KCursorPrivate::self()->setAutoHideCursor( w, enable, customEventFilter ); 00206 } 00207 00208 void KCursor::autoHideEventFilter( QObject *o, QEvent *e ) 00209 { 00210 KCursorPrivate::self()->eventFilter( o, e ); 00211 } 00212 00213 void KCursor::setHideCursorDelay( int ms ) 00214 { 00215 KCursorPrivate::self()->hideCursorDelay = ms; 00216 } 00217 00218 int KCursor::hideCursorDelay() 00219 { 00220 return KCursorPrivate::self()->hideCursorDelay; 00221 } 00222 00223 // ************************************************************************** 00224 00225 KCursorPrivateAutoHideEventFilter::KCursorPrivateAutoHideEventFilter( QWidget* widget ) 00226 : m_widget( widget ) 00227 , m_wasMouseTracking( m_widget->hasMouseTracking() ) 00228 , m_isCursorHidden( false ) 00229 , m_isOwnCursor( false ) 00230 { 00231 mouseWidget()->setMouseTracking( true ); 00232 connect( &m_autoHideTimer, SIGNAL( timeout() ), 00233 this, SLOT( hideCursor() ) ); 00234 } 00235 00236 KCursorPrivateAutoHideEventFilter::~KCursorPrivateAutoHideEventFilter() 00237 { 00238 if( m_widget != NULL ) 00239 mouseWidget()->setMouseTracking( m_wasMouseTracking ); 00240 } 00241 00242 void KCursorPrivateAutoHideEventFilter::resetWidget() 00243 { 00244 m_widget = NULL; 00245 } 00246 00247 void KCursorPrivateAutoHideEventFilter::hideCursor() 00248 { 00249 m_autoHideTimer.stop(); 00250 00251 if ( m_isCursorHidden ) 00252 return; 00253 00254 m_isCursorHidden = true; 00255 00256 QWidget* w = mouseWidget(); 00257 00258 m_isOwnCursor = w->testAttribute(Qt::WA_SetCursor); 00259 if ( m_isOwnCursor ) 00260 m_oldCursor = w->cursor(); 00261 00262 w->setCursor( QCursor( Qt::BlankCursor ) ); 00263 } 00264 00265 void KCursorPrivateAutoHideEventFilter::unhideCursor() 00266 { 00267 m_autoHideTimer.stop(); 00268 00269 if ( !m_isCursorHidden ) 00270 return; 00271 00272 m_isCursorHidden = false; 00273 00274 QWidget* w = mouseWidget(); 00275 00276 if ( w->cursor().shape() != Qt::BlankCursor ) // someone messed with the cursor already 00277 return; 00278 00279 if ( m_isOwnCursor ) 00280 w->setCursor( m_oldCursor ); 00281 else 00282 w->unsetCursor(); 00283 } 00284 00285 // The widget which gets mouse events, and that shows the cursor 00286 // (that is the viewport, for a QAbstractScrollArea) 00287 QWidget* KCursorPrivateAutoHideEventFilter::mouseWidget() const 00288 { 00289 QWidget* w = m_widget; 00290 00291 // Is w a QAbstractScrollArea ? Call setCursor on the viewport in that case. 00292 QAbstractScrollArea * sv = qobject_cast<QAbstractScrollArea *>( w ); 00293 if ( sv ) 00294 w = sv->viewport(); 00295 00296 return w; 00297 } 00298 00299 bool KCursorPrivateAutoHideEventFilter::eventFilter( QObject *o, QEvent *e ) 00300 { 00301 Q_UNUSED(o); 00302 // o is m_widget or its viewport 00303 //Q_ASSERT( o == m_widget ); 00304 00305 switch ( e->type() ) 00306 { 00307 case QEvent::Leave: 00308 case QEvent::FocusOut: 00309 case QEvent::WindowDeactivate: 00310 unhideCursor(); 00311 break; 00312 case QEvent::KeyPress: 00313 case QEvent::ShortcutOverride: 00314 hideCursor(); 00315 break; 00316 case QEvent::Enter: 00317 case QEvent::FocusIn: 00318 case QEvent::MouseButtonPress: 00319 case QEvent::MouseButtonRelease: 00320 case QEvent::MouseButtonDblClick: 00321 case QEvent::MouseMove: 00322 case QEvent::Show: 00323 case QEvent::Hide: 00324 case QEvent::Wheel: 00325 unhideCursor(); 00326 if ( m_widget->hasFocus() ) 00327 { 00328 m_autoHideTimer.setSingleShot( true ); 00329 m_autoHideTimer.start( KCursorPrivate::self()->hideCursorDelay ); 00330 } 00331 break; 00332 default: 00333 break; 00334 } 00335 00336 return false; 00337 } 00338 00339 KCursorPrivate * KCursorPrivate::s_self = 0L; 00340 00341 KCursorPrivate * KCursorPrivate::self() 00342 { 00343 if ( !s_self ) 00344 s_self = new KCursorPrivate; 00345 // WABA: Don't delete KCursorPrivate, it serves no real purpose. 00346 // Even worse it causes crashes because it seems to get deleted 00347 // during ~QApplication and ~QApplication doesn't seem to like it 00348 // when we delete a QCursor. No idea if that is a bug itself. 00349 00350 return s_self; 00351 } 00352 00353 KCursorPrivate::KCursorPrivate() 00354 { 00355 hideCursorDelay = 5000; // 5s default value 00356 00357 KConfigGroup cg( KGlobal::config(), QLatin1String("KDE") ); 00358 enabled = cg.readEntry( QLatin1String("Autohiding cursor enabled"), true); 00359 } 00360 00361 KCursorPrivate::~KCursorPrivate() 00362 { 00363 } 00364 00365 void KCursorPrivate::setAutoHideCursor( QWidget *w, bool enable, bool customEventFilter ) 00366 { 00367 if ( !w || !enabled ) 00368 return; 00369 00370 QWidget* viewport = 0; 00371 QAbstractScrollArea * sv = qobject_cast<QAbstractScrollArea *>( w ); 00372 if ( sv ) 00373 viewport = sv->viewport(); 00374 00375 if ( enable ) 00376 { 00377 if ( m_eventFilters.contains( w ) ) 00378 return; 00379 KCursorPrivateAutoHideEventFilter* filter = new KCursorPrivateAutoHideEventFilter( w ); 00380 m_eventFilters.insert( w, filter ); 00381 if (viewport) { 00382 m_eventFilters.insert( viewport, filter ); 00383 connect(viewport, SIGNAL(destroyed(QObject *)), this, SLOT(slotViewportDestroyed(QObject *))); 00384 } 00385 if ( !customEventFilter ) { 00386 w->installEventFilter( filter ); // for key events 00387 if (viewport) 00388 viewport->installEventFilter( filter ); // for mouse events 00389 } 00390 connect( w, SIGNAL( destroyed(QObject*) ), 00391 this, SLOT( slotWidgetDestroyed(QObject*) ) ); 00392 } 00393 else 00394 { 00395 KCursorPrivateAutoHideEventFilter* filter = m_eventFilters.take( w ); 00396 if ( filter == 0 ) 00397 return; 00398 w->removeEventFilter( filter ); 00399 if (viewport) { 00400 m_eventFilters.remove( viewport ); 00401 disconnect(viewport, SIGNAL(destroyed(QObject *)), this, SLOT(slotViewportDestroyed(QObject *))); 00402 viewport->removeEventFilter( filter ); 00403 } 00404 delete filter; 00405 disconnect( w, SIGNAL( destroyed(QObject*) ), 00406 this, SLOT( slotWidgetDestroyed(QObject*) ) ); 00407 } 00408 } 00409 00410 bool KCursorPrivate::eventFilter( QObject *o, QEvent *e ) 00411 { 00412 if ( !enabled ) 00413 return false; 00414 00415 KCursorPrivateAutoHideEventFilter* filter = m_eventFilters.value( o ); 00416 00417 Q_ASSERT( filter != 0 ); 00418 if ( filter == 0 ) 00419 return false; 00420 00421 return filter->eventFilter( o, e ); 00422 } 00423 00424 void KCursorPrivate::slotViewportDestroyed(QObject *o) 00425 { 00426 m_eventFilters.remove(o); 00427 } 00428 00429 void KCursorPrivate::slotWidgetDestroyed( QObject* o ) 00430 { 00431 KCursorPrivateAutoHideEventFilter* filter = m_eventFilters.take( o ); 00432 00433 Q_ASSERT( filter != 0 ); 00434 00435 filter->resetWidget(); // so that dtor doesn't access it 00436 delete filter; 00437 } 00438 00439 #include "kcursor_p.moc"
KDE 4.6 API Reference