]> git.saurik.com Git - wxWidgets.git/blob - src/common/dobjcmn.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / dobjcmn.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/dobjcmn.cpp
3 // Purpose: implementation of data object methods common to all platforms
4 // Author: Vadim Zeitlin, Robert Roebling
5 // Modified by:
6 // Created: 19.10.99
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWidgets Team
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_DATAOBJ
20
21 #include "wx/dataobj.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/app.h"
25 #endif
26
27 #include "wx/textbuf.h"
28
29 // ----------------------------------------------------------------------------
30 // lists
31 // ----------------------------------------------------------------------------
32
33 #include "wx/listimpl.cpp"
34
35 WX_DEFINE_LIST(wxSimpleDataObjectList)
36
37 // ----------------------------------------------------------------------------
38 // globals
39 // ----------------------------------------------------------------------------
40
41 static wxDataFormat dataFormatInvalid;
42 WXDLLEXPORT const wxDataFormat& wxFormatInvalid = dataFormatInvalid;
43
44 // ============================================================================
45 // implementation
46 // ============================================================================
47
48 // ----------------------------------------------------------------------------
49 // wxDataObjectBase
50 // ----------------------------------------------------------------------------
51
52 wxDataObjectBase::~wxDataObjectBase()
53 {
54 }
55
56 bool wxDataObjectBase::IsSupported(const wxDataFormat& format,
57 Direction dir) const
58 {
59 size_t nFormatCount = GetFormatCount( dir );
60 if ( nFormatCount == 1 )
61 {
62 return format == GetPreferredFormat( dir );
63 }
64 else
65 {
66 wxDataFormat *formats = new wxDataFormat[nFormatCount];
67 GetAllFormats( formats, dir );
68
69 size_t n;
70 for ( n = 0; n < nFormatCount; n++ )
71 {
72 if ( formats[n] == format )
73 break;
74 }
75
76 delete [] formats;
77
78 // found?
79 return n < nFormatCount;
80 }
81 }
82
83 // ----------------------------------------------------------------------------
84 // wxDataObjectComposite
85 // ----------------------------------------------------------------------------
86
87 wxDataObjectComposite::wxDataObjectComposite()
88 {
89 m_preferred = 0;
90 m_receivedFormat = wxFormatInvalid;
91 }
92
93 wxDataObjectComposite::~wxDataObjectComposite()
94 {
95 WX_CLEAR_LIST( wxSimpleDataObjectList, m_dataObjects );
96 }
97
98 wxDataObjectSimple *
99 wxDataObjectComposite::GetObject(const wxDataFormat& format, wxDataObjectBase::Direction dir) const
100 {
101 wxSimpleDataObjectList::compatibility_iterator node = m_dataObjects.GetFirst();
102
103 while ( node )
104 {
105 wxDataObjectSimple *dataObj = node->GetData();
106
107 if (dataObj->IsSupported(format,dir))
108 return dataObj;
109 node = node->GetNext();
110 }
111 return NULL;
112 }
113
114 void wxDataObjectComposite::Add(wxDataObjectSimple *dataObject, bool preferred)
115 {
116 if ( preferred )
117 m_preferred = m_dataObjects.GetCount();
118
119 m_dataObjects.Append( dataObject );
120 }
121
122 wxDataFormat wxDataObjectComposite::GetReceivedFormat() const
123 {
124 return m_receivedFormat;
125 }
126
127 wxDataFormat
128 wxDataObjectComposite::GetPreferredFormat(Direction WXUNUSED(dir)) const
129 {
130 wxSimpleDataObjectList::compatibility_iterator node = m_dataObjects.Item( m_preferred );
131
132 wxCHECK_MSG( node, wxFormatInvalid, wxT("no preferred format") );
133
134 wxDataObjectSimple* dataObj = node->GetData();
135
136 return dataObj->GetFormat();
137 }
138
139 #if defined(__WXMSW__)
140
141 size_t wxDataObjectComposite::GetBufferOffset( const wxDataFormat& format )
142 {
143 wxDataObjectSimple *dataObj = GetObject(format);
144
145 wxCHECK_MSG( dataObj, 0,
146 wxT("unsupported format in wxDataObjectComposite"));
147
148 return dataObj->GetBufferOffset( format );
149 }
150
151
152 const void* wxDataObjectComposite::GetSizeFromBuffer( const void* buffer,
153 size_t* size,
154 const wxDataFormat& format )
155 {
156 wxDataObjectSimple *dataObj = GetObject(format);
157
158 wxCHECK_MSG( dataObj, NULL,
159 wxT("unsupported format in wxDataObjectComposite"));
160
161 return dataObj->GetSizeFromBuffer( buffer, size, format );
162 }
163
164
165 void* wxDataObjectComposite::SetSizeInBuffer( void* buffer, size_t size,
166 const wxDataFormat& format )
167 {
168 wxDataObjectSimple *dataObj = GetObject( format );
169
170 wxCHECK_MSG( dataObj, NULL,
171 wxT("unsupported format in wxDataObjectComposite"));
172
173 return dataObj->SetSizeInBuffer( buffer, size, format );
174 }
175
176 #endif
177
178 size_t wxDataObjectComposite::GetFormatCount(Direction dir) const
179 {
180 size_t n = 0;
181
182 // NOTE: some wxDataObjectSimple objects may return a number greater than 1
183 // from GetFormatCount(): this is the case of e.g. wxTextDataObject
184 // under wxMac and wxGTK
185 wxSimpleDataObjectList::compatibility_iterator node;
186 for ( node = m_dataObjects.GetFirst(); node; node = node->GetNext() )
187 n += node->GetData()->GetFormatCount(dir);
188
189 return n;
190 }
191
192 void wxDataObjectComposite::GetAllFormats(wxDataFormat *formats,
193 Direction dir) const
194 {
195 size_t index(0);
196 wxSimpleDataObjectList::compatibility_iterator node;
197
198 for ( node = m_dataObjects.GetFirst(); node; node = node->GetNext() )
199 {
200 // NOTE: some wxDataObjectSimple objects may return more than 1 format
201 // from GetAllFormats(): this is the case of e.g. wxTextDataObject
202 // under wxMac and wxGTK
203 node->GetData()->GetAllFormats(formats+index, dir);
204 index += node->GetData()->GetFormatCount(dir);
205 }
206 }
207
208 size_t wxDataObjectComposite::GetDataSize(const wxDataFormat& format) const
209 {
210 wxDataObjectSimple *dataObj = GetObject(format);
211
212 wxCHECK_MSG( dataObj, 0,
213 wxT("unsupported format in wxDataObjectComposite"));
214
215 return dataObj->GetDataSize();
216 }
217
218 bool wxDataObjectComposite::GetDataHere(const wxDataFormat& format,
219 void *buf) const
220 {
221 wxDataObjectSimple *dataObj = GetObject( format );
222
223 wxCHECK_MSG( dataObj, false,
224 wxT("unsupported format in wxDataObjectComposite"));
225
226 return dataObj->GetDataHere( buf );
227 }
228
229 bool wxDataObjectComposite::SetData(const wxDataFormat& format,
230 size_t len,
231 const void *buf)
232 {
233 wxDataObjectSimple *dataObj = GetObject( format );
234
235 wxCHECK_MSG( dataObj, false,
236 wxT("unsupported format in wxDataObjectComposite"));
237
238 m_receivedFormat = format;
239
240 // Notice that we must pass "format" here as wxTextDataObject, that we can
241 // have as one of our "simple" sub-objects actually is not that simple and
242 // can support multiple formats (ASCII/UTF-8/UTF-16/...) and so needs to
243 // know which one it is given.
244 return dataObj->SetData( format, len, buf );
245 }
246
247 // ----------------------------------------------------------------------------
248 // wxTextDataObject
249 // ----------------------------------------------------------------------------
250
251 #ifdef wxNEEDS_UTF8_FOR_TEXT_DATAOBJ
252
253 // FIXME-UTF8: we should be able to merge wchar_t and UTF-8 versions once we
254 // have a way to get UTF-8 string (and its length) in both builds
255 // without loss of efficiency (i.e. extra buffer copy/strlen call)
256
257 #if wxUSE_UNICODE_WCHAR
258
259 static inline wxMBConv& GetConv(const wxDataFormat& format)
260 {
261 // use UTF8 for wxDF_UNICODETEXT and UCS4 for wxDF_TEXT
262 return format == wxDF_UNICODETEXT ? wxConvUTF8 : wxConvLibc;
263 }
264
265 size_t wxTextDataObject::GetDataSize(const wxDataFormat& format) const
266 {
267 wxCharBuffer buffer = GetConv(format).cWX2MB( GetText().c_str() );
268
269 return buffer ? strlen( buffer ) : 0;
270 }
271
272 bool wxTextDataObject::GetDataHere(const wxDataFormat& format, void *buf) const
273 {
274 if ( !buf )
275 return false;
276
277 wxCharBuffer buffer = GetConv(format).cWX2MB( GetText().c_str() );
278 if ( !buffer )
279 return false;
280
281 memcpy( (char*) buf, buffer, GetDataSize(format) );
282 // strcpy( (char*) buf, buffer );
283
284 return true;
285 }
286
287 bool wxTextDataObject::SetData(const wxDataFormat& format,
288 size_t WXUNUSED(len), const void *buf)
289 {
290 if ( buf == NULL )
291 return false;
292
293 wxWCharBuffer buffer = GetConv(format).cMB2WX( (const char*)buf );
294
295 SetText( buffer );
296
297 return true;
298 }
299
300 #else // wxUSE_UNICODE_UTF8
301
302 size_t wxTextDataObject::GetDataSize(const wxDataFormat& format) const
303 {
304 const wxString& text = GetText();
305 if ( format == wxDF_UNICODETEXT || wxLocaleIsUtf8 )
306 {
307 return text.utf8_length();
308 }
309 else // wxDF_TEXT
310 {
311 const wxCharBuffer buf(wxConvLocal.cWC2MB(text.wc_str()));
312 return buf ? strlen(buf) : 0;
313 }
314 }
315
316 bool wxTextDataObject::GetDataHere(const wxDataFormat& format, void *buf) const
317 {
318 if ( !buf )
319 return false;
320
321 const wxString& text = GetText();
322 if ( format == wxDF_UNICODETEXT || wxLocaleIsUtf8 )
323 {
324 memcpy(buf, text.utf8_str(), text.utf8_length());
325 }
326 else // wxDF_TEXT
327 {
328 const wxCharBuffer bufLocal(wxConvLocal.cWC2MB(text.wc_str()));
329 if ( !bufLocal )
330 return false;
331
332 memcpy(buf, bufLocal, strlen(bufLocal));
333 }
334
335 return true;
336 }
337
338 bool wxTextDataObject::SetData(const wxDataFormat& format,
339 size_t len, const void *buf_)
340 {
341 const char * const buf = static_cast<const char *>(buf_);
342
343 if ( buf == NULL )
344 return false;
345
346 if ( format == wxDF_UNICODETEXT || wxLocaleIsUtf8 )
347 {
348 // normally the data is in UTF-8 so we could use FromUTF8Unchecked()
349 // but it's not absolutely clear what GTK+ does if the clipboard data
350 // is not in UTF-8 so do an extra check for tranquility, it shouldn't
351 // matter much if we lose a bit of performance when pasting from
352 // clipboard
353 SetText(wxString::FromUTF8(buf, len));
354 }
355 else // wxDF_TEXT, convert from current (non-UTF8) locale
356 {
357 SetText(wxConvLocal.cMB2WC(buf, len, NULL));
358 }
359
360 return true;
361 }
362
363 #endif // wxUSE_UNICODE_WCHAR/wxUSE_UNICODE_UTF8
364
365 #elif defined(wxNEEDS_UTF16_FOR_TEXT_DATAOBJ)
366
367 namespace
368 {
369
370 inline wxMBConv& GetConv(const wxDataFormat& format)
371 {
372 static wxMBConvUTF16 s_UTF16Converter;
373
374 return format == wxDF_UNICODETEXT ? static_cast<wxMBConv&>(s_UTF16Converter)
375 : static_cast<wxMBConv&>(wxConvLocal);
376 }
377
378 } // anonymous namespace
379
380 size_t wxTextDataObject::GetDataSize(const wxDataFormat& format) const
381 {
382 return GetConv(format).WC2MB(NULL, GetText().wc_str(), 0);
383 }
384
385 bool wxTextDataObject::GetDataHere(const wxDataFormat& format, void *buf) const
386 {
387 if ( buf == NULL )
388 return false;
389
390 wxCharBuffer buffer(GetConv(format).cWX2MB(GetText().c_str()));
391
392 memcpy(buf, buffer.data(), buffer.length());
393
394 return true;
395 }
396
397 bool wxTextDataObject::SetData(const wxDataFormat& format,
398 size_t WXUNUSED(len),
399 const void *buf)
400 {
401 if ( buf == NULL )
402 return false;
403
404 SetText(GetConv(format).cMB2WX(static_cast<const char*>(buf)));
405
406 return true;
407 }
408
409 #else // !wxNEEDS_UTF{8,16}_FOR_TEXT_DATAOBJ
410
411 // NB: This branch, using native wxChar for the clipboard, is only used under
412 // Windows currently. It's just a coincidence, but Windows is also the only
413 // platform where we need to convert the text to the native EOL format, so
414 // wxTextBuffer::Translate() is only used here and not in the code above.
415
416 size_t wxTextDataObject::GetDataSize() const
417 {
418 return (wxTextBuffer::Translate(GetText()).length() + 1)*sizeof(wxChar);
419 }
420
421 bool wxTextDataObject::GetDataHere(void *buf) const
422 {
423 const wxString textNative = wxTextBuffer::Translate(GetText());
424
425 // NOTE: use wxTmemcpy() instead of wxStrncpy() to allow
426 // retrieval of strings with embedded NULLs
427 wxTmemcpy(static_cast<wxChar*>(buf),
428 textNative.t_str(),
429 textNative.length() + 1);
430
431 return true;
432 }
433
434 bool wxTextDataObject::SetData(size_t len, const void *buf)
435 {
436 const wxString
437 text = wxString(static_cast<const wxChar*>(buf), len/sizeof(wxChar));
438 SetText(wxTextBuffer::Translate(text, wxTextFileType_Unix));
439
440 return true;
441 }
442
443 #endif // different wxTextDataObject implementations
444
445 // ----------------------------------------------------------------------------
446 // wxHTMLDataObject
447 // ----------------------------------------------------------------------------
448
449 size_t wxHTMLDataObject::GetDataSize() const
450 {
451 const wxScopedCharBuffer buffer(GetHTML().utf8_str());
452
453 size_t size = buffer.length();
454
455 #ifdef __WXMSW__
456 // On Windows we need to add some stuff to the string to satisfy
457 // its clipboard format requirements.
458 size += 400;
459 #endif
460
461 return size;
462 }
463
464 bool wxHTMLDataObject::GetDataHere(void *buf) const
465 {
466 if ( !buf )
467 return false;
468
469 // Windows and Mac always use UTF-8, and docs suggest GTK does as well.
470 const wxScopedCharBuffer html(GetHTML().utf8_str());
471 if ( !html )
472 return false;
473
474 char* const buffer = static_cast<char*>(buf);
475
476 #ifdef __WXMSW__
477 // add the extra info that the MSW clipboard format requires.
478
479 // Create a template string for the HTML header...
480 strcpy(buffer,
481 "Version:0.9\r\n"
482 "StartHTML:00000000\r\n"
483 "EndHTML:00000000\r\n"
484 "StartFragment:00000000\r\n"
485 "EndFragment:00000000\r\n"
486 "<html><body>\r\n"
487 "<!--StartFragment -->\r\n");
488
489 // Append the HTML...
490 strcat(buffer, html);
491 strcat(buffer, "\r\n");
492 // Finish up the HTML format...
493 strcat(buffer,
494 "<!--EndFragment-->\r\n"
495 "</body>\r\n"
496 "</html>");
497
498 // Now go back, calculate all the lengths, and write out the
499 // necessary header information. Note, wsprintf() truncates the
500 // string when you overwrite it so you follow up with code to replace
501 // the 0 appended at the end with a '\r'...
502 char *ptr = strstr(buffer, "StartHTML");
503 sprintf(ptr+10, "%08u", (unsigned)(strstr(buffer, "<html>") - buffer));
504 *(ptr+10+8) = '\r';
505
506 ptr = strstr(buffer, "EndHTML");
507 sprintf(ptr+8, "%08u", (unsigned)strlen(buffer));
508 *(ptr+8+8) = '\r';
509
510 ptr = strstr(buffer, "StartFragment");
511 sprintf(ptr+14, "%08u", (unsigned)(strstr(buffer, "<!--StartFrag") - buffer));
512 *(ptr+14+8) = '\r';
513
514 ptr = strstr(buffer, "EndFragment");
515 sprintf(ptr+12, "%08u", (unsigned)(strstr(buffer, "<!--EndFrag") - buffer));
516 *(ptr+12+8) = '\r';
517 #else
518 strcpy(buffer, html);
519 #endif // __WXMSW__
520
521 return true;
522 }
523
524 bool wxHTMLDataObject::SetData(size_t WXUNUSED(len), const void *buf)
525 {
526 if ( buf == NULL )
527 return false;
528
529 // Windows and Mac always use UTF-8, and docs suggest GTK does as well.
530 wxString html = wxString::FromUTF8(static_cast<const char*>(buf));
531
532 #ifdef __WXMSW__
533 // To be consistent with other platforms, we only add the Fragment part
534 // of the Windows HTML clipboard format to the data object.
535 int fragmentStart = html.rfind("StartFragment");
536 int fragmentEnd = html.rfind("EndFragment");
537
538 if (fragmentStart != wxNOT_FOUND && fragmentEnd != wxNOT_FOUND)
539 {
540 int startCommentEnd = html.find("-->", fragmentStart) + 3;
541 int endCommentStart = html.rfind("<!--", fragmentEnd);
542
543 if (startCommentEnd != wxNOT_FOUND && endCommentStart != wxNOT_FOUND)
544 html = html.Mid(startCommentEnd, endCommentStart - startCommentEnd);
545 }
546 #endif // __WXMSW__
547
548 SetHTML( html );
549
550 return true;
551 }
552
553
554 // ----------------------------------------------------------------------------
555 // wxCustomDataObject
556 // ----------------------------------------------------------------------------
557
558 wxCustomDataObject::wxCustomDataObject(const wxDataFormat& format)
559 : wxDataObjectSimple(format)
560 {
561 m_data = NULL;
562 m_size = 0;
563 }
564
565 wxCustomDataObject::~wxCustomDataObject()
566 {
567 Free();
568 }
569
570 void wxCustomDataObject::TakeData(size_t size, void *data)
571 {
572 Free();
573
574 m_size = size;
575 m_data = data;
576 }
577
578 void *wxCustomDataObject::Alloc(size_t size)
579 {
580 return (void *)new char[size];
581 }
582
583 void wxCustomDataObject::Free()
584 {
585 delete [] (char*)m_data;
586 m_size = 0;
587 m_data = NULL;
588 }
589
590 size_t wxCustomDataObject::GetDataSize() const
591 {
592 return GetSize();
593 }
594
595 bool wxCustomDataObject::GetDataHere(void *buf) const
596 {
597 if ( buf == NULL )
598 return false;
599
600 void *data = GetData();
601 if ( data == NULL )
602 return false;
603
604 memcpy( buf, data, GetSize() );
605
606 return true;
607 }
608
609 bool wxCustomDataObject::SetData(size_t size, const void *buf)
610 {
611 Free();
612
613 m_data = Alloc(size);
614 if ( m_data == NULL )
615 return false;
616
617 m_size = size;
618 memcpy( m_data, buf, m_size );
619
620 return true;
621 }
622
623 // ============================================================================
624 // some common dnd related code
625 // ============================================================================
626
627 #if wxUSE_DRAG_AND_DROP
628
629 #include "wx/dnd.h"
630
631 // ----------------------------------------------------------------------------
632 // wxTextDropTarget
633 // ----------------------------------------------------------------------------
634
635 // NB: we can't use "new" in ctor initializer lists because this provokes an
636 // internal compiler error with VC++ 5.0 (hey, even gcc compiles this!),
637 // so use SetDataObject() instead
638
639 wxTextDropTarget::wxTextDropTarget()
640 {
641 SetDataObject(new wxTextDataObject);
642 }
643
644 wxDragResult wxTextDropTarget::OnData(wxCoord x, wxCoord y, wxDragResult def)
645 {
646 if ( !GetData() )
647 return wxDragNone;
648
649 wxTextDataObject *dobj = (wxTextDataObject *)m_dataObject;
650 return OnDropText( x, y, dobj->GetText() ) ? def : wxDragNone;
651 }
652
653 // ----------------------------------------------------------------------------
654 // wxFileDropTarget
655 // ----------------------------------------------------------------------------
656
657 wxFileDropTarget::wxFileDropTarget()
658 {
659 SetDataObject(new wxFileDataObject);
660 }
661
662 wxDragResult wxFileDropTarget::OnData(wxCoord x, wxCoord y, wxDragResult def)
663 {
664 if ( !GetData() )
665 return wxDragNone;
666
667 wxFileDataObject *dobj = (wxFileDataObject *)m_dataObject;
668 return OnDropFiles( x, y, dobj->GetFilenames() ) ? def : wxDragNone;
669 }
670
671 #endif // wxUSE_DRAG_AND_DROP
672
673 #endif // wxUSE_DATAOBJ