From 7129d2b11c77da50cc7c9721ee168c02ca7a8c03 Mon Sep 17 00:00:00 2001 From: valid-ptr Date: Sun, 22 Aug 2021 10:25:32 +0300 Subject: [PATCH 01/11] Accept multiple data formats via drag-and-drop in wxDataViewCtrl Allow specifying multiple formats to be accepted when dragging data to wxDataViewCtrl in the generic and Cocoa implementations. Add wxDataViewCtrlBase::EnableDropTarget() overload taking an array of wxDataFormats to support this at the API level. Add new DoEnableDropTarget() used by both EnableDropTarget() overloads and implement it in the generic and Cocoa ports. GTK implementation still uses only a single format, as before. Also refactor the Cocoa implementation: all operations using dragged data are now handled by wxDropTarget and unnecessary DataViewPboardType as removed. Update the dataview sample to show the new functionality. --- include/wx/dataview.h | 29 ++- include/wx/generic/dataview.h | 2 +- include/wx/gtk/dataview.h | 2 +- include/wx/osx/cocoa/dataview.h | 4 - include/wx/osx/cocoa/private.h | 3 +- include/wx/osx/dataview.h | 4 + include/wx/qt/dataview.h | 2 +- interface/wx/dataview.h | 18 +- samples/dataview/dataview.cpp | 9 +- src/common/datavcmn.cpp | 58 +++++ src/generic/datavgen.cpp | 37 ++- src/gtk/dataview.cpp | 17 +- src/osx/cocoa/dataview.mm | 423 +++++++------------------------- src/osx/cocoa/window.mm | 11 +- src/osx/dataview_osx.cpp | 17 ++ src/qt/dataview.cpp | 2 +- 16 files changed, 260 insertions(+), 378 deletions(-) diff --git a/include/wx/dataview.h b/include/wx/dataview.h index 0d7f46cd8f..624fbc3526 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -535,6 +535,10 @@ private: // wxDataViewCtrlBase // --------------------------------------------------------- +#if wxUSE_DRAG_AND_DROP +WX_DEFINE_ARRAY(wxDataFormat,wxDataFormatArray); +#endif + #define wxDV_SINGLE 0x0000 // for convenience #define wxDV_MULTIPLE 0x0001 // can select multiple items @@ -760,8 +764,21 @@ public: #if wxUSE_DRAG_AND_DROP virtual bool EnableDragSource(const wxDataFormat& WXUNUSED(format)) { return false; } - virtual bool EnableDropTarget(const wxDataFormat& WXUNUSED(format)) - { return false; } + + bool EnableDropTarget(const wxDataFormatArray& formats) + { return DoEnableDropTarget(formats); } + + bool EnableDropTarget(const wxDataFormat& format) + { + wxDataFormatArray formats; + if (format.GetType() != wxDF_INVALID) + { + formats.Add(format); + } + + return EnableDropTarget(formats); + } + #endif // wxUSE_DRAG_AND_DROP // define control visual attributes @@ -792,6 +809,14 @@ protected: virtual void DoSetExpanderColumn() = 0 ; virtual void DoSetIndent() = 0; +#if wxUSE_DRAG_AND_DROP + virtual wxDataObject* CreateDataObject(const wxDataFormatArray& formats); + + virtual bool DoEnableDropTarget(const wxDataFormatArray& WXUNUSED(formats)) + { return false; } + +#endif // wxUSE_DRAG_AND_DROP + // Just expand this item assuming it is already shown, i.e. its parent has // been already expanded using ExpandAncestors(). // diff --git a/include/wx/generic/dataview.h b/include/wx/generic/dataview.h index 262d318696..f8636336d8 100644 --- a/include/wx/generic/dataview.h +++ b/include/wx/generic/dataview.h @@ -284,7 +284,7 @@ public: #if wxUSE_DRAG_AND_DROP virtual bool EnableDragSource( const wxDataFormat &format ) wxOVERRIDE; - virtual bool EnableDropTarget( const wxDataFormat &format ) wxOVERRIDE; + virtual bool DoEnableDropTarget(const wxDataFormatArray& formats) wxOVERRIDE; #endif // wxUSE_DRAG_AND_DROP virtual wxBorder GetDefaultBorder() const wxOVERRIDE; diff --git a/include/wx/gtk/dataview.h b/include/wx/gtk/dataview.h index b5933cd33a..718ae843c4 100644 --- a/include/wx/gtk/dataview.h +++ b/include/wx/gtk/dataview.h @@ -166,7 +166,7 @@ public: virtual bool IsExpanded( const wxDataViewItem & item ) const wxOVERRIDE; virtual bool EnableDragSource( const wxDataFormat &format ) wxOVERRIDE; - virtual bool EnableDropTarget( const wxDataFormat &format ) wxOVERRIDE; + virtual bool DoEnableDropTarget( const wxDataFormatArray& formats ) wxOVERRIDE; virtual wxDataViewColumn *GetCurrentColumn() const wxOVERRIDE; diff --git a/include/wx/osx/cocoa/dataview.h b/include/wx/osx/cocoa/dataview.h index 90be1a8ae0..7d63ed0208 100644 --- a/include/wx/osx/cocoa/dataview.h +++ b/include/wx/osx/cocoa/dataview.h @@ -541,10 +541,6 @@ public: virtual void StartEditor( const wxDataViewItem & item, unsigned int column ) wxOVERRIDE; - // drag & drop helper methods - wxDataFormat GetDnDDataFormat(wxDataObjectComposite* dataObjects); - wxDataObjectComposite* GetDnDDataObjects(NSData* dataObject) const; - // Cocoa-specific helpers id GetItemAtRow(int row) const; diff --git a/include/wx/osx/cocoa/private.h b/include/wx/osx/cocoa/private.h index 48d4675acb..d35a452254 100644 --- a/include/wx/osx/cocoa/private.h +++ b/include/wx/osx/cocoa/private.h @@ -480,7 +480,8 @@ public: // this enum declares which methods should not be overridden in the native view classes enum wxOSXSkipOverrides { wxOSXSKIP_NONE = 0x0, - wxOSXSKIP_DRAW = 0x1 + wxOSXSKIP_DRAW = 0x1, + wxOSXSKIP_DND = 0x2 }; void WXDLLIMPEXP_CORE wxOSXCocoaClassAddWXMethods(Class c, wxOSXSkipOverrides skipFlags = wxOSXSKIP_NONE); diff --git a/include/wx/osx/dataview.h b/include/wx/osx/dataview.h index 457eddbebd..6a3e2197ec 100644 --- a/include/wx/osx/dataview.h +++ b/include/wx/osx/dataview.h @@ -208,6 +208,10 @@ public: virtual void EditItem(const wxDataViewItem& item, const wxDataViewColumn *column) wxOVERRIDE; +#if wxUSE_DRAG_AND_DROP + virtual bool DoEnableDropTarget( const wxDataFormatArray& formats ) wxOVERRIDE; +#endif // wxUSE_DRAG_AND_DROP + // returns the n-th pointer to a column; // this method is different from GetColumn(unsigned int pos) because here 'n' is not a position in the control but the n-th // position in the internal list/array of column pointers diff --git a/include/wx/qt/dataview.h b/include/wx/qt/dataview.h index 778bda4d67..a8b51ab7d2 100644 --- a/include/wx/qt/dataview.h +++ b/include/wx/qt/dataview.h @@ -119,7 +119,7 @@ public: virtual bool IsExpanded( const wxDataViewItem & item ) const; virtual bool EnableDragSource( const wxDataFormat &format ); - virtual bool EnableDropTarget( const wxDataFormat &format ); + virtual bool DoEnableDropTarget( const wxDataFormatArray& formats ) wxOVERRIDE; static wxVisualAttributes GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL); diff --git a/interface/wx/dataview.h b/interface/wx/dataview.h index c1c4327c20..de76145267 100644 --- a/interface/wx/dataview.h +++ b/interface/wx/dataview.h @@ -1390,9 +1390,23 @@ public: virtual bool EnableDragSource( const wxDataFormat &format ); /** - Enable drop operations using the given @a format. + Enable drop operations for each @a format from passed array. + Currently this is fully implemented in the generic and native macOS versions. + On GTK the only first element of array will be used. + + @note Passing empty array disables drop operations at all. + + @since 3.1.5 */ - virtual bool EnableDropTarget( const wxDataFormat &format ); + bool EnableDropTarget(const wxDataFormatArray& formats); + + /** + Enable drop operations using the given @a format. + Under the hood just calls overloaded EnableDropTarget() with an array holds single passed format. + + @note Since 3.1.5 wxDF_INVALID can be passed to disable drop operations at all. + */ + bool EnableDropTarget( const wxDataFormat &format ); /** Call this to ensure that the given item is visible. diff --git a/samples/dataview/dataview.cpp b/samples/dataview/dataview.cpp index 6eb1cb388b..b679cf91c0 100644 --- a/samples/dataview/dataview.cpp +++ b/samples/dataview/dataview.cpp @@ -1430,21 +1430,20 @@ void MyFrame::OnDrop( wxDataViewEvent &event ) return; } - wxTextDataObject obj; - obj.SetData( wxDF_UNICODETEXT, event.GetDataSize(), event.GetDataBuffer() ); + const wxTextDataObject* const obj = static_cast(event.GetDataObject()); if ( item.IsOk() ) { if (m_music_model->IsContainer(item)) { wxLogMessage("Text '%s' dropped in container '%s' (proposed index = %i)", - obj.GetText(), m_music_model->GetTitle(item), event.GetProposedDropIndex()); + obj->GetText(), m_music_model->GetTitle(item), event.GetProposedDropIndex()); } else - wxLogMessage("Text '%s' dropped on item '%s'", obj.GetText(), m_music_model->GetTitle(item)); + wxLogMessage("Text '%s' dropped on item '%s'", obj->GetText(), m_music_model->GetTitle(item)); } else - wxLogMessage("Text '%s' dropped on background (proposed index = %i)", obj.GetText(), event.GetProposedDropIndex()); + wxLogMessage("Text '%s' dropped on background (proposed index = %i)", obj->GetText(), event.GetProposedDropIndex()); } #endif // wxUSE_DRAG_AND_DROP diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index cd070d2870..1eaa975bc6 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -1688,6 +1688,64 @@ void wxDataViewCtrlBase::StartEditor(const wxDataViewItem& item, unsigned int co EditItem(item, GetColumn(column)); } +#if wxUSE_DRAG_AND_DROP + +wxDataObject* wxDataViewCtrlBase::CreateDataObject(const wxDataFormatArray& formats) +{ + if (formats.GetCount() == 0) + { + return NULL; + } + + wxDataObjectComposite *dataObject(new wxDataObjectComposite); + for (size_t i = 0; i < formats.GetCount(); ++i) + { + switch (formats.Item(i).GetType()) + { + case wxDF_TEXT: + case wxDF_OEMTEXT: + case wxDF_UNICODETEXT: + dataObject->Add(new wxTextDataObject); + break; + + case wxDF_BITMAP: + dataObject->Add(new wxBitmapDataObject); + break; + + case wxDF_FILENAME: + dataObject->Add(new wxFileDataObject); + break; + + case wxDF_HTML: + dataObject->Add(new wxHTMLDataObject); + break; + + case wxDF_METAFILE: + case wxDF_SYLK: + case wxDF_DIF: + case wxDF_TIFF: + case wxDF_DIB: + case wxDF_PALETTE: + case wxDF_PENDATA: + case wxDF_RIFF: + case wxDF_WAVE: + case wxDF_ENHMETAFILE: + case wxDF_LOCALE: + case wxDF_PRIVATE: + dataObject->Add(new wxCustomDataObject(formats.Item(i))); + break; + + case wxDF_INVALID: + case wxDF_MAX: + break; + } + } + + return dataObject; +} + +#endif // wxUSE_DRAG_AND_DROP + // --------------------------------------------------------- // wxDataViewEvent // --------------------------------------------------------- diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 2712368c6f..2568a72fba 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -922,7 +922,6 @@ public: }; bool EnableDragSource( const wxDataFormat &format ); - bool EnableDropTarget( const wxDataFormat &format ); void RefreshDropHint(); void RemoveDropHint(); @@ -1032,8 +1031,6 @@ private: bool m_dragEnabled; wxDataFormat m_dragFormat; - bool m_dropEnabled; - wxDataFormat m_dropFormat; DropItemInfo m_dropItemInfo; #endif // wxUSE_DRAG_AND_DROP @@ -2076,7 +2073,6 @@ wxDataViewMainWindow::wxDataViewMainWindow( wxDataViewCtrl *parent, wxWindowID i m_dragStart = wxPoint(0,0); m_dragEnabled = false; - m_dropEnabled = false; m_dropItemInfo = DropItemInfo(); #endif // wxUSE_DRAG_AND_DROP @@ -2132,17 +2128,6 @@ bool wxDataViewMainWindow::EnableDragSource( const wxDataFormat &format ) return true; } -bool wxDataViewMainWindow::EnableDropTarget( const wxDataFormat &format ) -{ - m_dropFormat = format; - m_dropEnabled = format != wxDF_INVALID; - - if (m_dropEnabled) - SetDropTarget( new wxDataViewDropTarget( new wxCustomDataObject( format ), this ) ); - - return true; -} - void wxDataViewMainWindow::RefreshDropHint() { const unsigned row = m_dropItemInfo.m_row; @@ -2389,18 +2374,18 @@ bool wxDataViewMainWindow::OnDrop( wxDataFormat format, wxCoord x, wxCoord y ) return true; } -wxDragResult wxDataViewMainWindow::OnData( wxDataFormat format, wxCoord x, wxCoord y, - wxDragResult def ) +wxDragResult wxDataViewMainWindow::OnData(wxDataFormat format, wxCoord x, wxCoord y, + wxDragResult def) { DropItemInfo dropItemInfo = GetDropItemInfo(x, y); - wxCustomDataObject *obj = (wxCustomDataObject *) GetDropTarget()->GetDataObject(); + wxDataObjectComposite *obj = static_cast(GetDropTarget()->GetDataObject()); wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_DROP, m_owner, dropItemInfo.m_item); event.SetProposedDropIndex(dropItemInfo.m_proposedDropIndex); event.SetDataFormat( format ); - event.SetDataSize( obj->GetSize() ); - event.SetDataBuffer( obj->GetData() ); + event.SetDataSize(obj->GetDataSize(format)); + event.SetDataObject(obj->GetObject(format)); event.SetDropEffect( def ); if ( !m_owner->HandleWindowEvent( event ) || !event.IsAllowed() ) return wxDragNone; @@ -5816,9 +5801,17 @@ bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format ) return m_clientArea->EnableDragSource( format ); } -bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat &format ) +bool wxDataViewCtrl::DoEnableDropTarget( const wxDataFormatArray &formats ) { - return m_clientArea->EnableDropTarget( format ); + wxDataViewDropTarget* dt = NULL; + if (wxDataObject* dataObject = CreateDataObject(formats)) + { + dt = new wxDataViewDropTarget(dataObject, m_clientArea); + } + + m_clientArea->SetDropTarget(dt); + + return true; } #endif // wxUSE_DRAG_AND_DROP diff --git a/src/gtk/dataview.cpp b/src/gtk/dataview.cpp index 7d8140d153..48b871272d 100644 --- a/src/gtk/dataview.cpp +++ b/src/gtk/dataview.cpp @@ -211,7 +211,7 @@ public: // dnd iface bool EnableDragSource( const wxDataFormat &format ); - bool EnableDropTarget( const wxDataFormat &format ); + bool EnableDropTarget( const wxDataFormatArray &formats ); gboolean row_draggable( GtkTreeDragSource *drag_source, GtkTreePath *path ); gboolean drag_data_delete( GtkTreeDragSource *drag_source, GtkTreePath* path ); @@ -3794,9 +3794,16 @@ bool wxDataViewCtrlInternal::EnableDragSource( const wxDataFormat &format ) return true; } -bool wxDataViewCtrlInternal::EnableDropTarget( const wxDataFormat &format ) +bool wxDataViewCtrlInternal::EnableDropTarget( const wxDataFormatArray& formats ) { - wxGtkString atom_str( gdk_atom_name( format ) ); + if (formats.GetCount() == 0) + { + gtk_tree_view_unset_rows_drag_dest(GTK_TREE_VIEW(m_owner->GtkGetTreeView())); + + return true; + } + + wxGtkString atom_str( gdk_atom_name( formats.Item(0) ) ); m_dropTargetTargetEntryTarget = wxCharBuffer( atom_str ); m_dropTargetTargetEntry.target = m_dropTargetTargetEntryTarget.data(); @@ -4922,10 +4929,10 @@ bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format ) return m_internal->EnableDragSource( format ); } -bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat &format ) +bool wxDataViewCtrl::DoEnableDropTarget( const wxDataFormatArray& formats ) { wxCHECK_MSG( m_internal, false, "model must be associated before calling EnableDragTarget" ); - return m_internal->EnableDropTarget( format ); + return m_internal->EnableDropTarget( formats ); } bool wxDataViewCtrl::AppendColumn( wxDataViewColumn *col ) diff --git a/src/osx/cocoa/dataview.mm b/src/osx/cocoa/dataview.mm index 06bbddf9cc..09e1286e25 100644 --- a/src/osx/cocoa/dataview.mm +++ b/src/osx/cocoa/dataview.mm @@ -26,6 +26,7 @@ #include "wx/osx/private.h" #include "wx/osx/private/available.h" +#include "wx/osx/private/datatransfer.h" #include "wx/osx/cocoa/dataview.h" #include "wx/renderer.h" #include "wx/stopwatch.h" @@ -708,71 +709,77 @@ outlineView:(NSOutlineView*)outlineView return [self setupAndCallDataViewEvents:wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE dropInfo:info item:item proposedChildIndex:index]; } --(NSDragOperation) callDataViewEvents:(wxEventType)eventType dataObjects:(wxDataObjectComposite*)dataObjects item:(id)item - proposedChildIndex:(NSInteger)index +-(NSDragOperation) setupAndCallDataViewEvents:(wxEventType)eventType dropInfo:(id)info item:(id)item + proposedChildIndex:(NSInteger)index { - NSDragOperation dragOperation = NSDragOperationNone; wxDataViewCtrl* const dvc(implementation->GetDataViewCtrl()); + + wxDropTarget* dt = dvc->GetDropTarget(); + if (!dt) + return NSDragOperationNone; + + NSPasteboard* pasteboard([info draggingPasteboard]); + wxOSXPasteboard wxPastboard(pasteboard); + dt->SetCurrentDragSource(&wxPastboard); + + wxDataFormat format = dt->GetMatchingPair(); + if (format == wxDF_INVALID) + return NSDragOperationNone; + + // Create event wxDataViewEvent event(eventType, dvc, wxDataViewItemFromItem(item)); - if (dataObjects && (dataObjects->GetFormatCount() > 0)) + + // Retrieve data info if user released mouse buttton (drop occured) + if (eventType == wxEVT_DATAVIEW_ITEM_DROP) { - // copy data into data object: - event.SetDataObject(dataObjects); - event.SetDataFormat(implementation->GetDnDDataFormat(dataObjects)); - event.SetProposedDropIndex(index); - if (index == -1) - { - event.SetDropEffect(wxDragCopy); - } - else - { - //if index is not -1, we're going to set the default - //for the drop effect to None to be compatible with - //the other wxPlatforms that don't support it. In the - //user code for for the event, they can set this to - //copy/move or similar to support it. - event.SetDropEffect(wxDragNone); - } - wxDataFormatId formatId = event.GetDataFormat().GetType(); - wxMemoryBuffer buffer; + if (!dt->GetData()) + return NSDragOperationNone; - // copy data into buffer: - if ( formatId != wxDF_INVALID) - { - size_t size = dataObjects->GetDataSize(formatId); + wxDataObjectComposite *obj = static_cast(dt->GetDataObject()); + event.SetDataSize(obj->GetDataSize(format)); + event.SetDataObject(obj->GetObject(format)); + } - event.SetDataSize(size); - dataObjects->GetDataHere(formatId,buffer.GetWriteBuf(size)); - buffer.UngetWriteBuf(size); - event.SetDataBuffer(buffer.GetData()); - } + // Setup other event properties + event.SetProposedDropIndex(index); + event.SetDataFormat(format); + if (index == -1) + { + event.SetDropEffect(wxDragCopy); + } + else + { + //if index is not -1, we're going to set the default + //for the drop effect to None to be compatible with + //the other wxPlatforms that don't support it. In the + //user code for for the event, they can set this to + //copy/move or similar to support it. + event.SetDropEffect(wxDragNone); + } - // finally, send event: - if (dvc->HandleWindowEvent(event) && event.IsAllowed()) + NSDragOperation dragOperation = NSDragOperationNone; + + // finally, send event: + if (dvc->HandleWindowEvent(event) && event.IsAllowed()) + { + switch (event.GetDropEffect()) { - switch (event.GetDropEffect()) - { - case wxDragCopy: - dragOperation = NSDragOperationCopy; - break; - case wxDragMove: - dragOperation = NSDragOperationMove; - break; - case wxDragLink: - dragOperation = NSDragOperationLink; - break; - case wxDragNone: - case wxDragCancel: - case wxDragError: - dragOperation = NSDragOperationNone; - break; - default: - dragOperation = NSDragOperationEvery; - } - } - else - { - dragOperation = NSDragOperationNone; + case wxDragCopy: + dragOperation = NSDragOperationCopy; + break; + case wxDragMove: + dragOperation = NSDragOperationMove; + break; + case wxDragLink: + dragOperation = NSDragOperationLink; + break; + case wxDragNone: + case wxDragCancel: + case wxDragError: + dragOperation = NSDragOperationNone; + break; + default: + dragOperation = NSDragOperationEvery; } } else @@ -783,201 +790,40 @@ outlineView:(NSOutlineView*)outlineView return dragOperation; } --(NSDragOperation) setupAndCallDataViewEvents:(wxEventType)eventType dropInfo:(id)info item:(id)item - proposedChildIndex:(NSInteger)index -{ - NSArray* supportedTypes( - [NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil] - ); - - NSPasteboard* pasteboard([info draggingPasteboard]); - - NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]); - - if ( bestType == nil ) - return NSDragOperationNone; - - NSDragOperation dragOperation = NSDragOperationNone; - wxDataViewCtrl* const dvc(implementation->GetDataViewCtrl()); - - wxCHECK_MSG(dvc, false, "Pointer to data view control not set correctly."); - wxCHECK_MSG(dvc->GetModel(), false, "Pointer to model not set correctly."); - - // wxDataViewEvent event(eventType, dvc, wxDataViewItemFromItem(item)); - if ([bestType compare:DataViewPboardType] == NSOrderedSame) - { - NSArray* dataArray((NSArray*)[pasteboard propertyListForType:DataViewPboardType]); - NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]); - - indexDraggedItem = 0; - while (indexDraggedItem < noOfDraggedItems) - { - wxDataObjectComposite* dataObjects(implementation->GetDnDDataObjects((NSData*)[dataArray objectAtIndex:indexDraggedItem])); - - dragOperation = [self callDataViewEvents:eventType dataObjects:dataObjects item:item proposedChildIndex:index]; - - if ( dragOperation != NSDragOperationNone ) - ++indexDraggedItem; - else - indexDraggedItem = noOfDraggedItems; - - // clean-up: - delete dataObjects; - } - } - else - { - // needed to convert internally used UTF-16 representation to a UTF-8 - // representation - CFDataRef osxData; - wxDataObjectComposite* dataObjects (new wxDataObjectComposite()); - wxTextDataObject* textDataObject(new wxTextDataObject()); - - osxData = ::CFStringCreateExternalRepresentation(kCFAllocatorDefault,(CFStringRef)[pasteboard stringForType:NSStringPboardType], -#if defined(wxNEEDS_UTF16_FOR_TEXT_DATAOBJ) - kCFStringEncodingUTF16, -#else - kCFStringEncodingUTF8, -#endif - 32); - if (textDataObject->SetData(::CFDataGetLength(osxData),::CFDataGetBytePtr(osxData))) - dataObjects->Add(textDataObject); - else - delete textDataObject; - // send event if data could be copied: - - dragOperation = [self callDataViewEvents:eventType dataObjects:dataObjects item:item proposedChildIndex:index]; - - // clean up: - ::CFRelease(osxData); - delete dataObjects; - } - - return dragOperation; -} - -(BOOL) outlineView:(NSOutlineView*)outlineView writeItems:(NSArray*)writeItems toPasteboard:(NSPasteboard*)pasteboard { wxUnusedVar(outlineView); - // the pasteboard will be filled up with an array containing the data as - // returned by the events (including the data type) and a concatenation of - // text (string) data; the text data will only be put onto the pasteboard - // if for all items a string representation exists wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); - wxDataViewItemArray dataViewItems; - - wxCHECK_MSG(dvc, false,"Pointer to data view control not set correctly."); wxCHECK_MSG(dvc->GetModel(),false,"Pointer to model not set correctly."); + BOOL result = NO; if ([writeItems count] > 0) { - bool dataStringAvailable(true); // a flag indicating if for all items a data string is available - NSMutableArray* dataArray = [NSMutableArray arrayWithCapacity:[writeItems count]]; // data of all items - wxString dataString; // contains the string data of all items + // Send a begin drag event for the first selected item and send wxEVT_DATAVIEW_ITEM_BEGIN_DRAG. + // If there are several items selected, user can process each in event handler and + // fill up the corresponding wxDataObject the way he wants. + const wxDataViewItem item = wxDataViewItemFromItem([writeItems objectAtIndex:0]); + wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, dvc, item); - // send a begin drag event for all selected items and proceed with - // dragging unless the event is vetoed: - for (size_t itemCounter=0; itemCounter<[writeItems count]; ++itemCounter) + // check if event has not been vetoed: + if (dvc->HandleWindowEvent(event) && event.IsAllowed() && event.GetDataObject()) { - bool itemStringAvailable(false); // a flag indicating if for the current item a string is available - wxDataObjectComposite* itemObject(new wxDataObjectComposite()); // data object for current item - wxString itemString; // contains the TAB concatenated data of an item + wxDataObject *dataObject = event.GetDataObject(); - const wxDataViewItem item = wxDataViewItemFromItem([writeItems objectAtIndex:itemCounter]); - wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, dvc, item); - itemString = ::ConcatenateDataViewItemValues(dvc, item); - itemObject->Add(new wxTextDataObject(itemString)); - event.SetDataObject(itemObject); - // check if event has not been vetoed: - if (dvc->HandleWindowEvent(event) && event.IsAllowed() && (event.GetDataObject()->GetFormatCount() > 0)) - { - size_t const noOfFormats = event.GetDataObject()->GetFormatCount(); - wxDataFormat* dataFormats(new wxDataFormat[noOfFormats]); + wxOSXPasteboard wxPastboard(pasteboard); - event.GetDataObject()->GetAllFormats(dataFormats,wxDataObject::Get); - for (size_t formatCounter=0; formatCounterGetDataSize(idDataFormat); - size_t const dataBufferSize = sizeof(wxDataFormatId)+dataSize; - // variable definitions (used in all case statements): - // give additional headroom for trailing NULL - wxMemoryBuffer dataBuffer(dataBufferSize+4); + wxPastboard.Clear(); + dataObject->WriteToSink(&wxPastboard); + wxPastboard.Flush(); - dataBuffer.AppendData(&idDataFormat,sizeof(wxDataFormatId)); - switch (idDataFormat) - { - case wxDF_TEXT: - // otherwise wxDF_UNICODETEXT already filled up - // the string; and the UNICODE representation has - // priority - if (!itemStringAvailable) - { - event.GetDataObject()->GetDataHere(wxDF_TEXT,dataBuffer.GetAppendBuf(dataSize)); - dataBuffer.UngetAppendBuf(dataSize); - [dataArray addObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]]; - itemString = wxString(static_cast(dataBuffer.GetData())+sizeof(wxDataFormatId),wxConvLocal); - itemStringAvailable = true; - } - break; - case wxDF_UNICODETEXT: - { - event.GetDataObject()->GetDataHere(wxDF_UNICODETEXT,dataBuffer.GetAppendBuf(dataSize)); - dataBuffer.UngetAppendBuf(dataSize); - if (itemStringAvailable) // does an object already exist as an ASCII text (see wxDF_TEXT case statement)? - [dataArray replaceObjectAtIndex:itemCounter withObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]]; - else - [dataArray addObject:[NSData dataWithBytes:dataBuffer.GetData() length:dataBufferSize]]; - itemString = wxString::FromUTF8(static_cast(dataBuffer.GetData())+sizeof(wxDataFormatId),dataSize); - itemStringAvailable = true; - } /* block */ - break; - default: - wxFAIL_MSG("Data object has invalid or unsupported data format"); - return NO; - } - } - delete[] dataFormats; - delete itemObject; - if (dataStringAvailable) - { - if (itemStringAvailable) - { - if (itemCounter > 0) - dataString << wxT('\n'); - dataString << itemString; - } - else - dataStringAvailable = false; - } - } - else - { - delete itemObject; - return NO; // dragging was vetoed or no data available - } + result = YES; } - if (dataStringAvailable) - { - wxCFStringRef osxString(dataString); - - [pasteboard declareTypes:[NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil] owner:nil]; - [pasteboard setPropertyList:dataArray forType:DataViewPboardType]; - [pasteboard setString:osxString.AsNSString() forType:NSStringPboardType]; - } - else - { - [pasteboard declareTypes:[NSArray arrayWithObject:DataViewPboardType] owner:nil]; - [pasteboard setPropertyList:dataArray forType:DataViewPboardType]; - } - return YES; } - else - return NO; // no items to drag (should never occur) + + return result; } // @@ -1639,7 +1485,7 @@ outlineView:(NSOutlineView*)outlineView if (!initialized) { initialized = YES; - wxOSXCocoaClassAddWXMethods( self ); + wxOSXCocoaClassAddWXMethods(self, wxOSXSKIP_DND); } } @@ -1654,8 +1500,6 @@ outlineView:(NSOutlineView*)outlineView currentlyEditedColumn = currentlyEditedRow = -1; - [self setAutoresizesOutlineColumn:NO]; - [self registerForDraggedTypes:[NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]]; [self setDelegate:self]; [self setDoubleAction:@selector(actionDoubleClick:)]; [self setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO]; @@ -2501,9 +2345,20 @@ bool wxCocoaDataViewControl::Update(const wxDataViewColumn *columnPtr) return false; } -bool wxCocoaDataViewControl::Update(const wxDataViewItem& WXUNUSED(parent), const wxDataViewItem& item) +bool wxCocoaDataViewControl::Update(const wxDataViewItem& parent, const wxDataViewItem& item) { - [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:item]]; + if (GetSortingColumn()) + { + if (parent.IsOk()) + [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:parent] reloadChildren:YES]; + else + [m_OutlineView reloadData]; + } + else + { + [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:item]]; + } + return true; } @@ -2718,96 +2573,6 @@ void wxCocoaDataViewControl::OnSize() [m_OutlineView sizeLastColumnToFit]; } -// -// drag & drop helper methods -// -wxDataFormat wxCocoaDataViewControl::GetDnDDataFormat(wxDataObjectComposite* dataObjects) -{ - wxDataFormat resultFormat; - if ( !dataObjects ) - return resultFormat; - - bool compatible(true); - - size_t const noOfFormats = dataObjects->GetFormatCount(); - size_t indexFormat; - - wxDataFormat* formats; - - // get all formats and check afterwards if the formats are compatible; if - // they are compatible the preferred format is returned otherwise - // wxDF_INVALID is returned; - // currently compatible types (ordered by priority are): - // - wxDF_UNICODETEXT - wxDF_TEXT - formats = new wxDataFormat[noOfFormats]; - dataObjects->GetAllFormats(formats); - indexFormat = 0; - while ((indexFormat < noOfFormats) && compatible) - { - switch (resultFormat.GetType()) - { - case wxDF_INVALID: - resultFormat.SetType(formats[indexFormat].GetType()); // first format (should only be reached if indexFormat == 0) - break; - case wxDF_TEXT: - if (formats[indexFormat].GetType() == wxDF_UNICODETEXT) - resultFormat.SetType(wxDF_UNICODETEXT); - else // incompatible - { - resultFormat.SetType(wxDF_INVALID); - compatible = false; - } - break; - case wxDF_UNICODETEXT: - if (formats[indexFormat].GetType() != wxDF_TEXT) - { - resultFormat.SetType(wxDF_INVALID); - compatible = false; - } - break; - default: - resultFormat.SetType(wxDF_INVALID); // not (yet) supported format - compatible = false; - } - ++indexFormat; - } - - delete[] formats; - - return resultFormat; -} - -wxDataObjectComposite* wxCocoaDataViewControl::GetDnDDataObjects(NSData* dataObject) const -{ - wxDataFormatId dataFormatID; - - - [dataObject getBytes:&dataFormatID length:sizeof(wxDataFormatId)]; - switch (dataFormatID) - { - case wxDF_TEXT: - case wxDF_UNICODETEXT: - { - wxTextDataObject* textDataObject(new wxTextDataObject()); - - if (textDataObject->SetData(wxDataFormat(dataFormatID),[dataObject length]-sizeof(wxDataFormatId),static_cast([dataObject bytes])+sizeof(wxDataFormatId))) - { - wxDataObjectComposite* dataObjectComposite(new wxDataObjectComposite()); - - dataObjectComposite->Add(textDataObject); - return dataObjectComposite; - } - else - { - delete textDataObject; - return NULL; - } - } - default: - return NULL; - } -} - id wxCocoaDataViewControl::GetItemAtRow(int row) const { return [m_OutlineView itemAtRow:row]; diff --git a/src/osx/cocoa/window.mm b/src/osx/cocoa/window.mm index 537d091595..82efa9ae54 100644 --- a/src/osx/cocoa/window.mm +++ b/src/osx/cocoa/window.mm @@ -2484,10 +2484,13 @@ void wxOSXCocoaClassAddWXMethods(Class c, wxOSXSkipOverrides skipFlags) wxOSX_CLASS_ADD_METHOD(c, @selector(controlDoubleAction:), (IMP) wxOSX_controlDoubleAction, "v@:@" ) #if wxUSE_DRAG_AND_DROP - wxOSX_CLASS_ADD_METHOD(c, @selector(draggingEntered:), (IMP) wxOSX_draggingEntered, "I@:@" ) - wxOSX_CLASS_ADD_METHOD(c, @selector(draggingUpdated:), (IMP) wxOSX_draggingUpdated, "I@:@" ) - wxOSX_CLASS_ADD_METHOD(c, @selector(draggingExited:), (IMP) wxOSX_draggingExited, "v@:@" ) - wxOSX_CLASS_ADD_METHOD(c, @selector(performDragOperation:), (IMP) wxOSX_performDragOperation, "c@:@" ) + if ( !(skipFlags & wxOSXSKIP_DND) ) + { + wxOSX_CLASS_ADD_METHOD(c, @selector(draggingEntered:), (IMP) wxOSX_draggingEntered, "I@:@" ) + wxOSX_CLASS_ADD_METHOD(c, @selector(draggingUpdated:), (IMP) wxOSX_draggingUpdated, "I@:@" ) + wxOSX_CLASS_ADD_METHOD(c, @selector(draggingExited:), (IMP) wxOSX_draggingExited, "v@:@" ) + wxOSX_CLASS_ADD_METHOD(c, @selector(performDragOperation:), (IMP) wxOSX_performDragOperation, "c@:@" ) + } #endif #if OBJC_API_VERSION < 2 diff --git a/src/osx/dataview_osx.cpp b/src/osx/dataview_osx.cpp index def9d099d4..8f885de027 100644 --- a/src/osx/dataview_osx.cpp +++ b/src/osx/dataview_osx.cpp @@ -687,6 +687,23 @@ void wxDataViewCtrl::EditItem(const wxDataViewItem& item, const wxDataViewColumn GetDataViewPeer()->StartEditor(item, GetColumnPosition(column)); } +#if wxUSE_DRAG_AND_DROP + +bool wxDataViewCtrl::DoEnableDropTarget(const wxDataFormatArray &formats) +{ + wxDropTarget* dt = NULL; + if (wxDataObject* dataObject = CreateDataObject(formats)) + { + dt = new wxDropTarget(dataObject); + } + + SetDropTarget(dt); + + return true; +} + +#endif // wxUSE_DRAG_AND_DROP + void wxDataViewCtrl::FinishCustomItemEditing() { if (GetCustomRendererItem().IsOk()) diff --git a/src/qt/dataview.cpp b/src/qt/dataview.cpp index 61cf681ca0..fd1b4b280e 100644 --- a/src/qt/dataview.cpp +++ b/src/qt/dataview.cpp @@ -285,7 +285,7 @@ bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format ) return false; } -bool wxDataViewCtrl::EnableDropTarget( const wxDataFormat &format ) +bool wxDataViewCtrl::DoEnableDropTarget( const wxDataFormatArray &formats ) { return false; } From a279c7b6077e99ca2ac406c0e6bf9975694f5ff2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 16 Aug 2021 17:52:44 +0200 Subject: [PATCH 02/11] Update the version in which EnableDropTarget() overload was added --- interface/wx/dataview.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/wx/dataview.h b/interface/wx/dataview.h index de76145267..008c71d765 100644 --- a/interface/wx/dataview.h +++ b/interface/wx/dataview.h @@ -1396,7 +1396,7 @@ public: @note Passing empty array disables drop operations at all. - @since 3.1.5 + @since 3.1.6 */ bool EnableDropTarget(const wxDataFormatArray& formats); @@ -1404,7 +1404,7 @@ public: Enable drop operations using the given @a format. Under the hood just calls overloaded EnableDropTarget() with an array holds single passed format. - @note Since 3.1.5 wxDF_INVALID can be passed to disable drop operations at all. + @note Since 3.1.6 wxDF_INVALID can be passed to disable drop operations at all. */ bool EnableDropTarget( const wxDataFormat &format ); From a0e32cadd9121cb5e463adf457d3d4c98007e873 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 16 Aug 2021 17:54:59 +0200 Subject: [PATCH 03/11] Slightly improve wording in EnableDropTarget() documentation No real changes. --- interface/wx/dataview.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/interface/wx/dataview.h b/interface/wx/dataview.h index 008c71d765..b6edf7fdd9 100644 --- a/interface/wx/dataview.h +++ b/interface/wx/dataview.h @@ -1391,10 +1391,11 @@ public: /** Enable drop operations for each @a format from passed array. - Currently this is fully implemented in the generic and native macOS versions. - On GTK the only first element of array will be used. - @note Passing empty array disables drop operations at all. + Currently this is fully implemented in the generic and native macOS + versions. In wxGTK only the first element of the array is used. + + @note Passing empty array disables drag and drop operations completely. @since 3.1.6 */ @@ -1402,9 +1403,10 @@ public: /** Enable drop operations using the given @a format. - Under the hood just calls overloaded EnableDropTarget() with an array holds single passed format. - @note Since 3.1.6 wxDF_INVALID can be passed to disable drop operations at all. + See the overload above for providing more than one supported format. + + @note Since 3.1.6 wxDF_INVALID can be passed to disable drag and drop support. */ bool EnableDropTarget( const wxDataFormat &format ); From 1499d7d45b8ae44b981dd2962e2a47b14130cf68 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 16 Aug 2021 18:21:18 +0200 Subject: [PATCH 04/11] Handle wxDF_PNG in wxDataViewCtrlBase::CreateDataObject() too If nothing else, this avoids warnings about missing enum element in the switch. --- src/common/datavcmn.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index 1eaa975bc6..2dc19c1285 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -1709,6 +1709,7 @@ wxDataObject* wxDataViewCtrlBase::CreateDataObject(const wxDataFormatArray& form break; case wxDF_BITMAP: + case wxDF_PNG: dataObject->Add(new wxBitmapDataObject); break; From cd555f9ff5d0de4b9f0218f0cb87c5f21c8da23a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 16 Aug 2021 18:22:08 +0200 Subject: [PATCH 05/11] Use vector instead of array of wxDataFormats Also rename EnableDropTarget() to EnableDropTargets(), as calling EnableDropTarget(wxDF_XXX) would be ambiguous due to the existence of a non-explicit wxVector ctor taking size_t (which is a mistake on its own, but is probably not worth changing any more). --- include/wx/dataview.h | 16 ++++++---------- include/wx/generic/dataview.h | 2 +- include/wx/gtk/dataview.h | 2 +- include/wx/osx/dataview.h | 2 +- include/wx/qt/dataview.h | 2 +- interface/wx/dataview.h | 6 +++--- src/common/datavcmn.cpp | 10 +++++----- src/generic/datavgen.cpp | 2 +- src/gtk/dataview.cpp | 11 ++++++----- src/osx/dataview_osx.cpp | 2 +- src/qt/dataview.cpp | 2 +- 11 files changed, 27 insertions(+), 30 deletions(-) diff --git a/include/wx/dataview.h b/include/wx/dataview.h index 624fbc3526..9023e66e33 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -535,10 +535,6 @@ private: // wxDataViewCtrlBase // --------------------------------------------------------- -#if wxUSE_DRAG_AND_DROP -WX_DEFINE_ARRAY(wxDataFormat,wxDataFormatArray); -#endif - #define wxDV_SINGLE 0x0000 // for convenience #define wxDV_MULTIPLE 0x0001 // can select multiple items @@ -765,18 +761,18 @@ public: virtual bool EnableDragSource(const wxDataFormat& WXUNUSED(format)) { return false; } - bool EnableDropTarget(const wxDataFormatArray& formats) + bool EnableDropTargets(const wxVector& formats) { return DoEnableDropTarget(formats); } bool EnableDropTarget(const wxDataFormat& format) { - wxDataFormatArray formats; + wxVector formats; if (format.GetType() != wxDF_INVALID) { - formats.Add(format); + formats.push_back(format); } - return EnableDropTarget(formats); + return DoEnableDropTarget(formats); } #endif // wxUSE_DRAG_AND_DROP @@ -810,9 +806,9 @@ protected: virtual void DoSetIndent() = 0; #if wxUSE_DRAG_AND_DROP - virtual wxDataObject* CreateDataObject(const wxDataFormatArray& formats); + virtual wxDataObject* CreateDataObject(const wxVector& formats); - virtual bool DoEnableDropTarget(const wxDataFormatArray& WXUNUSED(formats)) + virtual bool DoEnableDropTarget(const wxVector& WXUNUSED(formats)) { return false; } #endif // wxUSE_DRAG_AND_DROP diff --git a/include/wx/generic/dataview.h b/include/wx/generic/dataview.h index f8636336d8..5ebdd1c56e 100644 --- a/include/wx/generic/dataview.h +++ b/include/wx/generic/dataview.h @@ -284,7 +284,7 @@ public: #if wxUSE_DRAG_AND_DROP virtual bool EnableDragSource( const wxDataFormat &format ) wxOVERRIDE; - virtual bool DoEnableDropTarget(const wxDataFormatArray& formats) wxOVERRIDE; + virtual bool DoEnableDropTarget(const wxVector& formats) wxOVERRIDE; #endif // wxUSE_DRAG_AND_DROP virtual wxBorder GetDefaultBorder() const wxOVERRIDE; diff --git a/include/wx/gtk/dataview.h b/include/wx/gtk/dataview.h index 718ae843c4..0d76d0ad96 100644 --- a/include/wx/gtk/dataview.h +++ b/include/wx/gtk/dataview.h @@ -166,7 +166,7 @@ public: virtual bool IsExpanded( const wxDataViewItem & item ) const wxOVERRIDE; virtual bool EnableDragSource( const wxDataFormat &format ) wxOVERRIDE; - virtual bool DoEnableDropTarget( const wxDataFormatArray& formats ) wxOVERRIDE; + virtual bool DoEnableDropTarget( const wxVector& formats ) wxOVERRIDE; virtual wxDataViewColumn *GetCurrentColumn() const wxOVERRIDE; diff --git a/include/wx/osx/dataview.h b/include/wx/osx/dataview.h index 6a3e2197ec..0838c9b5ae 100644 --- a/include/wx/osx/dataview.h +++ b/include/wx/osx/dataview.h @@ -209,7 +209,7 @@ public: virtual void EditItem(const wxDataViewItem& item, const wxDataViewColumn *column) wxOVERRIDE; #if wxUSE_DRAG_AND_DROP - virtual bool DoEnableDropTarget( const wxDataFormatArray& formats ) wxOVERRIDE; + virtual bool DoEnableDropTarget( const wxVector& formats ) wxOVERRIDE; #endif // wxUSE_DRAG_AND_DROP // returns the n-th pointer to a column; diff --git a/include/wx/qt/dataview.h b/include/wx/qt/dataview.h index a8b51ab7d2..6ae9022db8 100644 --- a/include/wx/qt/dataview.h +++ b/include/wx/qt/dataview.h @@ -119,7 +119,7 @@ public: virtual bool IsExpanded( const wxDataViewItem & item ) const; virtual bool EnableDragSource( const wxDataFormat &format ); - virtual bool DoEnableDropTarget( const wxDataFormatArray& formats ) wxOVERRIDE; + virtual bool DoEnableDropTarget( const wxVector& formats ); static wxVisualAttributes GetClassDefaultAttributes(wxWindowVariant variant = wxWINDOW_VARIANT_NORMAL); diff --git a/interface/wx/dataview.h b/interface/wx/dataview.h index b6edf7fdd9..468cb5ec22 100644 --- a/interface/wx/dataview.h +++ b/interface/wx/dataview.h @@ -1390,7 +1390,7 @@ public: virtual bool EnableDragSource( const wxDataFormat &format ); /** - Enable drop operations for each @a format from passed array. + Enable drop operations using any of the specified @a formats. Currently this is fully implemented in the generic and native macOS versions. In wxGTK only the first element of the array is used. @@ -1399,12 +1399,12 @@ public: @since 3.1.6 */ - bool EnableDropTarget(const wxDataFormatArray& formats); + bool EnableDropTargets(const wxVector& formats); /** Enable drop operations using the given @a format. - See the overload above for providing more than one supported format. + See EnableDropTargets() for providing more than one supported format. @note Since 3.1.6 wxDF_INVALID can be passed to disable drag and drop support. */ diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index 2dc19c1285..f1691311ff 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -1690,17 +1690,17 @@ void wxDataViewCtrlBase::StartEditor(const wxDataViewItem& item, unsigned int co #if wxUSE_DRAG_AND_DROP -wxDataObject* wxDataViewCtrlBase::CreateDataObject(const wxDataFormatArray& formats) +wxDataObject* wxDataViewCtrlBase::CreateDataObject(const wxVector& formats) { - if (formats.GetCount() == 0) + if (formats.empty()) { return NULL; } wxDataObjectComposite *dataObject(new wxDataObjectComposite); - for (size_t i = 0; i < formats.GetCount(); ++i) + for (size_t i = 0; i < formats.size(); ++i) { - switch (formats.Item(i).GetType()) + switch (formats[i].GetType()) { case wxDF_TEXT: case wxDF_OEMTEXT: @@ -1733,7 +1733,7 @@ wxDataObject* wxDataViewCtrlBase::CreateDataObject(const wxDataFormatArray& form case wxDF_ENHMETAFILE: case wxDF_LOCALE: case wxDF_PRIVATE: - dataObject->Add(new wxCustomDataObject(formats.Item(i))); + dataObject->Add(new wxCustomDataObject(formats[i])); break; case wxDF_INVALID: diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index 2568a72fba..b14f69ac94 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -5801,7 +5801,7 @@ bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format ) return m_clientArea->EnableDragSource( format ); } -bool wxDataViewCtrl::DoEnableDropTarget( const wxDataFormatArray &formats ) +bool wxDataViewCtrl::DoEnableDropTarget( const wxVector &formats ) { wxDataViewDropTarget* dt = NULL; if (wxDataObject* dataObject = CreateDataObject(formats)) diff --git a/src/gtk/dataview.cpp b/src/gtk/dataview.cpp index 48b871272d..813b4f9b7b 100644 --- a/src/gtk/dataview.cpp +++ b/src/gtk/dataview.cpp @@ -211,7 +211,7 @@ public: // dnd iface bool EnableDragSource( const wxDataFormat &format ); - bool EnableDropTarget( const wxDataFormatArray &formats ); + bool EnableDropTarget( const wxVector &formats ); gboolean row_draggable( GtkTreeDragSource *drag_source, GtkTreePath *path ); gboolean drag_data_delete( GtkTreeDragSource *drag_source, GtkTreePath* path ); @@ -3794,16 +3794,17 @@ bool wxDataViewCtrlInternal::EnableDragSource( const wxDataFormat &format ) return true; } -bool wxDataViewCtrlInternal::EnableDropTarget( const wxDataFormatArray& formats ) +bool wxDataViewCtrlInternal::EnableDropTarget( const wxVector& formats ) { - if (formats.GetCount() == 0) + if (formats.empty()) { gtk_tree_view_unset_rows_drag_dest(GTK_TREE_VIEW(m_owner->GtkGetTreeView())); return true; } - wxGtkString atom_str( gdk_atom_name( formats.Item(0) ) ); + // TODO: Use all formats, not just the first one. + wxGtkString atom_str( gdk_atom_name( formats[0] ) ); m_dropTargetTargetEntryTarget = wxCharBuffer( atom_str ); m_dropTargetTargetEntry.target = m_dropTargetTargetEntryTarget.data(); @@ -4929,7 +4930,7 @@ bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format ) return m_internal->EnableDragSource( format ); } -bool wxDataViewCtrl::DoEnableDropTarget( const wxDataFormatArray& formats ) +bool wxDataViewCtrl::DoEnableDropTarget( const wxVector& formats ) { wxCHECK_MSG( m_internal, false, "model must be associated before calling EnableDragTarget" ); return m_internal->EnableDropTarget( formats ); diff --git a/src/osx/dataview_osx.cpp b/src/osx/dataview_osx.cpp index 8f885de027..1635ac554e 100644 --- a/src/osx/dataview_osx.cpp +++ b/src/osx/dataview_osx.cpp @@ -689,7 +689,7 @@ void wxDataViewCtrl::EditItem(const wxDataViewItem& item, const wxDataViewColumn #if wxUSE_DRAG_AND_DROP -bool wxDataViewCtrl::DoEnableDropTarget(const wxDataFormatArray &formats) +bool wxDataViewCtrl::DoEnableDropTarget(const wxVector &formats) { wxDropTarget* dt = NULL; if (wxDataObject* dataObject = CreateDataObject(formats)) diff --git a/src/qt/dataview.cpp b/src/qt/dataview.cpp index fd1b4b280e..6d249d398e 100644 --- a/src/qt/dataview.cpp +++ b/src/qt/dataview.cpp @@ -285,7 +285,7 @@ bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format ) return false; } -bool wxDataViewCtrl::DoEnableDropTarget( const wxDataFormatArray &formats ) +bool wxDataViewCtrl::DoEnableDropTarget( const wxVector &formats ) { return false; } From 352923dc8d0d01a0f00aa6ca9d01534b3592448b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 10 Dec 2021 19:31:24 +0100 Subject: [PATCH 06/11] Make wxDataViewCtrlBase::CreateDataObject() static This function doesn't need to be virtual as it's not meant to be overridden in, but just called from the derived classes. In fact, it doesn't even have to be a member function at all, but leave it in the class for now to avoid having to create a new header just for it, but make it static. Also add a comment explaining what is it for and what it does. --- include/wx/dataview.h | 6 ++++-- src/common/datavcmn.cpp | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/wx/dataview.h b/include/wx/dataview.h index 9023e66e33..1b22d2eca1 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -806,11 +806,13 @@ protected: virtual void DoSetIndent() = 0; #if wxUSE_DRAG_AND_DROP - virtual wxDataObject* CreateDataObject(const wxVector& formats); + // Helper function which can be used by DoEnableDropTarget() implementations + // in the derived classes: return a composite data object supporting the + // given formats or null if the vector is empty. + static wxDataObject* CreateDataObject(const wxVector& formats); virtual bool DoEnableDropTarget(const wxVector& WXUNUSED(formats)) { return false; } - #endif // wxUSE_DRAG_AND_DROP // Just expand this item assuming it is already shown, i.e. its parent has diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index f1691311ff..952eb2e176 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -1690,6 +1690,7 @@ void wxDataViewCtrlBase::StartEditor(const wxDataViewItem& item, unsigned int co #if wxUSE_DRAG_AND_DROP +/* static */ wxDataObject* wxDataViewCtrlBase::CreateDataObject(const wxVector& formats) { if (formats.empty()) From 31bdfd6523566956c4db1ca5fc2fc914e604f6a3 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 10 Dec 2021 20:06:30 +0100 Subject: [PATCH 07/11] Make CreateDataObject() return wxDataObjectComposite This function must return a composite data object and not just any wxDataObject because its callers cast the returned object to this class, so make it a bit more type-safe. --- include/wx/dataview.h | 3 ++- src/common/datavcmn.cpp | 3 ++- src/generic/datavgen.cpp | 16 ++++++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/include/wx/dataview.h b/include/wx/dataview.h index 1b22d2eca1..42dbd3772f 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -809,7 +809,8 @@ protected: // Helper function which can be used by DoEnableDropTarget() implementations // in the derived classes: return a composite data object supporting the // given formats or null if the vector is empty. - static wxDataObject* CreateDataObject(const wxVector& formats); + static wxDataObjectComposite* + CreateDataObject(const wxVector& formats); virtual bool DoEnableDropTarget(const wxVector& WXUNUSED(formats)) { return false; } diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index 952eb2e176..53f5545c9c 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -1691,7 +1691,8 @@ void wxDataViewCtrlBase::StartEditor(const wxDataViewItem& item, unsigned int co #if wxUSE_DRAG_AND_DROP /* static */ -wxDataObject* wxDataViewCtrlBase::CreateDataObject(const wxVector& formats) +wxDataObjectComposite* +wxDataViewCtrlBase::CreateDataObject(const wxVector& formats) { if (formats.empty()) { diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index b14f69ac94..dbf59f25cc 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -1701,12 +1701,15 @@ public: class wxDataViewDropTarget: public wxDropTarget { public: - wxDataViewDropTarget( wxDataObject *obj, wxDataViewMainWindow *win ) : - wxDropTarget( obj ) + wxDataViewDropTarget( wxDataObjectComposite *obj, wxDataViewMainWindow *win ) : + wxDropTarget( obj ), + m_obj(obj) { m_win = win; } + wxDataObjectComposite* GetCompositeDataObject() const { return m_obj; } + virtual wxDragResult OnDragOver( wxCoord x, wxCoord y, wxDragResult def ) wxOVERRIDE { wxDataFormat format = GetMatchingPair(); @@ -1736,6 +1739,9 @@ public: virtual void OnLeave() wxOVERRIDE { m_win->OnLeave(); } +private: + wxDataObjectComposite* const m_obj; + wxDataViewMainWindow *m_win; }; @@ -2379,7 +2385,9 @@ wxDragResult wxDataViewMainWindow::OnData(wxDataFormat format, wxCoord x, wxCoor { DropItemInfo dropItemInfo = GetDropItemInfo(x, y); - wxDataObjectComposite *obj = static_cast(GetDropTarget()->GetDataObject()); + wxDataViewDropTarget* const + target = static_cast(GetDropTarget()); + wxDataObjectComposite* const obj = target->GetCompositeDataObject(); wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_DROP, m_owner, dropItemInfo.m_item); event.SetProposedDropIndex(dropItemInfo.m_proposedDropIndex); @@ -5804,7 +5812,7 @@ bool wxDataViewCtrl::EnableDragSource( const wxDataFormat &format ) bool wxDataViewCtrl::DoEnableDropTarget( const wxVector &formats ) { wxDataViewDropTarget* dt = NULL; - if (wxDataObject* dataObject = CreateDataObject(formats)) + if (wxDataObjectComposite* dataObject = CreateDataObject(formats)) { dt = new wxDataViewDropTarget(dataObject, m_clientArea); } From 7797ffc730e33de6eef67737e1adc882aabc8250 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 10 Dec 2021 20:11:33 +0100 Subject: [PATCH 08/11] Add wxDataViewEvent::InitData() helper Reuse it from both the generic and macOS versions to avoid doing the same thing in both of them. --- include/wx/dataview.h | 3 +++ src/common/datavcmn.cpp | 12 ++++++++++++ src/generic/datavgen.cpp | 4 +--- src/osx/cocoa/dataview.mm | 10 ++++++---- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/include/wx/dataview.h b/include/wx/dataview.h index 42dbd3772f..a1c166ad21 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -936,6 +936,9 @@ public: // insertion of items, this is the proposed child index for the insertion. void SetProposedDropIndex(int index) { m_proposedDropIndex = index; } int GetProposedDropIndex() const { return m_proposedDropIndex;} + + // Internal, only used by wxWidgets itself. + void InitData(wxDataObjectComposite* obj, wxDataFormat format); #endif // wxUSE_DRAG_AND_DROP virtual wxEvent *Clone() const wxOVERRIDE { return new wxDataViewEvent(*this); } diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index 53f5545c9c..a257e1a365 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -1805,6 +1805,18 @@ void wxDataViewEvent::Init(wxDataViewCtrlBase* dvc, SetEventObject(dvc); } +#if wxUSE_DRAG_AND_DROP + +void wxDataViewEvent::InitData(wxDataObjectComposite* obj, wxDataFormat format) +{ + SetDataFormat(format); + + SetDataObject(obj->GetObject(format)); + SetDataSize(obj->GetDataSize(format)); +} + +#endif // wxUSE_DRAG_AND_DROP + #if wxUSE_SPINCTRL // ------------------------------------- diff --git a/src/generic/datavgen.cpp b/src/generic/datavgen.cpp index dbf59f25cc..e4f1e1cc1d 100644 --- a/src/generic/datavgen.cpp +++ b/src/generic/datavgen.cpp @@ -2391,9 +2391,7 @@ wxDragResult wxDataViewMainWindow::OnData(wxDataFormat format, wxCoord x, wxCoor wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_DROP, m_owner, dropItemInfo.m_item); event.SetProposedDropIndex(dropItemInfo.m_proposedDropIndex); - event.SetDataFormat( format ); - event.SetDataSize(obj->GetDataSize(format)); - event.SetDataObject(obj->GetObject(format)); + event.InitData(obj, format); event.SetDropEffect( def ); if ( !m_owner->HandleWindowEvent( event ) || !event.IsAllowed() ) return wxDragNone; diff --git a/src/osx/cocoa/dataview.mm b/src/osx/cocoa/dataview.mm index 09e1286e25..b8684d2c01 100644 --- a/src/osx/cocoa/dataview.mm +++ b/src/osx/cocoa/dataview.mm @@ -729,20 +729,22 @@ outlineView:(NSOutlineView*)outlineView // Create event wxDataViewEvent event(eventType, dvc, wxDataViewItemFromItem(item)); - // Retrieve data info if user released mouse buttton (drop occured) + // Retrieve the data itself if user released mouse button (drop occurred) if (eventType == wxEVT_DATAVIEW_ITEM_DROP) { if (!dt->GetData()) return NSDragOperationNone; wxDataObjectComposite *obj = static_cast(dt->GetDataObject()); - event.SetDataSize(obj->GetDataSize(format)); - event.SetDataObject(obj->GetObject(format)); + event.InitData(obj, format); + } + else // Otherwise set just the data format + { + event.SetDataFormat(format); } // Setup other event properties event.SetProposedDropIndex(index); - event.SetDataFormat(format); if (index == -1) { event.SetDropEffect(wxDragCopy); From 33018ea7a9a5e31524d93b44feccaf1c69d69251 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 10 Dec 2021 20:30:24 +0100 Subject: [PATCH 09/11] Copy dropped data to wxDataViewEvent Existing code relies on GetDataBuffer() returning a valid pointer, so we need to ensure that this is the case, even if this means copying the data into an internal buffer. --- include/wx/dataview.h | 1 + src/common/datavcmn.cpp | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/wx/dataview.h b/include/wx/dataview.h index a1c166ad21..5b556a4c13 100644 --- a/include/wx/dataview.h +++ b/include/wx/dataview.h @@ -972,6 +972,7 @@ protected: #if wxUSE_DRAG_AND_DROP wxDataObject *m_dataObject; + wxMemoryBuffer m_dataBuf; wxDataFormat m_dataFormat; void* m_dataBuffer; size_t m_dataSize; diff --git a/src/common/datavcmn.cpp b/src/common/datavcmn.cpp index a257e1a365..a704326f49 100644 --- a/src/common/datavcmn.cpp +++ b/src/common/datavcmn.cpp @@ -1812,7 +1812,17 @@ void wxDataViewEvent::InitData(wxDataObjectComposite* obj, wxDataFormat format) SetDataFormat(format); SetDataObject(obj->GetObject(format)); - SetDataSize(obj->GetDataSize(format)); + + const size_t size = obj->GetDataSize(format); + SetDataSize(size); + + if ( size ) + { + obj->GetDataHere(format, m_dataBuf.GetWriteBuf(size)); + m_dataBuf.UngetWriteBuf(size); + + SetDataBuffer(m_dataBuf.GetData()); + } } #endif // wxUSE_DRAG_AND_DROP From 5ed5803deb61011a8dd2e8c8cfc4557db5141a8b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 10 Dec 2021 19:34:54 +0000 Subject: [PATCH 10/11] Undo the now unnecessary change to the sample The previously working code should continue to work, so keep the original version now that it does work after the changes of the previous commit. Just mention in the comment that te data object can also be retrieved directly. --- samples/dataview/dataview.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/samples/dataview/dataview.cpp b/samples/dataview/dataview.cpp index b679cf91c0..3e66aa0155 100644 --- a/samples/dataview/dataview.cpp +++ b/samples/dataview/dataview.cpp @@ -1430,20 +1430,26 @@ void MyFrame::OnDrop( wxDataViewEvent &event ) return; } - const wxTextDataObject* const obj = static_cast(event.GetDataObject()); + // Note that instead of recreating a new data object here we could also + // retrieve the data object from the event, using its GetDataObject() + // method. This would be more efficient as it would avoid copying the text + // one more time, but would require a cast in the code and we don't really + // care about efficiency here. + wxTextDataObject obj; + obj.SetData( wxDF_UNICODETEXT, event.GetDataSize(), event.GetDataBuffer() ); if ( item.IsOk() ) { if (m_music_model->IsContainer(item)) { wxLogMessage("Text '%s' dropped in container '%s' (proposed index = %i)", - obj->GetText(), m_music_model->GetTitle(item), event.GetProposedDropIndex()); + obj.GetText(), m_music_model->GetTitle(item), event.GetProposedDropIndex()); } else - wxLogMessage("Text '%s' dropped on item '%s'", obj->GetText(), m_music_model->GetTitle(item)); + wxLogMessage("Text '%s' dropped on item '%s'", obj.GetText(), m_music_model->GetTitle(item)); } else - wxLogMessage("Text '%s' dropped on background (proposed index = %i)", obj->GetText(), event.GetProposedDropIndex()); + wxLogMessage("Text '%s' dropped on background (proposed index = %i)", obj.GetText(), event.GetProposedDropIndex()); } #endif // wxUSE_DRAG_AND_DROP From 2679f99f51ab1589a64523cdfa65e14abac13ee5 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 10 Dec 2021 20:40:51 +0100 Subject: [PATCH 11/11] Revert apparently unrelated changes to wxCocoaDataViewControl This reverts some changes of 7129d2b11c (Accept multiple data formats via drag-and-drop in wxDataViewCtrl, 2021-08-22) that don't seem to be related to its actual purpose and were probably done accidentally. --- src/osx/cocoa/dataview.mm | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/osx/cocoa/dataview.mm b/src/osx/cocoa/dataview.mm index b8684d2c01..b0517f5797 100644 --- a/src/osx/cocoa/dataview.mm +++ b/src/osx/cocoa/dataview.mm @@ -1502,6 +1502,7 @@ outlineView:(NSOutlineView*)outlineView currentlyEditedColumn = currentlyEditedRow = -1; + [self setAutoresizesOutlineColumn:NO]; [self setDelegate:self]; [self setDoubleAction:@selector(actionDoubleClick:)]; [self setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO]; @@ -2347,20 +2348,9 @@ bool wxCocoaDataViewControl::Update(const wxDataViewColumn *columnPtr) return false; } -bool wxCocoaDataViewControl::Update(const wxDataViewItem& parent, const wxDataViewItem& item) +bool wxCocoaDataViewControl::Update(const wxDataViewItem& WXUNUSED(parent), const wxDataViewItem& item) { - if (GetSortingColumn()) - { - if (parent.IsOk()) - [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:parent] reloadChildren:YES]; - else - [m_OutlineView reloadData]; - } - else - { - [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:item]]; - } - + [m_OutlineView reloadItem:[m_DataSource getDataViewItemFromBuffer:item]]; return true; }