47#include <nlohmann/json.hpp>
55#include <wx/datetime.h>
62#include <wx/mstream.h>
63#include <wx/settings.h>
65#include <wx/strconv.h>
66#include <wx/webview.h>
71 auto it = aObject.find( aKey );
73 if( it != aObject.end() && it->is_string() )
74 return wxString::FromUTF8( it->get<std::string>() );
80 std::vector<uint8_t>& aOutput,
81 wxString& aError )
const
83 if( aEncoded.empty() )
85 aError =
_(
"Missing payload data." );
89 wxMemoryBuffer buffer = wxBase64Decode( wxString::FromUTF8( aEncoded.c_str() ) );
91 if( buffer.IsEmpty() )
93 aError =
_(
"Failed to decode base64 payload." );
97 aOutput.resize( buffer.GetDataLen() );
98 memcpy( aOutput.data(), buffer.GetData(), buffer.GetDataLen() );
103 const std::vector<uint8_t>& aInput,
104 std::vector<uint8_t>& aOutput,
105 wxString& aError )
const
107 if( aCompression.empty() || aCompression ==
"NONE" )
113 if( aCompression !=
"ZSTD" )
115 aError = wxString::Format(
_(
"Unsupported compression '%s'." ),
116 wxString::FromUTF8( aCompression ) );
122 aError =
_(
"Compressed payload was empty." );
126 unsigned long long expectedSize =
127 ZSTD_getFrameContentSize( aInput.data(), aInput.size() );
129 if( expectedSize == ZSTD_CONTENTSIZE_ERROR || expectedSize == ZSTD_CONTENTSIZE_UNKNOWN )
130 expectedSize =
static_cast<unsigned long long>( aInput.size() ) * 4;
132 aOutput.resize( expectedSize );
134 size_t decompressed = ZSTD_decompress( aOutput.data(), expectedSize,
135 aInput.data(), aInput.size() );
137 if( ZSTD_isError( decompressed ) )
139 aError = wxString::Format(
_(
"ZSTD decompression failed: %s" ),
140 wxString::FromUTF8( ZSTD_getErrorName( decompressed ) ) );
144 aOutput.resize( decompressed );
150 wxString script = wxString::FromUTF8( aJson.c_str() );
151 script.Replace(
"\\",
"\\\\" );
152 script.Replace(
"'",
"\\'" );
157 const wxString& aDefault )
const
160 result.Trim(
true ).Trim(
false );
165 for(
size_t i = 0; i <
result.length(); ++i )
169 if( ch ==
'/' || ch ==
'\\' || ch ==
':' )
177 const std::vector<uint8_t>& aPayload,
178 wxString& aError )
const
180 if( aPayload.empty() )
182 aError =
_(
"Payload was empty." );
186 wxFileName targetDir = aOutput;
187 targetDir.SetFullName( wxEmptyString );
189 if( !targetDir.DirExists() )
191 if( !targetDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
193 aError = wxString::Format(
_(
"Unable to create '%s'." ), targetDir.GetFullPath() );
198 wxFFile file( aOutput.GetFullPath(), wxS(
"wb" ) );
200 if( !file.IsOpened() )
202 aError = wxString::Format(
_(
"Unable to open '%s' for writing." ), aOutput.GetFullPath() );
206 if( file.Write( aPayload.data(), aPayload.size() ) != aPayload.size() )
208 aError = wxString::Format(
_(
"Failed to write '%s'." ), aOutput.GetFullPath() );
218 const wxString& aLibItemName,
219 wxString& aError )
const
221 if( aPayload.empty() )
223 aError =
_(
"Symbol payload was empty." );
227 wxString tempPath = wxFileName::CreateTempFileName( wxS(
"remote_symbol" ) );
229 if( tempPath.IsEmpty() )
231 aError =
_(
"Unable to create a temporary file for the symbol payload." );
235 wxFileName tempFile( tempPath );
237 wxFFile file( tempFile.GetFullPath(), wxS(
"wb" ) );
239 if( !file.IsOpened() )
241 aError =
_(
"Unable to create a temporary file for the symbol payload." );
242 wxRemoveFile( tempFile.GetFullPath() );
246 if( file.Write( aPayload.data(), aPayload.size() ) != aPayload.size() )
248 aError =
_(
"Failed to write the temporary symbol payload." );
250 wxRemoveFile( tempFile.GetFullPath() );
260 aError =
_(
"Unable to access the KiCad symbol plugin." );
261 wxRemoveFile( tempFile.GetFullPath() );
265 std::unique_ptr<LIB_SYMBOL> symbol;
269 LIB_SYMBOL* loaded = plugin->LoadSymbol( tempFile.GetFullPath(), aLibItemName );
273 symbol.reset( loaded );
274 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
275 "loadSymbolFromPayload: loaded symbol %s from temporary file %s",
276 aLibItemName.ToUTF8().data(), tempFile.GetFullPath().ToUTF8().data() );
280 aError =
_(
"Symbol payload did not include the expected symbol." );
285 aError = wxString::Format(
_(
"Unable to decode the symbol payload: %s" ), e.
What() );
288 wxRemoveFile( tempFile.GetFullPath() );
301 m_messageIdCounter( 0 ),
302 m_pendingHandshake(
false )
304 wxBoxSizer* topSizer =
new wxBoxSizer( wxVERTICAL );
306 wxBoxSizer* controlsSizer =
new wxBoxSizer( wxHORIZONTAL );
307 m_dataSourceChoice =
new wxChoice(
this, wxID_ANY );
308 m_dataSourceChoice->SetMinSize( FromDIP( wxSize( 160, -1 ) ) );
309 m_dataSourceChoice->SetToolTip(
_(
"Select which remote data source to query." ) );
310 controlsSizer->Add( m_dataSourceChoice, 1, wxEXPAND | wxRIGHT, FromDIP( 2 ) );
314 m_configButton->SetPadding( FromDIP( 2 ) );
315 m_configButton->SetToolTip(
_(
"Configure remote data sources." ) );
316 controlsSizer->Add( m_configButton, 0, wxALIGN_CENTER_VERTICAL );
318 topSizer->Add( controlsSizer, 0, wxEXPAND | wxALL, FromDIP( 4 ) );
321 m_webView->AddMessageHandler( wxS(
"kicad" ),
322 [
this](
const wxString& payload )
324 onKicadMessage( payload );
326 m_webView->SetHandleExternalLinks(
true );
328 if( wxWebView* browser = m_webView->GetWebView() )
333 topSizer->Add( m_webView, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP( 2 ) );
335 SetSizer( topSizer );
340 RefreshDataSources();
344 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"PANEL_REMOTE_SYMBOL constructed (frame=%p)", (
void*)aParent );
356 if( wxWebView* browser =
m_webView->GetWebView() )
366 m_pcm->SetRepositoryList( cfg->m_PcmRepositories );
371 const std::vector<PCM_INSTALLATION_ENTRY> installed =
m_pcm->GetInstalledPackages();
378 wxString label = entry.package.name;
380 if( !entry.current_version.IsEmpty() )
381 label << wxS(
" (" ) << entry.current_version << wxS(
")" );
390 showMessage(
_(
"No remote data sources are currently installed." ) );
402 const int selection = aEvent.GetSelection();
404 if( selection == wxNOT_FOUND )
442 wxLogWarning(
"No JSON configuration found for data source %s", aEntry.
package.
identifier );
443 showMessage( wxString::Format(
_(
"No configuration JSON found for '%s'." ),
445 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"loadDataSource: no json for %s", aEntry.
package.
identifier );
449 wxFFile file( jsonPath->GetFullPath(),
"rb" );
451 if( !file.IsOpened() )
453 wxLogWarning(
"Unable to open remote data source JSON: %s", jsonPath->GetFullPath() );
454 showMessage( wxString::Format(
_(
"Unable to open '%s'." ), jsonPath->GetFullPath() ) );
455 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"loadDataSource: cannot open %s", jsonPath->GetFullPath() );
459 wxString jsonContent;
461 if( !file.ReadAll( &jsonContent, wxConvUTF8 ) )
463 wxLogWarning(
"Failed to read remote data source JSON: %s", jsonPath->GetFullPath() );
464 showMessage( wxString::Format(
_(
"Unable to read '%s'." ), jsonPath->GetFullPath() ) );
472 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"loadDataSource: loading URL %s", (*url).ToUTF8().data() );
476 wxLogWarning(
"Remote data source JSON did not produce a valid URL for %s",
478 showMessage( wxString::Format(
_(
"Unable to load remote data for '%s'." ),
480 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"loadDataSource: failed to find URL in %s", jsonPath->GetFullPath() );
485std::optional<wxFileName>
489 cleanId.Replace(
'.',
'_' );
491 wxFileName baseDir = wxFileName::DirName(
m_pcm->Get3rdPartyPath() );
492 baseDir.AppendDir( wxS(
"resources" ) );
493 baseDir.AppendDir( cleanId );
495 const wxString resourcesPath = baseDir.GetFullPath();
497 if( !wxDirExists( resourcesPath ) )
500 const std::vector<wxString> preferredNames = {
501 wxS(
"remote_symbol.json" ),
502 wxS(
"datasource.json" )
505 for(
const wxString& candidate : preferredNames )
507 wxFileName file( baseDir );
508 file.SetFullName( candidate );
510 if( file.FileExists() )
514 wxDir dir( resourcesPath );
516 if( !dir.IsOpened() )
521 if( dir.GetFirst( &jsonFile, wxS(
"*.json" ), wxDIR_FILES ) )
523 wxFileName fallback( baseDir );
524 fallback.SetFullName( jsonFile );
537 wxString escaped = aMessage;
538 escaped.Replace(
"&",
"&" );
539 escaped.Replace(
"<",
"<" );
540 escaped.Replace(
">",
">" );
542 wxColour bgColour = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
543 wxColour fgColour = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
546 html << wxS(
"<html><head><style>" )
547 << wxString::Format( wxS(
"body { background-color: #%02x%02x%02x; color: #%02x%02x%02x; "
548 "font-family: system-ui, sans-serif; padding: 10px; }" ),
549 bgColour.Red(), bgColour.Green(), bgColour.Blue(),
550 fgColour.Red(), fgColour.Green(), fgColour.Blue() )
551 << wxS(
"</style></head><body><p>" ) << escaped << wxS(
"</p></body></html>" );
559 if( aJsonContent.IsEmpty() )
562 wxScopedCharBuffer utf8 = aJsonContent.ToUTF8();
564 if( !utf8 || utf8.length() == 0 )
569 nlohmann::json parsed = nlohmann::json::parse( utf8.data() );
572 if( parsed.is_string() )
574 url = parsed.get<std::string>();
576 else if( parsed.is_object() )
578 auto extractString = [&](
const char* key ) -> std::string
580 auto it = parsed.find( key );
582 if( it != parsed.end() && it->is_string() )
583 return it->get<std::string>();
588 auto extractInt = [&](
const char* key ) -> std::optional<int>
590 auto it = parsed.find( key );
592 if( it == parsed.end() )
595 if( it->is_number_integer() )
596 return it->get<
int>();
598 if( it->is_string() )
602 return std::stoi( it->get<std::string>() );
612 std::string host = extractString(
"host" );
613 std::optional<int> port = extractInt(
"port" );
614 std::string
path = extractString(
"path" );
621 if( port && *port > 0 )
622 url = wxString::Format(
"%s:%d%s", host, *port,
path ).ToStdString();
628 url = extractString(
"url" );
632 for(
const char* key : {
"website",
"endpoint" } )
634 url = extractString( key );
643 for(
const auto& [
name, value] : parsed.items() )
645 if( value.is_string() )
647 const std::string candidate = value.get<std::string>();
649 if( candidate.rfind(
"http", 0 ) == 0 || candidate.rfind(
"file", 0 ) == 0 )
662 return wxString::FromUTF8( url.c_str() );
664 catch(
const std::exception& e )
666 wxLogWarning(
"Failed to parse remote symbol JSON: %s", e.what() );
674 wxScopedCharBuffer utf8 = aMessage.ToUTF8();
676 if( !utf8 || utf8.length() == 0 )
678 wxLogWarning(
"Remote symbol RPC: empty payload." );
679 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"onKicadMessage: empty payload" );
685 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"onKicadMessage: received payload size=%d", (
int)utf8.length() );
688 catch(
const std::exception& e )
690 wxLogWarning(
"Remote symbol RPC parse error: %s", e.what() );
691 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"onKicadMessage: parse error %s", e.what() );
698 wxUnusedVar( aEvent );
724 nlohmann::json params = nlohmann::json::object();
725 params[
"client_name"] =
"KiCad";
730 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"beginSessionHandshake: NEW_SESSION sent, session=%s",
m_sessionId.AsString() );
735 nlohmann::json aParameters,
736 std::optional<int> aResponseTo,
737 const wxString& aStatus,
738 const std::string& aData,
739 const wxString& aErrorCode,
740 const wxString& aErrorMessage )
745 nlohmann::json payload = nlohmann::json::object();
749 payload[
"command"] = aCommand.ToStdString();
752 payload[
"response_to"] = *aResponseTo;
754 if( !aStatus.IsEmpty() )
755 payload[
"status"] = aStatus.ToStdString();
757 if( !aParameters.is_null() && !aParameters.empty() )
758 payload[
"parameters"] = std::move( aParameters );
761 payload[
"data"] = aData;
763 if( !aErrorCode.IsEmpty() )
764 payload[
"error_code"] = aErrorCode.ToStdString();
766 if( !aErrorMessage.IsEmpty() )
767 payload[
"error_message"] = aErrorMessage.ToStdString();
769 wxString script = wxString::Format( wxS(
"window.kiclient.postMessage('%s');" ),
772 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"sendRpcMessage: command=%s message_id=%d status=%s",
773 aCommand.ToUTF8().data(), payload[
"message_id"].get<
int>(), aStatus.ToUTF8().data() );
778 const wxString& aErrorCode,
779 const wxString& aErrorMessage )
781 sendRpcMessage( aCommand, nlohmann::json::object(), aResponseTo, wxS(
"ERROR" ),
782 std::string(), aErrorCode, aErrorMessage );
788 if( !aMessage.is_object() )
791 const wxString command =
jsonString( aMessage,
"command" );
793 if( command.IsEmpty() )
796 auto messageIdIt = aMessage.find(
"message_id" );
798 if( messageIdIt == aMessage.end() || !messageIdIt->is_number_integer() )
801 const int messageId = messageIdIt->get<
int>();
803 const int version = aMessage.value(
"version", 0 );
808 wxString::Format(
_(
"Unsupported RPC version %d." ), version ) );
812 const wxString sessionId =
jsonString( aMessage,
"session_id" );
814 if( sessionId.IsEmpty() )
817 _(
"Missing session identifier." ) );
821 if( !sessionId.IsSameAs(
m_sessionId.AsString() ) )
822 wxLogWarning(
"Remote symbol RPC session mismatch (expected %s, got %s).",
825 const wxString status =
jsonString( aMessage,
"status" );
827 if( status.IsSameAs( wxS(
"ERROR" ),
false ) )
829 wxLogWarning(
"Remote symbol RPC error (%s): %s",
jsonString( aMessage,
"error_code" ),
834 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"handleRpcMessage: command=%s message_id=%d status=%s session=%s",
835 command.ToUTF8().data(), messageId, status.ToUTF8().data(), sessionId.ToUTF8().data() );
837 nlohmann::json params = nlohmann::json::object();
838 auto paramsIt = aMessage.find(
"parameters" );
840 if( paramsIt != aMessage.end() && paramsIt->is_object() )
843 const std::string data = aMessage.value(
"data", std::string() );
845 if( command == wxS(
"NEW_SESSION" ) )
847 nlohmann::json reply = nlohmann::json::object();
848 reply[
"client_name"] =
"KiCad";
854 else if( command == wxS(
"GET_KICAD_VERSION" ) )
856 nlohmann::json reply = nlohmann::json::object();
861 else if( command == wxS(
"LIST_SUPPORTED_VERSIONS" ) )
863 nlohmann::json reply = nlohmann::json::object();
868 else if( command == wxS(
"CAPABILITIES" ) )
870 nlohmann::json reply = nlohmann::json::object();
871 reply[
"commands"] = {
"NEW_SESSION",
"GET_KICAD_VERSION",
"LIST_SUPPORTED_VERSIONS",
872 "CAPABILITIES",
"PING",
"PONG",
"DL_SYMBOL",
"DL_FOOTPRINT",
873 "DL_SPICE",
"DL_3DMODEL" };
874 reply[
"compression"] = {
"NONE",
"ZSTD" };
875 reply[
"max_message_size"] = 0;
879 else if( command == wxS(
"PING" ) )
881 nlohmann::json reply = nlohmann::json::object();
883 if( params.contains(
"nonce" ) )
884 reply[
"nonce"] = params[
"nonce"];
889 else if( command == wxS(
"PONG" ) )
894 const wxString compression =
jsonString( params,
"compression" );
896 if( command.StartsWith( wxS(
"DL_" ) ) )
898 if( compression.IsEmpty() )
901 _(
"Missing compression metadata." ) );
905 std::vector<uint8_t> decoded;
914 std::vector<uint8_t> payload;
915 wxScopedCharBuffer compUtf8 = compression.ToUTF8();
916 std::string compressionStr = compUtf8 ? std::string( compUtf8.data() ) : std::string();
924 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"handleRpcMessage: decoded size=%zu decompressed size=%zu command=%s",
925 decoded.size(), payload.size(), command.ToUTF8().data() );
929 if( command == wxS(
"DL_SYMBOL" ) )
931 else if( command == wxS(
"DL_FOOTPRINT" ) )
933 else if( command == wxS(
"DL_3DMODEL" ) )
935 else if( command == wxS(
"DL_SPICE" ) )
940 wxString::Format(
_(
"Command '%s' is not supported." ), command ) );
948 error.IsEmpty() ?
_(
"Unable to store payload." ) : error );
954 wxString::Format(
_(
"Command '%s' is not supported." ), command ) );
964 aError =
_(
"Unable to load schematic settings." );
970 if( destination.IsEmpty() )
975 destination.Trim(
true ).Trim(
false );
977 if( destination.IsEmpty() )
979 aError =
_(
"Destination directory is not configured." );
983 wxFileName dir = wxFileName::DirName( destination );
986 if( !dir.DirExists() )
988 if( !dir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
990 aError = wxString::Format(
_(
"Unable to create directory '%s'." ), dir.GetFullPath() );
1001 const wxString& aNickname,
bool aGlobalTable,
1002 wxString& aError )
const
1010 aError =
_(
"Unable to access the symbol library table." );
1015 const wxString fullPath = aLibraryFile.GetFullPath();
1017 if(
table->HasRow( aNickname ) )
1019 if( std::optional<LIBRARY_TABLE_ROW*> rowOpt =
table->Row( aNickname ); rowOpt )
1023 if( row->
URI() != fullPath )
1027 if( !
table->Save() )
1029 aError =
_(
"Failed to update the symbol library table." );
1041 row.
SetType( wxS(
"KiCad" ) );
1046 if( !
table->Save() )
1048 aError =
_(
"Failed to save the symbol library table." );
1057 const wxString& aNickname,
1058 bool aGlobalTable, wxString& aError )
const
1066 aError =
_(
"Unable to access the footprint library table." );
1071 const wxString fullPath = aLibraryDir.GetFullPath();
1073 if(
table->HasRow( aNickname ) )
1075 if( std::optional<LIBRARY_TABLE_ROW*> rowOpt =
table->Row( aNickname ); rowOpt )
1079 if( row->
URI() != fullPath )
1083 if( !
table->Save() )
1085 aError =
_(
"Failed to update the footprint library table." );
1097 row.
SetType( wxS(
"KiCad" ) );
1102 if( !
table->Save() )
1104 aError =
_(
"Failed to save the footprint library table." );
1117 prefix = settings->m_RemoteSymbol.library_prefix;
1119 if( prefix.IsEmpty() )
1122 prefix.Trim(
true ).Trim(
false );
1124 if( prefix.IsEmpty() )
1125 prefix = wxS(
"remote" );
1127 for(
size_t i = 0; i < prefix.length(); ++i )
1129 wxUniChar ch = prefix[i];
1131 if( !( wxIsalnum( ch ) || ch ==
'_' || ch ==
'-' ) )
1140 const wxString& aLibItemName,
1145 aError =
_(
"No schematic editor is available for placement." );
1146 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"placeDownloadedSymbol: no frame available" );
1150 if( aNickname.IsEmpty() || aLibItemName.IsEmpty() )
1152 aError =
_(
"Downloaded symbol metadata is incomplete." );
1164 aError =
_(
"Unable to load the downloaded symbol for placement." );
1165 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"placeDownloadedSymbol: failed to load libSymbol %s:%s",
1166 aNickname.ToUTF8().data(), aLibItemName.ToUTF8().data() );
1170 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"placeDownloadedSymbol: loaded libSymbol %s:%s",
1171 aNickname.ToUTF8().data(), aLibItemName.ToUTF8().data() );
1186 aError =
_(
"Unable to access the schematic placement tools." );
1187 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"placeDownloadedSymbol: no tool manager available" );
1194 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"placeDownloadedSymbol: posted placeSymbol action for %s:%s",
1195 aNickname.ToUTF8().data(), aLibItemName.ToUTF8().data() );
1201 const std::vector<uint8_t>& aPayload,
1204 const wxString mode =
jsonString( aParams,
"mode" );
1205 const bool placeAfterDownload = mode.IsSameAs( wxS(
"PLACE" ),
false );
1207 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false )
1208 && !mode.IsSameAs( wxS(
"PLACE" ),
false ) )
1210 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1214 const wxString contentType =
jsonString( aParams,
"content_type" );
1216 if( !contentType.IsSameAs( wxS(
"KICAD_SYMBOL_V1" ),
false ) )
1218 aError =
_(
"Unsupported symbol payload type." );
1224 aError =
_(
"No schematic editor is available to store symbols." );
1232 aError =
_(
"Unable to load schematic settings." );
1243 wxFileName symDir = baseDir;
1244 symDir.AppendDir( wxS(
"symbols" ) );
1246 if( !symDir.DirExists() && !symDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1248 aError = wxString::Format(
_(
"Unable to create '%s'." ), symDir.GetFullPath() );
1253 wxString libItemName =
jsonString( aParams,
"name" );
1254 if( libItemName.IsEmpty() )
1255 libItemName = wxS(
"symbol" );
1256 libItemName.Trim(
true ).Trim(
false );
1259 wxString libraryName =
jsonString( aParams,
"library" );
1260 if( libraryName.IsEmpty() )
1261 libraryName = wxS(
"symbols" );
1262 libraryName.Trim(
true ).Trim(
false );
1265 const wxString nickname =
sanitizedPrefix() + wxS(
"_" ) + sanitizedLibName;
1267 wxFileName outFile( symDir );
1268 outFile.SetFullName( nickname + wxS(
".kicad_sym" ) );
1277 aError =
_(
"Unable to access the symbol library manager." );
1281 std::unique_ptr<LIB_SYMBOL> downloadedSymbol =
loadSymbolFromPayload( aPayload, libItemName, aError );
1283 if( !downloadedSymbol )
1285 if( aError.IsEmpty() )
1286 aError =
_(
"Unable to parse the downloaded symbol." );
1291 downloadedSymbol->SetName( libItemName );
1296 downloadedSymbol->SetLibId( savedId );
1298 if( adapter->
SaveSymbol( nickname, downloadedSymbol.get(),
true )
1301 aError =
_(
"Unable to save the downloaded symbol." );
1302 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1303 "receiveSymbol: failed to save symbol %s to library %s",
1304 libItemName.ToUTF8().data(), nickname.ToUTF8().data() );
1308 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1309 "receiveSymbol: saved symbol %s into library %s", libItemName.ToUTF8().data(),
1310 nickname.ToUTF8().data() );
1316 if( placeAfterDownload )
1318 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveSymbol: placing symbol now (nickname=%s libItem=%s)",
1319 nickname.ToUTF8().data(), libItemName.ToUTF8().data() );
1323 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveSymbol: saved symbol (nickname=%s libItem=%s)" ,
1324 nickname.ToUTF8().data(), libItemName.ToUTF8().data() );
1331 const std::vector<uint8_t>& aPayload,
1334 const wxString mode =
jsonString( aParams,
"mode" );
1336 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveFootprint: mode=%s", mode.ToUTF8().data() );
1338 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false )
1339 && !mode.IsSameAs( wxS(
"PLACE" ),
false ) )
1341 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1345 const wxString contentType =
jsonString( aParams,
"content_type" );
1347 if( !contentType.IsSameAs( wxS(
"KICAD_FOOTPRINT_V1" ),
false ) )
1349 aError =
_(
"Unsupported footprint payload type." );
1358 wxFileName fpRoot = baseDir;
1359 fpRoot.AppendDir( wxS(
"footprints" ) );
1361 if( !fpRoot.DirExists() && !fpRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1363 aError = wxString::Format(
_(
"Unable to create '%s'." ), fpRoot.GetFullPath() );
1368 wxString footprintName =
jsonString( aParams,
"name" );
1369 if( footprintName.IsEmpty() )
1370 footprintName = wxS(
"footprint" );
1374 wxString libraryName =
jsonString( aParams,
"library" );
1375 if( libraryName.IsEmpty() )
1376 libraryName = wxS(
"footprints" );
1377 libraryName.Trim(
true ).Trim(
false );
1380 wxString libNickname =
sanitizedPrefix() + wxS(
"_" ) + sanitizedLibName;
1382 wxFileName libDir = fpRoot;
1383 libDir.AppendDir( libNickname + wxS(
".pretty" ) );
1385 if( !libDir.DirExists() && !libDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1387 aError = wxString::Format(
_(
"Unable to create '%s'." ), libDir.GetFullPath() );
1391 wxString fileName = footprintName;
1393 if( !fileName.Lower().EndsWith( wxS(
".kicad_mod" ) ) )
1394 fileName += wxS(
".kicad_mod" );
1396 wxFileName outFile( libDir );
1397 outFile.SetFullName( fileName );
1402 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveFootprint: wrote footprint %s in lib %s",
1403 outFile.GetFullPath(), libNickname.ToUTF8().data() );
1409 aError =
_(
"Unable to load schematic settings." );
1427 const std::vector<uint8_t>& aPayload,
1430 const wxString mode =
jsonString( aParams,
"mode" );
1432 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receive3DModel: mode=%s", mode.ToUTF8().data() );
1434 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false )
1435 && !mode.IsSameAs( wxS(
"PLACE" ),
false ) )
1437 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1441 const wxString contentType =
jsonString( aParams,
"content_type" );
1443 if( !contentType.IsSameAs( wxS(
"KICAD_3D_MODEL_STEP" ),
false ) &&
1444 !contentType.IsSameAs( wxS(
"KICAD_3D_MODEL_WRL" ),
false ) )
1446 aError =
_(
"Unsupported 3D model payload type." );
1455 wxFileName modelDir = baseDir;
1458 if( !modelDir.DirExists() && !modelDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1460 aError = wxString::Format(
_(
"Unable to create '%s'." ), modelDir.GetFullPath() );
1464 wxString fileName =
jsonString( aParams,
"name" );
1466 if( fileName.IsEmpty() )
1471 wxString extension = wxS(
".step" );
1473 if( contentType.IsSameAs( wxS(
"KICAD_3D_MODEL_WRL" ),
false ) )
1474 extension = wxS(
".wrl" );
1476 if( !fileName.Lower().EndsWith( extension ) )
1477 fileName += extension;
1479 wxFileName outFile( modelDir );
1480 outFile.SetFullName( fileName );
1485 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receive3DModel: wrote model %s", outFile.GetFullPath() );
1492 const std::vector<uint8_t>& aPayload,
1495 const wxString mode =
jsonString( aParams,
"mode" );
1497 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveSPICEModel: mode=%s", mode.ToUTF8().data() );
1499 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false )
1500 && !mode.IsSameAs( wxS(
"PLACE" ),
false ) )
1502 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1506 const wxString contentType =
jsonString( aParams,
"content_type" );
1508 if( !contentType.IsSameAs( wxS(
"KICAD_SPICE_MODEL_V1" ),
false ) )
1510 aError =
_(
"Unsupported SPICE payload type." );
1519 wxFileName spiceDir = baseDir;
1522 if( !spiceDir.DirExists() && !spiceDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1524 aError = wxString::Format(
_(
"Unable to create '%s'." ), spiceDir.GetFullPath() );
1528 wxString fileName =
jsonString( aParams,
"name" );
1530 if( fileName.IsEmpty() )
1535 if( !fileName.Lower().EndsWith( wxS(
".cir" ) ) )
1536 fileName += wxS(
".cir" );
1538 wxFileName outFile( spiceDir );
1539 outFile.SetFullName( fileName );
1544 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveSPICEModel: wrote spice model %s", outFile.GetFullPath() );
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
wxString GetSemanticVersion()
Get the semantic version string for KiCad defined inside the KiCadVersion.cmake file in the variable ...
virtual void SetParent(EDA_ITEM *aParent)
REMOTE_SYMBOL_CONFIG m_RemoteSymbol
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
void ReloadLibraryEntry(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH)
std::optional< LIBRARY_TABLE * > Table(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope)
Retrieves a given table; creating a new empty project table if a valid project is loaded and the give...
void SetOptions(const wxString &aOptions)
void SetNickname(const wxString &aNickname)
void SetOk(bool aOk=true)
void SetType(const wxString &aType)
void SetDescription(const wxString &aDescription)
void SetURI(const wxString &aUri)
const wxString & URI() const
A logical library item identifier and consists of various portions much like a URI.
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Define a library symbol object.
wxString sanitizeFileComponent(const wxString &aComponent, const wxString &aDefault) const
std::optional< wxString > extractUrlFromJson(const wxString &aJsonContent) const
void sendRpcMessage(const wxString &aCommand, nlohmann::json aParameters=nlohmann::json::object(), std::optional< int > aResponseTo=std::nullopt, const wxString &aStatus=wxS("OK"), const std::string &aData=std::string(), const wxString &aErrorCode=wxEmptyString, const wxString &aErrorMessage=wxEmptyString)
bool receiveSymbol(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, wxString &aError)
void onWebViewLoaded(wxWebViewEvent &aEvent)
void onKicadMessage(const wxString &aMessage)
wxString sanitizedPrefix() const
void showMessage(const wxString &aMessage)
wxString jsonString(const nlohmann::json &aObject, const char *aKey) const
bool writeBinaryFile(const wxFileName &aFile, const std::vector< uint8_t > &aData, wxString &aError) const
void handleRpcMessage(const nlohmann::json &aMessage)
std::vector< PCM_INSTALLATION_ENTRY > m_dataSources
bool receiveFootprint(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, wxString &aError)
bool ensureSymbolLibraryEntry(const wxFileName &aLibraryFile, const wxString &aNickname, bool aGlobalTable, wxString &aError) const
void respondWithError(const wxString &aCommand, int aResponseTo, const wxString &aErrorCode, const wxString &aErrorMessage)
std::optional< wxFileName > findDataSourceJson(const PCM_INSTALLATION_ENTRY &aEntry) const
PANEL_REMOTE_SYMBOL(SCH_EDIT_FRAME *aParent)
wxChoice * m_dataSourceChoice
bool decompressIfNeeded(const std::string &aCompression, const std::vector< uint8_t > &aInput, std::vector< uint8_t > &aOutput, wxString &aError) const
bool decodeBase64Payload(const std::string &aMessage, std::vector< uint8_t > &aOutPayload, wxString &aError) const
void RefreshDataSources()
wxString sanitizeForScript(const std::string &aJson) const
void beginSessionHandshake()
std::unique_ptr< LIB_SYMBOL > loadSymbolFromPayload(const std::vector< uint8_t > &aPayload, const wxString &aLibItemName, wxString &aError) const
void onConfigure(wxCommandEvent &aEvent)
bool ensureDestinationRoot(wxFileName &aOutDir, wxString &aError) const
bool loadDataSource(size_t aIndex)
bool ensureFootprintLibraryEntry(const wxFileName &aLibraryDir, const wxString &aNickname, bool aGlobalTable, wxString &aError) const
bool receiveSPICEModel(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, wxString &aError)
BITMAP_BUTTON * m_configButton
void onDarkModeToggle(wxSysColourChangedEvent &aEvent)
std::shared_ptr< PLUGIN_CONTENT_MANAGER > m_pcm
virtual ~PANEL_REMOTE_SYMBOL()
WEBVIEW_PANEL * m_webView
bool receive3DModel(const nlohmann::json &aParams, const std::vector< uint8_t > &aPayload, wxString &aError)
void onDataSourceChanged(wxCommandEvent &aEvent)
bool placeDownloadedSymbol(const wxString &aNickname, const wxString &aLibItemName, wxString &aError)
virtual LIBRARY_MANAGER & GetLibraryManager() const
Main class of Plugin and Content Manager subsystem.
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
static TOOL_ACTION placeSymbol
Schematic editor (Eeschema) main window.
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
Automatically orient all the fields in the symbol.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
SAVE_T SaveSymbol(const wxString &aNickname, const LIB_SYMBOL *aSymbol, bool aOverwrite=true)
Write aSymbol to an existing library given by aNickname.
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
#define REMOTE_SYMBOL_SESSION_VERSION
SETTINGS_MANAGER * GetSettingsManager()
PGM_BASE & Pgm()
The global program "get" accessor.
T * GetAppSettings(const char *aFilename)
static wxString DefaultLibraryPrefix()
static wxString DefaultDestinationDir()
wxString result
Test unit parsing edge cases and error handling.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().