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 wxTheClipboard->SetData(
40 new wxTextDataObject( wxString( aTextUTF8.c_str(), wxConvUTF8 ) ) );
41
42 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
43 wxTheClipboard->Close();
44
45 return true;
46 }
47
48 return false;
49}
50
51
52bool SaveClipboard( const std::string& aTextUTF8, const std::vector<CLIPBOARD_MIME_DATA>& aMimeData )
53{
54 if( aMimeData.empty() )
55 return SaveClipboard( aTextUTF8 );
56
57 wxLogNull doNotLog; // disable logging of failed clipboard actions
58
59 if( wxTheClipboard->Open() )
60 {
61 wxDataObjectComposite* data = new wxDataObjectComposite();
62 data->Add( new wxTextDataObject( wxString( aTextUTF8.c_str(), wxConvUTF8 ) ), true );
63
64 for( const CLIPBOARD_MIME_DATA& entry : aMimeData )
65 {
66 // Skip entries with no data (check both buffer and image)
67 if( entry.m_data.GetDataLen() == 0 && !entry.m_image.has_value() )
68 continue;
69
70 // Handle pre-encoded PNG data (GTK optimization path).
71 // Use wxDF_BITMAP to prevent wx from setting the type to Private
72 if( entry.m_useRawPngData && entry.m_data.GetDataLen() > 0 )
73 {
74 wxCustomDataObject* custom = new wxCustomDataObject( wxDF_BITMAP );
75 custom->Alloc( entry.m_data.GetDataLen() );
76 custom->SetData( entry.m_data.GetDataLen(), entry.m_data.GetData() );
77 data->Add( custom );
78
79 continue;
80 }
81
82 // Handle bitmap image data using platform-native clipboard format.
83 // wxBitmapDataObject automatically converts to the appropriate format:
84 // - Windows: CF_DIB
85 // - macOS: kUTTypeTIFF
86 if( entry.m_image.has_value() && entry.m_image->IsOk() )
87 {
88 data->Add( new wxBitmapDataObject( *entry.m_image ) );
89 continue;
90 }
91
92 // Add custom format data - note that on GTK, custom MIME types may not work
93 // with all applications (they often become wxDF_PRIVATE internally), but they
94 // work for KiCad-to-KiCad transfers.
95 // We allocate and set data in a way that ensures the object is fully initialized.
96 wxDataFormat format( entry.m_mimeType );
97 wxCustomDataObject* custom = new wxCustomDataObject( format );
98
99 // Allocate buffer first, then copy data - this ensures proper initialization
100 custom->Alloc( entry.m_data.GetDataLen() );
101 custom->SetData( entry.m_data.GetDataLen(), entry.m_data.GetData() );
102 data->Add( custom );
103 }
104
105 wxTheClipboard->SetData( data );
106 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
107 wxTheClipboard->Close();
108
109 return true;
110 }
111
112 return false;
113}
114
115
116std::string GetClipboardUTF8()
117{
118 std::string result;
119
120 wxLogNull doNotLog; // disable logging of failed clipboard actions
121
122 // Check if clipboard is already open. This can happen if another event handler (such as
123 // wxTextEntry::CanPaste() called during UPDATE_UI processing) opened the clipboard and
124 // didn't close it. We handle this by reusing the existing session.
125 bool wasAlreadyOpen = wxTheClipboard->IsOpened();
126 bool isOpen = wasAlreadyOpen || wxTheClipboard->Open();
127
128 if( isOpen )
129 {
130 if( wxTheClipboard->IsSupported( wxDataFormat( wxS( "application/kicad" ) ) ) )
131 {
132 wxCustomDataObject data( wxDataFormat( wxS( "application/kicad" ) ) );
133
134 if( wxTheClipboard->GetData( data ) )
135 {
136 result.assign( static_cast<const char*>( data.GetData() ), data.GetSize() );
137
138 if( !wasAlreadyOpen )
139 wxTheClipboard->Close();
140
141 return result;
142 }
143 }
144
145 if( wxTheClipboard->IsSupported( wxDF_TEXT )
146 || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
147 {
148 wxTextDataObject data;
149 wxTheClipboard->GetData( data );
150
151 // The clipboard is expected containing a Unicode string, so return it
152 // as UTF8 string
153 result = data.GetText().utf8_str();
154 }
155
156 if( !wasAlreadyOpen )
157 wxTheClipboard->Close();
158 }
159
160 return result;
161}
162
163
164std::unique_ptr<wxBitmap> GetImageFromClipboard()
165{
166 std::unique_ptr<wxBitmap> bitmap;
167 wxLogNull doNotLog; // disable logging of failed clipboard actions
168
169 // Check if clipboard is already open (same handling as GetClipboardUTF8)
170 bool wasAlreadyOpen = wxTheClipboard->IsOpened();
171 bool isOpen = wasAlreadyOpen || wxTheClipboard->Open();
172
173 if( isOpen )
174 {
175 if( wxTheClipboard->IsSupported( wxDF_BITMAP ) )
176 {
177 wxBitmapDataObject data;
178
179 if( wxTheClipboard->GetData( data ) )
180 {
181 bitmap = std::make_unique<wxBitmap>( data.GetBitmap() );
182 }
183 }
184#ifdef __APPLE__
185 // macOS screenshots use PNG format with UTI "public.png"
186 else if( wxDataFormat pngFormat( wxS( "public.png" ) ); wxTheClipboard->IsSupported( pngFormat ) )
187 {
188 wxCustomDataObject pngData( pngFormat );
189
190 if( wxTheClipboard->GetData( pngData ) && pngData.GetSize() > 0 )
191 {
192 wxMemoryInputStream stream( pngData.GetData(), pngData.GetSize() );
193 wxImage img( stream, wxBITMAP_TYPE_PNG );
194
195 if( img.IsOk() )
196 bitmap = std::make_unique<wxBitmap>( img );
197 }
198 }
199#endif
200 else if( wxTheClipboard->IsSupported( wxDF_FILENAME ) )
201 {
202 wxFileDataObject data;
203
204 if( wxTheClipboard->GetData( data ) && data.GetFilenames().size() == 1 )
205 {
206 wxImage img( data.GetFilenames()[0] );
207 bitmap = std::make_unique<wxBitmap>( img );
208
209 if( !bitmap->IsOk() )
210 bitmap.reset();
211 }
212 }
213
214 if( !wasAlreadyOpen )
215 wxTheClipboard->Close();
216 }
217
218 return bitmap;
219}
220
221
222bool SaveTabularDataToClipboard( const std::vector<std::vector<wxString>>& aData )
223{
224 wxLogNull doNotLog; // disable logging of failed clipboard actions
225
226 if( wxTheClipboard->Open() )
227 {
228 wxDataObjectComposite* data = new wxDataObjectComposite();
229
230 // Set plain text CSV
231 {
232 wxStringOutputStream os;
233 CSV_WRITER writer( os );
234 writer.WriteLines( aData );
235
236 data->Add( new wxTextDataObject( os.GetString() ), true );
237 }
238
239 // At this point, it would be great if we could add some format that spreadsheet
240 // programs can understand without asking the user for options: perhaps SYLK or DIF.
241 // But it doesn't seem like WX allows to put arbitrary MIME types on the clipboard,
242 // even with wxCustomDataObject( wxDataFormat( "mime/type" ) ), which just ends up as
243 // wxDF_PRIVATE, and wxDF_SYLK/DIF aren't mapped on GTK.
244
245 wxTheClipboard->SetData( data );
246 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
247 wxTheClipboard->Close();
248
249 return true;
250 }
251
252 return false;
253}
254
255
256bool GetTabularDataFromClipboard( std::vector<std::vector<wxString>>& aData )
257{
258 // Again, it would be ideal if we could detect a spreadsheet mimetype here,
259 // but WX doesn't seem to do that on Linux, at least.
260
261 bool ok = false;
262
263 // Check if clipboard is already open (same handling as GetClipboardUTF8)
264 bool wasAlreadyOpen = wxTheClipboard->IsOpened();
265 bool isOpen = wasAlreadyOpen || wxTheClipboard->Open();
266
267 if( isOpen )
268 {
269 if( wxTheClipboard->IsSupported( wxDF_TEXT ) || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
270 {
271 wxTextDataObject data;
272
273 if( wxTheClipboard->GetData( data ) )
274 ok = AutoDecodeCSV( data.GetText(), aData );
275 }
276
277 // We could also handle .csv wxDF_FILENAMEs here
278
279 if( !wasAlreadyOpen )
280 wxTheClipboard->Close();
281 }
282
283 return ok;
284}
285
286
287bool EncodeImageToPng( const wxImage& aImage, wxMemoryBuffer& aOutput )
288{
289 if( !aImage.IsOk() )
290 return false;
291
292 wxImage imageCopy( aImage );
293
294 // Fast PNG settings optimized for clipboard graphics
295 imageCopy.SetOption( wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL, 1 ); // Z_BEST_SPEED
296 imageCopy.SetOption( wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY, 3 ); // Z_RLE
297 imageCopy.SetOption( wxIMAGE_OPTION_PNG_FILTER, 0x08 ); // PNG_FILTER_NONE
298
299 wxMemoryOutputStream memStream;
300 wxBufferedOutputStream bufferedStream( memStream );
301
302 if( !imageCopy.SaveFile( bufferedStream, wxBITMAP_TYPE_PNG ) )
303 {
304 wxLogDebug( wxS( "Failed to encode PNG" ) );
305 return false;
306 }
307
308 bufferedStream.Close();
309
310 auto stBuf = memStream.GetOutputStreamBuffer();
311 aOutput.SetDataLen( 0 );
312 aOutput.AppendData( stBuf->GetBufferStart(), stBuf->GetIntPosition() );
313
314 return true;
315}
316
317
318bool AddPngToClipboardData( wxDataObjectComposite* aData, const wxMemoryBuffer& aPngData,
319 const wxImage* aFallbackImage )
320{
321 wxCHECK( wxTheClipboard->IsOpened(), false );
322 wxCHECK( aPngData.GetDataLen() > 0, false );
323
324#if defined( __WXMSW__ )
325 aData->Add( new wxCustomDataObject( wxDataFormat( wxDataFormatId::wxDF_BITMAP ) ) );
326
327 wxCustomDataObject* pngObj = new wxCustomDataObject( wxDataFormat( "PNG" ) );
328 pngObj->SetData( aPngData.GetDataLen(), aPngData.GetData() );
329 aData->Add( pngObj );
330#elif defined( __WXGTK__ )
331 wxCustomDataObject* pngObj = new wxCustomDataObject( wxDF_BITMAP );
332 pngObj->SetData( aPngData.GetDataLen(), aPngData.GetData() );
333 aData->Add( pngObj );
334#else // __WXOSX__
335 wxCHECK( aFallbackImage && aFallbackImage->IsOk(), false );
336 aData->Add( new wxBitmapDataObject( wxBitmap( *aFallbackImage ) ) );
337#endif
338
339 return true;
340}
341
342
343bool AddTransparentImageToClipboardData( wxDataObjectComposite* aData, wxImage aImage )
344{
345 wxCHECK( wxTheClipboard->IsOpened(), false );
346 wxCHECK( aImage.IsOk(), false );
347
348#if defined( __WXGTK__ ) || defined( __WXMSW__ )
349 wxMemoryBuffer pngData;
350
351 if( !EncodeImageToPng( aImage, pngData ) )
352 return false;
353
354 return AddPngToClipboardData( aData, pngData );
355#else // __WXOSX__
356 aData->Add( new wxBitmapDataObject( wxBitmap( aImage ) ) );
357 return true;
358#endif
359}
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.