diff --git a/kstyle/breezewindowmanager.cpp b/kstyle/breezewindowmanager.cpp new file mode 100644 index 00000000..887a9865 --- /dev/null +++ b/kstyle/breezewindowmanager.cpp @@ -0,0 +1,790 @@ +// krazy:excludeall=qclasses + +////////////////////////////////////////////////////////////////////////////// +// breezewindowmanager.cpp +// pass some window mouse press/release/move event actions to window manager +// ------------------- +// +// Copyright (c) 2014 Hugo Pereira Da Costa +// +// Largely inspired from BeSpin style +// Copyright (C) 2007 Thomas Luebking +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +////////////////////////////////////////////////////////////////////////////// + +#include "breezewindowmanager.h" +#include "breezewindowmanager.moc" +#include "breezepropertynames.h" +#include "breezestyleconfigdata.h" +#include "breezehelper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if HAVE_X11 +#include +#endif + +namespace Breeze +{ + + //_____________________________________________________________ + WindowManager::WindowManager( QObject* parent ): + QObject( parent ), + _enabled( true ), + _useWMMoveResize( true ), + _dragMode( StyleConfigData::WD_FULL ), + _dragDistance( QApplication::startDragDistance() ), + _dragDelay( QApplication::startDragTime() ), + _dragAboutToStart( false ), + _dragInProgress( false ), + _locked( false ), + _cursorOverride( false ), + _isX11( false ) + { + + // install application wise event filter + _appEventFilter = new AppEventFilter( this ); + qApp->installEventFilter( _appEventFilter ); + + #if HAVE_X11 + _isX11 = QGuiApplication::platformName() == QStringLiteral("xcb"); + _moveResizeAtom = 0; + if( _isX11 ) + { + // create move-resize atom + xcb_connection_t* connection( QX11Info::connection() ); + const QString atomName( QStringLiteral( "_NET_WM_MOVERESIZE" ) ); + xcb_intern_atom_cookie_t cookie( xcb_intern_atom( connection, false, atomName.size(), qPrintable( atomName ) ) ); + Helper::ScopedPointer reply( xcb_intern_atom_reply( connection, cookie, nullptr) ); + _moveResizeAtom = reply ? reply->atom:0; + } + #endif + + } + + //_____________________________________________________________ + void WindowManager::initialize( void ) + { + + setEnabled( StyleConfigData::windowDragEnabled() ); + setDragMode( StyleConfigData::windowDragMode() ); + setUseWMMoveResize( StyleConfigData::useWMMoveResize() ); + + setDragDistance( QApplication::startDragDistance() ); + setDragDelay( QApplication::startDragTime() ); + + initializeWhiteList(); + initializeBlackList(); + + } + + //_____________________________________________________________ + void WindowManager::registerWidget( QWidget* widget ) + { + + if( isBlackListed( widget ) || isDragable( widget ) ) + { + + /* + install filter for dragable widgets. + also install filter for blacklisted widgets + to be able to catch the relevant events and prevent + the drag to happen + */ + widget->removeEventFilter( this ); + widget->installEventFilter( this ); + + } + + } + + //_____________________________________________________________ + void WindowManager::unregisterWidget( QWidget* widget ) + { + if( widget ) + { widget->removeEventFilter( this ); } + } + + //_____________________________________________________________ + void WindowManager::initializeWhiteList( void ) + { + + _whiteList.clear(); + + // add user specified whitelisted classnames + _whiteList.insert( ExceptionId( QStringLiteral( "MplayerWindow" ) ) ); + _whiteList.insert( ExceptionId( QStringLiteral( "ViewSliders@kmix" ) ) ); + _whiteList.insert( ExceptionId( QStringLiteral( "Sidebar_Widget@konqueror" ) ) ); + + foreach( const QString& exception, StyleConfigData::windowDragWhiteList() ) + { + ExceptionId id( exception ); + if( !id.className().isEmpty() ) + { _whiteList.insert( ExceptionId( exception ) ); } + } + } + + //_____________________________________________________________ + void WindowManager::initializeBlackList( void ) + { + + _blackList.clear(); + _blackList.insert( ExceptionId( QStringLiteral( "CustomTrackView@kdenlive" ) ) ); + _blackList.insert( ExceptionId( QStringLiteral( "MuseScore" ) ) ); + _blackList.insert( ExceptionId( QStringLiteral( "KGameCanvasWidget" ) ) ); + foreach( const QString& exception, StyleConfigData::windowDragBlackList() ) + { + ExceptionId id( exception ); + if( !id.className().isEmpty() ) + { _blackList.insert( ExceptionId( exception ) ); } + } + + } + + //_____________________________________________________________ + bool WindowManager::eventFilter( QObject* object, QEvent* event ) + { + if( !enabled() ) return false; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + return mousePressEvent( object, event ); + break; + + case QEvent::MouseMove: + if ( object == _target.data() ) return mouseMoveEvent( object, event ); + break; + + case QEvent::MouseButtonRelease: + if ( _target ) return mouseReleaseEvent( object, event ); + break; + + default: + break; + + } + + return false; + + } + + //_____________________________________________________________ + void WindowManager::timerEvent( QTimerEvent* event ) + { + + if( event->timerId() == _dragTimer.timerId() ) + { + + _dragTimer.stop(); + if( _target ) + { startDrag( _target.data(), _globalDragPoint ); } + + } else { + + return QObject::timerEvent( event ); + + } + + } + + //_____________________________________________________________ + bool WindowManager::mousePressEvent( QObject* object, QEvent* event ) + { + + // cast event and check buttons/modifiers + QMouseEvent *mouseEvent = static_cast( event ); + if( !( mouseEvent->modifiers() == Qt::NoModifier && mouseEvent->button() == Qt::LeftButton ) ) + { return false; } + + // check lock + if( isLocked() ) return false; + else setLocked( true ); + + // cast to widget + QWidget *widget = static_cast( object ); + + // check if widget can be dragged from current position + if( isBlackListed( widget ) || !canDrag( widget ) ) return false; + + // retrieve widget's child at event position + QPoint position( mouseEvent->pos() ); + QWidget* child = widget->childAt( position ); + if( !canDrag( widget, child, position ) ) return false; + + // save target and drag point + _target = widget; + _dragPoint = position; + _globalDragPoint = mouseEvent->globalPos(); + _dragAboutToStart = true; + + // send a move event to the current child with same position + // if received, it is caught to actually start the drag + QPoint localPoint( _dragPoint ); + if( child ) localPoint = child->mapFrom( widget, localPoint ); + else child = widget; + QMouseEvent localMouseEvent( QEvent::MouseMove, localPoint, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier ); + qApp->sendEvent( child, &localMouseEvent ); + + // never eat event + return false; + + } + + //_____________________________________________________________ + bool WindowManager::mouseMoveEvent( QObject* object, QEvent* event ) + { + + Q_UNUSED( object ); + + // stop timer + if( _dragTimer.isActive() ) _dragTimer.stop(); + + // cast event and check drag distance + QMouseEvent *mouseEvent = static_cast( event ); + if( !_dragInProgress ) + { + + if( _dragAboutToStart ) + { + if( mouseEvent->globalPos() == _globalDragPoint ) + { + // start timer, + _dragAboutToStart = false; + if( _dragTimer.isActive() ) _dragTimer.stop(); + _dragTimer.start( _dragDelay, this ); + + } else resetDrag(); + + } else if( QPoint( mouseEvent->globalPos() - _globalDragPoint ).manhattanLength() >= _dragDistance ) { + + _dragTimer.start( 0, this ); + + } + + return true; + + } else if( !useWMMoveResize() ) { + + // use QWidget::move for the grabbing + /* this works only if the sending object and the target are identical */ + QWidget* window( _target.data()->window() ); + window->move( window->pos() + mouseEvent->pos() - _dragPoint ); + return true; + + } else return false; + + } + + //_____________________________________________________________ + bool WindowManager::mouseReleaseEvent( QObject* object, QEvent* event ) + { + + Q_UNUSED( object ); + Q_UNUSED( event ); + resetDrag(); + return false; + } + + //_____________________________________________________________ + bool WindowManager::isDragable( QWidget* widget ) + { + + // check widget + if( !widget ) return false; + + // accepted default types + if( + ( qobject_cast( widget ) && widget->isWindow() ) || + ( qobject_cast( widget ) && widget->isWindow() ) || + qobject_cast( widget ) ) + { return true; } + + // more accepted types, provided they are not dock widget titles + if( ( qobject_cast( widget ) || + qobject_cast( widget ) || + qobject_cast( widget ) || + qobject_cast( widget ) ) && + !isDockWidgetTitle( widget ) ) + { return true; } + + if( widget->inherits( "KScreenSaver" ) && widget->inherits( "KCModule" ) ) + { return true; } + + if( isWhiteListed( widget ) ) + { return true; } + + // flat toolbuttons + if( QToolButton* toolButton = qobject_cast( widget ) ) + { if( toolButton->autoRaise() ) return true; } + + // viewports + /* + one needs to check that + 1/ the widget parent is a scrollarea + 2/ it matches its parent viewport + 3/ the parent is not blacklisted + */ + if( QListView* listView = qobject_cast( widget->parentWidget() ) ) + { if( listView->viewport() == widget && !isBlackListed( listView ) ) return true; } + + if( QTreeView* treeView = qobject_cast( widget->parentWidget() ) ) + { if( treeView->viewport() == widget && !isBlackListed( treeView ) ) return true; } + + /* + catch labels in status bars. + this is because of kstatusbar + who captures buttonPress/release events + */ + if( QLabel* label = qobject_cast( widget ) ) + { + if( label->textInteractionFlags().testFlag( Qt::TextSelectableByMouse ) ) return false; + + QWidget* parent = label->parentWidget(); + while( parent ) + { + if( qobject_cast( parent ) ) return true; + parent = parent->parentWidget(); + } + } + + return false; + + } + + //_____________________________________________________________ + bool WindowManager::isBlackListed( QWidget* widget ) + { + + // check against noAnimations propery + QVariant propertyValue( widget->property( PropertyNames::noWindowGrab ) ); + if( propertyValue.isValid() && propertyValue.toBool() ) return true; + + // list-based blacklisted widgets + QString appName( qApp->applicationName() ); + foreach( const ExceptionId& id, _blackList ) + { + if( !id.appName().isEmpty() && id.appName() != appName ) continue; + if( id.className() == QStringLiteral( "*" ) && !id.appName().isEmpty() ) + { + // if application name matches and all classes are selected + // disable the grabbing entirely + setEnabled( false ); + return true; + } + if( widget->inherits( id.className().toLatin1().data() ) ) return true; + } + + return false; + } + + //_____________________________________________________________ + bool WindowManager::isWhiteListed( QWidget* widget ) const + { + + QString appName( qApp->applicationName() ); + foreach( const ExceptionId& id, _whiteList ) + { + if( !id.appName().isEmpty() && id.appName() != appName ) continue; + if( widget->inherits( id.className().toLatin1().data() ) ) return true; + } + + return false; + } + + //_____________________________________________________________ + bool WindowManager::canDrag( QWidget* widget ) + { + + // check if enabled + if( !enabled() ) return false; + + // assume isDragable widget is already passed + // check some special cases where drag should not be effective + + // check mouse grabber + if( QWidget::mouseGrabber() ) return false; + + /* + check cursor shape. + Assume that a changed cursor means that some action is in progress + and should prevent the drag + */ + if( widget->cursor().shape() != Qt::ArrowCursor ) return false; + + // accept + return true; + + } + + //_____________________________________________________________ + bool WindowManager::canDrag( QWidget* widget, QWidget* child, const QPoint& position ) + { + + // retrieve child at given position and check cursor again + if( child && child->cursor().shape() != Qt::ArrowCursor ) return false; + + /* + check against children from which drag should never be enabled, + even if mousePress/Move has been passed to the parent + */ + if( child && ( + qobject_cast(child ) || + qobject_cast( child ) || + qobject_cast( child ) ) ) + { return false; } + + // tool buttons + if( QToolButton* toolButton = qobject_cast( widget ) ) + { + if( dragMode() == StyleConfigData::WD_MINIMAL && !qobject_cast(widget->parentWidget() ) ) return false; + return toolButton->autoRaise() && !toolButton->isEnabled(); + } + + // check menubar + if( QMenuBar* menuBar = qobject_cast( widget ) ) + { + + // do not drag from menubars embedded in Mdi windows + if( findParent( widget ) ) return false; + + // check if there is an active action + if( menuBar->activeAction() && menuBar->activeAction()->isEnabled() ) return false; + + // check if action at position exists and is enabled + if( QAction* action = menuBar->actionAt( position ) ) + { + if( action->isSeparator() ) return true; + if( action->isEnabled() ) return false; + } + + // return true in all other cases + return true; + + } + + /* + in MINIMAL mode, anything that has not been already accepted + and does not come from a toolbar is rejected + */ + if( dragMode() == StyleConfigData::WD_MINIMAL ) + { + if( qobject_cast( widget ) ) return true; + else return false; + } + + /* following checks are relevant only for WD_FULL mode */ + + // tabbar. Make sure no tab is under the cursor + if( QTabBar* tabBar = qobject_cast( widget ) ) + { return tabBar->tabAt( position ) == -1; } + + /* + check groupboxes + prevent drag if unchecking grouboxes + */ + if( QGroupBox *groupBox = qobject_cast( widget ) ) + { + // non checkable group boxes are always ok + if( !groupBox->isCheckable() ) return true; + + // gather options to retrieve checkbox subcontrol rect + QStyleOptionGroupBox opt; + opt.initFrom( groupBox ); + if( groupBox->isFlat() ) opt.features |= QStyleOptionFrameV2::Flat; + opt.lineWidth = 1; + opt.midLineWidth = 0; + opt.text = groupBox->title(); + opt.textAlignment = groupBox->alignment(); + opt.subControls = (QStyle::SC_GroupBoxFrame | QStyle::SC_GroupBoxCheckBox); + if (!groupBox->title().isEmpty()) opt.subControls |= QStyle::SC_GroupBoxLabel; + + opt.state |= (groupBox->isChecked() ? QStyle::State_On : QStyle::State_Off); + + // check against groupbox checkbox + if( groupBox->style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxCheckBox, groupBox ).contains( position ) ) + { return false; } + + // check against groupbox label + if( !groupBox->title().isEmpty() && groupBox->style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxLabel, groupBox ).contains( position ) ) + { return false; } + + return true; + + } + + // labels + if( QLabel* label = qobject_cast( widget ) ) + { if( label->textInteractionFlags().testFlag( Qt::TextSelectableByMouse ) ) return false; } + + // abstract item views + QAbstractItemView* itemView( NULL ); + if( + ( itemView = qobject_cast( widget->parentWidget() ) ) || + ( itemView = qobject_cast( widget->parentWidget() ) ) ) + { + if( widget == itemView->viewport() ) + { + // QListView + if( itemView->frameShape() != QFrame::NoFrame ) return false; + else if( + itemView->selectionMode() != QAbstractItemView::NoSelection && + itemView->selectionMode() != QAbstractItemView::SingleSelection && + itemView->model() && itemView->model()->rowCount() ) return false; + else if( itemView->model() && itemView->indexAt( position ).isValid() ) return false; + } + + } else if( ( itemView = qobject_cast( widget->parentWidget() ) ) ) { + + + if( widget == itemView->viewport() ) + { + // QAbstractItemView + if( itemView->frameShape() != QFrame::NoFrame ) return false; + else if( itemView->indexAt( position ).isValid() ) return false; + } + + } else if( QGraphicsView* graphicsView = qobject_cast( widget->parentWidget() ) ) { + + if( widget == graphicsView->viewport() ) + { + // QGraphicsView + if( graphicsView->frameShape() != QFrame::NoFrame ) return false; + else if( graphicsView->dragMode() != QGraphicsView::NoDrag ) return false; + else if( graphicsView->itemAt( position ) ) return false; + } + + } + + return true; + + } + + //____________________________________________________________ + void WindowManager::resetDrag( void ) + { + + if( (!useWMMoveResize() ) && _target && _cursorOverride ) { + + qApp->restoreOverrideCursor(); + _cursorOverride = false; + + } + + _target.clear(); + if( _dragTimer.isActive() ) _dragTimer.stop(); + _dragPoint = QPoint(); + _globalDragPoint = QPoint(); + _dragAboutToStart = false; + _dragInProgress = false; + + } + + //____________________________________________________________ + void WindowManager::startDrag( QWidget* widget, const QPoint& position ) + { + + if( !( enabled() && widget ) ) return; + if( QWidget::mouseGrabber() ) return; + + // ungrab pointer + if( useWMMoveResize() ) + { + + #if HAVE_X11 + // connection + xcb_connection_t* connection( QX11Info::connection() ); + + // window + const WId window( widget->window()->winId() ); + + // from bespin/virtuality + xcb_button_release_event_t releaseEvent; + memset(&releaseEvent, 0, sizeof(releaseEvent)); + + releaseEvent.response_type = XCB_BUTTON_RELEASE; + // releaseEvent.sequence = ??; + releaseEvent.event = window; + releaseEvent.child = XCB_WINDOW_NONE; + releaseEvent.root = QX11Info::appRootWindow(); + releaseEvent.event_x = _dragPoint.x(); + releaseEvent.event_y = _dragPoint.x(); + releaseEvent.root_x = position.x(); + releaseEvent.root_y = position.y(); + releaseEvent.detail = XCB_BUTTON_INDEX_1; + releaseEvent.state = XCB_BUTTON_MASK_1; + releaseEvent.time = XCB_CURRENT_TIME; + releaseEvent.same_screen = true; + xcb_send_event( connection, false, window, XCB_EVENT_MASK_BUTTON_RELEASE, reinterpret_cast(&releaseEvent)); + + xcb_ungrab_pointer( connection, XCB_TIME_CURRENT_TIME ); + + // from QtCurve + xcb_client_message_event_t clientMessageEvent; + memset(&clientMessageEvent, 0, sizeof(clientMessageEvent)); + + clientMessageEvent.response_type = XCB_CLIENT_MESSAGE; + clientMessageEvent.format = 32; + clientMessageEvent.window = window; + clientMessageEvent.type = _moveResizeAtom; + clientMessageEvent.data.data32[0] = position.x(); + clientMessageEvent.data.data32[1] = position.y(); + clientMessageEvent.data.data32[2] = 8; // NET::Move + clientMessageEvent.data.data32[3] = XCB_KEY_BUT_MASK_BUTTON_1; + xcb_send_event( connection, false, QX11Info::appRootWindow(), + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, reinterpret_cast(&clientMessageEvent) ); + + xcb_flush( connection ); + + #else + + Q_UNUSED( position ); + + #endif + + } else if( !_cursorOverride ) { + + qApp->setOverrideCursor( Qt::SizeAllCursor ); + _cursorOverride = true; + + } + + _dragInProgress = true; + + return; + + } + + //____________________________________________________________ + bool WindowManager::supportWMMoveResize( void ) const + { + + #if HAVE_X11 + return _isX11; + #endif + + return false; + + } + + //____________________________________________________________ + bool WindowManager::isDockWidgetTitle( const QWidget* widget ) const + { + + if( !widget ) return false; + if( const QDockWidget* dockWidget = qobject_cast( widget->parent() ) ) + { + + return widget == dockWidget->titleBarWidget(); + + } else return false; + + } + + //____________________________________________________________ + bool WindowManager::AppEventFilter::eventFilter( QObject* object, QEvent* event ) + { + + if( event->type() == QEvent::MouseButtonRelease ) + { + + // stop drag timer + if( _parent->_dragTimer.isActive() ) + { _parent->resetDrag(); } + + // unlock + if( _parent->isLocked() ) + { _parent->setLocked( false ); } + + } + + if( !_parent->enabled() ) return false; + + /* + if a drag is in progress, the widget will not receive any event + we trigger on the first MouseMove or MousePress events that are received + by any widget in the application to detect that the drag is finished + */ + if( _parent->useWMMoveResize() && _parent->_dragInProgress && _parent->_target && ( event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonPress ) ) + { return appMouseEvent( object, event ); } + + return false; + + } + + //_____________________________________________________________ + bool WindowManager::AppEventFilter::appMouseEvent( QObject* object, QEvent* event ) + { + + Q_UNUSED( object ); + + // store target window (see later) + QWidget* window( _parent->_target.data()->window() ); + + /* + post some mouseRelease event to the target, in order to counter balance + the mouse press that triggered the drag. Note that it triggers a resetDrag + */ + QMouseEvent mouseEvent( QEvent::MouseButtonRelease, _parent->_dragPoint, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier ); + qApp->sendEvent( _parent->_target.data(), &mouseEvent ); + + if( event->type() == QEvent::MouseMove ) + { + /* + HACK: quickly move the main cursor out of the window and back + this is needed to get the focus right for the window children + the origin of this issue is unknown at the moment + */ + const QPoint cursor = QCursor::pos(); + QCursor::setPos(window->mapToGlobal( window->rect().topRight() ) + QPoint(1, 0) ); + QCursor::setPos(cursor); + + } + + return false; + + } + + +} diff --git a/kstyle/breezewindowmanager.h b/kstyle/breezewindowmanager.h new file mode 100644 index 00000000..dcd0de5d --- /dev/null +++ b/kstyle/breezewindowmanager.h @@ -0,0 +1,329 @@ +#ifndef breezewindowmanager_h +#define breezewindowmanager_h + +////////////////////////////////////////////////////////////////////////////// +// breezewindowmanager.h +// pass some window mouse press/release/move event actions to window manager +// ------------------- +// +// Copyright (c) 2014 Hugo Pereira Da Costa +// +// Largely inspired from BeSpin style +// Copyright (C) 2007 Thomas Luebking +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +////////////////////////////////////////////////////////////////////////////// + +#include + +#include +#include +#include +#include +#include + +#include + +#if HAVE_X11 +#include +#endif + +namespace Breeze +{ + + class WindowManager: public QObject + { + + Q_OBJECT + + public: + + //! constructor + explicit WindowManager( QObject* ); + + //! destructor + virtual ~WindowManager( void ) + {} + + //! initialize + /*! read relevant options from BreezeStyleConfigData */ + void initialize( void ); + + //! register widget + void registerWidget( QWidget* ); + + //! unregister widget + void unregisterWidget( QWidget* ); + + //! event filter [reimplemented] + virtual bool eventFilter( QObject*, QEvent* ); + + protected: + + //! timer event, + /*! used to start drag if button is pressed for a long enough time */ + void timerEvent( QTimerEvent* ); + + //! mouse press event + bool mousePressEvent( QObject*, QEvent* ); + + //! mouse move event + bool mouseMoveEvent( QObject*, QEvent* ); + + //! mouse release event + bool mouseReleaseEvent( QObject*, QEvent* ); + + //!@name configuration + //@{ + + //! enable state + bool enabled( void ) const + { return _enabled; } + + //! enable state + void setEnabled( bool value ) + { _enabled = value; } + + //! returns true if window manager is used for moving + bool useWMMoveResize( void ) const + { return supportWMMoveResize() && _useWMMoveResize; } + + //! use window manager for moving, when available + void setUseWMMoveResize( bool value ) + { _useWMMoveResize = value; } + + //! drag mode + int dragMode( void ) const + { return _dragMode; } + + //! drag mode + void setDragMode( int value ) + { _dragMode = value; } + + //! drag distance (pixels) + void setDragDistance( int value ) + { _dragDistance = value; } + + //! drag delay (msec) + void setDragDelay( int value ) + { _dragDelay = value; } + + //! set list of whiteListed widgets + /*! + white list is read from options and is used to adjust + per-app window dragging issues + */ + void initializeWhiteList(); + + //! set list of blackListed widgets + /*! + black list is read from options and is used to adjust + per-app window dragging issues + */ + void initializeBlackList( void ); + + //@} + + //! returns true if widget is dragable + bool isDragable( QWidget* ); + + //! returns true if widget is dragable + bool isBlackListed( QWidget* ); + + //! returns true if widget is dragable + bool isWhiteListed( QWidget* ) const; + + //! returns true if drag can be started from current widget + bool canDrag( QWidget* ); + + //! returns true if drag can be started from current widget and position + /*! child at given position is passed as second argument */ + bool canDrag( QWidget*, QWidget*, const QPoint& ); + + //! reset drag + void resetDrag( void ); + + //! start drag + void startDrag( QWidget*, const QPoint& ); + + //! returns true if window manager is used for moving + /*! right now this is true only for X11 */ + bool supportWMMoveResize( void ) const; + + //! utility function + bool isDockWidgetTitle( const QWidget* ) const; + + //!@name lock + //@{ + + void setLocked( bool value ) + { _locked = value; } + + //! lock + bool isLocked( void ) const + { return _locked; } + + //@} + + //! returns first widget matching given class, or 0L if none + template T findParent( const QWidget* ) const; + + private: + + //! enability + bool _enabled; + + //! use WM moveResize + bool _useWMMoveResize; + + //! drag mode + int _dragMode; + + //! drag distance + /*! this is copied from kwin::geometry */ + int _dragDistance; + + //! drag delay + /*! this is copied from kwin::geometry */ + int _dragDelay; + + //! wrapper for exception id + class ExceptionId: public QPair + { + public: + + //! constructor + explicit ExceptionId( const QString& value ) + { + const QStringList args( value.split( QChar::fromLatin1( '@' ) ) ); + if( args.isEmpty() ) return; + second = args[0].trimmed(); + if( args.size()>1 ) first = args[1].trimmed(); + } + + const QString& appName( void ) const + { return first; } + + const QString& className( void ) const + { return second; } + + }; + + //! exception set + typedef QSet ExceptionSet; + + //! list of white listed special widgets + /*! + it is read from options and is used to adjust + per-app window dragging issues + */ + ExceptionSet _whiteList; + + //! list of black listed special widgets + /*! + it is read from options and is used to adjust + per-app window dragging issues + */ + ExceptionSet _blackList; + + //! drag point + QPoint _dragPoint; + QPoint _globalDragPoint; + + //! drag timer + QBasicTimer _dragTimer; + + //! target being dragged + /*! QPointer is used in case the target gets deleted while drag is in progress */ + QPointer _target; + + //! true if drag is about to start + bool _dragAboutToStart; + + //! true if drag is in progress + bool _dragInProgress; + + //! true if drag is locked + bool _locked; + + //! cursor override + /*! used to keep track of application cursor being overridden when dragging in non-WM mode */ + bool _cursorOverride; + + //! provide application-wise event filter + /*! + it us used to unlock dragging and make sure event look is properly restored + after a drag has occurred + */ + class AppEventFilter: public QObject + { + + public: + + //! constructor + explicit AppEventFilter( WindowManager* parent ): + QObject( parent ), + _parent( parent ) + {} + + //! event filter + virtual bool eventFilter( QObject*, QEvent* ); + + protected: + + //! application-wise event. + /*! needed to catch end of XMoveResize events */ + bool appMouseEvent( QObject*, QEvent* ); + + private: + + //! parent + WindowManager* _parent; + + }; + + //! application event filter + AppEventFilter* _appEventFilter; + + #if HAVE_X11 + xcb_atom_t _moveResizeAtom; + #endif + + bool _isX11; + + //! allow access of all private members to the app event filter + friend class AppEventFilter; + + }; + + //____________________________________________________________________ + template + T WindowManager::findParent( const QWidget* widget ) const + { + + if( !widget ) return 0L; + for( QWidget* parent = widget->parentWidget(); parent; parent = parent->parentWidget() ) + { if( T cast = qobject_cast(parent) ) return cast; } + + return 0L; + } + +} + +#endif