KiCad PCB EDA Suite
Loading...
Searching...
No Matches
footprint_wizard.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 (C) 2026 Jon Evans <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
22#include <api/api_utils.h>
23#include <footprint.h>
24#include <pgm_base.h>
25#include <wx/log.h>
26
27#include <google/protobuf/util/json_util.h>
28
29#include "footprint_wizard.h"
30
31
33{
34 for( const std::unique_ptr<WIZARD_PARAMETER>& param : m_info.parameters )
35 param->Reset();
36}
37
38
40{
41 m_wizards.clear();
42
44 std::vector<const PLUGIN_ACTION*> actions = manager.GetActionsForScope( PLUGIN_ACTION_SCOPE::FOOTPRINT_WIZARD );
45
46 for( const PLUGIN_ACTION* action : actions )
47 {
48 std::unique_ptr<FOOTPRINT_WIZARD> wizard = std::make_unique<FOOTPRINT_WIZARD>();
49 wizard->SetIdentifier( action->identifier );
50
51 if( RefreshInfo( wizard.get() ) )
52 m_wizards[wizard->Identifier()] = std::move( wizard );
53 }
54}
55
56
57std::vector<FOOTPRINT_WIZARD*> FOOTPRINT_WIZARD_MANAGER::Wizards() const
58{
59 std::vector<FOOTPRINT_WIZARD*> wizards;
60
61 for( const std::unique_ptr<FOOTPRINT_WIZARD>& wizard : m_wizards | std::views::values )
62 wizards.emplace_back( wizard.get() );
63
64 std::ranges::sort( wizards,
65 []( FOOTPRINT_WIZARD* const& lhs,
66 FOOTPRINT_WIZARD* const& rhs ) -> bool
67 {
68 if( !lhs || !rhs )
69 return false;
70
71 return lhs->Info().meta.name < rhs->Info().meta.name;
72 } );
73
74 return wizards;
75}
76
77
78std::optional<FOOTPRINT_WIZARD*> FOOTPRINT_WIZARD_MANAGER::GetWizard( const wxString& aIdentifier )
79{
80 if( m_wizards.contains( aIdentifier ) )
81 return m_wizards[aIdentifier].get();
82
83 return std::nullopt;
84}
85
86
88{
89 wxCHECK( aWizard, false );
90
92
93 const wxLanguageInfo* lang = wxLocale::GetLanguageInfo( Pgm().GetSelectedLanguageIdentifier() );
94
95 std::vector<wxString> args = {
96 wxS( "--get-info" ),
97 wxS( "--lang" ),
98 lang ? lang->CanonicalName : wxString( wxS( "en" ) )
99 };
100
101 wxString out, err;
102 int ret = manager.InvokeActionSync( aWizard->Identifier(), args, &out, &err );
103
104 if( ret != 0 )
105 return false;
106
107 kiapi::common::types::WizardInfo info;
108
109 google::protobuf::util::JsonParseOptions options;
110 options.ignore_unknown_fields = true;
111
112 if( !google::protobuf::util::JsonStringToMessage( out.ToStdString(), &info, options ).ok() )
113 return false;
114
115 aWizard->Info().FromProto( info );
116 return true;
117}
118
119
120tl::expected<FOOTPRINT*, wxString> FOOTPRINT_WIZARD_MANAGER::Generate( FOOTPRINT_WIZARD* aWizard )
121{
122 wxCHECK( aWizard, tl::unexpected( _( "Unexpected error with footprint wizard" ) ) );
124
125 const wxLanguageInfo* lang = wxLocale::GetLanguageInfo( Pgm().GetSelectedLanguageIdentifier() );
126
127 std::vector<wxString> args = {
128 wxS( "--generate" ),
129 wxS( "--lang" ),
130 lang->CanonicalName,
131 wxS( "--params" )
132 };
133
134 kiapi::common::types::WizardParameterList params;
135
136 for( const std::unique_ptr<WIZARD_PARAMETER>& param : aWizard->Info().parameters )
137 params.mutable_parameters()->Add( param->Pack() );
138
139 std::string paramsJson;
140
141 if( !google::protobuf::util::MessageToJsonString( params, &paramsJson ).ok() )
142 return tl::unexpected( _( "Unexpected error with footprint wizard" ) );
143
144 args.emplace_back( wxString::Format( wxS( "'%s'" ), paramsJson ) );
145
146 wxString out, err;
147 int ret = manager.InvokeActionSync( aWizard->Identifier(), args, &out, &err );
148
149 if( ret != 0 )
150 return tl::unexpected( wxString::Format( _( "Could not launch footprint wizard '%s'" ), aWizard->Info().meta.name ) );
151
152 kiapi::common::types::WizardGeneratedContent response;
153
154 google::protobuf::util::JsonParseOptions options;
155 options.ignore_unknown_fields = true;
156
157 if( !google::protobuf::util::JsonStringToMessage( out.ToStdString(), &response, options ).ok() )
158 {
159 wxLogTrace( traceApi, wxString::Format( "Could not decode response:\n%s", out ) );
160 return tl::unexpected( _( "Unexpected response from footprint wizard" ) );
161 }
162
163 if( response.status() != kiapi::common::types::WGS_OK )
164 return tl::unexpected( wxString::Format( _( "Footprint wizard error: %s" ), response.error_message() ) );
165
166 std::unique_ptr<FOOTPRINT> fp = std::make_unique<FOOTPRINT>( nullptr );
167
168 if( !fp->Deserialize( response.content() ) )
169 return tl::unexpected( _( "Unexpected response from footprint wizard" ) );
170
171 return fp.release();
172}
173
174
175void WIZARD_META_INFO::FromProto( const kiapi::common::types::WizardMetaInfo& aProto )
176{
177 identifier = wxString::FromUTF8( aProto.identifier() );
178 name = wxString::FromUTF8( aProto.name() );
179 description = wxString::FromUTF8( aProto.description() );
180
181 types_generated.clear();
182
183 for( int type : aProto.types_generated() )
184 types_generated.insert( static_cast<kiapi::common::types::WizardContentType>( type ) );
185}
186
187
188wxString WIZARD_PARAMETER::ParameterCategoryName( kiapi::common::types::WizardParameterCategory aCategory )
189{
190 using namespace kiapi::common::types;
191
192 switch( aCategory )
193 {
194 case WPC_PACKAGE: return _( "Package" );
195 case WPC_PADS: return _( "Pads" );
196 case WPC_3DMODEL: return _( "3D Model" );
197 case WPC_METADATA: return _( "General" );
198 case WPC_RULES: return _( "Design Rules" );
199 case WPC_UNKNOWN:
200 default:
201 wxCHECK_MSG( false, wxEmptyString, "Unhandled parameter category type!" );
202 }
203}
204
205
206std::unique_ptr<WIZARD_PARAMETER> WIZARD_PARAMETER::Create( const kiapi::common::types::WizardParameter& aProto )
207{
208 std::unique_ptr<WIZARD_PARAMETER> p;
209
210 if( aProto.has_int_() )
211 {
212 p = std::make_unique<WIZARD_INT_PARAMETER>();
213 static_cast<WIZARD_INT_PARAMETER*>( p.get() )->FromProto( aProto.int_() );
214 }
215 else if( aProto.has_real() )
216 {
217 p = std::make_unique<WIZARD_REAL_PARAMETER>();
218 static_cast<WIZARD_REAL_PARAMETER*>( p.get() )->FromProto( aProto.real() );
219 }
220 else if( aProto.has_string() )
221 {
222 p = std::make_unique<WIZARD_STRING_PARAMETER>();
223 static_cast<WIZARD_STRING_PARAMETER*>( p.get() )->FromProto( aProto.string() );
224 }
225 else if( aProto.has_bool_() )
226 {
227 p = std::make_unique<WIZARD_BOOL_PARAMETER>();
228 static_cast<WIZARD_BOOL_PARAMETER*>( p.get() )->FromProto( aProto.bool_() );
229 }
230 else if( aProto.has_enum_() )
231 {
232 p = std::make_unique<WIZARD_ENUM_PARAMETER>();
233 static_cast<WIZARD_ENUM_PARAMETER*>( p.get() )->FromProto( aProto.enum_() );
234 }
235
236 if( !p )
237 return p;
238
239 p->identifier = wxString::FromUTF8( aProto.identifier() );
240 p->name = wxString::FromUTF8( aProto.name() );
241 p->description = wxString::FromUTF8( aProto.description() );
242 p->category = aProto.category();
243 p->type = aProto.type();
244
245 return p;
246}
247
248
249kiapi::common::types::WizardParameter WIZARD_PARAMETER::Pack( bool aCompact )
250{
251 kiapi::common::types::WizardParameter packed;
252
253 packed.set_identifier( identifier.ToUTF8() );
254
255 if( !aCompact )
256 {
257 packed.set_name( name.ToUTF8() );
258 packed.set_description( description.ToUTF8() );
259 packed.set_category( category );
260 packed.set_type( type );
261 }
262
263 return packed;
264}
265
266
267kiapi::common::types::WizardParameter WIZARD_INT_PARAMETER::Pack( bool aCompact )
268{
269 kiapi::common::types::WizardParameter packed = WIZARD_PARAMETER::Pack();
270
271 packed.mutable_int_()->set_value( value );
272
273 if( !aCompact )
274 {
275 packed.mutable_int_()->set_default_( default_value );
276
277 if( min )
278 packed.mutable_int_()->set_min( *min );
279
280 if( max )
281 packed.mutable_int_()->set_max( *max );
282
283 if( multiple )
284 packed.mutable_int_()->set_multiple( *multiple );
285 }
286
287 return packed;
288}
289
290
291kiapi::common::types::WizardParameter WIZARD_REAL_PARAMETER::Pack( bool aCompact )
292{
293 kiapi::common::types::WizardParameter packed = WIZARD_PARAMETER::Pack();
294
295 packed.mutable_real()->set_value( value );
296
297 if( !aCompact )
298 {
299 packed.mutable_real()->set_default_( default_value );
300
301 if( min )
302 packed.mutable_real()->set_min( *min );
303
304 if( max )
305 packed.mutable_real()->set_max( *max );
306 }
307
308 return packed;
309}
310
311
312kiapi::common::types::WizardParameter WIZARD_BOOL_PARAMETER::Pack( bool aCompact )
313{
314 kiapi::common::types::WizardParameter packed = WIZARD_PARAMETER::Pack();
315
316 packed.mutable_bool_()->set_value( value );
317
318 if( !aCompact )
319 packed.mutable_bool_()->set_default_( default_value );
320
321 return packed;
322}
323
324
325kiapi::common::types::WizardParameter WIZARD_STRING_PARAMETER::Pack( bool aCompact )
326{
327 kiapi::common::types::WizardParameter packed = WIZARD_PARAMETER::Pack();
328
329 packed.mutable_string()->set_value( value );
330
331 if( !aCompact )
332 packed.mutable_string()->set_default_( default_value );
333
334 return packed;
335}
336
337
338kiapi::common::types::WizardParameter WIZARD_ENUM_PARAMETER::Pack( bool aCompact )
339{
340 kiapi::common::types::WizardParameter packed = WIZARD_PARAMETER::Pack();
341
342 packed.mutable_enum_()->set_value( value );
343
344 if( !aCompact )
345 {
346 packed.mutable_enum_()->set_default_( default_value );
347
348 for( const auto& [id, label] : choices )
349 {
350 kiapi::common::types::WizardEnumChoice* choice = packed.mutable_enum_()->add_choices();
351 choice->set_identifier( id.ToUTF8() );
352 choice->set_label( label.ToUTF8() );
353 }
354 }
355
356 return packed;
357}
358
359
360void WIZARD_INT_PARAMETER::FromProto( const kiapi::common::types::WizardIntParameter& aProto )
361{
362 value = aProto.value();
363 default_value = aProto.default_();
364
365 if( aProto.has_min() )
366 min = aProto.min();
367 else
368 min.reset();
369
370 if( aProto.has_max() )
371 max = aProto.max();
372 else
373 max.reset();
374
375 if( aProto.has_multiple() )
376 multiple = aProto.multiple();
377 else
378 multiple.reset();
379}
380
381
382void WIZARD_REAL_PARAMETER::FromProto( const kiapi::common::types::WizardRealParameter& aProto )
383{
384 value = aProto.value();
385 default_value = aProto.default_();
386
387 if( aProto.has_min() )
388 min = aProto.min();
389 else
390 min.reset();
391
392 if( aProto.has_max() )
393 max = aProto.max();
394 else
395 max.reset();
396}
397
398
399void WIZARD_BOOL_PARAMETER::FromProto( const kiapi::common::types::WizardBoolParameter& aProto )
400{
401 value = aProto.value();
402 default_value = aProto.default_();
403}
404
405
406void WIZARD_STRING_PARAMETER::FromProto( const kiapi::common::types::WizardStringParameter& aProto )
407{
408 value = wxString::FromUTF8( aProto.value() );
409 default_value = wxString::FromUTF8( aProto.default_() );
410
411 if( aProto.has_validation_regex() )
412 validation_regex = wxString::FromUTF8( aProto.validation_regex() );
413 else
414 validation_regex.reset();
415}
416
417
418void WIZARD_ENUM_PARAMETER::FromProto( const kiapi::common::types::WizardEnumParameter& aProto )
419{
420 value = aProto.value();
421 default_value = aProto.default_();
422
423 choices.clear();
424 choices.reserve( aProto.choices_size() );
425
426 for( const kiapi::common::types::WizardEnumChoice& choice : aProto.choices() )
427 choices.emplace_back( wxString::FromUTF8( choice.identifier() ), wxString::FromUTF8( choice.label() ) );
428}
429
430
431void WIZARD_INFO::FromProto( const kiapi::common::types::WizardInfo& aProto )
432{
433 meta.FromProto( aProto.meta() );
434
435 parameters.clear();
436 parameters.reserve( aProto.parameters_size() );
437
438 for( const kiapi::common::types::WizardParameter& parameter : aProto.parameters() )
439 {
440 if( std::unique_ptr<WIZARD_PARAMETER> param = WIZARD_PARAMETER::Create( parameter ); param )
441 parameters.emplace_back( std::move( param ) );
442 }
443}
Responsible for loading plugin definitions for API-based plugins (ones that do not run inside KiCad i...
int InvokeActionSync(const wxString &aIdentifier, std::vector< wxString > aExtraArgs, wxString *aStdout=nullptr, wxString *aStderr=nullptr, std::shared_ptr< REPORTER > aReporter=nullptr)
Invokes an action synchronously, capturing its output.
std::vector< const PLUGIN_ACTION * > GetActionsForScope(PLUGIN_ACTION_SCOPE aScope)
std::map< wxString, std::unique_ptr< FOOTPRINT_WIZARD > > m_wizards
void ReloadWizards()
Goes through the list of IPC API plugins that provide wizard actions and attempts to refresh the info...
std::vector< FOOTPRINT_WIZARD * > Wizards() const
static bool RefreshInfo(FOOTPRINT_WIZARD *aWizard)
Runs a wizard plugin with the –get-info argument, which should result in the plugin dumping a WizardI...
std::optional< FOOTPRINT_WIZARD * > GetWizard(const wxString &aIdentifier)
tl::expected< FOOTPRINT *, wxString > Generate(FOOTPRINT_WIZARD *aWizard)
Generates a footprint using a given wizard.
const wxString & Identifier() const
WIZARD_INFO & Info()
virtual API_PLUGIN_MANAGER & GetPluginManager() const
Definition pgm_base.h:139
void FromProto(const kiapi::common::types::WizardBoolParameter &aProto)
kiapi::common::types::WizardParameter Pack(bool aCompact=true) override
Packs the current state of this parameter back into a protobuf message.
std::vector< std::pair< wxString, wxString > > choices
pair<identifier, label>
kiapi::common::types::WizardParameter Pack(bool aCompact=true) override
Packs the current state of this parameter back into a protobuf message.
void FromProto(const kiapi::common::types::WizardEnumParameter &aProto)
void FromProto(const kiapi::common::types::WizardIntParameter &aProto)
std::optional< int > min
std::optional< int > multiple
kiapi::common::types::WizardParameter Pack(bool aCompact=true) override
Packs the current state of this parameter back into a protobuf message.
std::optional< int > max
virtual kiapi::common::types::WizardParameter Pack(bool aCompact=true)
Packs the current state of this parameter back into a protobuf message.
kiapi::common::types::WizardParameterDataType type
static wxString ParameterCategoryName(kiapi::common::types::WizardParameterCategory aCategory)
static std::unique_ptr< WIZARD_PARAMETER > Create(const kiapi::common::types::WizardParameter &aProto)
kiapi::common::types::WizardParameterCategory category
kiapi::common::types::WizardParameter Pack(bool aCompact=true) override
Packs the current state of this parameter back into a protobuf message.
void FromProto(const kiapi::common::types::WizardRealParameter &aProto)
std::optional< double > min
std::optional< double > max
std::optional< wxString > validation_regex
kiapi::common::types::WizardParameter Pack(bool aCompact=true) override
Packs the current state of this parameter back into a protobuf message.
void FromProto(const kiapi::common::types::WizardStringParameter &aProto)
#define _(s)
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
Definition api_utils.cpp:33
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
An action performed by a plugin via the IPC API.
Definition api_plugin.h:72
std::vector< std::unique_ptr< WIZARD_PARAMETER > > parameters
WIZARD_META_INFO meta
void FromProto(const kiapi::common::types::WizardInfo &aProto)
void FromProto(const kiapi::common::types::WizardMetaInfo &aProto)
std::set< kiapi::common::types::WizardContentType > types_generated