Implemented copy-on-demand for wxClipboard.
[wxWidgets.git] / src / motif / clipbrd.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: clipbrd.cpp
3 // Purpose: Clipboard functionality
4 // Author: Julian Smart
5 // Modified by: Mattia Barbon (added support for generic wxDataObjects)
6 // Created: 17/09/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #pragma implementation "clipbrd.h"
15 #endif
16
17 #include "wx/defs.h"
18
19 #if wxUSE_CLIPBOARD
20
21 #include "wx/app.h"
22 #include "wx/bitmap.h"
23 #include "wx/utils.h"
24 #include "wx/clipbrd.h"
25 #include "wx/dataobj.h"
26 #include "wx/ptr_scpd.h"
27
28 #ifdef __VMS__
29 #pragma message disable nosimpint
30 #endif
31 #include <Xm/Xm.h>
32 #include <Xm/CutPaste.h>
33 #ifdef __VMS__
34 #pragma message enable nosimpint
35 #endif
36
37 #include "wx/motif/private.h"
38
39 bool wxOpenClipboard()
40 {
41 return wxTheClipboard->Open();
42 }
43
44 bool wxCloseClipboard()
45 {
46 wxTheClipboard->Close();
47
48 return true;
49 }
50
51 bool wxEmptyClipboard()
52 {
53 wxTheClipboard->Clear();
54 return true;
55 }
56
57 bool wxClipboardOpen()
58 {
59 return wxTheClipboard->IsOpened();
60 }
61
62 bool wxIsClipboardFormatAvailable(wxDataFormat dataFormat)
63 {
64 return wxTheClipboard->IsSupported( dataFormat );
65 }
66
67 bool wxSetClipboardData(wxDataFormat dataFormat, wxObject *obj,
68 int WXUNUSED(width), int WXUNUSED(height))
69 {
70 wxDataObject* dobj = NULL;
71
72 if( dataFormat == wxDF_TEXT )
73 {
74 wxChar* data = (wxChar*)obj;
75 dobj = new wxTextDataObject( data );
76 }
77 else if( dataFormat = wxDF_BITMAP )
78 {
79 wxBitmap* data = (wxBitmap*)obj;
80 dobj = new wxBitmapDataObject( *data );
81 }
82
83 if( !dobj )
84 return false;
85
86 return wxTheClipboard->SetData( dobj );
87 }
88
89 wxObject *wxGetClipboardData(wxDataFormat dataFormat, long *len)
90 {
91 wxDataObject* dobj = NULL;
92 wxTextDataObject* tobj = NULL;
93 wxBitmapDataObject* bobj = NULL;
94
95 if( dataFormat == wxDF_TEXT )
96 {
97 dobj = tobj = new wxTextDataObject;
98 }
99 else if( dataFormat = wxDF_BITMAP )
100 {
101 dobj = bobj = new wxBitmapDataObject;
102 }
103
104 if( !dobj || !wxTheClipboard->GetData( *dobj ) )
105 return NULL;
106
107 if( tobj )
108 {
109 wxString text = tobj->GetText();
110 wxChar* buf = new wxChar[text.length() + 1];
111
112 if( len ) *len = text.length();
113 return (wxObject*)wxStrcpy( buf, text.c_str() );
114 }
115 else if( bobj )
116 {
117 if( len ) *len = 0;
118 return new wxBitmap( bobj->GetBitmap() );
119 }
120
121 return NULL; // just in case...
122 }
123
124 wxDataFormat wxEnumClipboardFormats(wxDataFormat dataFormat)
125 {
126 // Only wxDF_TEXT supported
127 if (dataFormat == wxDF_TEXT)
128 return wxDF_TEXT;
129 else
130 return wxDF_INVALID;
131 }
132
133 wxDataFormat wxRegisterClipboardFormat(char *WXUNUSED(formatName))
134 {
135 // Not supported
136 return wxDF_INVALID;
137 }
138
139 bool wxGetClipboardFormatName(wxDataFormat dataFormat, char *formatName,
140 int maxCount)
141 {
142 wxStrncpy( formatName, dataFormat.GetId().c_str(), maxCount );
143
144 return true;
145 }
146
147 //-----------------------------------------------------------------------------
148 // wxClipboard
149 //-----------------------------------------------------------------------------
150
151 struct wxDataIdToDataObject
152 {
153 wxDataIdToDataObject( wxDataObject* o, long d, size_t s )
154 : object( o ), size( s ), dataId( d ) { }
155
156 wxDataObject* object;
157 size_t size;
158 long dataId;
159 };
160
161 #include "wx/listimpl.cpp"
162
163 WX_DEFINE_LIST(wxDataObjectList);
164 WX_DEFINE_LIST(wxDataIdToDataObjectList);
165
166 static void wxClipboardCallback( Widget widget, long* data_id,
167 long* priv, int* reason );
168
169
170 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
171
172 wxClipboard::wxClipboard()
173 {
174 m_open = false;
175 }
176
177 wxClipboard::~wxClipboard()
178 {
179 Clear();
180 }
181
182 void wxClipboard::Clear()
183 {
184 wxDataObjectList::Node* node = m_data.GetFirst();
185 while (node)
186 {
187 delete node->GetData();
188 node = node->GetNext();
189 }
190 m_data.Clear();
191
192 for( wxDataIdToDataObjectList::Node* node2 = m_idToObject.GetFirst();
193 node2; node2 = node2->GetNext() )
194 delete node->GetData();
195 m_idToObject.Clear();
196 }
197
198 bool wxClipboard::Open()
199 {
200 wxCHECK_MSG( !m_open, false, "clipboard already open" );
201
202 m_open = true;
203
204 return true;
205 }
206
207 bool wxClipboard::SetData( wxDataObject *data )
208 {
209 wxCHECK_MSG( data, false, "data is invalid" );
210 wxCHECK_MSG( m_open, false, "clipboard not open" );
211
212 Clear();
213
214 return AddData( data );
215 }
216
217 wxDECLARE_SCOPED_ARRAY( wxDataFormat, wxDataFormatScopedArray );
218 wxDEFINE_SCOPED_ARRAY( wxDataFormat, wxDataFormatScopedArray );
219
220 void wxClipboardCallback( Widget xwidget, long* data_id,
221 long* priv, int* reason )
222 {
223 Display* xdisplay = XtDisplay( xwidget );
224 Window xwindow = XtWindow( xwidget );
225 wxDataObject* dobj = NULL;
226 size_t size = 0;
227
228 for( wxDataIdToDataObjectList::Node* node2 =
229 wxTheClipboard->m_idToObject.GetFirst();
230 node2; node2 = node2->GetNext() )
231 {
232 wxDataIdToDataObject* dido = node2->GetData();
233 if( dido->dataId == *data_id )
234 {
235 dobj = dido->object;
236 size = dido->size;
237 break;
238 }
239 }
240
241 if( !dobj ) return;
242
243 wxCharBuffer buffer(size);
244 size_t count = dobj->GetFormatCount( wxDataObject::Get );
245 wxDataFormatScopedArray dfarr( new wxDataFormat[count] );
246 dobj->GetAllFormats( dfarr.get(), wxDataObject::Get );
247
248 if( !dobj->GetDataHere( dfarr[*priv], buffer.data() ) )
249 return;
250
251 while( XmClipboardCopyByName( xdisplay, xwindow, *data_id,
252 buffer.data(), size, 0 )
253 == XmClipboardLocked );
254 }
255
256 bool wxClipboard::AddData( wxDataObject *data )
257 {
258 wxCHECK_MSG( data, false, "data is invalid" );
259 wxCHECK_MSG( m_open, false, "clipboard not open" );
260
261 m_data.Append( data );
262
263 Display* xdisplay = wxGlobalDisplay();
264 Widget xwidget = (Widget)wxTheApp->GetTopLevelWidget();
265 Window xwindow = XtWindow( xwidget );
266 wxXmString label( wxTheApp->GetAppName() );
267 Time timestamp = XtLastTimestampProcessed( xdisplay );
268 long itemId;
269
270 int retval;
271
272 while( ( retval = XmClipboardStartCopy( xdisplay, xwindow, label(),
273 timestamp, xwidget,
274 wxClipboardCallback,
275 &itemId ) )
276 == XmClipboardLocked );
277 if( retval != XmClipboardSuccess )
278 return false;
279
280 size_t count = data->GetFormatCount( wxDataObject::Get );
281 wxDataFormatScopedArray dfarr( new wxDataFormat[count] );
282 data->GetAllFormats( dfarr.get(), wxDataObject::Get );
283
284 for( size_t i = 0; i < count; ++i )
285 {
286 size_t size = data->GetDataSize( dfarr[i] );
287 long data_id;
288 wxString id = dfarr[i].GetId();
289
290 while( ( retval = XmClipboardCopy( xdisplay, xwindow, itemId,
291 wxConstCast(id.c_str(), char),
292 NULL, size, i, &data_id ) )
293 == XmClipboardLocked );
294
295 m_idToObject.Append( new wxDataIdToDataObject( data, data_id, size ) );
296 }
297
298 while( XmClipboardEndCopy( xdisplay, xwindow, itemId )
299 == XmClipboardLocked );
300
301 return true;
302 }
303
304 void wxClipboard::Close()
305 {
306 wxCHECK_RET( m_open, "clipboard not open" );
307
308 m_open = false;
309 }
310
311 bool wxClipboard::IsSupported(const wxDataFormat& format)
312 {
313 Display* xdisplay = wxGlobalDisplay();
314 Window xwindow = XtWindow( (Widget)wxTheApp->GetTopLevelWidget() );
315 bool isSupported = false;
316 int retval, count;
317 unsigned long max_name_length;
318 wxString id = format.GetId();
319
320 while( ( retval = XmClipboardLock( xdisplay, xwindow ) )
321 == XmClipboardLocked );
322 if( retval != XmClipboardSuccess )
323 return false;
324
325 if( XmClipboardInquireCount( xdisplay, xwindow, &count, &max_name_length )
326 == XmClipboardSuccess )
327 {
328 wxCharBuffer buf( max_name_length + 1 );
329 unsigned long copied;
330
331 for( int i = 0; i < count; ++i )
332 {
333 if( XmClipboardInquireFormat( xdisplay, xwindow, i + 1,
334 (XtPointer)buf.data(),
335 max_name_length, &copied )
336 != XmClipboardSuccess )
337 continue;
338
339 buf.data()[copied] = '\0';
340
341 if( buf == id )
342 {
343 isSupported = true;
344 break;
345 }
346 }
347 }
348
349 XmClipboardUnlock( xdisplay, xwindow, False );
350
351 return isSupported;
352 }
353
354 class wxClipboardEndRetrieve
355 {
356 public:
357 wxClipboardEndRetrieve( Display* display, Window window )
358 : m_display( display ), m_window( window ) { }
359 ~wxClipboardEndRetrieve()
360 {
361 while( XmClipboardEndRetrieve( m_display, m_window )
362 == XmClipboardLocked );
363 }
364 private:
365 Display* m_display;
366 Window m_window;
367 };
368
369 bool wxClipboard::GetData( wxDataObject& data )
370 {
371 wxCHECK_MSG( m_open, false, "clipboard not open" );
372
373 Display* xdisplay = wxGlobalDisplay();
374 Window xwindow = XtWindow( (Widget)wxTheApp->GetTopLevelWidget() );
375 Time timestamp = XtLastTimestampProcessed( xdisplay );
376
377 wxDataFormat chosenFormat;
378 int retval;
379
380 ///////////////////////////////////////////////////////////////////////////
381 // determine if the cliboard holds any format we like
382 ///////////////////////////////////////////////////////////////////////////
383 while( ( retval = XmClipboardStartRetrieve( xdisplay, xwindow,
384 timestamp ) )
385 == XmClipboardLocked );
386 if( retval != XmClipboardSuccess )
387 return false;
388
389 wxClipboardEndRetrieve endRetrieve( xdisplay, xwindow );
390
391 int count;
392 unsigned long max_name_length;
393 size_t dfcount = data.GetFormatCount( wxDataObject::Set );
394 wxDataFormatScopedArray dfarr( new wxDataFormat[dfcount] );
395 data.GetAllFormats( dfarr.get(), wxDataObject::Set );
396
397 if( XmClipboardInquireCount( xdisplay, xwindow, &count, &max_name_length )
398 == XmClipboardSuccess )
399 {
400 wxCharBuffer buf( max_name_length + 1 );
401 unsigned long copied;
402
403 for( int i = 0; i < count; ++i )
404 {
405 if( XmClipboardInquireFormat( xdisplay, xwindow, i + 1,
406 (XtPointer)buf.data(),
407 max_name_length, &copied )
408 != XmClipboardSuccess )
409 continue;
410
411 buf.data()[copied] = '\0';
412
413 // try preferred format
414 if( buf == data.GetPreferredFormat( wxDataObject::Set ).GetId() )
415 {
416 chosenFormat = data.GetPreferredFormat( wxDataObject::Set );
417 break;
418 }
419
420 // try all other formats
421 for( size_t i = 0; i < dfcount; ++i )
422 {
423 if( buf == dfarr[i].GetId() )
424 chosenFormat = dfarr[i];
425 }
426 }
427 }
428
429 if( chosenFormat == wxDF_INVALID )
430 return false;
431
432 ///////////////////////////////////////////////////////////////////////////
433 // now retrieve the data
434 ///////////////////////////////////////////////////////////////////////////
435 unsigned long length, dummy1;
436 long dummy2;
437 wxString id = chosenFormat.GetId();
438
439 while( ( retval = XmClipboardInquireLength( xdisplay, xwindow,
440 wxConstCast(id.c_str(), char),
441 &length ) )
442 == XmClipboardLocked );
443 if( retval != XmClipboardSuccess )
444 return false;
445
446 wxCharBuffer buf(length);
447
448 while( ( retval = XmClipboardRetrieve( xdisplay, xwindow,
449 wxConstCast(id.c_str(), char),
450 (XtPointer)buf.data(),
451 length, &dummy1, &dummy2 ) )
452 == XmClipboardLocked );
453 if( retval != XmClipboardSuccess )
454 return false;
455
456 if( !data.SetData( chosenFormat, length, buf.data() ) )
457 return false;
458
459 return true;
460 }
461
462 #endif // wxUSE_CLIPBOARD