Blame style/adwaitawindowmanager.cpp

Packit 8e9c33
/*************************************************************************
Packit 8e9c33
 * Copyright (C) 2014 by Hugo Pereira Da Costa <hugo.pereira@free.fr>    *
Packit 8e9c33
 *                                                                       *
Packit 8e9c33
 * This program is free software; you can redistribute it and/or modify  *
Packit 8e9c33
 * it under the terms of the GNU General Public License as published by  *
Packit 8e9c33
 * the Free Software Foundation; either version 2 of the License, or     *
Packit 8e9c33
 * (at your option) any later version.                                   *
Packit 8e9c33
 *                                                                       *
Packit 8e9c33
 * This program is distributed in the hope that it will be useful,       *
Packit 8e9c33
 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
Packit 8e9c33
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
Packit 8e9c33
 * GNU General Public License for more details.                          *
Packit 8e9c33
 *                                                                       *
Packit 8e9c33
 * You should have received a copy of the GNU General Public License     *
Packit 8e9c33
 * along with this program; if not, write to the                         *
Packit 8e9c33
 * Free Software Foundation, Inc.,                                       *
Packit 8e9c33
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
Packit 8e9c33
 *************************************************************************/
Packit 8e9c33
Packit 8e9c33
//////////////////////////////////////////////////////////////////////////////
Packit 8e9c33
// adwaitawindowmanager.cpp
Packit 8e9c33
// pass some window mouse press/release/move event actions to window manager
Packit 8e9c33
// -------------------
Packit 8e9c33
//
Packit 8e9c33
// Copyright (c) 2014 Hugo Pereira Da Costa <hugo.pereira@free.fr>
Packit 8e9c33
//
Packit 8e9c33
// Largely inspired from BeSpin style
Packit 8e9c33
// Copyright (C) 2007 Thomas Luebking <thomas.luebking@web.de>
Packit 8e9c33
//
Packit 8e9c33
// Permission is hereby granted, free of charge, to any person obtaining a copy
Packit 8e9c33
// of this software and associated documentation files (the "Software"), to
Packit 8e9c33
// deal in the Software without restriction, including without limitation the
Packit 8e9c33
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
Packit 8e9c33
// sell copies of the Software, and to permit persons to whom the Software is
Packit 8e9c33
// furnished to do so, subject to the following conditions:
Packit 8e9c33
//
Packit 8e9c33
// The above copyright notice and this permission notice shall be included in
Packit 8e9c33
// all copies or substantial portions of the Software.
Packit 8e9c33
//
Packit 8e9c33
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Packit 8e9c33
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Packit 8e9c33
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Packit 8e9c33
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Packit 8e9c33
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Packit 8e9c33
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
Packit 8e9c33
// IN THE SOFTWARE.
Packit 8e9c33
//////////////////////////////////////////////////////////////////////////////
Packit 8e9c33
Packit 8e9c33
#include "adwaitawindowmanager.h"
Packit 8e9c33
#include "adwaitapropertynames.h"
Packit 8e9c33
#include "fakeadwaitastyleconfigdata.h"
Packit 8e9c33
#include "adwaitahelper.h"
Packit 8e9c33
Packit 8e9c33
#include <QApplication>
Packit 8e9c33
#include <QComboBox>
Packit 8e9c33
#include <QDialog>
Packit 8e9c33
#include <QDockWidget>
Packit 8e9c33
#include <QGraphicsView>
Packit 8e9c33
#include <QGroupBox>
Packit 8e9c33
#include <QLabel>
Packit 8e9c33
#include <QListView>
Packit 8e9c33
#include <QMainWindow>
Packit 8e9c33
#include <QMdiSubWindow>
Packit 8e9c33
#include <QMenuBar>
Packit 8e9c33
#include <QMouseEvent>
Packit 8e9c33
#include <QProgressBar>
Packit 8e9c33
#include <QScrollBar>
Packit 8e9c33
#include <QStatusBar>
Packit 8e9c33
#include <QStyle>
Packit 8e9c33
#include <QStyleOptionGroupBox>
Packit 8e9c33
#include <QTabBar>
Packit 8e9c33
#include <QTabWidget>
Packit 8e9c33
#include <QToolBar>
Packit 8e9c33
#include <QToolButton>
Packit 8e9c33
#include <QTreeView>
Packit 8e9c33
Packit 8e9c33
#include <QTextStream>
Packit 8e9c33
Packit 8e9c33
#if QT_VERSION >= 0x050300
Packit 8e9c33
// needed to deal with device pixel ratio
Packit 8e9c33
#include <QWindow>
Packit 8e9c33
#endif
Packit 8e9c33
Packit 8e9c33
#if ADWAITA_HAVE_X11
Packit 8e9c33
#include <QX11Info>
Packit 8e9c33
#include <xcb/xcb.h>
Packit 8e9c33
Packit 8e9c33
#if ADWAITA_USE_KDE4
Packit 8e9c33
#include <NETRootInfo>
Packit 8e9c33
#else
Packit 8e9c33
#include <NETWM>
Packit 8e9c33
#endif
Packit 8e9c33
Packit 8e9c33
#endif
Packit 8e9c33
Packit 8e9c33
#if ADWAITA_HAVE_KWAYLAND
Packit 8e9c33
#include <KWayland/Client/connection_thread.h>
Packit 8e9c33
#include <KWayland/Client/pointer.h>
Packit 8e9c33
#include <KWayland/Client/registry.h>
Packit 8e9c33
#include <KWayland/Client/shell.h>
Packit 8e9c33
#include <KWayland/Client/seat.h>
Packit 8e9c33
#endif
Packit 8e9c33
Packit 8e9c33
namespace Adwaita
Packit 8e9c33
{
Packit 8e9c33
Packit 8e9c33
    //* provide application-wise event filter
Packit 8e9c33
    /**
Packit 8e9c33
    it us used to unlock dragging and make sure event look is properly restored
Packit 8e9c33
    after a drag has occurred
Packit 8e9c33
    */
Packit 8e9c33
    class AppEventFilter: public QObject
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        public:
Packit 8e9c33
Packit 8e9c33
        //* constructor
Packit 8e9c33
        explicit AppEventFilter( WindowManager* parent ):
Packit 8e9c33
            QObject( parent ),
Packit 8e9c33
            _parent( parent )
Packit 8e9c33
        {}
Packit 8e9c33
Packit 8e9c33
        //* event filter
Packit 8e9c33
        virtual bool eventFilter( QObject* object, QEvent* event )
Packit 8e9c33
        {
Packit 8e9c33
Packit 8e9c33
            if( event->type() == QEvent::MouseButtonRelease )
Packit 8e9c33
            {
Packit 8e9c33
Packit 8e9c33
                // stop drag timer
Packit 8e9c33
                if( _parent->_dragTimer.isActive() )
Packit 8e9c33
                { _parent->resetDrag(); }
Packit 8e9c33
Packit 8e9c33
                // unlock
Packit 8e9c33
                if( _parent->isLocked() )
Packit 8e9c33
                { _parent->setLocked( false ); }
Packit 8e9c33
Packit 8e9c33
            }
Packit 8e9c33
Packit 8e9c33
            if( !_parent->enabled() ) return false;
Packit 8e9c33
Packit 8e9c33
            /*
Packit 8e9c33
            if a drag is in progress, the widget will not receive any event
Packit 8e9c33
            we trigger on the first MouseMove or MousePress events that are received
Packit 8e9c33
            by any widget in the application to detect that the drag is finished
Packit 8e9c33
            */
Packit 8e9c33
            if( _parent->useWMMoveResize() && _parent->_dragInProgress && _parent->_target && ( event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonPress ) )
Packit 8e9c33
            { return appMouseEvent( object, event ); }
Packit 8e9c33
Packit 8e9c33
            return false;
Packit 8e9c33
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        protected:
Packit 8e9c33
Packit 8e9c33
        //* application-wise event.
Packit 8e9c33
        /** needed to catch end of XMoveResize events */
Packit 8e9c33
        bool appMouseEvent( QObject*, QEvent* event )
Packit 8e9c33
        {
Packit 8e9c33
Packit 8e9c33
            #if ADWAITA_USE_KDE4
Packit 8e9c33
            // store target window (see later)
Packit 8e9c33
            QWidget* window( _parent->_target.data()->window() );
Packit 8e9c33
            #else
Packit 8e9c33
            Q_UNUSED( event );
Packit 8e9c33
            #endif
Packit 8e9c33
Packit 8e9c33
            /*
Packit 8e9c33
            post some mouseRelease event to the target, in order to counter balance
Packit 8e9c33
            the mouse press that triggered the drag. Note that it triggers a resetDrag
Packit 8e9c33
            */
Packit 8e9c33
            QMouseEvent mouseEvent( QEvent::MouseButtonRelease, _parent->_dragPoint, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
Packit 8e9c33
            qApp->sendEvent( _parent->_target.data(), &mouseEvent );
Packit 8e9c33
Packit 8e9c33
            #if ADWAITA_USE_KDE4
Packit 8e9c33
            if( event->type() == QEvent::MouseMove )
Packit 8e9c33
            {
Packit 8e9c33
                /*
Packit 8e9c33
                HACK: quickly move the main cursor out of the window and back
Packit 8e9c33
                this is needed to get the focus right for the window children
Packit 8e9c33
                the origin of this issue is unknown at the moment.
Packit 8e9c33
                This apparently got fixed with qt5
Packit 8e9c33
                */
Packit 8e9c33
                QPoint cursor = QCursor::pos();
Packit 8e9c33
                QCursor::setPos(window->mapToGlobal( window->rect().topRight() ) + QPoint(1, 0) );
Packit 8e9c33
                QCursor::setPos(cursor);
Packit 8e9c33
Packit 8e9c33
            }
Packit 8e9c33
            #endif
Packit 8e9c33
Packit 8e9c33
            return false;
Packit 8e9c33
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        private:
Packit 8e9c33
Packit 8e9c33
        //* parent
Packit 8e9c33
        WindowManager* _parent;
Packit 8e9c33
Packit 8e9c33
    };
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    WindowManager::WindowManager( QObject* parent ):
Packit 8e9c33
        QObject( parent ),
Packit 8e9c33
        _enabled( true ),
Packit 8e9c33
        _useWMMoveResize( true ),
Packit 8e9c33
        _dragMode( StyleConfigData::WD_FULL ),
Packit 8e9c33
        _dragDistance( QApplication::startDragDistance() ),
Packit 8e9c33
        _dragDelay( QApplication::startDragTime() ),
Packit 8e9c33
        _dragAboutToStart( false ),
Packit 8e9c33
        _dragInProgress( false ),
Packit 8e9c33
        _locked( false ),
Packit 8e9c33
        _cursorOverride( false )
Packit 8e9c33
        #if ADWAITA_HAVE_KWAYLAND
Packit 8e9c33
        , _seat( Q_NULLPTR )
Packit 8e9c33
        , _pointer( Q_NULLPTR )
Packit 8e9c33
        , _waylandSerial( 0 )
Packit 8e9c33
        #endif
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        // install application wise event filter
Packit 8e9c33
        _appEventFilter = new AppEventFilter( this );
Packit 8e9c33
        qApp->installEventFilter( _appEventFilter );
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    void WindowManager::initialize( void )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        setEnabled( StyleConfigData::windowDragMode() != StyleConfigData::WD_NONE );
Packit 8e9c33
        setDragMode( StyleConfigData::windowDragMode() );
Packit 8e9c33
        setUseWMMoveResize( StyleConfigData::useWMMoveResize() );
Packit 8e9c33
Packit 8e9c33
        setDragDistance( QApplication::startDragDistance() );
Packit 8e9c33
        setDragDelay( QApplication::startDragTime() );
Packit 8e9c33
Packit 8e9c33
        initializeWhiteList();
Packit 8e9c33
        initializeBlackList();
Packit 8e9c33
        initializeWayland();
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_______________________________________________________
Packit 8e9c33
    void WindowManager::initializeWayland()
Packit 8e9c33
    {
Packit 8e9c33
        #if ADWAITA_HAVE_KWAYLAND
Packit 8e9c33
        if( !Helper::isWayland() ) return;
Packit 8e9c33
Packit 8e9c33
        if( _seat ) {
Packit 8e9c33
            // already initialized
Packit 8e9c33
            return;
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        using namespace KWayland::Client;
Packit 8e9c33
        auto connection = ConnectionThread::fromApplication( this );
Packit 8e9c33
        if( !connection ) {
Packit 8e9c33
            return;
Packit 8e9c33
        }
Packit 8e9c33
        Registry *registry = new Registry( this );
Packit 8e9c33
        registry->create( connection );
Packit 8e9c33
        connect(registry, &Registry::interfacesAnnounced, this,
Packit 8e9c33
            [registry, this] {
Packit 8e9c33
                auto interface = registry->interface( Registry::Interface::Seat );
Packit 8e9c33
                if( interface.name != 0 ) {
Packit 8e9c33
                    _seat = registry->createSeat( interface.name, interface.version, this );
Packit 8e9c33
                    connect(_seat, &Seat::hasPointerChanged, this, &WindowManager::waylandHasPointerChanged);
Packit 8e9c33
                }
Packit 8e9c33
            }
Packit 8e9c33
        );
Packit 8e9c33
Packit 8e9c33
        registry->setup();
Packit 8e9c33
        connection->roundtrip();
Packit 8e9c33
        #endif
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_______________________________________________________
Packit 8e9c33
    void WindowManager::waylandHasPointerChanged(bool hasPointer)
Packit 8e9c33
    {
Packit 8e9c33
        #if ADWAITA_HAVE_KWAYLAND
Packit 8e9c33
        Q_ASSERT( _seat );
Packit 8e9c33
        if( hasPointer ) {
Packit 8e9c33
            if( !_pointer ) {
Packit 8e9c33
                _pointer = _seat->createPointer(this);
Packit 8e9c33
                connect(_pointer, &KWayland::Client::Pointer::buttonStateChanged, this,
Packit 8e9c33
                    [this] (quint32 serial) {
Packit 8e9c33
                        _waylandSerial = serial;
Packit 8e9c33
                    }
Packit 8e9c33
                );
Packit 8e9c33
            }
Packit 8e9c33
        } else {
Packit 8e9c33
            delete _pointer;
Packit 8e9c33
            _pointer = nullptr;
Packit 8e9c33
        }
Packit 8e9c33
        #else
Packit 8e9c33
        Q_UNUSED( hasPointer );
Packit 8e9c33
        #endif
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    void WindowManager::registerWidget( QWidget* widget )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        if( isBlackListed( widget ) || isDragable( widget ) )
Packit 8e9c33
        {
Packit 8e9c33
Packit 8e9c33
            /*
Packit 8e9c33
            install filter for dragable widgets.
Packit 8e9c33
            also install filter for blacklisted widgets
Packit 8e9c33
            to be able to catch the relevant events and prevent
Packit 8e9c33
            the drag to happen
Packit 8e9c33
            */
Packit 8e9c33
            widget->removeEventFilter( this );
Packit 8e9c33
            widget->installEventFilter( this );
Packit 8e9c33
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    void WindowManager::unregisterWidget( QWidget* widget )
Packit 8e9c33
    {
Packit 8e9c33
        if( widget )
Packit 8e9c33
        { widget->removeEventFilter( this ); }
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    void WindowManager::initializeWhiteList( void )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        _whiteList.clear();
Packit 8e9c33
Packit 8e9c33
        // add user specified whitelisted classnames
Packit 8e9c33
        _whiteList.insert( ExceptionId( QStringLiteral( "MplayerWindow" ) ) );
Packit 8e9c33
        _whiteList.insert( ExceptionId( QStringLiteral( "ViewSliders@kmix" ) ) );
Packit 8e9c33
        _whiteList.insert( ExceptionId( QStringLiteral( "Sidebar_Widget@konqueror" ) ) );
Packit 8e9c33
Packit 8e9c33
        foreach( const QString& exception, StyleConfigData::windowDragWhiteList() )
Packit 8e9c33
        {
Packit 8e9c33
            ExceptionId id( exception );
Packit 8e9c33
            if( !id.className().isEmpty() )
Packit 8e9c33
            { _whiteList.insert( ExceptionId( exception ) ); }
Packit 8e9c33
        }
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    void WindowManager::initializeBlackList( void )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        _blackList.clear();
Packit 8e9c33
        _blackList.insert( ExceptionId( QStringLiteral( "CustomTrackView@kdenlive" ) ) );
Packit 8e9c33
        _blackList.insert( ExceptionId( QStringLiteral( "MuseScore" ) ) );
Packit 8e9c33
        _blackList.insert( ExceptionId( QStringLiteral( "KGameCanvasWidget" ) ) );
Packit 8e9c33
        foreach( const QString& exception, StyleConfigData::windowDragBlackList() )
Packit 8e9c33
        {
Packit 8e9c33
            ExceptionId id( exception );
Packit 8e9c33
            if( !id.className().isEmpty() )
Packit 8e9c33
            { _blackList.insert( ExceptionId( exception ) ); }
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    bool WindowManager::eventFilter( QObject* object, QEvent* event )
Packit 8e9c33
    {
Packit 8e9c33
        if( !enabled() ) return false;
Packit 8e9c33
Packit 8e9c33
        switch ( event->type() )
Packit 8e9c33
        {
Packit 8e9c33
            case QEvent::MouseButtonPress:
Packit 8e9c33
            return mousePressEvent( object, event );
Packit 8e9c33
            break;
Packit 8e9c33
Packit 8e9c33
            case QEvent::MouseMove:
Packit 8e9c33
            if ( object == _target.data() ) return mouseMoveEvent( object, event );
Packit 8e9c33
            break;
Packit 8e9c33
Packit 8e9c33
            case QEvent::MouseButtonRelease:
Packit 8e9c33
            if ( _target ) return mouseReleaseEvent( object, event );
Packit 8e9c33
            break;
Packit 8e9c33
Packit 8e9c33
            default:
Packit 8e9c33
            break;
Packit 8e9c33
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        return false;
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    void WindowManager::timerEvent( QTimerEvent* event )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        if( event->timerId() == _dragTimer.timerId() )
Packit 8e9c33
        {
Packit 8e9c33
Packit 8e9c33
            _dragTimer.stop();
Packit 8e9c33
            if( _target )
Packit 8e9c33
            { startDrag( _target.data(), _globalDragPoint ); }
Packit 8e9c33
Packit 8e9c33
        } else {
Packit 8e9c33
Packit 8e9c33
            return QObject::timerEvent( event );
Packit 8e9c33
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    bool WindowManager::mousePressEvent( QObject* object, QEvent* event )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        // cast event and check buttons/modifiers
Packit 8e9c33
        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>( event );
Packit 8e9c33
        if( !( mouseEvent->modifiers() == Qt::NoModifier && mouseEvent->button() == Qt::LeftButton ) )
Packit 8e9c33
        { return false; }
Packit 8e9c33
Packit 8e9c33
        // check lock
Packit 8e9c33
        if( isLocked() ) return false;
Packit 8e9c33
        else setLocked( true );
Packit 8e9c33
Packit 8e9c33
        // cast to widget
Packit 8e9c33
        QWidget *widget = static_cast<QWidget*>( object );
Packit 8e9c33
Packit 8e9c33
        // check if widget can be dragged from current position
Packit 8e9c33
        if( isBlackListed( widget ) || !canDrag( widget ) ) return false;
Packit 8e9c33
Packit 8e9c33
        // retrieve widget's child at event position
Packit 8e9c33
        QPoint position( mouseEvent->pos() );
Packit 8e9c33
        QWidget* child = widget->childAt( position );
Packit 8e9c33
        if( !canDrag( widget, child, position ) ) return false;
Packit 8e9c33
Packit 8e9c33
        // save target and drag point
Packit 8e9c33
        _target = widget;
Packit 8e9c33
        _dragPoint = position;
Packit 8e9c33
        _globalDragPoint = mouseEvent->globalPos();
Packit 8e9c33
        _dragAboutToStart = true;
Packit 8e9c33
Packit 8e9c33
        // send a move event to the current child with same position
Packit 8e9c33
        // if received, it is caught to actually start the drag
Packit 8e9c33
        QPoint localPoint( _dragPoint );
Packit 8e9c33
        if( child ) localPoint = child->mapFrom( widget, localPoint );
Packit 8e9c33
        else child = widget;
Packit 8e9c33
        QMouseEvent localMouseEvent( QEvent::MouseMove, localPoint, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier );
Packit 8e9c33
        qApp->sendEvent( child, &localMouseEvent );
Packit 8e9c33
Packit 8e9c33
        // never eat event
Packit 8e9c33
        return false;
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    bool WindowManager::mouseMoveEvent( QObject* object, QEvent* event )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        Q_UNUSED( object );
Packit 8e9c33
Packit 8e9c33
        // stop timer
Packit 8e9c33
        if( _dragTimer.isActive() ) _dragTimer.stop();
Packit 8e9c33
Packit 8e9c33
        // cast event and check drag distance
Packit 8e9c33
        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>( event );
Packit 8e9c33
        if( !_dragInProgress )
Packit 8e9c33
        {
Packit 8e9c33
Packit 8e9c33
            if( _dragAboutToStart )
Packit 8e9c33
            {
Packit 8e9c33
                if( mouseEvent->pos() == _dragPoint )
Packit 8e9c33
                {
Packit 8e9c33
                    // start timer,
Packit 8e9c33
                    _dragAboutToStart = false;
Packit 8e9c33
                    if( _dragTimer.isActive() ) _dragTimer.stop();
Packit 8e9c33
                    _dragTimer.start( _dragDelay, this );
Packit 8e9c33
Packit 8e9c33
                } else resetDrag();
Packit 8e9c33
Packit 8e9c33
            } else if( QPoint( mouseEvent->globalPos() - _globalDragPoint ).manhattanLength() >= _dragDistance ) {
Packit 8e9c33
Packit 8e9c33
                _dragTimer.start( 0, this );
Packit 8e9c33
Packit 8e9c33
            }
Packit 8e9c33
Packit 8e9c33
            return true;
Packit 8e9c33
Packit 8e9c33
        } else if( !useWMMoveResize() ) {
Packit 8e9c33
Packit 8e9c33
            // use QWidget::move for the grabbing
Packit 8e9c33
            /* this works only if the sending object and the target are identical */
Packit 8e9c33
            QWidget* window( _target.data()->window() );
Packit 8e9c33
            window->move( window->pos() + mouseEvent->pos() - _dragPoint );
Packit 8e9c33
            return true;
Packit 8e9c33
Packit 8e9c33
        } else return false;
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    bool WindowManager::mouseReleaseEvent( QObject* object, QEvent* event )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        Q_UNUSED( object );
Packit 8e9c33
        Q_UNUSED( event );
Packit 8e9c33
        resetDrag();
Packit 8e9c33
        return false;
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    bool WindowManager::isDragable( QWidget* widget )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        // check widget
Packit 8e9c33
        if( !widget ) return false;
Packit 8e9c33
Packit 8e9c33
        // accepted default types
Packit 8e9c33
        if(
Packit 8e9c33
            ( qobject_cast<QDialog*>( widget ) && widget->isWindow() ) ||
Packit 8e9c33
            ( qobject_cast<QMainWindow*>( widget ) && widget->isWindow() ) ||
Packit 8e9c33
            qobject_cast<QGroupBox*>( widget ) )
Packit 8e9c33
        { return true; }
Packit 8e9c33
Packit 8e9c33
        // more accepted types, provided they are not dock widget titles
Packit 8e9c33
        if( ( qobject_cast<QMenuBar*>( widget ) ||
Packit 8e9c33
            qobject_cast<QTabBar*>( widget ) ||
Packit 8e9c33
            qobject_cast<QStatusBar*>( widget ) ||
Packit 8e9c33
            qobject_cast<QToolBar*>( widget ) ) &&
Packit 8e9c33
            !isDockWidgetTitle( widget ) )
Packit 8e9c33
        { return true; }
Packit 8e9c33
Packit 8e9c33
        if( widget->inherits( "KScreenSaver" ) && widget->inherits( "KCModule" ) )
Packit 8e9c33
        { return true; }
Packit 8e9c33
Packit 8e9c33
        if( isWhiteListed( widget ) )
Packit 8e9c33
        { return true; }
Packit 8e9c33
Packit 8e9c33
        // flat toolbuttons
Packit 8e9c33
        if( QToolButton* toolButton = qobject_cast<QToolButton*>( widget ) )
Packit 8e9c33
        { if( toolButton->autoRaise() ) return true; }
Packit 8e9c33
Packit 8e9c33
        // viewports
Packit 8e9c33
        /*
Packit 8e9c33
        one needs to check that
Packit 8e9c33
        1/ the widget parent is a scrollarea
Packit 8e9c33
        2/ it matches its parent viewport
Packit 8e9c33
        3/ the parent is not blacklisted
Packit 8e9c33
        */
Packit 8e9c33
        if( QListView* listView = qobject_cast<QListView*>( widget->parentWidget() ) )
Packit 8e9c33
        { if( listView->viewport() == widget && !isBlackListed( listView ) ) return true; }
Packit 8e9c33
Packit 8e9c33
        if( QTreeView* treeView = qobject_cast<QTreeView*>( widget->parentWidget() ) )
Packit 8e9c33
        { if( treeView->viewport() == widget && !isBlackListed( treeView ) ) return true; }
Packit 8e9c33
Packit 8e9c33
        /*
Packit 8e9c33
        catch labels in status bars.
Packit 8e9c33
        this is because of kstatusbar
Packit 8e9c33
        who captures buttonPress/release events
Packit 8e9c33
        */
Packit 8e9c33
        if( QLabel* label = qobject_cast<QLabel*>( widget ) )
Packit 8e9c33
        {
Packit 8e9c33
            if( label->textInteractionFlags().testFlag( Qt::TextSelectableByMouse ) ) return false;
Packit 8e9c33
Packit 8e9c33
            QWidget* parent = label->parentWidget();
Packit 8e9c33
            while( parent )
Packit 8e9c33
            {
Packit 8e9c33
                if( qobject_cast<QStatusBar*>( parent ) ) return true;
Packit 8e9c33
                parent = parent->parentWidget();
Packit 8e9c33
            }
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        return false;
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    bool WindowManager::isBlackListed( QWidget* widget )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        // check against noAnimations propery
Packit 8e9c33
        QVariant propertyValue( widget->property( PropertyNames::noWindowGrab ) );
Packit 8e9c33
        if( propertyValue.isValid() && propertyValue.toBool() ) return true;
Packit 8e9c33
Packit 8e9c33
        // list-based blacklisted widgets
Packit 8e9c33
        QString appName( qApp->applicationName() );
Packit 8e9c33
        foreach( const ExceptionId& id, _blackList )
Packit 8e9c33
        {
Packit 8e9c33
            if( !id.appName().isEmpty() && id.appName() != appName ) continue;
Packit 8e9c33
            if( id.className() == QStringLiteral( "*" ) && !id.appName().isEmpty() )
Packit 8e9c33
            {
Packit 8e9c33
                // if application name matches and all classes are selected
Packit 8e9c33
                // disable the grabbing entirely
Packit 8e9c33
                setEnabled( false );
Packit 8e9c33
                return true;
Packit 8e9c33
            }
Packit 8e9c33
            if( widget->inherits( id.className().toLatin1().data() ) ) return true;
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        return false;
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    bool WindowManager::isWhiteListed( QWidget* widget ) const
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        QString appName( qApp->applicationName() );
Packit 8e9c33
        foreach( const ExceptionId& id, _whiteList )
Packit 8e9c33
        {
Packit 8e9c33
            if( !id.appName().isEmpty() && id.appName() != appName ) continue;
Packit 8e9c33
            if( widget->inherits( id.className().toLatin1().data() ) ) return true;
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        return false;
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    bool WindowManager::canDrag( QWidget* widget )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        // check if enabled
Packit 8e9c33
        if( !enabled() ) return false;
Packit 8e9c33
Packit 8e9c33
        // assume isDragable widget is already passed
Packit 8e9c33
        // check some special cases where drag should not be effective
Packit 8e9c33
Packit 8e9c33
        // check mouse grabber
Packit 8e9c33
        if( QWidget::mouseGrabber() ) return false;
Packit 8e9c33
Packit 8e9c33
        /*
Packit 8e9c33
        check cursor shape.
Packit 8e9c33
        Assume that a changed cursor means that some action is in progress
Packit 8e9c33
        and should prevent the drag
Packit 8e9c33
        */
Packit 8e9c33
        if( widget->cursor().shape() != Qt::ArrowCursor ) return false;
Packit 8e9c33
Packit 8e9c33
        // accept
Packit 8e9c33
        return true;
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_____________________________________________________________
Packit 8e9c33
    bool WindowManager::canDrag( QWidget* widget, QWidget* child, const QPoint& position )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        // retrieve child at given position and check cursor again
Packit 8e9c33
        if( child && child->cursor().shape() != Qt::ArrowCursor ) return false;
Packit 8e9c33
Packit 8e9c33
        /*
Packit 8e9c33
        check against children from which drag should never be enabled,
Packit 8e9c33
        even if mousePress/Move has been passed to the parent
Packit 8e9c33
        */
Packit 8e9c33
        if( child && (
Packit 8e9c33
            qobject_cast<QComboBox*>(child ) ||
Packit 8e9c33
            qobject_cast<QProgressBar*>( child ) ||
Packit 8e9c33
            qobject_cast<QScrollBar*>( child ) ) )
Packit 8e9c33
        { return false; }
Packit 8e9c33
Packit 8e9c33
        // tool buttons
Packit 8e9c33
        if( QToolButton* toolButton = qobject_cast<QToolButton*>( widget ) )
Packit 8e9c33
        {
Packit 8e9c33
            if( dragMode() == StyleConfigData::WD_MINIMAL && !qobject_cast<QToolBar*>(widget->parentWidget() ) ) return false;
Packit 8e9c33
            return toolButton->autoRaise() && !toolButton->isEnabled();
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        // check menubar
Packit 8e9c33
        if( QMenuBar* menuBar = qobject_cast<QMenuBar*>( widget ) )
Packit 8e9c33
        {
Packit 8e9c33
Packit 8e9c33
            // do not drag from menubars embedded in Mdi windows
Packit 8e9c33
            if( findParent<QMdiSubWindow*>( widget ) ) return false;
Packit 8e9c33
Packit 8e9c33
            // check if there is an active action
Packit 8e9c33
            if( menuBar->activeAction() && menuBar->activeAction()->isEnabled() ) return false;
Packit 8e9c33
Packit 8e9c33
            // check if action at position exists and is enabled
Packit 8e9c33
            if( QAction* action = menuBar->actionAt( position ) )
Packit 8e9c33
            {
Packit 8e9c33
                if( action->isSeparator() ) return true;
Packit 8e9c33
                if( action->isEnabled() ) return false;
Packit 8e9c33
            }
Packit 8e9c33
Packit 8e9c33
            // return true in all other cases
Packit 8e9c33
            return true;
Packit 8e9c33
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        /*
Packit 8e9c33
        in MINIMAL mode, anything that has not been already accepted
Packit 8e9c33
        and does not come from a toolbar is rejected
Packit 8e9c33
        */
Packit 8e9c33
        if( dragMode() == StyleConfigData::WD_MINIMAL )
Packit 8e9c33
        {
Packit 8e9c33
            if( qobject_cast<QToolBar*>( widget ) ) return true;
Packit 8e9c33
            else return false;
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        /* following checks are relevant only for WD_FULL mode */
Packit 8e9c33
Packit 8e9c33
        // tabbar. Make sure no tab is under the cursor
Packit 8e9c33
        if( QTabBar* tabBar = qobject_cast<QTabBar*>( widget ) )
Packit 8e9c33
        { return tabBar->tabAt( position ) == -1; }
Packit 8e9c33
Packit 8e9c33
        /*
Packit 8e9c33
        check groupboxes
Packit 8e9c33
        prevent drag if unchecking grouboxes
Packit 8e9c33
        */
Packit 8e9c33
        if( QGroupBox *groupBox = qobject_cast<QGroupBox*>( widget ) )
Packit 8e9c33
        {
Packit 8e9c33
            // non checkable group boxes are always ok
Packit 8e9c33
            if( !groupBox->isCheckable() ) return true;
Packit 8e9c33
Packit 8e9c33
            // gather options to retrieve checkbox subcontrol rect
Packit 8e9c33
            QStyleOptionGroupBox opt;
Packit 8e9c33
            opt.initFrom( groupBox );
Packit 8e9c33
            if( groupBox->isFlat() ) opt.features |= QStyleOptionFrameV2::Flat;
Packit 8e9c33
            opt.lineWidth = 1;
Packit 8e9c33
            opt.midLineWidth = 0;
Packit 8e9c33
            opt.text = groupBox->title();
Packit 8e9c33
            opt.textAlignment = groupBox->alignment();
Packit 8e9c33
            opt.subControls = (QStyle::SC_GroupBoxFrame | QStyle::SC_GroupBoxCheckBox);
Packit 8e9c33
            if (!groupBox->title().isEmpty()) opt.subControls |= QStyle::SC_GroupBoxLabel;
Packit 8e9c33
Packit 8e9c33
            opt.state |= (groupBox->isChecked() ? QStyle::State_On : QStyle::State_Off);
Packit 8e9c33
Packit 8e9c33
            // check against groupbox checkbox
Packit 8e9c33
            if( groupBox->style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxCheckBox, groupBox ).contains( position ) )
Packit 8e9c33
            { return false; }
Packit 8e9c33
Packit 8e9c33
            // check against groupbox label
Packit 8e9c33
            if( !groupBox->title().isEmpty() && groupBox->style()->subControlRect(QStyle::CC_GroupBox, &opt, QStyle::SC_GroupBoxLabel, groupBox ).contains( position ) )
Packit 8e9c33
            { return false; }
Packit 8e9c33
Packit 8e9c33
            return true;
Packit 8e9c33
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        // labels
Packit 8e9c33
        if( QLabel* label = qobject_cast<QLabel*>( widget ) )
Packit 8e9c33
        { if( label->textInteractionFlags().testFlag( Qt::TextSelectableByMouse ) ) return false; }
Packit 8e9c33
Packit 8e9c33
        // abstract item views
Packit 8e9c33
        QAbstractItemView* itemView( nullptr );
Packit 8e9c33
        if(
Packit 8e9c33
            ( itemView = qobject_cast<QListView*>( widget->parentWidget() ) ) ||
Packit 8e9c33
            ( itemView = qobject_cast<QTreeView*>( widget->parentWidget() ) ) )
Packit 8e9c33
        {
Packit 8e9c33
            if( widget == itemView->viewport() )
Packit 8e9c33
            {
Packit 8e9c33
                // QListView
Packit 8e9c33
                if( itemView->frameShape() != QFrame::NoFrame ) return false;
Packit 8e9c33
                else if(
Packit 8e9c33
                    itemView->selectionMode() != QAbstractItemView::NoSelection &&
Packit 8e9c33
                    itemView->selectionMode() != QAbstractItemView::SingleSelection &&
Packit 8e9c33
                    itemView->model() && itemView->model()->rowCount() ) return false;
Packit 8e9c33
                else if( itemView->model() && itemView->indexAt( position ).isValid() ) return false;
Packit 8e9c33
            }
Packit 8e9c33
Packit 8e9c33
        } else if( ( itemView = qobject_cast<QAbstractItemView*>( widget->parentWidget() ) ) ) {
Packit 8e9c33
Packit 8e9c33
Packit 8e9c33
            if( widget == itemView->viewport() )
Packit 8e9c33
            {
Packit 8e9c33
                // QAbstractItemView
Packit 8e9c33
                if( itemView->frameShape() != QFrame::NoFrame ) return false;
Packit 8e9c33
                else if( itemView->indexAt( position ).isValid() ) return false;
Packit 8e9c33
            }
Packit 8e9c33
Packit 8e9c33
        } else if( QGraphicsView* graphicsView =  qobject_cast<QGraphicsView*>( widget->parentWidget() ) )  {
Packit 8e9c33
Packit 8e9c33
            if( widget == graphicsView->viewport() )
Packit 8e9c33
            {
Packit 8e9c33
                // QGraphicsView
Packit 8e9c33
                if( graphicsView->frameShape() != QFrame::NoFrame ) return false;
Packit 8e9c33
                else if( graphicsView->dragMode() != QGraphicsView::NoDrag ) return false;
Packit 8e9c33
                else if( graphicsView->itemAt( position ) ) return false;
Packit 8e9c33
            }
Packit 8e9c33
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        return true;
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //____________________________________________________________
Packit 8e9c33
    void WindowManager::resetDrag( void )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        if( (!useWMMoveResize() ) && _target && _cursorOverride ) {
Packit 8e9c33
Packit 8e9c33
          qApp->restoreOverrideCursor();
Packit 8e9c33
          _cursorOverride = false;
Packit 8e9c33
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        _target.clear();
Packit 8e9c33
        if( _dragTimer.isActive() ) _dragTimer.stop();
Packit 8e9c33
        _dragPoint = QPoint();
Packit 8e9c33
        _globalDragPoint = QPoint();
Packit 8e9c33
        _dragAboutToStart = false;
Packit 8e9c33
        _dragInProgress = false;
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //____________________________________________________________
Packit 8e9c33
    void WindowManager::startDrag( QWidget* widget, const QPoint& position )
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        if( !( enabled() && widget ) ) return;
Packit 8e9c33
        if( QWidget::mouseGrabber() ) return;
Packit 8e9c33
Packit 8e9c33
        // ungrab pointer
Packit 8e9c33
        if( useWMMoveResize() )
Packit 8e9c33
        {
Packit 8e9c33
Packit 8e9c33
            if( Helper::isX11() ) {
Packit 8e9c33
                startDragX11( widget, position );
Packit 8e9c33
            } else if( Helper::isWayland() ) {
Packit 8e9c33
                startDragWayland( widget, position );
Packit 8e9c33
            }
Packit 8e9c33
Packit 8e9c33
        } else if( !_cursorOverride ) {
Packit 8e9c33
Packit 8e9c33
            qApp->setOverrideCursor( Qt::SizeAllCursor );
Packit 8e9c33
            _cursorOverride = true;
Packit 8e9c33
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        _dragInProgress = true;
Packit 8e9c33
Packit 8e9c33
        return;
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_______________________________________________________
Packit 8e9c33
    void WindowManager::startDragX11( QWidget* widget, const QPoint& position )
Packit 8e9c33
    {
Packit 8e9c33
        #if ADWAITA_HAVE_X11
Packit 8e9c33
        // connection
Packit 8e9c33
        xcb_connection_t* connection( Helper::connection() );
Packit 8e9c33
Packit 8e9c33
        // window
Packit 8e9c33
        WId window( widget->window()->winId() );
Packit 8e9c33
Packit 8e9c33
        #if QT_VERSION >= 0x050300
Packit 8e9c33
        qreal dpiRatio = 1;
Packit 8e9c33
        QWindow* windowHandle = widget->window()->windowHandle();
Packit 8e9c33
        if( windowHandle ) dpiRatio = windowHandle->devicePixelRatio();
Packit 8e9c33
        else dpiRatio = qApp->devicePixelRatio();
Packit 8e9c33
        dpiRatio = qApp->devicePixelRatio();
Packit 8e9c33
        #else
Packit 8e9c33
        qreal dpiRatio = 1;
Packit 8e9c33
        #endif
Packit 8e9c33
Packit 8e9c33
        #if ADWAITA_USE_KDE4
Packit 8e9c33
        Display* net_connection = QX11Info::display();
Packit 8e9c33
        #else
Packit 8e9c33
        xcb_connection_t* net_connection = connection;
Packit 8e9c33
        #endif
Packit 8e9c33
Packit 8e9c33
        xcb_ungrab_pointer( connection, XCB_TIME_CURRENT_TIME );
Packit 8e9c33
        NETRootInfo( net_connection, NET::WMMoveResize ).moveResizeRequest(
Packit 8e9c33
            window, position.x() * dpiRatio,
Packit 8e9c33
            position.y() * dpiRatio,
Packit 8e9c33
            NET::Move );
Packit 8e9c33
Packit 8e9c33
        #else
Packit 8e9c33
Packit 8e9c33
        Q_UNUSED( widget );
Packit 8e9c33
        Q_UNUSED( position );
Packit 8e9c33
Packit 8e9c33
        #endif
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //_______________________________________________________
Packit 8e9c33
    void WindowManager::startDragWayland( QWidget* widget, const QPoint& position )
Packit 8e9c33
    {
Packit 8e9c33
        #if ADWAITA_HAVE_KWAYLAND
Packit 8e9c33
        if( !_seat ) {
Packit 8e9c33
            return;
Packit 8e9c33
        }
Packit 8e9c33
        /* TODO RETURN THIS
Packit 8e9c33
        QWindow* windowHandle = widget->window()->windowHandle();
Packit 8e9c33
        auto shellSurface = KWayland::Client::ShellSurface::fromWindow(windowHandle);
Packit 8e9c33
        if( !shellSurface ) {
Packit 8e9c33
            // TODO: also check for xdg-shell in future
Packit 8e9c33
            return;
Packit 8e9c33
        }
Packit 8e9c33
Packit 8e9c33
        shellSurface->requestMove( _seat, _waylandSerial );
Packit 8e9c33
        */
Packit 8e9c33
        #else
Packit 8e9c33
        Q_UNUSED( widget );
Packit 8e9c33
        Q_UNUSED( position );
Packit 8e9c33
        #endif
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //____________________________________________________________
Packit 8e9c33
    bool WindowManager::supportWMMoveResize( void ) const
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        #if ADWAITA_HAVE_KWAYLAND
Packit 8e9c33
        if( Helper::isWayland() ) {
Packit 8e9c33
            return true;
Packit 8e9c33
        }
Packit 8e9c33
        #endif
Packit 8e9c33
Packit 8e9c33
        #if ADWAITA_HAVE_X11
Packit 8e9c33
        return Helper::isX11();
Packit 8e9c33
        #else
Packit 8e9c33
        return false;
Packit 8e9c33
        #endif
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
    //____________________________________________________________
Packit 8e9c33
    bool WindowManager::isDockWidgetTitle( const QWidget* widget ) const
Packit 8e9c33
    {
Packit 8e9c33
Packit 8e9c33
        if( !widget ) return false;
Packit 8e9c33
        if( const QDockWidget* dockWidget = qobject_cast<const QDockWidget*>( widget->parent() ) )
Packit 8e9c33
        {
Packit 8e9c33
Packit 8e9c33
            return widget == dockWidget->titleBarWidget();
Packit 8e9c33
Packit 8e9c33
        } else return false;
Packit 8e9c33
Packit 8e9c33
    }
Packit 8e9c33
Packit 8e9c33
}