src/gui/kernel/qdnd_qws.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qapplication.h"
       
    43 
       
    44 #ifndef QT_NO_DRAGANDDROP
       
    45 
       
    46 #include "qwidget.h"
       
    47 #include "qdatetime.h"
       
    48 #include "qbitmap.h"
       
    49 #include "qcursor.h"
       
    50 #include "qevent.h"
       
    51 #include "qpainter.h"
       
    52 #include "qdnd_p.h"
       
    53 
       
    54 QT_BEGIN_NAMESPACE
       
    55 
       
    56 QT_USE_NAMESPACE
       
    57 
       
    58 static QPixmap *defaultPm = 0;
       
    59 static const int default_pm_hotx = -2;
       
    60 static const int default_pm_hoty = -16;
       
    61 static const char *const default_pm[] = {
       
    62 "13 9 3 1",
       
    63 ".      c None",
       
    64 "       c #000000",
       
    65 "X      c #FFFFFF",
       
    66 "X X X X X X X",
       
    67 " X X X X X X ",
       
    68 "X ......... X",
       
    69 " X.........X ",
       
    70 "X ......... X",
       
    71 " X.........X ",
       
    72 "X ......... X",
       
    73 " X X X X X X ",
       
    74 "X X X X X X X",
       
    75 };
       
    76 
       
    77 // Shift/Ctrl handling, and final drop status
       
    78 static Qt::DropAction global_accepted_action = Qt::CopyAction;
       
    79 static Qt::DropActions possible_actions = Qt::IgnoreAction;
       
    80 
       
    81 
       
    82 // static variables in place of a proper cross-process solution
       
    83 static QDrag *drag_object;
       
    84 static bool qt_qws_dnd_dragging = false;
       
    85 
       
    86 
       
    87 static Qt::KeyboardModifiers oldstate;
       
    88 
       
    89 class QShapedPixmapWidget : public QWidget {
       
    90     QPixmap pixmap;
       
    91 public:
       
    92     QShapedPixmapWidget() :
       
    93         QWidget(0, Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint)
       
    94     {
       
    95         // ### Temporary workaround for 4.2-rc1!!! To prevent flickering when
       
    96         // using drag'n drop in a client application. (task 126956)
       
    97         // setAttribute() should be done unconditionally!
       
    98         if (QApplication::type() == QApplication::GuiServer)
       
    99             setAttribute(Qt::WA_TransparentForMouseEvents);
       
   100     }
       
   101 
       
   102     void setPixmap(QPixmap pm)
       
   103     {
       
   104         pixmap = pm;
       
   105         if (!pixmap.mask().isNull()) {
       
   106             setMask(pixmap.mask());
       
   107         } else {
       
   108             clearMask();
       
   109         }
       
   110         resize(pm.width(),pm.height());
       
   111     }
       
   112 
       
   113     void paintEvent(QPaintEvent*)
       
   114     {
       
   115         QPainter p(this);
       
   116         p.drawPixmap(0,0,pixmap);
       
   117     }
       
   118 };
       
   119 
       
   120 
       
   121 static QShapedPixmapWidget *qt_qws_dnd_deco = 0;
       
   122 
       
   123 
       
   124 void QDragManager::updatePixmap()
       
   125 {
       
   126     if (qt_qws_dnd_deco) {
       
   127         QPixmap pm;
       
   128         QPoint pm_hot(default_pm_hotx,default_pm_hoty);
       
   129         if (drag_object) {
       
   130             pm = drag_object->pixmap();
       
   131             if (!pm.isNull())
       
   132                 pm_hot = drag_object->hotSpot();
       
   133         }
       
   134         if (pm.isNull()) {
       
   135             if (!defaultPm)
       
   136                 defaultPm = new QPixmap(default_pm);
       
   137             pm = *defaultPm;
       
   138         }
       
   139         qt_qws_dnd_deco->setPixmap(pm);
       
   140         qt_qws_dnd_deco->move(QCursor::pos()-pm_hot);
       
   141         if (willDrop) {
       
   142             qt_qws_dnd_deco->show();
       
   143         } else {
       
   144             qt_qws_dnd_deco->hide();
       
   145         }
       
   146     }
       
   147 }
       
   148 
       
   149 void QDragManager::timerEvent(QTimerEvent *) { }
       
   150 
       
   151 void QDragManager::move(const QPoint &) { }
       
   152 
       
   153 void QDragManager::updateCursor()
       
   154 {
       
   155 #ifndef QT_NO_CURSOR
       
   156     if (willDrop) {
       
   157         if (qt_qws_dnd_deco)
       
   158             qt_qws_dnd_deco->show();
       
   159         if (currentActionForOverrideCursor != global_accepted_action) {
       
   160             QApplication::changeOverrideCursor(QCursor(dragCursor(global_accepted_action), 0, 0));
       
   161             currentActionForOverrideCursor = global_accepted_action;
       
   162         }
       
   163     } else {
       
   164         QCursor *overrideCursor = QApplication::overrideCursor();
       
   165         if (!overrideCursor || overrideCursor->shape() != Qt::ForbiddenCursor) {
       
   166             QApplication::changeOverrideCursor(QCursor(Qt::ForbiddenCursor));
       
   167             currentActionForOverrideCursor = Qt::IgnoreAction;
       
   168         }
       
   169         if (qt_qws_dnd_deco)
       
   170             qt_qws_dnd_deco->hide();
       
   171     }
       
   172 #endif
       
   173 }
       
   174 
       
   175 
       
   176 bool QDragManager::eventFilter(QObject *o, QEvent *e)
       
   177 {
       
   178  if (beingCancelled) {
       
   179      if (e->type() == QEvent::KeyRelease && static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
       
   180             qApp->removeEventFilter(this);
       
   181             Q_ASSERT(object == 0);
       
   182             beingCancelled = false;
       
   183             eventLoop->exit();
       
   184             return true; // block the key release
       
   185         }
       
   186         return false;
       
   187     }
       
   188 
       
   189 
       
   190 
       
   191     if (!o->isWidgetType())
       
   192         return false;
       
   193 
       
   194     switch(e->type()) {
       
   195 
       
   196         case QEvent::KeyPress:
       
   197         case QEvent::KeyRelease:
       
   198         {
       
   199             QKeyEvent *ke = ((QKeyEvent*)e);
       
   200             if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
       
   201                 cancel();
       
   202                 qApp->removeEventFilter(this);
       
   203                 beingCancelled = false;
       
   204                 eventLoop->exit();
       
   205             } else {
       
   206                 updateCursor();
       
   207             }
       
   208             return true; // Eat all key events
       
   209         }
       
   210 
       
   211         case QEvent::MouseButtonPress:
       
   212         case QEvent::MouseMove:
       
   213         {
       
   214             if (!object) { //#### this should not happen
       
   215                 qWarning("QDragManager::eventFilter: No object");
       
   216                 return true;
       
   217             }
       
   218 
       
   219             QDragManager *manager = QDragManager::self();
       
   220             QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
       
   221             if (manager->object)
       
   222                 possible_actions =  manager->dragPrivate()->possible_actions;
       
   223             else
       
   224                 possible_actions = Qt::IgnoreAction;
       
   225 
       
   226             QMouseEvent *me = (QMouseEvent *)e;
       
   227             if (me->buttons()) {
       
   228                 Qt::DropAction prevAction = global_accepted_action;
       
   229                 QWidget *cw = QApplication::widgetAt(me->globalPos());
       
   230 
       
   231                 // Fix for when we move mouse on to the deco widget
       
   232                 if (qt_qws_dnd_deco && cw == qt_qws_dnd_deco)
       
   233                     cw = object->target();
       
   234 
       
   235                 while (cw && !cw->acceptDrops() && !cw->isWindow())
       
   236                     cw = cw->parentWidget();
       
   237 
       
   238                 if (object->target() != cw) {
       
   239                     if (object->target()) {
       
   240                         QDragLeaveEvent dle;
       
   241                         QApplication::sendEvent(object->target(), &dle);
       
   242                         willDrop = false;
       
   243                         global_accepted_action = Qt::IgnoreAction;
       
   244                         updateCursor();
       
   245                         restoreCursor = true;
       
   246                         object->d_func()->target = 0;
       
   247                     }
       
   248                     if (cw && cw->acceptDrops()) {
       
   249                         object->d_func()->target = cw;
       
   250                         QDragEnterEvent dee(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData,
       
   251                                             me->buttons(), me->modifiers());
       
   252                         QApplication::sendEvent(object->target(), &dee);
       
   253                         willDrop = dee.isAccepted() && dee.dropAction() != Qt::IgnoreAction;
       
   254                         global_accepted_action = willDrop ? dee.dropAction() : Qt::IgnoreAction;
       
   255                         updateCursor();
       
   256                         restoreCursor = true;
       
   257                     }
       
   258                 } else if (cw) {
       
   259                     QDragMoveEvent dme(cw->mapFromGlobal(me->globalPos()), possible_actions, dropData,
       
   260                                        me->buttons(), me->modifiers());
       
   261                     if (global_accepted_action != Qt::IgnoreAction) {
       
   262                         dme.setDropAction(global_accepted_action);
       
   263                         dme.accept();
       
   264                     }
       
   265                     QApplication::sendEvent(cw, &dme);
       
   266                     willDrop = dme.isAccepted();
       
   267                     global_accepted_action = willDrop ? dme.dropAction() : Qt::IgnoreAction;
       
   268                     updatePixmap();
       
   269                     updateCursor();
       
   270                 }
       
   271                 if (global_accepted_action != prevAction)
       
   272                     emitActionChanged(global_accepted_action);
       
   273             }
       
   274             return true; // Eat all mouse events
       
   275         }
       
   276 
       
   277         case QEvent::MouseButtonRelease:
       
   278         {
       
   279             qApp->removeEventFilter(this);
       
   280             if (restoreCursor) {
       
   281                 willDrop = false;
       
   282 #ifndef QT_NO_CURSOR
       
   283                 QApplication::restoreOverrideCursor();
       
   284 #endif
       
   285                 restoreCursor = false;
       
   286             }
       
   287             if (object && object->target()) {
       
   288                 QMouseEvent *me = (QMouseEvent *)e;
       
   289 
       
   290                 QDragManager *manager = QDragManager::self();
       
   291                 QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
       
   292 
       
   293                 QDropEvent de(object->target()->mapFromGlobal(me->globalPos()), possible_actions, dropData,
       
   294                               me->buttons(), me->modifiers());
       
   295                 QApplication::sendEvent(object->target(), &de);
       
   296                 if (de.isAccepted())
       
   297                     global_accepted_action = de.dropAction();
       
   298                 else
       
   299                     global_accepted_action = Qt::IgnoreAction;
       
   300 
       
   301                 if (object)
       
   302                     object->deleteLater();
       
   303                 drag_object = object = 0;
       
   304             }
       
   305             eventLoop->exit();
       
   306             return true; // Eat all mouse events
       
   307         }
       
   308 
       
   309         default:
       
   310              break;
       
   311     }
       
   312 
       
   313     return false;
       
   314 }
       
   315 
       
   316 Qt::DropAction QDragManager::drag(QDrag *o)
       
   317 {
       
   318     if (object == o || !o || !o->source())
       
   319          return Qt::IgnoreAction;
       
   320 
       
   321     if (object) {
       
   322         cancel();
       
   323         qApp->removeEventFilter(this);
       
   324         beingCancelled = false;
       
   325     }
       
   326 
       
   327     object = drag_object = o;
       
   328     qt_qws_dnd_deco = new QShapedPixmapWidget();
       
   329     oldstate = Qt::NoModifier; // #### Should use state that caused the drag
       
   330 //    drag_mode = mode;
       
   331 
       
   332     willDrop = false;
       
   333     updatePixmap();
       
   334     updateCursor();
       
   335     restoreCursor = true;
       
   336     object->d_func()->target = 0;
       
   337     qApp->installEventFilter(this);
       
   338 
       
   339     global_accepted_action = Qt::CopyAction;
       
   340 #ifndef QT_NO_CURSOR
       
   341     qApp->setOverrideCursor(Qt::ArrowCursor);
       
   342     restoreCursor = true;
       
   343     updateCursor();
       
   344 #endif
       
   345 
       
   346     qt_qws_dnd_dragging = true;
       
   347 
       
   348     eventLoop = new QEventLoop;
       
   349     (void) eventLoop->exec();
       
   350     delete eventLoop;
       
   351     eventLoop = 0;
       
   352 
       
   353     delete qt_qws_dnd_deco;
       
   354     qt_qws_dnd_deco = 0;
       
   355     qt_qws_dnd_dragging = false;
       
   356 
       
   357 
       
   358     return global_accepted_action;
       
   359 }
       
   360 
       
   361 
       
   362 void QDragManager::cancel(bool deleteSource)
       
   363 {
       
   364 //    qDebug("QDragManager::cancel");
       
   365     beingCancelled = true;
       
   366 
       
   367     if (object->target()) {
       
   368         QDragLeaveEvent dle;
       
   369         QApplication::sendEvent(object->target(), &dle);
       
   370     }
       
   371 
       
   372 #ifndef QT_NO_CURSOR
       
   373     if (restoreCursor) {
       
   374         QApplication::restoreOverrideCursor();
       
   375         restoreCursor = false;
       
   376     }
       
   377 #endif
       
   378 
       
   379     if (drag_object) {
       
   380         if (deleteSource)
       
   381             object->deleteLater();
       
   382         drag_object = object = 0;
       
   383     }
       
   384 
       
   385     delete qt_qws_dnd_deco;
       
   386     qt_qws_dnd_deco = 0;
       
   387 
       
   388     global_accepted_action = Qt::IgnoreAction;
       
   389 }
       
   390 
       
   391 
       
   392 void QDragManager::drop()
       
   393 {
       
   394 }
       
   395 
       
   396 QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type type) const
       
   397 {
       
   398     if (!drag_object)
       
   399         return QVariant();
       
   400     QByteArray data =  drag_object->mimeData()->data(mimetype);
       
   401     if (type == QVariant::String)
       
   402         return QString::fromUtf8(data);
       
   403     return data;
       
   404 }
       
   405 
       
   406 bool QDropData::hasFormat_sys(const QString &format) const
       
   407 {
       
   408     return formats().contains(format);
       
   409 }
       
   410 
       
   411 QStringList QDropData::formats_sys() const
       
   412 {
       
   413     if (drag_object)
       
   414         return drag_object->mimeData()->formats();
       
   415     return QStringList();
       
   416 }
       
   417 
       
   418 
       
   419 #endif // QT_NO_DRAGANDDROP
       
   420 
       
   421 
       
   422 QT_END_NAMESPACE