Changeset 448 for trunk/src/gui/kernel/qdnd_pm.cpp
- Timestamp:
- Jan 13, 2010, 9:14:29 PM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/gui/kernel/qdnd_pm.cpp
r447 r448 52 52 #include "qcursor.h" 53 53 #include "qdesktopwidget.h" 54 #include "qfile.h" 54 55 #include "qdnd_p.h" 55 56 #include "qdebug.h" … … 68 69 69 70 #if !defined(QT_NO_DRAGANDDROP) && !defined(QT_NO_CLIPBOARD) 71 72 extern void qt_pmMouseButtonUp(); // defined in qapplication_pm.cpp 73 extern void qt_DrgFreeDragtransfer(DRAGTRANSFER *xfer); // defined in qmime_pm.cpp 70 74 71 75 /** \internal … … 271 275 return DO_MOVE; 272 276 return DO_UNKNOWN; 277 } 278 279 static USHORT toPmDragDropOps(Qt::DropActions actions) 280 { 281 USHORT op = DO_UNKNOWN; 282 if (actions & Qt::LinkAction) 283 op |= DO_LINKABLE; 284 if (actions & Qt::CopyAction) 285 op |= DO_COPYABLE; 286 if (actions & Qt::MoveAction) 287 op |= DO_MOVEABLE; 288 return op; 273 289 } 274 290 … … 617 633 } 618 634 635 //--------------------------------------------------------------------- 636 // QPMCoopDragWorker 637 //--------------------------------------------------------------------- 638 639 class QPMCoopDragWorker : public QPMMime::DragWorker, public QPMObjectWindow 640 { 641 public: 642 QPMCoopDragWorker() : info(0) {} 643 bool collectWorkers(QDrag *o); 644 645 // DragWorker interface 646 void init(); 647 bool cleanup(bool isCancelled); 648 bool isExclusive() const { return true; } 649 ULONG itemCount() const { return 0; } 650 HWND hwnd() const; 651 DRAGINFO *createDragInfo(const QString &targetName, USHORT supportedOps); 652 653 // QPMObjectWindow interface 654 MRESULT message(ULONG msg, MPARAM mp1, MPARAM mp2); 655 656 private: 657 QList<DragWorker*> workers; 658 // todo check workers! 659 //QPtrList<DragWorker> workers; 660 DRAGINFO *info; 661 }; 662 663 bool QPMCoopDragWorker::collectWorkers(QDrag *o) 664 { 665 Q_ASSERT(o); 666 if (!o) 667 return false; 668 669 bool gotExcl = false; // got isExclusive() worker? 670 bool skipExcl = false; // skip isExclusive() or itemCount() > 1 workers? 671 ULONG coopLevel = 0; // itemCount() level for !isExclusive() workers 672 673 bool gotExclForMime = false; 674 675 // go through all formats and all converters to collect DragWorkers 676 QMimeData *mimeData = o->mimeData(); 677 foreach (const QString &fmt, mimeData->formats()) { 678 DEBUG(() << "QPMCoopDragWorker: Searching for worker for mime" << fmt); 679 foreach (QPMMime *mime, QPMMime::all()) { 680 DragWorker *wrk = mime->dragWorkerFor(fmt, mimeData); 681 if (!wrk) 682 continue; 683 DEBUG(() << "QPMCoopDragWorker: Got worker" << wrk 684 << "(isExclusive" << wrk->isExclusive() << ", " 685 << "itemCount" << wrk->itemCount() << ") from convertor" 686 << mime->convertorName() << " (gotExclForMime" 687 << gotExclForMime << ", " << "gotExcl" << gotExcl 688 << ", skipExcl" << skipExcl << ", coopLevel" 689 << coopLevel << ")"); 690 if (wrk->isExclusive()) { 691 if (!skipExcl && !gotExclForMime) { 692 gotExclForMime = true; 693 if (!gotExcl) { 694 gotExcl = true; 695 workers.append(wrk); 696 } else { 697 // skip everything exclusive unless it's exactly the 698 // same worker 699 skipExcl = !workers.contains(wrk); 700 } 701 } 702 // continue to search for a fall-back cooperative 1-item worker 703 // (like QPMMimeAnyMime) for the case if this worker quits 704 // the game 705 continue; 706 } 707 ULONG itemCnt = wrk->itemCount(); 708 if (itemCnt == 0) { 709 DEBUG(() << "QPMCoopDragWorker: Cooperative DragWorker" 710 << wrk << "for mime " << fmt << " has " 711 << "itemCount = 0!"); 712 continue; 713 } 714 if (itemCnt > 1) { 715 // coop workers with item count > 1 are also considered exclusive 716 // here, because may not co-exist with 1-item workers that should 717 // always be able to contribute 718 if (!gotExcl && !skipExcl && !gotExclForMime) { 719 gotExclForMime = true; 720 workers.append(wrk); 721 if (!coopLevel) 722 coopLevel = itemCnt; 723 // only those for the same number of items can proceed 724 if (itemCnt != coopLevel) 725 skipExcl = true; 726 } 727 // continue to search for a fall-back cooperative 1-item worker 728 // (like QPMMimeAnyMime) for the case if this worker quits 729 // the game 730 continue; 731 } 732 workers.append(wrk); 733 // Don't search for other workrers for the same mime type -- 734 // we've already got a drag worker for it and adding another 735 // one would just introduce mime type duplicates on the drop 736 // target's side (where the first encountered drop worker 737 // for the given RMF would be used for actual data transfer 738 // anyway). See also QClipboard::setData(). 739 break; 740 } 741 if (gotExclForMime) { 742 // ensure we have a fall-back coop (should be the last added item) 743 DragWorker *w = workers.last(); 744 if (w->isExclusive() || w->itemCount() > 1) { 745 DEBUG(() << "QPMCoopDragWorker: DragWorker" << w 746 << "for" << fmt << "(isExclusive" << w->isExclusive() 747 << ", itemCount" << w->itemCount() 748 <<") has no fall-back cooperative 1-item worker!"); 749 workers.removeLast(); 750 } 751 gotExclForMime = false; 752 } else { 753 // got a regular (non-fall-back) 1-item coop, skip evreything else 754 skipExcl = true; 755 } 756 } 757 758 // remove either all exclusive workers or all their fall-back workers 759 // (depending on skipExcl) and remove duplicates 760 for (QList<DragWorker*>::iterator it = workers.begin(); 761 it <= workers.end();) { 762 DragWorker *wrk = *it; 763 bool excl = wrk->isExclusive() || wrk->itemCount() > 1; 764 if (skipExcl == excl || workers.count(wrk) > 1) { 765 it = workers.erase(it); 766 } else { 767 ++it; 768 } 769 } 770 771 #if defined(QDND_DEBUG) 772 foreach (DragWorker *wrk, workers) { 773 DEBUG(() << "QPMCoopDragWorker: Will use worker" << wrk 774 << "(isExclusive" << wrk->isExclusive() 775 << ", itemCount" << wrk->itemCount() << ")"); 776 } 777 #endif 778 779 Q_ASSERT(workers.count() > 0); 780 return workers.count() > 0; 781 } 782 783 HWND QPMCoopDragWorker::hwnd() const 784 { 785 DragWorker *firstWorker = workers.first(); 786 Q_ASSERT(firstWorker); 787 if (!firstWorker) 788 return 0; 789 790 if (firstWorker->isExclusive() && firstWorker->itemCount() == 0) { 791 // this is a super exclusive worker that will do everything on its own 792 return firstWorker->hwnd(); 793 } 794 795 return QPMObjectWindow::hwnd(); 796 } 797 798 void QPMCoopDragWorker::init() 799 { 800 Q_ASSERT(source()); 801 foreach(DragWorker *wrk, workers) { 802 wrk->src = source(); 803 wrk->init(); 804 } 805 } 806 807 bool QPMCoopDragWorker::cleanup(bool isCancelled) 808 { 809 bool moveDisallowed = false; 810 811 foreach(DragWorker *wrk, workers) { 812 // disallow the Move operation if at least one worker asked so 813 moveDisallowed |= wrk->cleanup(isCancelled); 814 wrk->src = 0; 815 } 816 workers.clear(); 817 info = 0; 818 return moveDisallowed; 819 } 820 821 DRAGINFO *QPMCoopDragWorker::createDragInfo(const QString &targetName, 822 USHORT supportedOps) 823 { 824 Q_ASSERT(!info); 825 if (info) 826 return 0; 827 828 DragWorker *firstWorker = workers.first(); 829 Q_ASSERT(firstWorker); 830 if (!firstWorker) 831 return 0; 832 833 ULONG itemCnt = firstWorker->itemCount(); 834 835 if (firstWorker->isExclusive() && itemCnt == 0) { 836 // this is a super exclusive worker that will do everything on its own 837 DEBUG(() << "QPMCoopDragWorker: Will redirect to super worker" 838 << firstWorker); 839 return firstWorker->createDragInfo(targetName, supportedOps); 840 } 841 842 // note that all workers at this place require the same amount of items 843 // (guaranteed by collectWorkers()) 844 845 DEBUG(() << "QPMCoopDragWorker: itemCnt" << itemCnt); 846 847 info = DrgAllocDraginfo(itemCnt); 848 Q_ASSERT(info); 849 if (!info) 850 return 0; 851 852 // collect all mechanism/format pairs 853 QByteArray allFormats; 854 foreach (DragWorker *wrk, workers) { 855 QByteArray formats = wrk->composeFormatString(); 856 Q_ASSERT(!formats.isNull()); 857 if (!formats.isNull()) { 858 if (allFormats.isNull()) 859 allFormats = formats; 860 else { 861 allFormats += ","; 862 allFormats += formats; 863 } 864 } 865 } 866 867 DEBUG(() << "QPMCoopDragWorker: allFormats" << allFormats); 868 869 static ULONG itemID = 0; 870 871 const char *type = 0; 872 const char *ext = 0; 873 firstWorker->defaultFileType(type, ext); 874 875 bool ok = true; 876 for (ULONG i = 0; i < itemCnt; ++i) { 877 DRAGITEM *item = DrgQueryDragitemPtr(info, i); 878 Q_ASSERT(item); 879 if (!item) { 880 ok = false; 881 break; 882 } 883 884 QString name; 885 if (itemCnt == 1) 886 name = targetName; 887 else 888 name = QString(QLatin1String("%1 %2")).arg(targetName).arg(i + 1); 889 890 if (ext) { 891 name += QLatin1Char('.'); 892 name += QFile::decodeName(QByteArray(ext)); 893 } 894 895 DEBUG(() << "QPMCoopDragWorker: item" << i << ": type" << type 896 << " name" << name); 897 898 // Note 1: DRAGITEM::hstrType is actually ignored by WPS, 899 // only the target extension matters. 900 901 // Note 2: We're not required to fill in the hwndItem field because we 902 // use the DC_PREPARE flag (to indicate it will be filled later, after 903 // DM_RENDERPREPARE); however, Mozilla refuses to render if hwndItem 904 // is initially 0. Set it to our HWND instead (we'll issue a warning if 905 // DM_RENDER or DM_ENDCONVERSATION is erroneously sent to us) 906 907 item->hwndItem = hwnd(); 908 item->ulItemID = itemID ++; 909 item->hstrType = DrgAddStrHandle(type ? type : DRT_UNKNOWN); 910 item->hstrRMF = DrgAddStrHandle(allFormats); 911 item->hstrContainerName = 0; 912 item->hstrSourceName = 0; 913 item->hstrTargetName = DrgAddStrHandle(QFile::encodeName(name)); 914 item->cxOffset = 0; 915 item->cyOffset = 0; 916 item->fsControl = DC_PREPARE; // require DM_RENDERPREPARE from target 917 item->fsSupportedOps = supportedOps; 918 } 919 920 if (!ok) { 921 DrgFreeDraginfo(info); 922 info = 0; 923 } 924 925 return info; 926 } 927 928 MRESULT QPMCoopDragWorker::message(ULONG msg, MPARAM mp1, MPARAM mp2) 929 { 930 if (msg == DM_RENDERPREPARE) { 931 if (!info) { 932 qWarning("Drop target sent DM_RENDERPREPARE after the DnD session " 933 "is over!"); 934 // free the given DRAGTRANSFER structure to avoud memory leak 935 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1; 936 if (xfer) 937 qt_DrgFreeDragtransfer(xfer); 938 return (MRESULT)FALSE; 939 } 940 941 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1; 942 Q_ASSERT(xfer && xfer->pditem); 943 if (!xfer || !xfer->pditem) 944 return (MRESULT)FALSE; 945 946 // find the item's index (ordinal number) 947 ULONG itemCnt = DrgQueryDragitemCount(info); 948 ULONG index = 0; 949 for (; index < itemCnt; ++index) 950 if (DrgQueryDragitemPtr(info, index) == xfer->pditem) 951 break; 952 953 Q_ASSERT(index < itemCnt); 954 if (index >= itemCnt) 955 return (MRESULT)FALSE; 956 957 DEBUG(() << "QPMCoopDragWorker: Got DM_RENDERPREPARE to" 958 << QPMMime::queryHSTR(xfer->hstrSelectedRMF) << "for item" 959 << index << "(id" << xfer->pditem->ulItemID << ")"); 960 961 QByteArray drm, drf; 962 if (!QPMMime::parseRMF(xfer->hstrSelectedRMF, drm, drf)) { 963 Q_ASSERT(false); 964 return (MRESULT)FALSE; 965 } 966 967 DragWorker *wrk = 0; 968 foreach(wrk, workers) 969 if (wrk->prepare(drm, drf, xfer->pditem, index)) 970 break; 971 if (!wrk) { 972 DEBUG(() << "QPMCoopDragWorker: No suitable worker found"); 973 return (MRESULT)FALSE; 974 } 975 976 xfer->pditem->hwndItem = wrk->hwnd(); 977 Q_ASSERT(xfer->pditem->hwndItem); 978 return (MRESULT)(xfer->pditem->hwndItem ? TRUE : FALSE); 979 } 980 981 if (msg == DM_RENDER || msg == DM_ENDCONVERSATION) { 982 qWarning("Drop target sent DM_RENDER or DM_ENDCONVERSATION to the " 983 "drag source window instead of the drag item window!"); 984 if (msg == DM_RENDER) { 985 // free the given DRAGTRANSFER structure to avoud memory leak 986 DRAGTRANSFER *xfer = (DRAGTRANSFER *)mp1; 987 if (xfer) 988 qt_DrgFreeDragtransfer(xfer); 989 } 990 } 991 992 return (MRESULT)FALSE; 993 } 994 995 //--------------------------------------------------------------------- 996 // QDragManager 997 //--------------------------------------------------------------------- 998 619 999 Qt::DropAction QDragManager::drag(QDrag *o) 620 1000 … … 622 1002 DEBUG(() << "QDragManager::drag"); 623 1003 624 // @todo implement 625 return Qt::IgnoreAction; 626 } 627 628 void QDragManager::cancel(bool /* deleteSource */) 629 { 630 // @todo implement 1004 if (object == o || !o || !o->d_func()->source) 1005 return Qt::IgnoreAction; 1006 1007 if (object) { 1008 cancel(); 1009 qApp->removeEventFilter(this); 1010 beingCancelled = false; 1011 } 1012 1013 // detect a mouse button to end dragging 1014 LONG vkTerminate = 0; 1015 { 1016 ULONG msg = WinQuerySysValue(HWND_DESKTOP, SV_BEGINDRAG) & 0xFFFF; 1017 switch(msg) { 1018 case WM_BUTTON1MOTIONSTART: vkTerminate = VK_BUTTON1; break; 1019 case WM_BUTTON2MOTIONSTART: vkTerminate = VK_BUTTON2; break; 1020 case WM_BUTTON3MOTIONSTART: vkTerminate = VK_BUTTON3; break; 1021 } 1022 1023 if (WinGetKeyState(HWND_DESKTOP, vkTerminate) & 0x8000) { 1024 // prefer the default button if it is pressed 1025 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON2) & 0x8000) { 1026 vkTerminate = VK_BUTTON2; 1027 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON1) & 0x8000) { 1028 vkTerminate = VK_BUTTON1; 1029 } else if (WinGetKeyState(HWND_DESKTOP, VK_BUTTON3) & 0x8000) { 1030 vkTerminate = VK_BUTTON3; 1031 } else { 1032 vkTerminate = 0; 1033 } 1034 } 1035 1036 if (!vkTerminate) { 1037 DEBUG(() << "QDragManager::drag: No valid mouse button pressed, " 1038 "dragging cancelled!"); 1039 o->deleteLater(); 1040 return Qt::IgnoreAction; 1041 } 1042 1043 USHORT supportedOps = toPmDragDropOps(dragPrivate()->possible_actions); 1044 1045 static QPMCoopDragWorker dragWorker; 1046 1047 bool ok = dragWorker.collectWorkers(o); 1048 Q_ASSERT(ok); 1049 Q_ASSERT(dragWorker.hwnd()); 1050 if (!ok || !dragWorker.hwnd()) { 1051 o->deleteLater(); 1052 return Qt::IgnoreAction; 1053 } 1054 1055 dragWorker.src = o->mimeData(); 1056 dragWorker.init(); 1057 DRAGINFO *info = dragWorker.createDragInfo(o->objectName(), supportedOps); 1058 1059 Q_ASSERT(info); 1060 if (!info) { 1061 dragWorker.cleanup(true /* isCancelled */); 1062 dragWorker.src = 0; 1063 o->deleteLater(); 1064 return Qt::IgnoreAction; 1065 } 1066 1067 object = o; 1068 1069 DEBUG(() << "QDragManager::drag: actions" 1070 << dragActionsToString(dragPrivate()->possible_actions)); 1071 1072 dragPrivate()->target = 0; 1073 1074 #ifndef QT_NO_ACCESSIBILITY 1075 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart); 1076 #endif 1077 1078 // @todo custom drag pixmap? 1079 1080 DRAGIMAGE img; 1081 img.cb = sizeof(DRAGIMAGE); 1082 img.hImage = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE); 1083 img.fl = DRG_ICON; 1084 img.cxOffset = 0; 1085 img.cyOffset = 0; 1086 1087 // the mouse is most likely captured by Qt at this point, uncapture it 1088 // or DrgDrag() will definitely fail 1089 WinSetCapture(HWND_DESKTOP, 0); 1090 1091 HWND target = DrgDrag(dragWorker.hwnd(), info, &img, 1, vkTerminate, 1092 (PVOID)0x80000000L); // don't lock the desktop PS 1093 1094 DEBUG(("QDragManager::drag: DrgDrag() returned %08lX (error 0x%08lX)", 1095 target, WinGetLastError(0))); 1096 1097 // we won't get any mouse release event, so manually adjust qApp state 1098 qt_pmMouseButtonUp(); 1099 1100 bool moveDisallowed = dragWorker.cleanup(beingCancelled || target == 0); 1101 dragWorker.src = 0; 1102 1103 moveDisallowed |= beingCancelled || target == 0 || 1104 info->usOperation != DO_MOVE; 1105 1106 DEBUG(() << "QDragManager::drag: moveDisallowed" << moveDisallowed); 1107 1108 Qt::DropAction ret = Qt::IgnoreAction; 1109 if (target != 0) { 1110 ret = toQDragDropAction(info->usOperation); 1111 if (moveDisallowed && info->usOperation == DO_MOVE) 1112 ret = Qt::TargetMoveAction; 1113 } 1114 1115 DEBUG(() << "QDragManager::drag: result" << dragActionsToString(ret)); 1116 1117 if (target == 0) 1118 DrgDeleteDraginfoStrHandles(info); 1119 DrgFreeDraginfo(info); 1120 1121 if (!beingCancelled) { 1122 dragPrivate()->target = QWidget::find(target); 1123 cancel(); // this will delete o (object) 1124 } 1125 1126 beingCancelled = false; 1127 1128 #ifndef QT_NO_ACCESSIBILITY 1129 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd); 1130 #endif 1131 1132 return ret; 1133 } 1134 1135 void QDragManager::cancel(bool deleteSource) 1136 { 1137 // Note: the only place where this function is called with 1138 // deleteSource = false so far is QDrag::~QDrag() 1139 1140 Q_ASSERT(object && !beingCancelled); 1141 if (!object || beingCancelled) 1142 return; 1143 1144 beingCancelled = true; 1145 1146 object->setMimeData(0); 1147 1148 if (deleteSource) 1149 object->deleteLater(); 1150 object = 0; 1151 1152 #ifndef QT_NO_CURSOR 1153 // insert cancel code here ######## todo 1154 1155 if (restoreCursor) { 1156 QApplication::restoreOverrideCursor(); 1157 restoreCursor = false; 1158 } 1159 #endif 1160 #ifndef QT_NO_ACCESSIBILITY 1161 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropEnd); 1162 #endif 631 1163 } 632 1164
Note:
See TracChangeset
for help on using the changeset viewer.