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 std::vector<uint8_t>& aPayload,
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();
342 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"PANEL_REMOTE_SYMBOL constructed (frame=%p)", (
void*)aParent );
348 if( wxWebView* browser =
m_webView->GetWebView() )
358 m_pcm->SetRepositoryList( cfg->m_PcmRepositories );
363 const std::vector<PCM_INSTALLATION_ENTRY> installed =
m_pcm->GetInstalledPackages();
370 wxString label = entry.package.name;
372 if( !entry.current_version.IsEmpty() )
373 label << wxS(
" (" ) << entry.current_version << wxS(
")" );
382 showMessage(
_(
"No remote data sources are currently installed." ) );
394 const int selection = aEvent.GetSelection();
396 if( selection == wxNOT_FOUND )
428 wxLogWarning(
"No JSON configuration found for data source %s", aEntry.
package.
identifier );
429 showMessage( wxString::Format(
_(
"No configuration JSON found for '%s'." ),
431 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"loadDataSource: no json for %s", aEntry.
package.
identifier );
435 wxFFile file( jsonPath->GetFullPath(),
"rb" );
437 if( !file.IsOpened() )
439 wxLogWarning(
"Unable to open remote data source JSON: %s", jsonPath->GetFullPath() );
440 showMessage( wxString::Format(
_(
"Unable to open '%s'." ), jsonPath->GetFullPath() ) );
441 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"loadDataSource: cannot open %s", jsonPath->GetFullPath() );
445 wxString jsonContent;
447 if( !file.ReadAll( &jsonContent, wxConvUTF8 ) )
449 wxLogWarning(
"Failed to read remote data source JSON: %s", jsonPath->GetFullPath() );
450 showMessage( wxString::Format(
_(
"Unable to read '%s'." ), jsonPath->GetFullPath() ) );
458 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"loadDataSource: loading URL %s", (*url).ToUTF8().data() );
462 wxLogWarning(
"Remote data source JSON did not produce a valid URL for %s",
464 showMessage( wxString::Format(
_(
"Unable to load remote data for '%s'." ),
466 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"loadDataSource: failed to find URL in %s", jsonPath->GetFullPath() );
471std::optional<wxFileName>
475 cleanId.Replace(
'.',
'_' );
477 wxFileName baseDir = wxFileName::DirName(
m_pcm->Get3rdPartyPath() );
478 baseDir.AppendDir( wxS(
"resources" ) );
479 baseDir.AppendDir( cleanId );
481 const wxString resourcesPath = baseDir.GetFullPath();
483 if( !wxDirExists( resourcesPath ) )
486 const std::vector<wxString> preferredNames = {
487 wxS(
"remote_symbol.json" ),
488 wxS(
"datasource.json" )
491 for(
const wxString& candidate : preferredNames )
493 wxFileName file( baseDir );
494 file.SetFullName( candidate );
496 if( file.FileExists() )
500 wxDir dir( resourcesPath );
502 if( !dir.IsOpened() )
507 if( dir.GetFirst( &jsonFile, wxS(
"*.json" ), wxDIR_FILES ) )
509 wxFileName fallback( baseDir );
510 fallback.SetFullName( jsonFile );
523 wxString escaped = aMessage;
524 escaped.Replace(
"&",
"&" );
525 escaped.Replace(
"<",
"<" );
526 escaped.Replace(
">",
">" );
528 wxColour bgColour = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
529 wxColour fgColour = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
532 html << wxS(
"<html><head><style>" )
533 << wxString::Format( wxS(
"body { background-color: #%02x%02x%02x; color: #%02x%02x%02x; "
534 "font-family: system-ui, sans-serif; padding: 10px; }" ),
535 bgColour.Red(), bgColour.Green(), bgColour.Blue(),
536 fgColour.Red(), fgColour.Green(), fgColour.Blue() )
537 << wxS(
"</style></head><body><p>" ) << escaped << wxS(
"</p></body></html>" );
545 if( aJsonContent.IsEmpty() )
548 wxScopedCharBuffer utf8 = aJsonContent.ToUTF8();
550 if( !utf8 || utf8.length() == 0 )
555 nlohmann::json parsed = nlohmann::json::parse( utf8.data() );
558 if( parsed.is_string() )
560 url = parsed.get<std::string>();
562 else if( parsed.is_object() )
564 auto extractString = [&](
const char* key ) -> std::string
566 auto it = parsed.find( key );
568 if( it != parsed.end() && it->is_string() )
569 return it->get<std::string>();
574 auto extractInt = [&](
const char* key ) -> std::optional<int>
576 auto it = parsed.find( key );
578 if( it == parsed.end() )
581 if( it->is_number_integer() )
582 return it->get<
int>();
584 if( it->is_string() )
588 return std::stoi( it->get<std::string>() );
598 std::string host = extractString(
"host" );
599 std::optional<int> port = extractInt(
"port" );
600 std::string
path = extractString(
"path" );
607 if( port && *port > 0 )
608 url = wxString::Format(
"%s:%d%s", host, *port,
path ).ToStdString();
614 url = extractString(
"url" );
618 for(
const char* key : {
"website",
"endpoint" } )
620 url = extractString( key );
629 for(
const auto& [
name, value] : parsed.items() )
631 if( value.is_string() )
633 const std::string candidate = value.get<std::string>();
635 if( candidate.rfind(
"http", 0 ) == 0 || candidate.rfind(
"file", 0 ) == 0 )
648 return wxString::FromUTF8( url.c_str() );
650 catch(
const std::exception& e )
652 wxLogWarning(
"Failed to parse remote symbol JSON: %s", e.what() );
660 wxScopedCharBuffer utf8 = aMessage.ToUTF8();
662 if( !utf8 || utf8.length() == 0 )
664 wxLogWarning(
"Remote symbol RPC: empty payload." );
665 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"onKicadMessage: empty payload" );
671 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"onKicadMessage: received payload size=%d", (
int)utf8.length() );
674 catch(
const std::exception& e )
676 wxLogWarning(
"Remote symbol RPC parse error: %s", e.what() );
677 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"onKicadMessage: parse error %s", e.what() );
684 wxUnusedVar( aEvent );
710 nlohmann::json params = nlohmann::json::object();
711 params[
"client_name"] =
"KiCad";
716 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"beginSessionHandshake: NEW_SESSION sent, session=%s",
m_sessionId.AsString() );
721 nlohmann::json aParameters,
722 std::optional<int> aResponseTo,
723 const wxString& aStatus,
724 const std::string& aData,
725 const wxString& aErrorCode,
726 const wxString& aErrorMessage )
731 nlohmann::json payload = nlohmann::json::object();
735 payload[
"command"] = aCommand.ToStdString();
738 payload[
"response_to"] = *aResponseTo;
740 if( !aStatus.IsEmpty() )
741 payload[
"status"] = aStatus.ToStdString();
743 if( !aParameters.is_null() && !aParameters.empty() )
744 payload[
"parameters"] = std::move( aParameters );
747 payload[
"data"] = aData;
749 if( !aErrorCode.IsEmpty() )
750 payload[
"error_code"] = aErrorCode.ToStdString();
752 if( !aErrorMessage.IsEmpty() )
753 payload[
"error_message"] = aErrorMessage.ToStdString();
755 wxString script = wxString::Format( wxS(
"window.kiclient.postMessage('%s');" ),
758 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"sendRpcMessage: command=%s message_id=%d status=%s",
759 aCommand.ToUTF8().data(), payload[
"message_id"].get<
int>(), aStatus.ToUTF8().data() );
764 const wxString& aErrorCode,
765 const wxString& aErrorMessage )
767 sendRpcMessage( aCommand, nlohmann::json::object(), aResponseTo, wxS(
"ERROR" ),
768 std::string(), aErrorCode, aErrorMessage );
774 if( !aMessage.is_object() )
777 const wxString command =
jsonString( aMessage,
"command" );
779 if( command.IsEmpty() )
782 auto messageIdIt = aMessage.find(
"message_id" );
784 if( messageIdIt == aMessage.end() || !messageIdIt->is_number_integer() )
787 const int messageId = messageIdIt->get<
int>();
789 const int version = aMessage.value(
"version", 0 );
794 wxString::Format(
_(
"Unsupported RPC version %d." ), version ) );
798 const wxString sessionId =
jsonString( aMessage,
"session_id" );
800 if( sessionId.IsEmpty() )
803 _(
"Missing session identifier." ) );
807 if( !sessionId.IsSameAs(
m_sessionId.AsString() ) )
808 wxLogWarning(
"Remote symbol RPC session mismatch (expected %s, got %s).",
811 const wxString status =
jsonString( aMessage,
"status" );
813 if( status.IsSameAs( wxS(
"ERROR" ),
false ) )
815 wxLogWarning(
"Remote symbol RPC error (%s): %s",
jsonString( aMessage,
"error_code" ),
820 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"handleRpcMessage: command=%s message_id=%d status=%s session=%s",
821 command.ToUTF8().data(), messageId, status.ToUTF8().data(), sessionId.ToUTF8().data() );
823 nlohmann::json params = nlohmann::json::object();
824 auto paramsIt = aMessage.find(
"parameters" );
826 if( paramsIt != aMessage.end() && paramsIt->is_object() )
829 const std::string data = aMessage.value(
"data", std::string() );
831 if( command == wxS(
"NEW_SESSION" ) )
833 nlohmann::json reply = nlohmann::json::object();
834 reply[
"client_name"] =
"KiCad";
840 else if( command == wxS(
"GET_KICAD_VERSION" ) )
842 nlohmann::json reply = nlohmann::json::object();
847 else if( command == wxS(
"LIST_SUPPORTED_VERSIONS" ) )
849 nlohmann::json reply = nlohmann::json::object();
854 else if( command == wxS(
"CAPABILITIES" ) )
856 nlohmann::json reply = nlohmann::json::object();
857 reply[
"commands"] = {
"NEW_SESSION",
"GET_KICAD_VERSION",
"LIST_SUPPORTED_VERSIONS",
858 "CAPABILITIES",
"PING",
"PONG",
"DL_SYMBOL",
"DL_FOOTPRINT",
859 "DL_SPICE",
"DL_3DMODEL" };
860 reply[
"compression"] = {
"NONE",
"ZSTD" };
861 reply[
"max_message_size"] = 0;
865 else if( command == wxS(
"PING" ) )
867 nlohmann::json reply = nlohmann::json::object();
869 if( params.contains(
"nonce" ) )
870 reply[
"nonce"] = params[
"nonce"];
875 else if( command == wxS(
"PONG" ) )
880 const wxString compression =
jsonString( params,
"compression" );
882 if( command.StartsWith( wxS(
"DL_" ) ) )
884 if( compression.IsEmpty() )
887 _(
"Missing compression metadata." ) );
891 std::vector<uint8_t> decoded;
900 std::vector<uint8_t> payload;
901 wxScopedCharBuffer compUtf8 = compression.ToUTF8();
902 std::string compressionStr = compUtf8 ? std::string( compUtf8.data() ) : std::string();
910 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"handleRpcMessage: decoded size=%zu decompressed size=%zu command=%s",
911 decoded.size(), payload.size(), command.ToUTF8().data() );
915 if( command == wxS(
"DL_SYMBOL" ) )
917 else if( command == wxS(
"DL_FOOTPRINT" ) )
919 else if( command == wxS(
"DL_3DMODEL" ) )
921 else if( command == wxS(
"DL_SPICE" ) )
926 wxString::Format(
_(
"Command '%s' is not supported." ), command ) );
934 error.IsEmpty() ?
_(
"Unable to store payload." ) : error );
940 wxString::Format(
_(
"Command '%s' is not supported." ), command ) );
950 aError =
_(
"Unable to load schematic settings." );
956 if( destination.IsEmpty() )
961 destination.Trim(
true ).Trim(
false );
963 if( destination.IsEmpty() )
965 aError =
_(
"Destination directory is not configured." );
969 wxFileName dir = wxFileName::DirName( destination );
972 if( !dir.DirExists() )
974 if( !dir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
976 aError = wxString::Format(
_(
"Unable to create directory '%s'." ), dir.GetFullPath() );
987 const wxString& aNickname,
bool aGlobalTable,
988 wxString& aError )
const
996 aError =
_(
"Unable to access the symbol library table." );
1001 const wxString fullPath = aLibraryFile.GetFullPath();
1003 if(
table->HasRow( aNickname ) )
1005 if( std::optional<LIBRARY_TABLE_ROW*> rowOpt =
table->Row( aNickname ); rowOpt )
1009 if( row->
URI() != fullPath )
1013 if( !
table->Save() )
1015 aError =
_(
"Failed to update the symbol library table." );
1027 row.
SetType( wxS(
"KiCad" ) );
1032 if( !
table->Save() )
1034 aError =
_(
"Failed to save the symbol library table." );
1043 const wxString& aNickname,
1044 bool aGlobalTable, wxString& aError )
const
1052 aError =
_(
"Unable to access the footprint library table." );
1057 const wxString fullPath = aLibraryDir.GetFullPath();
1059 if(
table->HasRow( aNickname ) )
1061 if( std::optional<LIBRARY_TABLE_ROW*> rowOpt =
table->Row( aNickname ); rowOpt )
1065 if( row->
URI() != fullPath )
1069 if( !
table->Save() )
1071 aError =
_(
"Failed to update the footprint library table." );
1083 row.
SetType( wxS(
"KiCad" ) );
1088 if( !
table->Save() )
1090 aError =
_(
"Failed to save the footprint library table." );
1103 prefix = settings->m_RemoteSymbol.library_prefix;
1105 if( prefix.IsEmpty() )
1108 prefix.Trim(
true ).Trim(
false );
1110 if( prefix.IsEmpty() )
1111 prefix = wxS(
"remote" );
1113 for(
size_t i = 0; i < prefix.length(); ++i )
1115 wxUniChar ch = prefix[i];
1117 if( !( wxIsalnum( ch ) || ch ==
'_' || ch ==
'-' ) )
1126 const wxString& aLibItemName,
1131 aError =
_(
"No schematic editor is available for placement." );
1132 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"placeDownloadedSymbol: no frame available" );
1136 if( aNickname.IsEmpty() || aLibItemName.IsEmpty() )
1138 aError =
_(
"Downloaded symbol metadata is incomplete." );
1150 aError =
_(
"Unable to load the downloaded symbol for placement." );
1151 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"placeDownloadedSymbol: failed to load libSymbol %s:%s",
1152 aNickname.ToUTF8().data(), aLibItemName.ToUTF8().data() );
1156 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"placeDownloadedSymbol: loaded libSymbol %s:%s",
1157 aNickname.ToUTF8().data(), aLibItemName.ToUTF8().data() );
1172 aError =
_(
"Unable to access the schematic placement tools." );
1173 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"placeDownloadedSymbol: no tool manager available" );
1180 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"placeDownloadedSymbol: posted placeSymbol action for %s:%s",
1181 aNickname.ToUTF8().data(), aLibItemName.ToUTF8().data() );
1187 const std::vector<uint8_t>& aPayload,
1190 const wxString mode =
jsonString( aParams,
"mode" );
1191 const bool placeAfterDownload = mode.IsSameAs( wxS(
"PLACE" ),
false );
1193 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false )
1194 && !mode.IsSameAs( wxS(
"PLACE" ),
false ) )
1196 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1200 const wxString contentType =
jsonString( aParams,
"content_type" );
1202 if( !contentType.IsSameAs( wxS(
"KICAD_SYMBOL_V1" ),
false ) )
1204 aError =
_(
"Unsupported symbol payload type." );
1210 aError =
_(
"No schematic editor is available to store symbols." );
1218 aError =
_(
"Unable to load schematic settings." );
1229 wxFileName symDir = baseDir;
1230 symDir.AppendDir( wxS(
"symbols" ) );
1232 if( !symDir.DirExists() && !symDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1234 aError = wxString::Format(
_(
"Unable to create '%s'." ), symDir.GetFullPath() );
1239 wxString libItemName =
jsonString( aParams,
"name" );
1240 if( libItemName.IsEmpty() )
1241 libItemName = wxS(
"symbol" );
1242 libItemName.Trim(
true ).Trim(
false );
1245 wxString libraryName =
jsonString( aParams,
"library" );
1246 if( libraryName.IsEmpty() )
1247 libraryName = wxS(
"symbols" );
1248 libraryName.Trim(
true ).Trim(
false );
1251 const wxString nickname =
sanitizedPrefix() + wxS(
"_" ) + sanitizedLibName;
1253 wxFileName outFile( symDir );
1254 outFile.SetFullName( nickname + wxS(
".kicad_sym" ) );
1263 aError =
_(
"Unable to access the symbol library manager." );
1267 std::unique_ptr<LIB_SYMBOL> downloadedSymbol =
loadSymbolFromPayload( aPayload, libItemName, aError );
1269 if( !downloadedSymbol )
1271 if( aError.IsEmpty() )
1272 aError =
_(
"Unable to parse the downloaded symbol." );
1277 downloadedSymbol->SetName( libItemName );
1282 downloadedSymbol->SetLibId( savedId );
1284 if( adapter->
SaveSymbol( nickname, downloadedSymbol.get(),
true )
1287 aError =
_(
"Unable to save the downloaded symbol." );
1288 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1289 "receiveSymbol: failed to save symbol %s to library %s",
1290 libItemName.ToUTF8().data(), nickname.ToUTF8().data() );
1294 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
1295 "receiveSymbol: saved symbol %s into library %s", libItemName.ToUTF8().data(),
1296 nickname.ToUTF8().data() );
1302 if( placeAfterDownload )
1304 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveSymbol: placing symbol now (nickname=%s libItem=%s)",
1305 nickname.ToUTF8().data(), libItemName.ToUTF8().data() );
1309 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveSymbol: saved symbol (nickname=%s libItem=%s)" ,
1310 nickname.ToUTF8().data(), libItemName.ToUTF8().data() );
1317 const std::vector<uint8_t>& aPayload,
1320 const wxString mode =
jsonString( aParams,
"mode" );
1322 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveFootprint: mode=%s", mode.ToUTF8().data() );
1324 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false )
1325 && !mode.IsSameAs( wxS(
"PLACE" ),
false ) )
1327 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1331 const wxString contentType =
jsonString( aParams,
"content_type" );
1333 if( !contentType.IsSameAs( wxS(
"KICAD_FOOTPRINT_V1" ),
false ) )
1335 aError =
_(
"Unsupported footprint payload type." );
1344 wxFileName fpRoot = baseDir;
1345 fpRoot.AppendDir( wxS(
"footprints" ) );
1347 if( !fpRoot.DirExists() && !fpRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1349 aError = wxString::Format(
_(
"Unable to create '%s'." ), fpRoot.GetFullPath() );
1354 wxString footprintName =
jsonString( aParams,
"name" );
1355 if( footprintName.IsEmpty() )
1356 footprintName = wxS(
"footprint" );
1360 wxString libraryName =
jsonString( aParams,
"library" );
1361 if( libraryName.IsEmpty() )
1362 libraryName = wxS(
"footprints" );
1363 libraryName.Trim(
true ).Trim(
false );
1366 wxString libNickname =
sanitizedPrefix() + wxS(
"_" ) + sanitizedLibName;
1368 wxFileName libDir = fpRoot;
1369 libDir.AppendDir( libNickname + wxS(
".pretty" ) );
1371 if( !libDir.DirExists() && !libDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1373 aError = wxString::Format(
_(
"Unable to create '%s'." ), libDir.GetFullPath() );
1377 wxString fileName = footprintName;
1379 if( !fileName.Lower().EndsWith( wxS(
".kicad_mod" ) ) )
1380 fileName += wxS(
".kicad_mod" );
1382 wxFileName outFile( libDir );
1383 outFile.SetFullName( fileName );
1388 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveFootprint: wrote footprint %s in lib %s",
1389 outFile.GetFullPath(), libNickname.ToUTF8().data() );
1395 aError =
_(
"Unable to load schematic settings." );
1413 const std::vector<uint8_t>& aPayload,
1416 const wxString mode =
jsonString( aParams,
"mode" );
1418 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receive3DModel: mode=%s", mode.ToUTF8().data() );
1420 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false )
1421 && !mode.IsSameAs( wxS(
"PLACE" ),
false ) )
1423 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1427 const wxString contentType =
jsonString( aParams,
"content_type" );
1429 if( !contentType.IsSameAs( wxS(
"KICAD_3D_MODEL_STEP" ),
false ) &&
1430 !contentType.IsSameAs( wxS(
"KICAD_3D_MODEL_WRL" ),
false ) )
1432 aError =
_(
"Unsupported 3D model payload type." );
1441 wxFileName modelDir = baseDir;
1444 if( !modelDir.DirExists() && !modelDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1446 aError = wxString::Format(
_(
"Unable to create '%s'." ), modelDir.GetFullPath() );
1450 wxString fileName =
jsonString( aParams,
"name" );
1452 if( fileName.IsEmpty() )
1457 wxString extension = wxS(
".step" );
1459 if( contentType.IsSameAs( wxS(
"KICAD_3D_MODEL_WRL" ),
false ) )
1460 extension = wxS(
".wrl" );
1462 if( !fileName.Lower().EndsWith( extension ) )
1463 fileName += extension;
1465 wxFileName outFile( modelDir );
1466 outFile.SetFullName( fileName );
1471 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receive3DModel: wrote model %s", outFile.GetFullPath() );
1478 const std::vector<uint8_t>& aPayload,
1481 const wxString mode =
jsonString( aParams,
"mode" );
1483 wxLogTrace( wxS(
"KI_TRACE_REMOTE_SYMBOL" ),
"receiveSPICEModel: mode=%s", mode.ToUTF8().data() );
1485 if( !mode.IsEmpty() && !mode.IsSameAs( wxS(
"SAVE" ),
false )
1486 && !mode.IsSameAs( wxS(
"PLACE" ),
false ) )
1488 aError = wxString::Format(
_(
"Unsupported transfer mode '%s'." ), mode );
1492 const wxString contentType =
jsonString( aParams,
"content_type" );
1494 if( !contentType.IsSameAs( wxS(
"KICAD_SPICE_MODEL_V1" ),
false ) )
1496 aError =
_(
"Unsupported SPICE payload type." );
1505 wxFileName spiceDir = baseDir;
1508 if( !spiceDir.DirExists() && !spiceDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
1510 aError = wxString::Format(
_(
"Unable to create '%s'." ), spiceDir.GetFullPath() );
1514 wxString fileName =
jsonString( aParams,
"name" );
1516 if( fileName.IsEmpty() )
1521 if( !fileName.Lower().EndsWith( wxS(
".cir" ) ) )
1522 fileName += wxS(
".cir" );
1524 wxFileName outFile( spiceDir );
1525 outFile.SetFullName( fileName );
1530 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
std::shared_ptr< PLUGIN_CONTENT_MANAGER > m_pcm
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().