1///////////////////////////////////////////////////////////////////////////// 2// Name: src/x11/clipbrd.cpp 3// Purpose: Clipboard functionality 4// Author: Robert Roebling 5// Created: 6// RCS-ID: $Id: clipbrd.cpp 56402 2008-10-17 13:45:34Z VZ $ 7// Copyright: (c) Robert Roebling 8// Licence: wxWindows licence 9///////////////////////////////////////////////////////////////////////////// 10 11// for compilers that support precompilation, includes "wx.h". 12#include "wx/wxprec.h" 13 14#if wxUSE_CLIPBOARD 15 16#include "wx/clipbrd.h" 17 18#ifndef WX_PRECOMP 19 #include "wx/log.h" 20 #include "wx/utils.h" 21 #include "wx/dataobj.h" 22#endif 23 24#include "wx/x11/private.h" 25 26//----------------------------------------------------------------------------- 27// data 28//----------------------------------------------------------------------------- 29 30#if !wxUSE_NANOX 31Atom g_clipboardAtom = 0; 32Atom g_targetsAtom = 0; 33#endif 34 35// avoid warnings about unused static variable (notice that we still use it 36// even in release build if the compiler doesn't support variadic macros) 37#if defined(__WXDEBUG__) || !defined(HAVE_VARIADIC_MACROS) 38 39// the trace mask we use with wxLogTrace() - call 40// wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here 41// (there will be a *lot* of them!) 42static const wxChar *TRACE_CLIPBOARD = _T("clipboard"); 43 44#endif // __WXDEBUG__ 45 46//----------------------------------------------------------------------------- 47// reminder 48//----------------------------------------------------------------------------- 49 50/* The contents of a selection are returned in a GtkSelectionData 51 structure. selection/target identify the request. 52 type specifies the type of the return; if length < 0, and 53 the data should be ignored. This structure has object semantics - 54 no fields should be modified directly, they should not be created 55 directly, and pointers to them should not be stored beyond the duration of 56 a callback. (If the last is changed, we'll need to add reference 57 counting) 58 59struct _GtkSelectionData 60{ 61 GdkAtom selection; 62 GdkAtom target; 63 GdkAtom type; 64 gint format; 65 guchar *data; 66 gint length; 67}; 68 69*/ 70 71//----------------------------------------------------------------------------- 72// "selection_received" for targets 73//----------------------------------------------------------------------------- 74 75#if 0 76 77static void 78targets_selection_received( GtkWidget *WXUNUSED(widget), 79 GtkSelectionData *selection_data, 80#if (GTK_MINOR_VERSION > 0) 81 guint32 WXUNUSED(time), 82#endif 83 wxClipboard *clipboard ) 84{ 85 if ( wxTheClipboard && selection_data->length > 0 ) 86 { 87 /* make sure we got the data in the correct form */ 88 GdkAtom type = selection_data->type; 89 if ( type != GDK_SELECTION_TYPE_ATOM ) 90 { 91 if ( strcmp(gdk_atom_name(type), "TARGETS") ) 92 { 93 wxLogTrace( TRACE_CLIPBOARD, 94 _T("got unsupported clipboard target") ); 95 96 clipboard->m_waiting = false; 97 return; 98 } 99 } 100 101#ifdef __WXDEBUG__ 102 wxDataFormat clip( selection_data->selection ); 103 wxLogTrace( TRACE_CLIPBOARD, 104 wxT("selection received for targets, clipboard %s"), 105 clip.GetId().c_str() ); 106#endif // __WXDEBUG__ 107 108 // the atoms we received, holding a list of targets (= formats) 109 GdkAtom *atoms = (GdkAtom *)selection_data->data; 110 111 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++) 112 { 113 wxDataFormat format( atoms[i] ); 114 115 wxLogTrace( TRACE_CLIPBOARD, 116 wxT("selection received for targets, format %s"), 117 format.GetId().c_str() ); 118 119 if (format == clipboard->m_targetRequested) 120 { 121 clipboard->m_waiting = false; 122 clipboard->m_formatSupported = true; 123 return; 124 } 125 } 126 } 127 128 clipboard->m_waiting = false; 129} 130 131//----------------------------------------------------------------------------- 132// "selection_received" for the actual data 133//----------------------------------------------------------------------------- 134 135static void 136selection_received( GtkWidget *WXUNUSED(widget), 137 GtkSelectionData *selection_data, 138#if (GTK_MINOR_VERSION > 0) 139 guint32 WXUNUSED(time), 140#endif 141 wxClipboard *clipboard ) 142{ 143 if (!wxTheClipboard) 144 { 145 clipboard->m_waiting = false; 146 return; 147 } 148 149 wxDataObject *data_object = clipboard->m_receivedData; 150 151 if (!data_object) 152 { 153 clipboard->m_waiting = false; 154 return; 155 } 156 157 if (selection_data->length <= 0) 158 { 159 clipboard->m_waiting = false; 160 return; 161 } 162 163 wxDataFormat format( selection_data->target ); 164 165 /* make sure we got the data in the correct format */ 166 if (!data_object->IsSupportedFormat( format ) ) 167 { 168 clipboard->m_waiting = false; 169 return; 170 } 171 172 /* make sure we got the data in the correct form (selection type). 173 if so, copy data to target object */ 174 if (selection_data->type != GDK_SELECTION_TYPE_STRING) 175 { 176 clipboard->m_waiting = false; 177 return; 178 } 179 180 data_object->SetData( format, (size_t) selection_data->length, (const char*) selection_data->data ); 181 182 wxTheClipboard->m_formatSupported = true; 183 clipboard->m_waiting = false; 184} 185 186//----------------------------------------------------------------------------- 187// "selection_clear" 188//----------------------------------------------------------------------------- 189 190static gint 191selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event ) 192{ 193 if (!wxTheClipboard) return TRUE; 194 195 if (event->selection == GDK_SELECTION_PRIMARY) 196 { 197 wxTheClipboard->m_ownsPrimarySelection = false; 198 } 199 else 200 if (event->selection == g_clipboardAtom) 201 { 202 wxTheClipboard->m_ownsClipboard = false; 203 } 204 else 205 { 206 wxTheClipboard->m_waiting = false; 207 return FALSE; 208 } 209 210 if ((!wxTheClipboard->m_ownsPrimarySelection) && 211 (!wxTheClipboard->m_ownsClipboard)) 212 { 213 /* the clipboard is no longer in our hands. we can the delete clipboard data. */ 214 if (wxTheClipboard->m_data) 215 { 216 wxLogTrace(TRACE_CLIPBOARD, wxT("wxClipboard will get cleared" )); 217 218 delete wxTheClipboard->m_data; 219 wxTheClipboard->m_data = (wxDataObject*) NULL; 220 } 221 } 222 223 wxTheClipboard->m_waiting = false; 224 return TRUE; 225} 226 227//----------------------------------------------------------------------------- 228// selection handler for supplying data 229//----------------------------------------------------------------------------- 230 231static void 232selection_handler( GtkWidget *WXUNUSED(widget), 233 GtkSelectionData *selection_data, 234 guint WXUNUSED(info), 235 guint WXUNUSED(time), 236 gpointer WXUNUSED(data) ) 237{ 238 if (!wxTheClipboard) return; 239 240 if (!wxTheClipboard->m_data) return; 241 242 wxDataObject *data = wxTheClipboard->m_data; 243 244 wxDataFormat format( selection_data->target ); 245 246 if (!data->IsSupportedFormat( format )) return; 247 248 int size = data->GetDataSize( format ); 249 250 if (size == 0) return; 251 252 void *d = malloc(size); 253 254 data->GetDataHere( selection_data->target, d ); 255 256 // transform Unicode text into multibyte before putting it on clipboard 257#if wxUSE_UNICODE 258 if ( format.GetType() == wxDF_TEXT || format.GetType() == wxDF_UNICODETEXT) 259 { 260 const wchar_t *wstr = (const wchar_t *)d; 261 size_t len = wxConvCurrent->WC2MB(NULL, wstr, 0); 262 if ( len == wxCONV_FAILED ) 263 { 264 free(d); 265 return; 266 } 267 268 char *str = malloc(len + 1); 269 wxConvCurrent->WC2MB(str, wstr, len + 1); 270 str[len] = '\0'; 271 272 free(d); 273 d = str; 274 } 275#endif // wxUSE_UNICODE 276 277 gtk_selection_data_set( 278 selection_data, 279 GDK_SELECTION_TYPE_STRING, 280 8*sizeof(gchar), 281 (unsigned char*) d, 282 size ); 283 284 free(d); 285} 286 287#endif 288 289//----------------------------------------------------------------------------- 290// wxClipboard 291//----------------------------------------------------------------------------- 292 293IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject) 294 295wxClipboard::wxClipboard() 296{ 297 m_open = false; 298 299 m_ownsClipboard = false; 300 m_ownsPrimarySelection = false; 301 302 m_data = (wxDataObject*) NULL; 303 m_receivedData = (wxDataObject*) NULL; 304 305 /* we use m_targetsWidget to query what formats are available */ 306 307 /* we use m_clipboardWidget to get and to offer data */ 308#if !wxUSE_NANOX 309 if (!g_clipboardAtom) g_clipboardAtom = XInternAtom( (Display*) wxGetDisplay(), "CLIPBOARD", False ); 310 if (!g_targetsAtom) g_targetsAtom = XInternAtom( (Display*) wxGetDisplay(), "TARGETS", False ); 311#endif 312 313 m_formatSupported = false; 314 m_targetRequested = 0; 315 316 m_usePrimary = false; 317} 318 319wxClipboard::~wxClipboard() 320{ 321 Clear(); 322 323// if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget ); 324// if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget ); 325} 326 327void wxClipboard::Clear() 328{ 329 if (m_data) 330 { 331#if wxUSE_THREADS 332 /* disable GUI threads */ 333#endif 334 335 /* As we have data we also own the clipboard. Once we no longer own 336 it, clear_selection is called which will set m_data to zero */ 337#if 0 338 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window) 339 { 340 m_waiting = true; 341 342 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom, 343 (guint32) GDK_CURRENT_TIME ); 344 345 while (m_waiting) gtk_main_iteration(); 346 } 347 348 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window) 349 { 350 m_waiting = true; 351 352 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY, 353 (guint32) GDK_CURRENT_TIME ); 354 355 while (m_waiting) gtk_main_iteration(); 356 } 357#endif 358 359 if (m_data) 360 { 361 delete m_data; 362 m_data = (wxDataObject*) NULL; 363 } 364 365#if wxUSE_THREADS 366 /* re-enable GUI threads */ 367#endif 368 } 369 370 m_targetRequested = 0; 371 m_formatSupported = false; 372} 373 374bool wxClipboard::Open() 375{ 376 wxCHECK_MSG( !m_open, false, wxT("clipboard already open") ); 377 378 m_open = true; 379 380 return true; 381} 382 383bool wxClipboard::SetData( wxDataObject *data ) 384{ 385 wxCHECK_MSG( m_open, false, wxT("clipboard not open") ); 386 387 wxCHECK_MSG( data, false, wxT("data is invalid") ); 388 389 Clear(); 390 391 return AddData( data ); 392} 393 394bool wxClipboard::AddData( wxDataObject *data ) 395{ 396#if wxUSE_NANOX 397 return false; 398#else 399 wxCHECK_MSG( m_open, false, wxT("clipboard not open") ); 400 401 wxCHECK_MSG( data, false, wxT("data is invalid") ); 402 403 /* we can only store one wxDataObject */ 404 Clear(); 405 406 m_data = data; 407 408 /* get formats from wxDataObjects */ 409 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ]; 410 m_data->GetAllFormats( array ); 411 412#if 0 413 /* primary selection or clipboard */ 414 Atom clipboard = m_usePrimary ? (Atom) 1 // 1 = primary selection 415 : g_clipboardAtom; 416#endif // 0 417 418 419 for (size_t i = 0; i < m_data->GetFormatCount(); i++) 420 { 421 wxLogTrace( TRACE_CLIPBOARD, 422 wxT("wxClipboard now supports atom %s"), 423 array[i].GetId().c_str() ); 424 425#if 0 426 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget), 427 clipboard, 428 array[i], 429 0 ); /* what is info ? */ 430#endif 431 } 432 433 delete[] array; 434 435#if 0 436 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget), 437 "selection_get", 438 GTK_SIGNAL_FUNC(selection_handler), 439 (gpointer) NULL ); 440#endif 441 442#if wxUSE_THREADS 443 /* disable GUI threads */ 444#endif 445 446 bool res = false; 447#if 0 448 /* Tell the world we offer clipboard data */ 449 res = (gtk_selection_owner_set( m_clipboardWidget, 450 clipboard, 451 (guint32) GDK_CURRENT_TIME )); 452#endif 453 454 if (m_usePrimary) 455 m_ownsPrimarySelection = res; 456 else 457 m_ownsClipboard = res; 458 459#if wxUSE_THREADS 460 /* re-enable GUI threads */ 461#endif 462 463 return res; 464#endif 465} 466 467void wxClipboard::Close() 468{ 469 wxCHECK_RET( m_open, wxT("clipboard not open") ); 470 471 m_open = false; 472} 473 474bool wxClipboard::IsOpened() const 475{ 476 return m_open; 477} 478 479bool wxClipboard::IsSupported( const wxDataFormat& format ) 480{ 481 /* reentrance problems */ 482 if (m_waiting) return false; 483 484 /* store requested format to be asked for by callbacks */ 485 m_targetRequested = format; 486 487#if 0 488 wxLogTrace( TRACE_CLIPBOARD, 489 wxT("wxClipboard:IsSupported: requested format: %s"), 490 format.GetId().c_str() ); 491#endif 492 493 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") ); 494 495 m_formatSupported = false; 496 497 /* perform query. this will set m_formatSupported to 498 true if m_targetRequested is supported. 499 also, we have to wait for the "answer" from the 500 clipboard owner which is an asynchronous process. 501 therefore we set m_waiting = true here and wait 502 until the callback "targets_selection_received" 503 sets it to false */ 504 505 m_waiting = true; 506 507#if 0 508 gtk_selection_convert( m_targetsWidget, 509 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY 510 : g_clipboardAtom, 511 g_targetsAtom, 512 (guint32) GDK_CURRENT_TIME ); 513 514 while (m_waiting) gtk_main_iteration(); 515#endif 516 517 if (!m_formatSupported) return false; 518 519 return true; 520} 521 522bool wxClipboard::GetData( wxDataObject& data ) 523{ 524 wxCHECK_MSG( m_open, false, wxT("clipboard not open") ); 525 526 /* get formats from wxDataObjects */ 527 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ]; 528 data.GetAllFormats( array ); 529 530 for (size_t i = 0; i < data.GetFormatCount(); i++) 531 { 532 wxDataFormat format( array[i] ); 533 534 wxLogTrace( TRACE_CLIPBOARD, 535 wxT("wxClipboard::GetData: requested format: %s"), 536 format.GetId().c_str() ); 537 538 /* is data supported by clipboard ? */ 539 540 /* store requested format to be asked for by callbacks */ 541 m_targetRequested = format; 542 543 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") ); 544 545 m_formatSupported = false; 546 547 /* perform query. this will set m_formatSupported to 548 true if m_targetRequested is supported. 549 also, we have to wait for the "answer" from the 550 clipboard owner which is an asynchronous process. 551 therefore we set m_waiting = true here and wait 552 until the callback "targets_selection_received" 553 sets it to false */ 554 555 m_waiting = true; 556 557#if 0 558 gtk_selection_convert( m_targetsWidget, 559 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY 560 : g_clipboardAtom, 561 g_targetsAtom, 562 (guint32) GDK_CURRENT_TIME ); 563 564 while (m_waiting) gtk_main_iteration(); 565#endif 566 567 if (!m_formatSupported) continue; 568 569 /* store pointer to data object to be filled up by callbacks */ 570 m_receivedData = &data; 571 572 /* store requested format to be asked for by callbacks */ 573 m_targetRequested = format; 574 575 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") ); 576 577 /* start query */ 578 m_formatSupported = false; 579 580 /* ask for clipboard contents. this will set 581 m_formatSupported to true if m_targetRequested 582 is supported. 583 also, we have to wait for the "answer" from the 584 clipboard owner which is an asynchronous process. 585 therefore we set m_waiting = true here and wait 586 until the callback "targets_selection_received" 587 sets it to false */ 588 589 m_waiting = true; 590 591 wxLogTrace( TRACE_CLIPBOARD, 592 wxT("wxClipboard::GetData: format found, start convert") ); 593 594#if 0 595 gtk_selection_convert( m_clipboardWidget, 596 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY 597 : g_clipboardAtom, 598 m_targetRequested, 599 (guint32) GDK_CURRENT_TIME ); 600 601 while (m_waiting) gtk_main_iteration(); 602#endif 603 604 /* this is a true error as we checked for the presence of such data before */ 605 wxCHECK_MSG( m_formatSupported, false, wxT("error retrieving data from clipboard") ); 606 607 /* return success */ 608 delete[] array; 609 return true; 610 } 611 612 wxLogTrace( TRACE_CLIPBOARD, 613 wxT("wxClipboard::GetData: format not found") ); 614 615 /* return failure */ 616 delete[] array; 617 return false; 618} 619 620#endif 621 // wxUSE_CLIPBOARD 622