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