parent
3d0eefe116
commit
fa4504e965
2 changed files with 1119 additions and 0 deletions
@ -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 <hugo.pereira@free.fr>
|
||||
//
|
||||
// Largely inspired from BeSpin style
|
||||
// Copyright (C) 2007 Thomas Luebking <thomas.luebking@web.de>
|
||||
//
|
||||
// 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 <QApplication> |
||||
#include <QComboBox> |
||||
#include <QDialog> |
||||
#include <QDockWidget> |
||||
#include <QGraphicsView> |
||||
#include <QGroupBox> |
||||
#include <QLabel> |
||||
#include <QListView> |
||||
#include <QMainWindow> |
||||
#include <QMdiSubWindow> |
||||
#include <QMenuBar> |
||||
#include <QMouseEvent> |
||||
#include <QProgressBar> |
||||
#include <QScrollBar> |
||||
#include <QStatusBar> |
||||
#include <QStyle> |
||||
#include <QStyleOptionGroupBox> |
||||
#include <QTabBar> |
||||
#include <QTabWidget> |
||||
#include <QToolBar> |
||||
#include <QToolButton> |
||||
#include <QTreeView> |
||||
|
||||
#include <QTextStream> |
||||
|
||||
#if HAVE_X11 |
||||
#include <QX11Info> |
||||
#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<xcb_intern_atom_reply_t> 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<QMouseEvent*>( 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<QWidget*>( 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<QMouseEvent*>( 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<QDialog*>( widget ) && widget->isWindow() ) || |
||||
( qobject_cast<QMainWindow*>( widget ) && widget->isWindow() ) || |
||||
qobject_cast<QGroupBox*>( widget ) ) |
||||
{ return true; } |
||||
|
||||
// more accepted types, provided they are not dock widget titles
|
||||
if( ( qobject_cast<QMenuBar*>( widget ) || |
||||
qobject_cast<QTabBar*>( widget ) || |
||||
qobject_cast<QStatusBar*>( widget ) || |
||||
qobject_cast<QToolBar*>( 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<QToolButton*>( 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<QListView*>( widget->parentWidget() ) ) |
||||
{ if( listView->viewport() == widget && !isBlackListed( listView ) ) return true; } |
||||
|
||||
if( QTreeView* treeView = qobject_cast<QTreeView*>( 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<QLabel*>( widget ) ) |
||||
{ |
||||
if( label->textInteractionFlags().testFlag( Qt::TextSelectableByMouse ) ) return false; |
||||
|
||||
QWidget* parent = label->parentWidget(); |
||||
while( parent ) |
||||
{ |
||||
if( qobject_cast<QStatusBar*>( 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<QComboBox*>(child ) || |
||||
qobject_cast<QProgressBar*>( child ) || |
||||
qobject_cast<QScrollBar*>( child ) ) ) |
||||
{ return false; } |
||||
|
||||
// tool buttons
|
||||
if( QToolButton* toolButton = qobject_cast<QToolButton*>( widget ) ) |
||||
{ |
||||
if( dragMode() == StyleConfigData::WD_MINIMAL && !qobject_cast<QToolBar*>(widget->parentWidget() ) ) return false; |
||||
return toolButton->autoRaise() && !toolButton->isEnabled(); |
||||
} |
||||
|
||||
// check menubar
|
||||
if( QMenuBar* menuBar = qobject_cast<QMenuBar*>( widget ) ) |
||||
{ |
||||
|
||||
// do not drag from menubars embedded in Mdi windows
|
||||
if( findParent<QMdiSubWindow*>( 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<QToolBar*>( 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<QTabBar*>( widget ) ) |
||||
{ return tabBar->tabAt( position ) == -1; } |
||||
|
||||
/*
|
||||
check groupboxes |
||||
prevent drag if unchecking grouboxes |
||||
*/ |
||||
if( QGroupBox *groupBox = qobject_cast<QGroupBox*>( 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<QLabel*>( widget ) ) |
||||
{ if( label->textInteractionFlags().testFlag( Qt::TextSelectableByMouse ) ) return false; } |
||||
|
||||
// abstract item views
|
||||
QAbstractItemView* itemView( NULL ); |
||||
if( |
||||
( itemView = qobject_cast<QListView*>( widget->parentWidget() ) ) || |
||||
( itemView = qobject_cast<QTreeView*>( 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<QAbstractItemView*>( 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<QGraphicsView*>( 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<const char*>(&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<const char*>(&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<const QDockWidget*>( 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; |
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
@ -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 <hugo.pereira@free.fr>
|
||||
//
|
||||
// Largely inspired from BeSpin style
|
||||
// Copyright (C) 2007 Thomas Luebking <thomas.luebking@web.de>
|
||||
//
|
||||
// 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 <QEvent> |
||||
|
||||
#include <QBasicTimer> |
||||
#include <QObject> |
||||
#include <QSet> |
||||
#include <QString> |
||||
#include <QPointer> |
||||
|
||||
#include <QWidget> |
||||
|
||||
#if HAVE_X11 |
||||
#include <xcb/xcb.h> |
||||
#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<typename T> 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<QString, QString> |
||||
{ |
||||
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<ExceptionId> 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<QWidget> _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<typename T> |
||||
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<T>(parent) ) return cast; } |
||||
|
||||
return 0L; |
||||
} |
||||
|
||||
} |
||||
|
||||
#endif |
||||
Loading…
Reference in new issue