KiCad PCB EDA Suite
Loading...
Searching...
No Matches
clipboard.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "clipboard.h"
21
22#include <wx/clipbrd.h>
23#include <wx/dataobj.h>
24#include <wx/image.h>
25#include <wx/log.h>
26#include <wx/mstream.h>
27#include <wx/string.h>
28#include <wx/sstream.h>
29
30#include <io/csv.h>
31
32bool SaveClipboard( const std::string& aTextUTF8 )
33{
34 wxLogNull doNotLog; // disable logging of failed clipboard actions
35
36 if( wxTheClipboard->Open() )
37 {
38 // Store the UTF8 string as Unicode string in clipboard:
39 bool saved = wxTheClipboard->SetData(
40 new wxTextDataObject( wxString( aTextUTF8.c_str(), wxConvUTF8 ) ) );
41
42 if( saved )
43 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
44
45 wxTheClipboard->Close();
46 return saved;
47 }
48
49 return false;
50}
51
52
53bool SaveClipboard( const std::string& aTextUTF8, const std::vector<CLIPBOARD_MIME_DATA>& aMimeData )
54{
55 if( aMimeData.empty() )
56 return SaveClipboard( aTextUTF8 );
57
58 wxLogNull doNotLog; // disable logging of failed clipboard actions
59
60 if( wxTheClipboard->Open() )
61 {
62 wxDataObjectComposite* data = new wxDataObjectComposite();
63 data->Add( new wxTextDataObject( wxString( aTextUTF8.c_str(), wxConvUTF8 ) ), true );
64
65 for( const CLIPBOARD_MIME_DATA& entry : aMimeData )
66 {
67 // Skip entries with no data (check both buffer and image)
68 if( entry.m_data.GetDataLen() == 0 && !entry.m_image.has_value() )
69 continue;
70
71 // Handle bitmap image data using platform-native clipboard format.
72 // wxBitmapDataObject automatically converts to the appropriate format:
73 // - Windows: CF_DIB
74 // - macOS: kUTTypeTIFF
75 if( entry.m_image.has_value() && entry.m_image->IsOk() )
76 {
77 data->Add( new wxBitmapDataObject( *entry.m_image ) );
78 continue;
79 }
80
81 // Add custom format data - note that on GTK, custom MIME types may not work
82 // with all applications (they often become wxDF_PRIVATE internally), but they
83 // work for KiCad-to-KiCad transfers.
84 // We allocate and set data in a way that ensures the object is fully initialized.
85 wxDataFormat format( entry.m_mimeType );
86 wxCustomDataObject* custom = new wxCustomDataObject( format );
87
88 // Allocate buffer first, then copy data - this ensures proper initialization
89 custom->Alloc( entry.m_data.GetDataLen() );
90 custom->SetData( entry.m_data.GetDataLen(), entry.m_data.GetData() );
91 data->Add( custom );
92 }
93
94 bool saved = wxTheClipboard->SetData( data );
95
96 if( saved )
97 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
98
99 wxTheClipboard->Close();
100 return saved;
101 }
102
103 return false;
104}
105
106
107std::string GetClipboardUTF8()
108{
109 std::string result;
110
111 wxLogNull doNotLog; // disable logging of failed clipboard actions
112
113 // Check if clipboard is already open. This can happen if another event handler (such as
114 // wxTextEntry::CanPaste() called during UPDATE_UI processing) opened the clipboard and
115 // didn't close it. We handle this by reusing the existing session.
116 bool wasAlreadyOpen = wxTheClipboard->IsOpened();
117 bool isOpen = wasAlreadyOpen || wxTheClipboard->Open();
118
119 if( isOpen )
120 {
121 if( wxTheClipboard->IsSupported( wxDataFormat( wxS( "application/kicad" ) ) ) )
122 {
123 wxCustomDataObject data( wxDataFormat( wxS( "application/kicad" ) ) );
124
125 if( wxTheClipboard->GetData( data ) )
126 {
127 result.assign( static_cast<const char*>( data.GetData() ), data.GetSize() );
128
129 if( !wasAlreadyOpen )
130 wxTheClipboard->Close();
131
132 return result;
133 }
134 }
135
136 if( wxTheClipboard->IsSupported( wxDF_TEXT )
137 || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
138 {
139 wxTextDataObject data;
140 wxTheClipboard->GetData( data );
141
142 // The clipboard is expected containing a Unicode string, so return it
143 // as UTF8 string
144 result = data.GetText().utf8_str();
145 }
146
147 if( !wasAlreadyOpen )
148 wxTheClipboard->Close();
149 }
150
151 return result;
152}
153
154
155std::unique_ptr<wxBitmap> GetImageFromClipboard()
156{
157 std::unique_ptr<wxBitmap> bitmap;
158 wxLogNull doNotLog; // disable logging of failed clipboard actions
159
160 // Check if clipboard is already open (same handling as GetClipboardUTF8)
161 bool wasAlreadyOpen = wxTheClipboard->IsOpened();
162 bool isOpen = wasAlreadyOpen || wxTheClipboard->Open();
163
164 if( isOpen )
165 {
166 if( wxTheClipboard->IsSupported( wxDF_BITMAP ) )
167 {
168 wxBitmapDataObject data;
169
170 if( wxTheClipboard->GetData( data ) )
171 {
172 bitmap = std::make_unique<wxBitmap>( data.GetBitmap() );
173 }
174 }
175#ifdef __APPLE__
176 // macOS screenshots use PNG format with UTI "public.png"
177 else if( wxDataFormat pngFormat( wxS( "public.png" ) ); wxTheClipboard->IsSupported( pngFormat ) )
178 {
179 wxCustomDataObject pngData( pngFormat );
180
181 if( wxTheClipboard->GetData( pngData ) && pngData.GetSize() > 0 )
182 {
183 wxMemoryInputStream stream( pngData.GetData(), pngData.GetSize() );
184 wxImage img( stream, wxBITMAP_TYPE_PNG );
185
186 if( img.IsOk() )
187 bitmap = std::make_unique<wxBitmap>( img );
188 }
189 }
190#endif
191 else if( wxTheClipboard->IsSupported( wxDF_FILENAME ) )
192 {
193 wxFileDataObject data;
194
195 if( wxTheClipboard->GetData( data ) && data.GetFilenames().size() == 1 )
196 {
197 wxImage img( data.GetFilenames()[0] );
198 bitmap = std::make_unique<wxBitmap>( img );
199
200 if( !bitmap->IsOk() )
201 bitmap.reset();
202 }
203 }
204
205 if( !wasAlreadyOpen )
206 wxTheClipboard->Close();
207 }
208
209 return bitmap;
210}
211
212
213bool SaveTabularDataToClipboard( const std::vector<std::vector<wxString>>& aData )
214{
215 wxLogNull doNotLog; // disable logging of failed clipboard actions
216
217 if( wxTheClipboard->Open() )
218 {
219 wxDataObjectComposite* data = new wxDataObjectComposite();
220
221 // Set plain text CSV
222 {
223 wxStringOutputStream os;
224 CSV_WRITER writer( os );
225 writer.WriteLines( aData );
226
227 data->Add( new wxTextDataObject( os.GetString() ), true );
228 }
229
230 // At this point, it would be great if we could add some format that spreadsheet
231 // programs can understand without asking the user for options: perhaps SYLK or DIF.
232 // But it doesn't seem like WX allows to put arbitrary MIME types on the clipboard,
233 // even with wxCustomDataObject( wxDataFormat( "mime/type" ) ), which just ends up as
234 // wxDF_PRIVATE, and wxDF_SYLK/DIF aren't mapped on GTK.
235
236 bool saved = wxTheClipboard->SetData( data );
237
238 if( saved )
239 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
240
241 wxTheClipboard->Close();
242 return saved;
243 }
244
245 return false;
246}
247
248
249bool GetTabularDataFromClipboard( std::vector<std::vector<wxString>>& aData )
250{
251 // Again, it would be ideal if we could detect a spreadsheet mimetype here,
252 // but WX doesn't seem to do that on Linux, at least.
253
254 bool ok = false;
255
256 // Check if clipboard is already open (same handling as GetClipboardUTF8)
257 bool wasAlreadyOpen = wxTheClipboard->IsOpened();
258 bool isOpen = wasAlreadyOpen || wxTheClipboard->Open();
259
260 if( isOpen )
261 {
262 if( wxTheClipboard->IsSupported( wxDF_TEXT ) || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
263 {
264 wxTextDataObject data;
265
266 if( wxTheClipboard->GetData( data ) )
267 ok = AutoDecodeCSV( data.GetText(), aData );
268 }
269
270 // We could also handle .csv wxDF_FILENAMEs here
271
272 if( !wasAlreadyOpen )
273 wxTheClipboard->Close();
274 }
275
276 return ok;
277}
278
279
280bool EncodeImageToPng( const wxImage& aImage, wxMemoryBuffer& aOutput )
281{
282 if( !aImage.IsOk() )
283 return false;
284
285 wxImage imageCopy( aImage );
286
287 // Fast PNG settings optimized for clipboard graphics
288 imageCopy.SetOption( wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL, 1 ); // Z_BEST_SPEED
289 imageCopy.SetOption( wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY, 3 ); // Z_RLE
290 imageCopy.SetOption( wxIMAGE_OPTION_PNG_FILTER, 0x08 ); // PNG_FILTER_NONE
291
292 wxMemoryOutputStream memStream;
293 wxBufferedOutputStream bufferedStream( memStream );
294
295 if( !imageCopy.SaveFile( bufferedStream, wxBITMAP_TYPE_PNG ) )
296 return false;
297
298 bufferedStream.Close();
299
300 auto stBuf = memStream.GetOutputStreamBuffer();
301 aOutput.SetDataLen( 0 );
302 aOutput.AppendData( stBuf->GetBufferStart(), stBuf->GetIntPosition() );
303
304 return true;
305}
306
307
308#if defined( __WXMSW__ )
309// CF_DIB has no usable alpha, so blend onto white
310static wxImage FlattenAlphaOnWhite( const wxImage& aImage )
311{
312 wxImage flat = aImage.Copy();
313
314 if( !flat.HasAlpha() )
315 return flat;
316
317 unsigned char* rgb = flat.GetData();
318 const unsigned char* alpha = flat.GetAlpha();
319 const size_t count = static_cast<size_t>( flat.GetWidth() ) * flat.GetHeight();
320
321 for( size_t i = 0; i < count; ++i )
322 {
323 const int a = alpha[i];
324
325 for( int c = 0; c < 3; ++c )
326 rgb[i * 3 + c] = static_cast<unsigned char>( ( rgb[i * 3 + c] * a + 255 * ( 255 - a ) + 127 ) / 255 );
327 }
328
329 flat.ClearAlpha();
330 return flat;
331}
332#endif
333
334
335bool AddPngToClipboardData( wxDataObjectComposite* aData, const wxMemoryBuffer& aPngData,
336 const wxImage* aFallbackImage )
337{
338 wxCHECK( wxTheClipboard->IsOpened(), false );
339 wxCHECK( aPngData.GetDataLen() > 0, false );
340
341#if defined( __WXMSW__ )
342 // PNG goes first so image consumers that take the first image format get the alpha channel
343 wxCustomDataObject* pngObj = new wxCustomDataObject( wxDataFormat( "PNG" ) );
344 pngObj->SetData( aPngData.GetDataLen(), aPngData.GetData() );
345 aData->Add( pngObj );
346
347 // CF_DIB for readers without PNG support, Windows synthesizes CF_BITMAP and CF_DIBV5 from it
348 if( aFallbackImage && aFallbackImage->IsOk() )
349 aData->Add( new wxBitmapDataObject( wxBitmap( FlattenAlphaOnWhite( *aFallbackImage ) ) ) );
350#elif defined( __WXGTK__ )
351 wxCustomDataObject* pngObj = new wxCustomDataObject( wxDF_BITMAP );
352 pngObj->SetData( aPngData.GetDataLen(), aPngData.GetData() );
353 aData->Add( pngObj );
354#else // __WXOSX__
355 wxCHECK( aFallbackImage && aFallbackImage->IsOk(), false );
356 aData->Add( new wxBitmapDataObject( wxBitmap( *aFallbackImage ) ) );
357#endif
358
359 return true;
360}
361
362
363bool AddTransparentImageToClipboardData( wxDataObjectComposite* aData, wxImage aImage )
364{
365 wxCHECK( wxTheClipboard->IsOpened(), false );
366 wxCHECK( aImage.IsOk(), false );
367
368#if defined( __WXGTK__ ) || defined( __WXMSW__ )
369 wxMemoryBuffer pngData;
370
371 if( !EncodeImageToPng( aImage, pngData ) )
372 return false;
373
374 return AddPngToClipboardData( aData, pngData, &aImage );
375#else // __WXOSX__
376 aData->Add( new wxBitmapDataObject( wxBitmap( aImage ) ) );
377 return true;
378#endif
379}
void WriteLines(const std::vector< std::vector< wxString > > &aRows)
Write a vector of rows to the stream.
Definition csv.cpp:18
std::unique_ptr< wxBitmap > GetImageFromClipboard()
Get image data from the clipboard, if there is any.
bool EncodeImageToPng(const wxImage &aImage, wxMemoryBuffer &aOutput)
Encode an image to PNG format with fast compression settings optimized for clipboard use.
bool AddTransparentImageToClipboardData(wxDataObjectComposite *aData, wxImage aImage)
Adds an image to clipboard data in a platform-specific way such that transparency is supported.
bool SaveTabularDataToClipboard(const std::vector< std::vector< wxString > > &aData)
Store tabular data to the system clipboard.
bool SaveClipboard(const std::string &aTextUTF8)
Store information to the system clipboard.
Definition clipboard.cpp:32
std::string GetClipboardUTF8()
Return the information currently stored in the system clipboard.
bool AddPngToClipboardData(wxDataObjectComposite *aData, const wxMemoryBuffer &aPngData, const wxImage *aFallbackImage)
Adds pre-encoded PNG data to clipboard in a platform-specific way.
bool GetTabularDataFromClipboard(std::vector< std::vector< wxString > > &aData)
Attempt to get tabular data from the clipboard.
bool AutoDecodeCSV(const wxString &aInput, std::vector< std::vector< wxString > > &aData)
Try to guess the format of a T/CSV file and decode it into aData.
Definition csv.cpp:66
wxString result
Test unit parsing edge cases and error handling.