KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_geda.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 3
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 along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * This file contains embedded symbol definitions and file format knowledge
20 * derived from the gEDA and Lepton EDA projects:
21 *
22 * gEDA/gaf - Copyright (C) 1998-2010 Ales Hvezda
23 * Copyright (C) 1998-2016 gEDA Contributors
24 * Lepton EDA - Copyright (C) 2017-2024 Lepton EDA Contributors
25 *
26 * Both projects are licensed under the GNU General Public License v2 or later.
27 * See https://github.com/lepton-eda/lepton-eda and
28 * https://github.com/rlutz/geda-gaf
29 */
30
32
33#include <wx/dir.h>
34#include <wx/file.h>
35#include <wx/filename.h>
36#include <wx/textfile.h>
37#include <wx/tokenzr.h>
38#include <wx/log.h>
39
40#include <eda_shape.h>
41#include <lib_id.h>
42#include <page_info.h>
43#include <lib_symbol.h>
44#include <project.h>
45#include <project_sch.h>
46#include <default_values.h>
47#include <sch_bus_entry.h>
48#include <sch_junction.h>
49#include <sch_label.h>
50#include <sch_no_connect.h>
51#include <sch_line.h>
52#include <sch_pin.h>
53#include <sch_screen.h>
54#include <sch_shape.h>
55#include <sch_sheet.h>
56#include <sch_sheet_path.h>
57#include <sch_sheet_pin.h>
58#include <sch_symbol.h>
59#include <sch_bitmap.h>
60#include <sch_text.h>
61#include <schematic.h>
62#include <string_utils.h>
63#include <stroke_params.h>
65#include <base_units.h>
66#include <pin_type.h>
67#include <reporter.h>
69#include <math/util.h>
70#include <reference_image.h>
71#include <wx/base64.h>
72#include <wx/image.h>
73
74#include <algorithm>
75#include <climits>
76#include <vector>
77
78// Default text size for imported text (50 mils)
79static constexpr int GEDA_DEFAULT_TEXT_SIZE_MILS = 50;
80
81// Default body size for fallback rectangular symbols
82static constexpr int DEFAULT_SYMBOL_SIZE_MILS = 200;
83
90static wxString convertOverbars( const wxString& aInput )
91{
92 wxString result;
93 result.reserve( aInput.length() + 8 );
94
95 size_t i = 0;
96
97 while( i < aInput.length() )
98 {
99 if( i + 1 < aInput.length() && aInput[i] == '\\' && aInput[i + 1] == '_' )
100 {
101 size_t barEnd = aInput.find( wxT( "\\_" ), i + 2 );
102
103 if( barEnd != wxString::npos )
104 {
105 result += wxT( "~{" );
106 result += aInput.Mid( i + 2, barEnd - ( i + 2 ) );
107 result += wxT( "}" );
108 i = barEnd + 2;
109 }
110 else
111 {
112 result += aInput[i];
113 i++;
114 }
115 }
116 else if( i + 1 < aInput.length() && aInput[i] == '\\' && aInput[i + 1] == '\\' )
117 {
118 result += '\\';
119 i += 2;
120 }
121 else
122 {
123 result += aInput[i];
124 i++;
125 }
126 }
127
128 return result;
129}
130
131
132// Standard gEDA symbol definitions embedded from the geda-gaf project (GPL v2+).
133// These provide correct pin positions for common symbols so the importer works
134// without requiring a gEDA system installation.
135const std::map<wxString, wxString>& SCH_IO_GEDA::getBuiltinSymbols()
136{
137 static const std::map<wxString, wxString> symbols = {
138 { wxT( "resistor-1.sym" ),
139 wxT( "v 20031231 1\n"
140 "L 600 200 500 0 3 0 0 0 -1 -1\n"
141 "L 500 0 400 200 3 0 0 0 -1 -1\n"
142 "L 400 200 300 0 3 0 0 0 -1 -1\n"
143 "L 300 0 200 200 3 0 0 0 -1 -1\n"
144 "T 300 400 5 10 0 0 0 0 1\n"
145 "device=RESISTOR\n"
146 "L 600 200 700 0 3 0 0 0 -1 -1\n"
147 "L 700 0 750 100 3 0 0 0 -1 -1\n"
148 "P 900 100 750 100 1 0 0\n"
149 "{\n"
150 "T 800 150 5 8 0 1 0 0 1\n"
151 "pinnumber=2\n"
152 "T 800 150 5 8 0 0 0 0 1\n"
153 "pinseq=2\n"
154 "T 800 150 5 8 0 1 0 0 1\n"
155 "pinlabel=2\n"
156 "T 800 150 5 8 0 1 0 0 1\n"
157 "pintype=pas\n"
158 "}\n"
159 "P 0 100 152 100 1 0 0\n"
160 "{\n"
161 "T 100 150 5 8 0 1 0 0 1\n"
162 "pinnumber=1\n"
163 "T 100 150 5 8 0 0 0 0 1\n"
164 "pinseq=1\n"
165 "T 100 150 5 8 0 1 0 0 1\n"
166 "pinlabel=1\n"
167 "T 100 150 5 8 0 1 0 0 1\n"
168 "pintype=pas\n"
169 "}\n"
170 "L 201 200 150 100 3 0 0 0 -1 -1\n"
171 "T 200 300 8 10 1 1 0 0 1\n"
172 "refdes=R?\n" ) },
173
174 { wxT( "resistor-2.sym" ),
175 wxT( "v 20031231 1\n"
176 "B 200 0 600 200 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
177 "T 300 400 5 10 0 0 0 0 1\n"
178 "device=RESISTOR\n"
179 "P 900 100 800 100 1 0 0\n"
180 "{\n"
181 "T 800 150 5 8 0 1 0 0 1\n"
182 "pinnumber=2\n"
183 "T 800 150 5 8 0 0 0 0 1\n"
184 "pinseq=2\n"
185 "T 800 150 5 8 0 1 0 0 1\n"
186 "pinlabel=2\n"
187 "T 800 150 5 8 0 1 0 0 1\n"
188 "pintype=pas\n"
189 "}\n"
190 "P 0 100 200 100 1 0 0\n"
191 "{\n"
192 "T 100 150 5 8 0 1 0 0 1\n"
193 "pinnumber=1\n"
194 "T 100 150 5 8 0 0 0 0 1\n"
195 "pinseq=1\n"
196 "T 100 150 5 8 0 1 0 0 1\n"
197 "pinlabel=1\n"
198 "T 100 150 5 8 0 1 0 0 1\n"
199 "pintype=pas\n"
200 "}\n"
201 "T 200 300 8 10 1 1 0 0 1\n"
202 "refdes=R?\n" ) },
203
204 { wxT( "capacitor-1.sym" ),
205 wxT( "v 20050820 1\n"
206 "P 0 200 200 200 1 0 0\n"
207 "{\n"
208 "T 150 250 5 8 0 1 0 6 1\n"
209 "pinnumber=1\n"
210 "T 150 150 5 8 0 1 0 8 1\n"
211 "pinseq=1\n"
212 "T 200 200 9 8 0 1 0 0 1\n"
213 "pinlabel=1\n"
214 "T 200 200 5 8 0 1 0 2 1\n"
215 "pintype=pas\n"
216 "}\n"
217 "P 900 200 700 200 1 0 0\n"
218 "{\n"
219 "T 750 250 5 8 0 1 0 0 1\n"
220 "pinnumber=2\n"
221 "T 750 150 5 8 0 1 0 2 1\n"
222 "pinseq=2\n"
223 "T 700 200 9 8 0 1 0 6 1\n"
224 "pinlabel=2\n"
225 "T 700 200 5 8 0 1 0 8 1\n"
226 "pintype=pas\n"
227 "}\n"
228 "L 400 400 400 0 3 0 0 0 -1 -1\n"
229 "L 500 400 500 0 3 0 0 0 -1 -1\n"
230 "L 700 200 500 200 3 0 0 0 -1 -1\n"
231 "L 400 200 200 200 3 0 0 0 -1 -1\n"
232 "T 200 700 5 10 0 0 0 0 1\n"
233 "device=CAPACITOR\n"
234 "T 200 500 8 10 1 1 0 0 1\n"
235 "refdes=C?\n" ) },
236
237 { wxT( "capacitor-2.sym" ),
238 wxT( "v 20050820 1\n"
239 "P 0 200 200 200 1 0 0\n"
240 "{\n"
241 "T 150 250 5 8 1 1 0 6 1\n"
242 "pinnumber=1\n"
243 "T 200 150 5 8 0 1 0 8 1\n"
244 "pinseq=1\n"
245 "T 250 200 9 8 0 1 0 0 1\n"
246 "pinlabel=+\n"
247 "T 250 200 5 8 0 1 0 2 1\n"
248 "pintype=pas\n"
249 "}\n"
250 "P 900 200 700 200 1 0 0\n"
251 "{\n"
252 "T 750 250 5 8 1 1 0 0 1\n"
253 "pinnumber=2\n"
254 "T 700 150 5 8 0 1 0 2 1\n"
255 "pinseq=2\n"
256 "T 650 200 9 8 0 1 0 6 1\n"
257 "pinlabel=-\n"
258 "T 650 200 5 8 0 1 0 8 1\n"
259 "pintype=pas\n"
260 "}\n"
261 "L 400 400 400 0 3 0 0 0 -1 -1\n"
262 "A 1200 200 700 165 30 3 0 0 0 -1 -1\n"
263 "L 700 200 500 200 3 0 0 0 -1 -1\n"
264 "L 400 200 200 200 3 0 0 0 -1 -1\n"
265 "L 289 400 289 300 3 0 0 0 -1 -1\n"
266 "L 340 349 240 349 3 0 0 0 -1 -1\n"
267 "T 200 700 5 10 0 0 0 0 1\n"
268 "device=POLARIZED_CAPACITOR\n"
269 "T 200 500 8 10 1 1 0 0 1\n"
270 "refdes=C?\n" ) },
271
272 { wxT( "gnd-1.sym" ),
273 wxT( "v 20031231 1\n"
274 "P 100 100 100 300 1 0 1\n"
275 "{\n"
276 "T 158 161 5 4 0 1 0 0 1\n"
277 "pinnumber=1\n"
278 "T 158 161 5 4 0 0 0 0 1\n"
279 "pinseq=1\n"
280 "T 158 161 5 4 0 1 0 0 1\n"
281 "pinlabel=1\n"
282 "T 158 161 5 4 0 1 0 0 1\n"
283 "pintype=pwr\n"
284 "}\n"
285 "L 0 100 200 100 3 0 0 0 -1 -1\n"
286 "L 55 50 145 50 3 0 0 0 -1 -1\n"
287 "L 80 10 120 10 3 0 0 0 -1 -1\n"
288 "T 300 50 8 10 0 0 0 0 1\n"
289 "net=GND:1\n" ) },
290
291 { wxT( "generic-power.sym" ),
292 wxT( "v 20031231 1\n"
293 "P 200 0 200 200 1 0 0\n"
294 "{\n"
295 "T 250 50 5 6 0 1 0 0 1\n"
296 "pinnumber=1\n"
297 "T 250 50 5 6 0 0 0 0 1\n"
298 "pinseq=1\n"
299 "T 250 50 5 6 0 1 0 0 1\n"
300 "pinlabel=1\n"
301 "T 250 50 5 6 0 1 0 0 1\n"
302 "pintype=pwr\n"
303 "}\n"
304 "L 50 200 350 200 3 0 0 0 -1 -1\n"
305 "T 200 250 8 10 1 1 0 3 1\n"
306 "net=Vcc:1\n" ) },
307
308 { wxT( "input-1.sym" ),
309 wxT( "v 20031231 1\n"
310 "P 600 100 800 100 1 0 1\n"
311 "{\n"
312 "T 450 50 5 6 0 1 0 0 1\n"
313 "pinnumber=1\n"
314 "T 450 50 5 6 0 0 0 0 1\n"
315 "pinseq=1\n"
316 "}\n"
317 "L 0 200 0 0 3 0 0 0 -1 -1\n"
318 "L 0 200 500 200 3 0 0 0 -1 -1\n"
319 "L 500 200 600 100 3 0 0 0 -1 -1\n"
320 "L 600 100 500 0 3 0 0 0 -1 -1\n"
321 "L 500 0 0 0 3 0 0 0 -1 -1\n"
322 "T 0 300 5 10 0 0 0 0 1\n"
323 "device=INPUT\n" ) },
324
325 { wxT( "output-1.sym" ),
326 wxT( "v 20031231 1\n"
327 "P 0 100 200 100 1 0 0\n"
328 "{\n"
329 "T 250 50 5 6 0 1 0 0 1\n"
330 "pinnumber=1\n"
331 "T 250 50 5 6 0 0 0 0 1\n"
332 "pinseq=1\n"
333 "}\n"
334 "L 200 200 200 0 3 0 0 0 -1 -1\n"
335 "L 200 200 700 200 3 0 0 0 -1 -1\n"
336 "L 700 200 800 100 3 0 0 0 -1 -1\n"
337 "L 800 100 700 0 3 0 0 0 -1 -1\n"
338 "L 700 0 200 0 3 0 0 0 -1 -1\n"
339 "T 100 300 5 10 0 0 0 0 1\n"
340 "device=OUTPUT\n" ) },
341
342 { wxT( "nc-right-1.sym" ),
343 wxT( "v 20060123 1\n"
344 "P 0 100 200 100 1 0 0\n"
345 "{\n"
346 "T 600 100 5 10 0 0 180 8 1\n"
347 "pinseq=1\n"
348 "T 600 300 5 10 0 0 180 8 1\n"
349 "pinnumber=1\n"
350 "}\n"
351 "L 200 0 200 200 3 0 0 0 -1 -1\n"
352 "T 100 500 8 10 0 0 0 0 1\n"
353 "value=NoConnection\n"
354 "T 250 100 9 10 1 0 0 1 1\n"
355 "NC\n"
356 "T 0 600 8 10 0 0 0 0 1\n"
357 "device=DRC_Directive\n"
358 "T 100 900 8 10 0 0 0 0 1\n"
359 "graphical=1\n" ) },
360
361 { wxT( "nc-left-1.sym" ),
362 wxT( "v 20060123 1\n"
363 "P 400 100 200 100 1 0 1\n"
364 "{\n"
365 "T 0 100 5 10 0 0 0 0 1\n"
366 "pinseq=1\n"
367 "T 0 300 5 10 0 0 0 0 1\n"
368 "pinnumber=1\n"
369 "}\n"
370 "L 200 0 200 200 3 0 0 0 -1 -1\n"
371 "T 100 500 8 10 0 0 0 0 1\n"
372 "value=NoConnection\n"
373 "T 50 100 9 10 1 0 0 5 1\n"
374 "NC\n"
375 "T 0 600 8 10 0 0 0 0 1\n"
376 "device=DRC_Directive\n"
377 "T 100 900 8 10 0 0 0 0 1\n"
378 "graphical=1\n" ) },
379
380 { wxT( "terminal-1.sym" ),
381 wxT( "v 20041228 1\n"
382 "T 310 750 8 10 0 0 0 0 1\n"
383 "device=terminal\n"
384 "P 560 100 900 100 1 0 1\n"
385 "{\n"
386 "T 660 150 5 10 0 0 0 0 1\n"
387 "pinseq=1\n"
388 "T 510 100 5 10 0 1 0 0 1\n"
389 "pinnumber=1\n"
390 "T 510 300 5 10 0 1 0 0 1\n"
391 "pintype=pas\n"
392 "T 800 100 5 10 0 1 0 0 1\n"
393 "pinlabel=terminal\n"
394 "}\n"
395 "V 460 100 100 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
396 "T 250 50 8 10 1 1 0 6 1\n"
397 "refdes=T?\n" ) },
398
399 // Power symbols
400 { wxT( "vcc-1.sym" ),
401 wxT( "v 20031231 1\n"
402 "P 200 0 200 200 1 0 0\n"
403 "{\n"
404 "T 250 50 5 6 0 1 0 0 1\n"
405 "pinnumber=1\n"
406 "T 250 50 5 6 0 0 0 0 1\n"
407 "pinseq=1\n"
408 "T 250 50 5 6 0 1 0 0 1\n"
409 "pinlabel=1\n"
410 "T 250 50 5 6 0 1 0 0 1\n"
411 "pintype=pwr\n"
412 "}\n"
413 "L 50 200 350 200 3 0 0 0 -1 -1\n"
414 "T 75 250 9 8 1 0 0 0 1\n"
415 "Vcc\n"
416 "T 450 200 8 10 0 0 0 0 1\n"
417 "net=Vcc:1\n" ) },
418
419 { wxT( "vdd-1.sym" ),
420 wxT( "v 20031231 1\n"
421 "P 200 0 200 200 1 0 0\n"
422 "{\n"
423 "T 250 50 5 6 0 1 0 0 1\n"
424 "pinnumber=1\n"
425 "T 250 50 5 6 0 0 0 0 1\n"
426 "pinseq=1\n"
427 "T 250 50 5 6 0 1 0 0 1\n"
428 "pinlabel=1\n"
429 "T 250 50 5 6 0 1 0 0 1\n"
430 "pintype=pwr\n"
431 "}\n"
432 "L 50 200 350 200 3 0 0 0 -1 -1\n"
433 "T 75 250 9 8 1 0 0 0 1\n"
434 "Vdd\n"
435 "T 450 200 8 10 0 0 0 0 1\n"
436 "net=Vdd:1\n" ) },
437
438 { wxT( "5V-plus-1.sym" ),
439 wxT( "v 20031231 1\n"
440 "P 200 0 200 200 1 0 0\n"
441 "{\n"
442 "T 250 50 5 6 0 1 0 0 1\n"
443 "pinnumber=1\n"
444 "T 250 50 5 6 0 0 0 0 1\n"
445 "pinseq=1\n"
446 "T 250 50 5 6 0 1 0 0 1\n"
447 "pinlabel=1\n"
448 "T 250 50 5 6 0 1 0 0 1\n"
449 "pintype=pwr\n"
450 "}\n"
451 "L 50 200 350 200 3 0 0 0 -1 -1\n"
452 "T 75 250 9 8 1 0 0 0 1\n"
453 "+5V\n"
454 "T 300 0 8 8 0 0 0 0 1\n"
455 "net=+5V:1\n" ) },
456
457 { wxT( "3.3V-plus-1.sym" ),
458 wxT( "v 20031231 1\n"
459 "P 200 0 200 200 1 0 0\n"
460 "{\n"
461 "T 250 50 5 6 0 1 0 0 1\n"
462 "pinnumber=1\n"
463 "T 250 50 5 6 0 0 0 0 1\n"
464 "pinseq=1\n"
465 "T 250 50 5 6 0 1 0 0 1\n"
466 "pinlabel=1\n"
467 "T 250 50 5 6 0 1 0 0 1\n"
468 "pintype=pwr\n"
469 "}\n"
470 "L 50 200 350 200 3 0 0 0 -1 -1\n"
471 "T 75 250 9 8 1 0 0 0 1\n"
472 "+3.3V\n"
473 "T 300 0 8 8 0 0 0 0 1\n"
474 "net=+3.3V:1\n" ) },
475
476 { wxT( "12V-plus-1.sym" ),
477 wxT( "v 20031231 1\n"
478 "P 200 0 200 200 1 0 0\n"
479 "{\n"
480 "T 250 50 5 6 0 1 0 0 1\n"
481 "pinnumber=1\n"
482 "T 250 50 5 6 0 0 0 0 1\n"
483 "pinseq=1\n"
484 "T 250 50 5 6 0 1 0 0 1\n"
485 "pinlabel=1\n"
486 "T 250 50 5 6 0 1 0 0 1\n"
487 "pintype=pwr\n"
488 "}\n"
489 "L 50 200 350 200 3 0 0 0 -1 -1\n"
490 "T 75 250 9 8 1 0 0 0 1\n"
491 "+12V\n"
492 "T 300 0 9 8 0 0 0 0 1\n"
493 "net=+12V:1\n" ) },
494
495 { wxT( "vcc-2.sym" ),
496 wxT( "v 20031231 1\n"
497 "V 200 350 50 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
498 "P 200 300 200 0 1 0 1\n"
499 "{\n"
500 "T 300 50 5 10 0 1 0 0 1\n"
501 "pinnumber=1\n"
502 "T 300 50 5 10 0 0 0 0 1\n"
503 "pinseq=1\n"
504 "T 300 50 5 10 0 1 0 0 1\n"
505 "pinlabel=1\n"
506 "T 300 50 5 10 0 1 0 0 1\n"
507 "pintype=pwr\n"
508 "}\n"
509 "T 0 450 9 10 1 0 0 0 1\n"
510 "Vcc\n"
511 "T 400 300 8 8 0 0 0 0 1\n"
512 "net=Vcc:1\n" ) },
513
514 { wxT( "vss-1.sym" ),
515 wxT( "v 20031231 1\n"
516 "P 200 0 200 200 1 0 0\n"
517 "{\n"
518 "T 250 50 5 6 0 1 0 0 1\n"
519 "pinnumber=1\n"
520 "T 250 50 5 6 0 0 0 0 1\n"
521 "pinseq=1\n"
522 "T 250 50 5 6 0 1 0 0 1\n"
523 "pinlabel=1\n"
524 "T 250 50 5 6 0 1 0 0 1\n"
525 "pintype=pwr\n"
526 "}\n"
527 "L 50 200 350 200 3 0 0 0 -1 -1\n"
528 "T 75 250 9 8 1 0 0 0 1\n"
529 "Vss\n"
530 "T 450 200 8 10 0 0 0 0 1\n"
531 "net=Vss:1\n" ) },
532
533 { wxT( "vee-1.sym" ),
534 wxT( "v 20031231 1\n"
535 "P 200 0 200 200 1 0 0\n"
536 "{\n"
537 "T 250 50 5 6 0 1 0 0 1\n"
538 "pinnumber=1\n"
539 "T 250 50 5 6 0 0 0 0 1\n"
540 "pinseq=1\n"
541 "T 250 50 5 6 0 1 0 0 1\n"
542 "pinlabel=1\n"
543 "T 250 50 5 6 0 1 0 0 1\n"
544 "pintype=pwr\n"
545 "}\n"
546 "L 50 200 350 200 3 0 0 0 -1 -1\n"
547 "T 75 250 9 8 1 0 0 0 1\n"
548 "Vee\n"
549 "T 450 200 8 10 0 0 0 0 1\n"
550 "net=Vee:1\n" ) },
551
552 { wxT( "gnd-2.sym" ),
553 wxT( "v 20031231 1\n"
554 "P 100 100 100 300 1 0 1\n"
555 "{\n"
556 "T 158 161 5 4 0 1 0 0 1\n"
557 "pinnumber=1\n"
558 "T 158 161 5 4 0 0 0 0 1\n"
559 "pinseq=1\n"
560 "T 300 200 3 10 1 1 0 0 1\n"
561 "pinlabel=PGND\n"
562 "T 158 161 5 4 0 1 0 0 1\n"
563 "pintype=pwr\n"
564 "}\n"
565 "L 0 100 200 100 3 0 0 0 -1 -1\n"
566 "L 55 50 145 50 3 0 0 0 -1 -1\n"
567 "L 80 10 120 10 3 0 0 0 -1 -1\n"
568 "T 300 50 8 10 0 0 0 0 1\n"
569 "net=PGND:1\n" ) },
570
571 // Diodes
572 { wxT( "diode-1.sym" ),
573 wxT( "v 20031231 1\n"
574 "L 300 400 300 0 3 0 0 0 -1 -1\n"
575 "L 300 400 600 200 3 0 0 0 -1 -1\n"
576 "T 400 600 5 10 0 0 0 0 1\n"
577 "device=DIODE\n"
578 "L 600 200 300 0 3 0 0 0 -1 -1\n"
579 "L 600 400 600 0 3 0 0 0 -1 -1\n"
580 "P 0 200 200 200 1 0 0\n"
581 "{\n"
582 "T 100 250 5 8 0 1 0 0 1\n"
583 "pinnumber=1\n"
584 "T 100 250 5 8 0 0 0 0 1\n"
585 "pinseq=1\n"
586 "T 100 250 5 8 0 1 0 0 1\n"
587 "pinlabel=1\n"
588 "T 100 250 5 8 0 1 0 0 1\n"
589 "pintype=pas\n"
590 "}\n"
591 "P 900 200 700 200 1 0 0\n"
592 "{\n"
593 "T 700 250 5 8 0 1 0 0 1\n"
594 "pinnumber=2\n"
595 "T 700 250 5 8 0 0 0 0 1\n"
596 "pinseq=2\n"
597 "T 700 250 5 8 0 1 0 0 1\n"
598 "pinlabel=2\n"
599 "T 700 250 5 8 0 1 0 0 1\n"
600 "pintype=pas\n"
601 "}\n"
602 "L 700 200 600 200 3 0 0 0 -1 -1\n"
603 "L 300 200 200 200 3 0 0 0 -1 -1\n"
604 "T 300 500 8 10 1 1 0 0 1\n"
605 "refdes=D?\n" ) },
606
607 { wxT( "zener-1.sym" ),
608 wxT( "v 20031231 1\n"
609 "L 300 400 300 0 3 0 0 0 -1 -1\n"
610 "L 600 200 300 0 3 0 0 0 -1 -1\n"
611 "L 600 200 300 400 3 0 0 0 -1 -1\n"
612 "T 400 600 5 10 0 0 0 0 1\n"
613 "device=ZENER_DIODE\n"
614 "L 600 400 600 0 3 0 0 0 -1 -1\n"
615 "P 900 200 700 200 1 0 0\n"
616 "{\n"
617 "T 700 250 5 8 0 1 0 0 1\n"
618 "pinnumber=2\n"
619 "T 700 250 5 8 0 0 0 0 1\n"
620 "pinseq=2\n"
621 "T 700 250 5 8 0 1 0 0 1\n"
622 "pinlabel=2\n"
623 "T 700 250 5 8 0 1 0 0 1\n"
624 "pintype=pas\n"
625 "}\n"
626 "P 200 200 0 200 1 0 1\n"
627 "{\n"
628 "T 100 250 5 8 0 1 0 0 1\n"
629 "pinnumber=1\n"
630 "T 100 250 5 8 0 0 0 0 1\n"
631 "pinseq=1\n"
632 "T 100 250 5 8 0 1 0 0 1\n"
633 "pinlabel=1\n"
634 "T 100 250 5 8 0 1 0 0 1\n"
635 "pintype=pas\n"
636 "}\n"
637 "L 700 200 600 200 3 0 0 0 -1 -1\n"
638 "L 300 200 200 200 3 0 0 0 -1 -1\n"
639 "L 600 400 500 400 3 0 0 0 -1 -1\n"
640 "L 600 0 700 0 3 0 0 0 -1 -1\n"
641 "T 300 500 8 10 1 1 0 0 1\n"
642 "refdes=Z?\n" ) },
643
644 { wxT( "schottky-1.sym" ),
645 wxT( "v 20041228 1\n"
646 "L 300 400 300 0 3 0 0 0 -1 -1\n"
647 "L 300 400 600 200 3 0 0 0 -1 -1\n"
648 "T 322 672 8 10 0 0 0 0 1\n"
649 "device=DIODE\n"
650 "L 600 200 300 0 3 0 0 0 -1 -1\n"
651 "L 600 400 600 0 3 0 0 0 -1 -1\n"
652 "P 0 200 200 200 1 0 0\n"
653 "{\n"
654 "T 205 256 5 8 0 1 0 0 1\n"
655 "pinnumber=2\n"
656 "T 170 286 5 8 0 0 90 0 1\n"
657 "pinseq=2\n"
658 "T -10 81 5 10 0 1 0 0 1\n"
659 "pintype=pas\n"
660 "T 75 288 5 10 0 1 90 0 1\n"
661 "pinlabel=anode\n"
662 "}\n"
663 "P 900 200 700 200 1 0 0\n"
664 "{\n"
665 "T 700 250 5 8 0 1 0 0 1\n"
666 "pinnumber=1\n"
667 "T 730 50 5 8 0 0 0 0 1\n"
668 "pinseq=1\n"
669 "T 798 265 5 10 0 1 0 0 1\n"
670 "pintype=pas\n"
671 "T 931 138 5 10 0 1 0 0 1\n"
672 "pinlabel=cathode\n"
673 "}\n"
674 "L 700 200 600 200 3 0 0 0 -1 -1\n"
675 "L 300 200 200 200 3 0 0 0 -1 -1\n"
676 "A 650 400 50 0 180 3 0 0 0 -1 -1\n"
677 "A 550 0 50 180 180 3 0 0 0 -1 -1\n"
678 "T 300 500 8 10 1 1 0 0 1\n"
679 "refdes=D?\n"
680 "T 567 515 8 10 0 0 0 0 1\n"
681 "numslots=0\n" ) },
682
683 { wxT( "led-1.sym" ),
684 wxT( "v 20210407 2\n"
685 "P 0 200 200 200 1 0 0\n"
686 "{\n"
687 "T 150 250 5 8 1 1 0 6 1\n"
688 "pinnumber=1\n"
689 "T 150 150 5 8 0 1 0 8 1\n"
690 "pinseq=1\n"
691 "T 250 200 9 8 0 1 0 0 1\n"
692 "pinlabel=A\n"
693 "T 250 200 5 8 0 1 0 2 1\n"
694 "pintype=pas\n"
695 "}\n"
696 "P 900 200 700 200 1 0 0\n"
697 "{\n"
698 "T 750 250 5 8 1 1 0 0 1\n"
699 "pinnumber=2\n"
700 "T 750 150 5 8 0 1 0 2 1\n"
701 "pinseq=2\n"
702 "T 650 200 9 8 0 1 0 6 1\n"
703 "pinlabel=K\n"
704 "T 650 200 5 8 0 1 0 8 1\n"
705 "pintype=pas\n"
706 "}\n"
707 "L 400 300 500 200 3 0 0 0 -1 -1\n"
708 "L 500 200 400 100 3 0 0 0 -1 -1\n"
709 "L 400 300 400 100 3 0 0 0 -1 -1\n"
710 "L 500 300 500 100 3 0 0 0 -1 -1\n"
711 "L 500 200 700 200 3 0 0 0 -1 -1\n"
712 "L 400 200 200 200 3 0 0 0 -1 -1\n"
713 "V 450 200 200 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
714 "T 800 600 5 10 0 0 0 0 1\n"
715 "device=LED\n"
716 "T 800 400 8 10 1 1 0 0 1\n"
717 "refdes=LED?\n"
718 "T 800 1000 5 10 0 0 0 0 1\n"
719 "numslots=0\n"
720 "T 800 800 5 10 0 0 0 0 1\n"
721 "symversion=0.2\n" ) },
722
723 // Transistors
724 { wxT( "npn-1.sym" ),
725 wxT( "v 20210407 2\n"
726 "L 200 800 200 200 3 0 0 0 -1 -1\n"
727 "T 600 500 5 10 0 0 0 0 1\n"
728 "device=NPN_TRANSISTOR\n"
729 "L 500 800 200 500 3 0 0 0 -1 -1\n"
730 "L 200 500 500 200 3 0 0 0 -1 -1\n"
731 "H 3 0 0 0 -1 -1 1 -1 -1 -1 -1 -1 5\n"
732 "M 410,240\n"
733 "L 501,200\n"
734 "L 455,295\n"
735 "L 435,265\n"
736 "z\n"
737 "P 0 500 200 500 1 0 0\n"
738 "{\n"
739 "T 100 550 5 6 1 1 0 0 1\n"
740 "pinnumber=B\n"
741 "T 100 550 5 6 0 0 0 0 1\n"
742 "pinseq=2\n"
743 "T 100 550 5 6 0 1 0 0 1\n"
744 "pinlabel=B\n"
745 "T 100 550 5 6 0 1 0 0 1\n"
746 "pintype=pas\n"
747 "}\n"
748 "P 500 1000 500 800 1 0 0\n"
749 "{\n"
750 "T 400 850 5 6 1 1 0 0 1\n"
751 "pinnumber=C\n"
752 "T 400 850 5 6 0 0 0 0 1\n"
753 "pinseq=1\n"
754 "T 400 850 5 6 0 1 0 0 1\n"
755 "pinlabel=C\n"
756 "T 400 850 5 6 0 1 0 0 1\n"
757 "pintype=pas\n"
758 "}\n"
759 "P 500 200 500 0 1 0 1\n"
760 "{\n"
761 "T 400 50 5 6 1 1 0 0 1\n"
762 "pinnumber=E\n"
763 "T 400 50 5 6 0 0 0 0 1\n"
764 "pinseq=3\n"
765 "T 400 50 5 6 0 1 0 0 1\n"
766 "pinlabel=E\n"
767 "T 400 50 5 6 0 1 0 0 1\n"
768 "pintype=pas\n"
769 "}\n"
770 "T 600 500 8 10 1 1 0 0 1\n"
771 "refdes=Q?\n" ) },
772
773 { wxT( "pnp-1.sym" ),
774 wxT( "v 20210407 2\n"
775 "L 200 800 200 200 3 0 0 0 -1 -1\n"
776 "T 600 500 5 10 0 0 0 0 1\n"
777 "device=PNP_TRANSISTOR\n"
778 "L 500 800 200 500 3 0 0 0 -1 -1\n"
779 "L 200 500 500 200 3 0 0 0 -1 -1\n"
780 "P 0 500 200 500 1 0 0\n"
781 "{\n"
782 "T 100 550 5 6 1 1 0 0 1\n"
783 "pinnumber=B\n"
784 "T 100 550 5 6 0 0 0 0 1\n"
785 "pinseq=2\n"
786 "T 100 550 5 6 0 1 0 0 1\n"
787 "pinlabel=B\n"
788 "T 100 550 5 6 0 1 0 0 1\n"
789 "pintype=pas\n"
790 "}\n"
791 "P 500 1000 500 800 1 0 0\n"
792 "{\n"
793 "T 400 850 5 6 1 1 0 0 1\n"
794 "pinnumber=C\n"
795 "T 400 850 5 6 0 0 0 0 1\n"
796 "pinseq=1\n"
797 "T 400 850 5 6 0 1 0 0 1\n"
798 "pinlabel=C\n"
799 "T 400 850 5 6 0 1 0 0 1\n"
800 "pintype=pas\n"
801 "}\n"
802 "P 500 200 500 0 1 0 1\n"
803 "{\n"
804 "T 400 50 5 6 1 1 0 0 1\n"
805 "pinnumber=E\n"
806 "T 400 50 5 6 0 0 0 0 1\n"
807 "pinseq=3\n"
808 "T 400 50 5 6 0 1 0 0 1\n"
809 "pinlabel=E\n"
810 "T 400 50 5 6 0 1 0 0 1\n"
811 "pintype=pas\n"
812 "}\n"
813 "T 600 500 8 10 1 1 0 0 1\n"
814 "refdes=Q?\n"
815 "H 3 0 0 0 -1 -1 1 -1 -1 -1 -1 -1 5\n"
816 "M 340,290\n"
817 "L 300,401\n"
818 "L 395,355\n"
819 "L 365,335\n"
820 "z\n" ) },
821
822 { wxT( "nmos-1.sym" ),
823 wxT( "v 20210407 2\n"
824 "L 300 600 300 200 3 0 0 0 -1 -1\n"
825 "T 700 800 5 10 0 0 0 0 1\n"
826 "device=NMOS_TRANSISTOR\n"
827 "L 200 600 200 200 3 0 0 0 -1 -1\n"
828 "L 300 600 500 600 3 0 0 0 -1 -1\n"
829 "L 300 200 500 200 3 0 0 0 -1 -1\n"
830 "L 300 400 500 400 3 0 0 0 -1 -1\n"
831 "L 300 400 400 500 3 0 0 0 -1 -1\n"
832 "L 300 400 400 300 3 0 0 0 -1 -1\n"
833 "P 0 400 200 400 1 0 0\n"
834 "{\n"
835 "T 150 450 5 8 0 1 0 6 1\n"
836 "pinnumber=G\n"
837 "T 150 350 5 8 0 1 0 8 1\n"
838 "pinseq=2\n"
839 "T 200 400 9 8 0 1 0 0 1\n"
840 "pinlabel=G\n"
841 "T 200 400 5 8 0 1 0 2 1\n"
842 "pintype=in\n"
843 "}\n"
844 "P 500 600 500 800 1 0 1\n"
845 "{\n"
846 "T 550 650 5 8 0 1 0 0 1\n"
847 "pinnumber=D\n"
848 "T 550 650 5 8 0 1 0 2 1\n"
849 "pinseq=1\n"
850 "T 500 600 9 8 0 1 0 5 1\n"
851 "pinlabel=D\n"
852 "T 500 400 5 8 0 1 0 5 1\n"
853 "pintype=pas\n"
854 "}\n"
855 "P 500 200 500 0 1 0 1\n"
856 "{\n"
857 "T 550 50 5 8 0 1 0 0 1\n"
858 "pinnumber=S\n"
859 "T 550 50 5 8 0 1 0 2 1\n"
860 "pinseq=3\n"
861 "T 500 200 9 8 0 1 0 3 1\n"
862 "pinlabel=S\n"
863 "T 500 500 5 8 0 1 0 3 1\n"
864 "pintype=pas\n"
865 "}\n"
866 "T 700 600 8 10 1 1 0 0 1\n"
867 "refdes=Q?\n"
868 "T 700 1000 5 10 0 0 0 0 1\n"
869 "symversion=0.2\n" ) },
870
871 { wxT( "pmos-1.sym" ),
872 wxT( "v 20210407 2\n"
873 "L 300 600 300 200 3 0 0 0 -1 -1\n"
874 "T 600 200 5 10 0 0 0 0 1\n"
875 "device=PMOS_TRANSISTOR\n"
876 "L 200 600 200 200 3 0 0 0 -1 -1\n"
877 "L 300 600 500 600 3 0 0 0 -1 -1\n"
878 "L 300 200 500 200 3 0 0 0 -1 -1\n"
879 "L 300 400 500 400 3 0 0 0 -1 -1\n"
880 "L 500 400 400 300 3 0 0 0 -1 -1\n"
881 "L 500 400 400 500 3 0 0 0 -1 -1\n"
882 "P 500 600 500 800 1 0 1\n"
883 "{\n"
884 "T 300 700 5 10 0 1 0 0 1\n"
885 "pinnumber=D\n"
886 "T 300 700 5 10 0 0 0 0 1\n"
887 "pinseq=1\n"
888 "T 300 700 5 10 0 1 0 0 1\n"
889 "pinlabel=D\n"
890 "T 300 700 5 10 0 1 0 0 1\n"
891 "pintype=pas\n"
892 "}\n"
893 "P 500 200 500 0 1 0 1\n"
894 "{\n"
895 "T 300 0 5 10 0 1 0 0 1\n"
896 "pinnumber=S\n"
897 "T 300 0 5 10 0 0 0 0 1\n"
898 "pinseq=3\n"
899 "T 300 0 5 10 0 1 0 0 1\n"
900 "pinlabel=S\n"
901 "T 300 0 5 10 0 1 0 0 1\n"
902 "pintype=pas\n"
903 "}\n"
904 "P 200 400 0 400 1 0 1\n"
905 "{\n"
906 "T 0 500 5 10 0 1 0 0 1\n"
907 "pinnumber=G\n"
908 "T 0 500 5 10 0 0 0 0 1\n"
909 "pinseq=2\n"
910 "T 0 500 5 10 0 1 0 0 1\n"
911 "pinlabel=G\n"
912 "T 0 500 5 10 0 1 0 0 1\n"
913 "pintype=pas\n"
914 "}\n"
915 "T 700 600 8 10 1 1 0 0 1\n"
916 "refdes=Q?\n"
917 "T 1200 500 8 10 0 0 0 0 1\n"
918 "symversion=0.1\n" ) },
919
920 // Analog
921 { wxT( "opamp-1.sym" ),
922 wxT( "v 20050820 1\n"
923 "L 200 800 200 0 3 0 0 0 -1 -1\n"
924 "L 200 800 800 400 3 0 0 0 -1 -1\n"
925 "T 700 800 5 10 0 0 0 0 1\n"
926 "device=OPAMP\n"
927 "L 800 400 200 0 3 0 0 0 -1 -1\n"
928 "L 300 650 300 550 3 0 0 0 -1 -1\n"
929 "L 250 600 350 600 3 0 0 0 -1 -1\n"
930 "L 250 200 350 200 3 0 0 0 -1 -1\n"
931 "P 0 600 200 600 1 0 0\n"
932 "{\n"
933 "T 150 650 5 8 1 1 0 6 1\n"
934 "pinnumber=1\n"
935 "T 150 550 5 8 0 1 0 8 1\n"
936 "pinseq=1\n"
937 "T 250 600 9 8 0 1 0 0 1\n"
938 "pinlabel=in+\n"
939 "T 250 600 5 8 0 1 0 2 1\n"
940 "pintype=in\n"
941 "}\n"
942 "P 0 200 200 200 1 0 0\n"
943 "{\n"
944 "T 150 250 5 8 1 1 0 6 1\n"
945 "pinnumber=2\n"
946 "T 150 150 5 8 0 1 0 8 1\n"
947 "pinseq=2\n"
948 "T 250 200 9 8 0 1 0 0 1\n"
949 "pinlabel=in-\n"
950 "T 250 200 5 8 0 1 0 2 1\n"
951 "pintype=in\n"
952 "}\n"
953 "P 800 400 1000 400 1 0 1\n"
954 "{\n"
955 "T 800 450 5 8 1 1 0 0 1\n"
956 "pinnumber=5\n"
957 "T 800 350 5 8 0 1 0 2 1\n"
958 "pinseq=5\n"
959 "T 750 400 9 8 0 1 0 6 1\n"
960 "pinlabel=out\n"
961 "T 750 400 5 8 0 1 0 8 1\n"
962 "pintype=out\n"
963 "}\n"
964 "P 500 600 500 800 1 0 1\n"
965 "{\n"
966 "T 550 600 5 8 1 1 0 0 1\n"
967 "pinnumber=3\n"
968 "T 550 600 5 8 0 1 0 2 1\n"
969 "pinseq=3\n"
970 "T 500 600 9 8 0 1 0 5 1\n"
971 "pinlabel=V+\n"
972 "T 500 550 5 8 0 1 0 5 1\n"
973 "pintype=pwr\n"
974 "}\n"
975 "P 500 200 500 0 1 0 1\n"
976 "{\n"
977 "T 550 100 5 8 1 1 0 0 1\n"
978 "pinnumber=4\n"
979 "T 550 100 5 8 0 1 0 2 1\n"
980 "pinseq=4\n"
981 "T 500 200 9 8 0 1 0 3 1\n"
982 "pinlabel=V-\n"
983 "T 500 300 5 8 0 1 0 3 1\n"
984 "pintype=pwr\n"
985 "}\n"
986 "T 700 600 8 10 1 1 0 0 1\n"
987 "refdes=U?\n"
988 "T 700 1000 5 10 0 0 0 0 1\n"
989 "numslots=0\n"
990 "T 700 1400 5 10 0 0 0 0 1\n"
991 "symversion=0.1\n" ) },
992
993 { wxT( "inductor-1.sym" ),
994 wxT( "v 20210407 2\n"
995 "P 900 100 750 100 1 0 0\n"
996 "{\n"
997 "T 800 150 5 8 0 1 0 0 1\n"
998 "pinnumber=2\n"
999 "T 800 50 5 8 0 1 0 2 1\n"
1000 "pinseq=2\n"
1001 "T 700 100 9 8 0 1 0 6 1\n"
1002 "pinlabel=2\n"
1003 "T 700 100 5 8 0 1 0 8 1\n"
1004 "pintype=pas\n"
1005 "}\n"
1006 "P 0 100 150 100 1 0 0\n"
1007 "{\n"
1008 "T 100 150 5 8 0 1 0 6 1\n"
1009 "pinnumber=1\n"
1010 "T 100 50 5 8 0 1 0 8 1\n"
1011 "pinseq=1\n"
1012 "T 200 100 9 8 0 1 0 0 1\n"
1013 "pinlabel=1\n"
1014 "T 200 100 5 8 0 1 0 2 1\n"
1015 "pintype=pas\n"
1016 "}\n"
1017 "A 237 100 75 0 180 3 0 0 0 -1 -1\n"
1018 "A 379 100 75 0 180 3 0 0 0 -1 -1\n"
1019 "A 521 100 75 0 180 3 0 0 0 -1 -1\n"
1020 "A 663 100 75 0 180 3 0 0 0 -1 -1\n"
1021 "T 200 500 5 10 0 0 0 0 1\n"
1022 "device=INDUCTOR\n"
1023 "T 200 300 8 10 1 1 0 0 1\n"
1024 "refdes=L?\n"
1025 "T 200 700 5 10 0 0 0 0 1\n"
1026 "symversion=0.2\n" ) },
1027
1028 // 7400-series logic gates
1029 { wxT( "7400-1.sym" ),
1030 wxT( "v 20031231 1\n"
1031 "L 300 200 300 800 3 0 0 0 -1 -1\n"
1032 "T 300 0 9 8 1 0 0 0 1\n"
1033 "7400\n"
1034 "L 300 800 700 800 3 0 0 0 -1 -1\n"
1035 "T 500 900 5 10 0 0 0 0 1\n"
1036 "device=7400\n"
1037 "T 500 1100 5 10 0 0 0 0 1\n"
1038 "slot=1\n"
1039 "T 500 1300 5 10 0 0 0 0 1\n"
1040 "numslots=4\n"
1041 "T 500 1500 5 10 0 0 0 0 1\n"
1042 "slotdef=1:1,2,3\n"
1043 "T 500 1700 5 10 0 0 0 0 1\n"
1044 "slotdef=2:4,5,6\n"
1045 "T 500 1900 5 10 0 0 0 0 1\n"
1046 "slotdef=3:9,10,8\n"
1047 "T 500 2100 5 10 0 0 0 0 1\n"
1048 "slotdef=4:12,13,11\n"
1049 "L 300 200 700 200 3 0 0 0 -1 -1\n"
1050 "A 700 500 300 270 180 3 0 0 0 -1 -1\n"
1051 "V 1050 500 50 6 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
1052 "P 1100 500 1300 500 1 0 1\n"
1053 "{\n"
1054 "T 1100 550 5 8 1 1 0 0 1\n"
1055 "pinnumber=3\n"
1056 "T 1100 450 5 8 0 1 0 2 1\n"
1057 "pinseq=3\n"
1058 "T 950 500 9 8 0 1 0 6 1\n"
1059 "pinlabel=Y\n"
1060 "T 950 500 5 8 0 1 0 8 1\n"
1061 "pintype=out\n"
1062 "}\n"
1063 "P 300 300 0 300 1 0 1\n"
1064 "{\n"
1065 "T 200 350 5 8 1 1 0 6 1\n"
1066 "pinnumber=2\n"
1067 "T 200 250 5 8 0 1 0 8 1\n"
1068 "pinseq=2\n"
1069 "T 350 300 9 8 0 1 0 0 1\n"
1070 "pinlabel=B\n"
1071 "T 350 300 5 8 0 1 0 2 1\n"
1072 "pintype=in\n"
1073 "}\n"
1074 "P 300 700 0 700 1 0 1\n"
1075 "{\n"
1076 "T 200 750 5 8 1 1 0 6 1\n"
1077 "pinnumber=1\n"
1078 "T 200 650 5 8 0 1 0 8 1\n"
1079 "pinseq=1\n"
1080 "T 350 700 9 8 0 1 0 0 1\n"
1081 "pinlabel=A\n"
1082 "T 350 700 5 8 0 1 0 2 1\n"
1083 "pintype=in\n"
1084 "}\n"
1085 "T 300 900 8 10 1 1 0 0 1\n"
1086 "refdes=U?\n"
1087 "T 500 2850 5 10 0 0 0 0 1\n"
1088 "net=Vcc:14\n"
1089 "T 500 3050 5 10 0 0 0 0 1\n"
1090 "net=GND:7\n" ) },
1091
1092 { wxT( "7404-1.sym" ),
1093 wxT( "v 20031231 1\n"
1094 "L 300 800 800 500 3 0 0 0 -1 -1\n"
1095 "T 600 900 5 10 0 0 0 0 1\n"
1096 "device=7404\n"
1097 "T 600 1100 5 10 0 0 0 0 1\n"
1098 "slot=1\n"
1099 "T 600 1300 5 10 0 0 0 0 1\n"
1100 "numslots=6\n"
1101 "T 600 1500 5 10 0 0 0 0 1\n"
1102 "slotdef=1:1,2\n"
1103 "T 600 1700 5 10 0 0 0 0 1\n"
1104 "slotdef=2:3,4\n"
1105 "T 600 1900 5 10 0 0 0 0 1\n"
1106 "slotdef=3:5,6\n"
1107 "T 600 2100 5 10 0 0 0 0 1\n"
1108 "slotdef=4:9,8\n"
1109 "T 600 2300 5 10 0 0 0 0 1\n"
1110 "slotdef=5:11,10\n"
1111 "T 600 2500 5 10 0 0 0 0 1\n"
1112 "slotdef=6:13,12\n"
1113 "L 800 500 300 200 3 0 0 0 -1 -1\n"
1114 "L 300 800 300 200 3 0 0 0 -1 -1\n"
1115 "V 850 500 50 6 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
1116 "P 300 500 0 500 1 0 1\n"
1117 "{\n"
1118 "T 200 550 5 8 1 1 0 6 1\n"
1119 "pinnumber=1\n"
1120 "T 200 450 5 8 0 1 0 8 1\n"
1121 "pinseq=1\n"
1122 "T 350 500 9 8 0 1 0 0 1\n"
1123 "pinlabel=A\n"
1124 "T 350 500 5 8 0 1 0 2 1\n"
1125 "pintype=in\n"
1126 "}\n"
1127 "P 1100 500 900 500 1 0 0\n"
1128 "{\n"
1129 "T 900 550 5 8 1 1 0 0 1\n"
1130 "pinnumber=2\n"
1131 "T 900 450 5 8 0 1 0 2 1\n"
1132 "pinseq=2\n"
1133 "T 750 500 9 8 0 1 0 6 1\n"
1134 "pinlabel=Y\n"
1135 "T 750 500 5 8 0 1 0 8 1\n"
1136 "pintype=out\n"
1137 "}\n"
1138 "T 300 0 9 8 1 0 0 0 1\n"
1139 "7404\n"
1140 "T 300 900 8 10 1 1 0 0 1\n"
1141 "refdes=U?\n"
1142 "T 600 3100 5 10 0 0 0 0 1\n"
1143 "net=Vcc:14\n"
1144 "T 600 3300 5 10 0 0 0 0 1\n"
1145 "net=GND:7\n" ) },
1146
1147 { wxT( "7408-1.sym" ),
1148 wxT( "v 20031231 1\n"
1149 "L 300 200 300 800 3 0 0 0 -1 -1\n"
1150 "T 300 0 9 8 1 0 0 0 1\n"
1151 "7408\n"
1152 "L 300 800 700 800 3 0 0 0 -1 -1\n"
1153 "T 700 900 5 10 0 0 0 0 1\n"
1154 "device=7408\n"
1155 "T 700 1100 5 10 0 0 0 0 1\n"
1156 "slot=1\n"
1157 "T 700 1300 5 10 0 0 0 0 1\n"
1158 "numslots=4\n"
1159 "T 700 1500 5 10 0 0 0 0 1\n"
1160 "slotdef=1:1,2,3\n"
1161 "T 700 1700 5 10 0 0 0 0 1\n"
1162 "slotdef=2:4,5,6\n"
1163 "T 700 1900 5 10 0 0 0 0 1\n"
1164 "slotdef=3:9,10,8\n"
1165 "T 700 2100 5 10 0 0 0 0 1\n"
1166 "slotdef=4:12,13,11\n"
1167 "L 300 200 700 200 3 0 0 0 -1 -1\n"
1168 "A 700 500 300 270 180 3 0 0 0 -1 -1\n"
1169 "P 1000 500 1300 500 1 0 1\n"
1170 "{\n"
1171 "T 1100 550 5 8 1 1 0 0 1\n"
1172 "pinnumber=3\n"
1173 "T 1100 450 5 8 0 1 0 2 1\n"
1174 "pinseq=3\n"
1175 "T 950 500 9 8 0 1 0 6 1\n"
1176 "pinlabel=Y\n"
1177 "T 950 500 5 8 0 1 0 8 1\n"
1178 "pintype=out\n"
1179 "}\n"
1180 "P 300 700 0 700 1 0 1\n"
1181 "{\n"
1182 "T 200 750 5 8 1 1 0 6 1\n"
1183 "pinnumber=1\n"
1184 "T 200 650 5 8 0 1 0 8 1\n"
1185 "pinseq=1\n"
1186 "T 350 700 9 8 0 1 0 0 1\n"
1187 "pinlabel=A\n"
1188 "T 350 700 5 8 0 1 0 2 1\n"
1189 "pintype=in\n"
1190 "}\n"
1191 "P 300 300 0 300 1 0 1\n"
1192 "{\n"
1193 "T 200 350 5 8 1 1 0 6 1\n"
1194 "pinnumber=2\n"
1195 "T 200 250 5 8 0 1 0 8 1\n"
1196 "pinseq=2\n"
1197 "T 350 300 9 8 0 1 0 0 1\n"
1198 "pinlabel=B\n"
1199 "T 350 300 5 8 0 1 0 2 1\n"
1200 "pintype=in\n"
1201 "}\n"
1202 "T 300 900 8 10 1 1 0 0 1\n"
1203 "refdes=U?\n"
1204 "T 700 2700 5 10 0 0 0 0 1\n"
1205 "net=Vcc:14\n"
1206 "T 700 2900 5 10 0 0 0 0 1\n"
1207 "net=GND:7\n" ) },
1208
1209 { wxT( "7432-1.sym" ),
1210 wxT( "v 20031231 1\n"
1211 "L 260 200 600 200 3 0 0 0 -1 -1\n"
1212 "L 260 800 600 800 3 0 0 0 -1 -1\n"
1213 "T 600 900 5 10 0 0 0 0 1\n"
1214 "device=7432\n"
1215 "T 600 1100 5 10 0 0 0 0 1\n"
1216 "slot=1\n"
1217 "T 600 1300 5 10 0 0 0 0 1\n"
1218 "numslots=4\n"
1219 "T 600 1500 5 10 0 0 0 0 1\n"
1220 "slotdef=1:1,2,3\n"
1221 "T 600 1700 5 10 0 0 0 0 1\n"
1222 "slotdef=2:4,5,6\n"
1223 "T 600 1900 5 10 0 0 0 0 1\n"
1224 "slotdef=3:9,10,8\n"
1225 "T 600 2100 5 10 0 0 0 0 1\n"
1226 "slotdef=4:12,13,11\n"
1227 "T 300 0 9 8 1 0 0 0 1\n"
1228 "7432\n"
1229 "A 0 500 400 312 97 3 0 0 0 -1 -1\n"
1230 "P 300 700 0 700 1 0 1\n"
1231 "{\n"
1232 "T 200 750 5 8 1 1 0 6 1\n"
1233 "pinnumber=1\n"
1234 "T 200 650 5 8 0 1 0 8 1\n"
1235 "pinseq=1\n"
1236 "T 350 700 9 8 0 1 0 0 1\n"
1237 "pinlabel=A\n"
1238 "T 350 700 5 8 0 1 0 2 1\n"
1239 "pintype=in\n"
1240 "}\n"
1241 "P 300 300 0 300 1 0 1\n"
1242 "{\n"
1243 "T 200 350 5 8 1 1 0 6 1\n"
1244 "pinnumber=2\n"
1245 "T 200 250 5 8 0 1 0 8 1\n"
1246 "pinseq=2\n"
1247 "T 350 300 9 8 0 1 0 0 1\n"
1248 "pinlabel=B\n"
1249 "T 350 300 5 8 0 1 0 2 1\n"
1250 "pintype=in\n"
1251 "}\n"
1252 "P 1300 500 988 500 1 0 0\n"
1253 "{\n"
1254 "T 1100 550 5 8 1 1 0 0 1\n"
1255 "pinnumber=3\n"
1256 "T 1100 450 5 8 0 1 0 2 1\n"
1257 "pinseq=3\n"
1258 "T 950 500 9 8 0 1 0 6 1\n"
1259 "pinlabel=Y\n"
1260 "T 950 500 5 8 0 1 0 8 1\n"
1261 "pintype=out\n"
1262 "}\n"
1263 "A 600 600 400 270 76 3 0 0 0 -1 -1\n"
1264 "A 600 400 400 14 76 3 0 0 0 -1 -1\n"
1265 "T 300 900 8 10 1 1 0 0 1\n"
1266 "refdes=U?\n"
1267 "T 600 2700 5 10 0 0 0 0 1\n"
1268 "net=Vcc:14\n"
1269 "T 600 2900 5 10 0 0 0 0 1\n"
1270 "net=GND:7\n" ) },
1271
1272 { wxT( "7486-1.sym" ),
1273 wxT( "v 20031231 1\n"
1274 "L 260 200 600 200 3 0 0 0 -1 -1\n"
1275 "L 260 800 600 800 3 0 0 0 -1 -1\n"
1276 "T 700 900 5 10 0 0 0 0 1\n"
1277 "device=7486\n"
1278 "T 700 1100 5 10 0 0 0 0 1\n"
1279 "slot=1\n"
1280 "T 700 1300 5 10 0 0 0 0 1\n"
1281 "numslots=4\n"
1282 "T 700 1500 5 10 0 0 0 0 1\n"
1283 "slotdef=1:1,2,3\n"
1284 "T 700 1700 5 10 0 0 0 0 1\n"
1285 "slotdef=2:4,5,6\n"
1286 "T 700 1900 5 10 0 0 0 0 1\n"
1287 "slotdef=3:9,10,8\n"
1288 "T 700 2100 5 10 0 0 0 0 1\n"
1289 "slotdef=4:12,13,11\n"
1290 "A 0 500 400 312 97 3 0 0 0 -1 -1\n"
1291 "A -100 500 400 312 97 3 0 0 0 -1 -1\n"
1292 "P 300 700 0 700 1 0 1\n"
1293 "{\n"
1294 "T 150 750 5 8 1 1 0 6 1\n"
1295 "pinnumber=1\n"
1296 "T 150 650 5 8 0 1 0 8 1\n"
1297 "pinseq=1\n"
1298 "T 350 700 9 8 0 1 0 0 1\n"
1299 "pinlabel=A\n"
1300 "T 350 700 5 8 0 1 0 2 1\n"
1301 "pintype=in\n"
1302 "}\n"
1303 "P 300 300 0 300 1 0 1\n"
1304 "{\n"
1305 "T 150 350 5 8 1 1 0 6 1\n"
1306 "pinnumber=2\n"
1307 "T 150 250 5 8 0 1 0 8 1\n"
1308 "pinseq=2\n"
1309 "T 350 300 9 8 0 1 0 0 1\n"
1310 "pinlabel=B\n"
1311 "T 350 300 5 8 0 1 0 2 1\n"
1312 "pintype=in\n"
1313 "}\n"
1314 "P 988 500 1300 500 1 0 1\n"
1315 "{\n"
1316 "T 1100 550 5 8 1 1 0 0 1\n"
1317 "pinnumber=3\n"
1318 "T 1100 450 5 8 0 1 0 2 1\n"
1319 "pinseq=3\n"
1320 "T 950 500 9 8 0 1 0 6 1\n"
1321 "pinlabel=Y\n"
1322 "T 950 500 5 8 0 1 0 8 1\n"
1323 "pintype=out\n"
1324 "}\n"
1325 "A 600 600 400 270 76 3 0 0 0 -1 -1\n"
1326 "A 600 400 400 14 76 3 0 0 0 -1 -1\n"
1327 "T 300 900 8 10 1 1 0 0 1\n"
1328 "refdes=U?\n"
1329 "T 300 0 9 8 1 0 0 0 1\n"
1330 "7486\n"
1331 "T 700 2700 5 10 0 0 0 0 1\n"
1332 "net=Vcc:14\n"
1333 "T 700 2900 5 10 0 0 0 0 1\n"
1334 "net=GND:7\n" ) },
1335
1336 { wxT( "7402-1.sym" ),
1337 wxT( "v 20031231 1\n"
1338 "L 260 200 600 200 3 0 0 0 -1 -1\n"
1339 "L 260 800 600 800 3 0 0 0 -1 -1\n"
1340 "T 600 900 5 10 0 0 0 0 1\n"
1341 "device=7402\n"
1342 "T 600 1100 5 10 0 0 0 0 1\n"
1343 "slot=1\n"
1344 "T 600 1300 5 10 0 0 0 0 1\n"
1345 "numslots=4\n"
1346 "T 600 1500 5 10 0 0 0 0 1\n"
1347 "slotdef=1:1,2,3\n"
1348 "T 600 1700 5 10 0 0 0 0 1\n"
1349 "slotdef=2:4,5,6\n"
1350 "T 600 1900 5 10 0 0 0 0 1\n"
1351 "slotdef=3:10,8,9\n"
1352 "T 600 2100 5 10 0 0 0 0 1\n"
1353 "slotdef=4:13,11,12\n"
1354 "T 300 0 9 8 1 0 0 0 1\n"
1355 "7402\n"
1356 "A 0 500 400 312 97 3 0 0 0 -1 -1\n"
1357 "P 300 700 0 700 1 0 1\n"
1358 "{\n"
1359 "T 200 750 5 8 1 1 0 6 1\n"
1360 "pinnumber=3\n"
1361 "T 200 650 5 8 0 1 0 8 1\n"
1362 "pinseq=3\n"
1363 "T 350 700 9 8 0 1 0 0 1\n"
1364 "pinlabel=B\n"
1365 "T 350 700 5 8 0 1 0 2 1\n"
1366 "pintype=in\n"
1367 "}\n"
1368 "P 300 300 0 300 1 0 1\n"
1369 "{\n"
1370 "T 200 350 5 8 1 1 0 6 1\n"
1371 "pinnumber=2\n"
1372 "T 200 250 5 8 0 1 0 8 1\n"
1373 "pinseq=2\n"
1374 "T 350 300 9 8 0 1 0 0 1\n"
1375 "pinlabel=A\n"
1376 "T 350 300 5 8 0 1 0 2 1\n"
1377 "pintype=in\n"
1378 "}\n"
1379 "V 1038 500 50 6 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
1380 "P 1300 500 1088 500 1 0 0\n"
1381 "{\n"
1382 "T 1100 550 5 8 1 1 0 0 1\n"
1383 "pinnumber=1\n"
1384 "T 1100 450 5 8 0 1 0 2 1\n"
1385 "pinseq=1\n"
1386 "T 950 500 9 8 0 1 0 6 1\n"
1387 "pinlabel=Y\n"
1388 "T 950 500 5 8 0 1 0 8 1\n"
1389 "pintype=out\n"
1390 "}\n"
1391 "A 600 600 400 270 76 3 0 0 0 -1 -1\n"
1392 "A 600 400 400 14 76 3 0 0 0 -1 -1\n"
1393 "T 300 900 8 10 1 1 0 0 1\n"
1394 "refdes=U?\n"
1395 "T 600 2700 5 10 0 0 0 0 1\n"
1396 "net=Vcc:14\n"
1397 "T 600 2900 5 10 0 0 0 0 1\n"
1398 "net=GND:7\n" ) },
1399
1400 // Miscellaneous
1401 { wxT( "busripper-1.sym" ),
1402 wxT( "v 20031231 1\n"
1403 "T 0 400 5 8 0 0 0 0 1\n"
1404 "device=none\n"
1405 "P 0 0 100 100 1 0 0\n"
1406 "{\n"
1407 "T 0 500 5 8 0 0 0 0 1\n"
1408 "pinseq=1\n"
1409 "T 0 600 5 8 0 0 0 0 1\n"
1410 "pinnumber=1\n"
1411 "T 0 700 5 8 0 0 0 0 1\n"
1412 "pintype=pas\n"
1413 "T 0 800 5 8 0 0 0 0 1\n"
1414 "pinlabel=netside\n"
1415 "}\n"
1416 "T 0 300 5 8 0 0 0 0 1\n"
1417 "graphical=1\n"
1418 "L 200 200 100 100 10 30 0 0 -1 -1\n" ) },
1419
1420 { wxT( "busripper-2.sym" ),
1421 wxT( "v 20031231 1\n"
1422 "T 0 400 5 8 0 0 0 0 1\n"
1423 "device=none\n"
1424 "P 0 0 0 100 1 0 0\n"
1425 "{\n"
1426 "T 0 500 5 8 0 0 0 0 1\n"
1427 "pinseq=1\n"
1428 "T 0 600 5 8 0 0 0 0 1\n"
1429 "pinnumber=1\n"
1430 "T 0 700 5 8 0 0 0 0 1\n"
1431 "pintype=pas\n"
1432 "T 0 800 5 8 0 0 0 0 1\n"
1433 "pinlabel=netside\n"
1434 "}\n"
1435 "T 0 300 5 8 0 0 0 0 1\n"
1436 "graphical=1\n"
1437 "L 0 100 100 200 3 0 0 0 -1 -1\n"
1438 "L 0 100 -100 200 3 0 0 0 -1 -1\n" ) },
1439
1440 { wxT( "title-B.sym" ),
1441 wxT( "v 20031231 1\n"
1442 "B 0 0 17000 11000 15 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
1443 "T 14400 1500 5 10 0 0 0 0 1\n"
1444 "graphical=1\n"
1445 "L 12900 600 12900 0 15 0 0 0 -1 -1\n"
1446 "T 9500 400 15 8 1 0 0 0 1\n"
1447 "FILE:\n"
1448 "T 13000 400 15 8 1 0 0 0 1\n"
1449 "REVISION:\n"
1450 "T 13000 100 15 8 1 0 0 0 1\n"
1451 "DRAWN BY:\n"
1452 "T 9500 100 15 8 1 0 0 0 1\n"
1453 "PAGE\n"
1454 "T 11200 100 15 8 1 0 0 0 1\n"
1455 "OF\n"
1456 "T 9500 700 15 8 1 0 0 0 1\n"
1457 "TITLE\n"
1458 "B 9400 0 7600 1400 15 0 0 0 -1 -1 0 -1 -1 -1 -1 -1\n"
1459 "L 9400 600 17000 600 15 0 0 0 -1 -1\n" ) },
1460 };
1461
1462 return symbols;
1463}
1464
1465
1466// ==========================================================================
1467// Construction
1468// ==========================================================================
1469
1471 SCH_IO( wxS( "gEDA/gschem Schematic" ) ),
1472 m_screen( nullptr ),
1473 m_rootSheet( nullptr ),
1474 m_schematic( nullptr ),
1475 m_maxY( 0 ),
1476 m_releaseVersion( 0 ),
1478 m_symLibraryInitialized( false ),
1479 m_powerCounter( 0 ),
1480 m_properties( nullptr )
1481{
1483}
1484
1485
1486SCH_IO_GEDA::~SCH_IO_GEDA() = default;
1487
1488
1489// ==========================================================================
1490// Format detection
1491// ==========================================================================
1492
1493bool SCH_IO_GEDA::CanReadSchematicFile( const wxString& aFileName ) const
1494{
1495 wxFileName fn( aFileName );
1496
1497 if( fn.GetExt().CmpNoCase( wxT( "sch" ) ) != 0 )
1498 return false;
1499
1500 wxTextFile file;
1501
1502 if( !file.Open( aFileName ) )
1503 return false;
1504
1505 if( file.GetLineCount() == 0 )
1506 return false;
1507
1508 wxString firstLine = file.GetFirstLine().Trim( false );
1509
1510 if( !firstLine.StartsWith( wxT( "v " ) ) )
1511 return false;
1512
1513 wxStringTokenizer tok( firstLine );
1514 tok.GetNextToken(); // skip 'v'
1515
1516 if( !tok.HasMoreTokens() )
1517 return false;
1518
1519 wxString dateStr = tok.GetNextToken();
1520
1521 if( dateStr.length() != 8 )
1522 return false;
1523
1524 long dateVal = 0;
1525 return dateStr.ToLong( &dateVal ) && dateVal >= 19700101;
1526}
1527
1528
1529// ==========================================================================
1530// Coordinate transformation
1531// ==========================================================================
1532
1533int SCH_IO_GEDA::toKiCadDist( int aMils ) const
1534{
1535 return static_cast<int>( static_cast<int64_t>( aMils ) * MILS_TO_IU );
1536}
1537
1538
1539VECTOR2I SCH_IO_GEDA::toKiCad( int aGedaX, int aGedaY ) const
1540{
1541 return VECTOR2I( toKiCadDist( aGedaX ), toKiCadDist( m_maxY - aGedaY ) );
1542}
1543
1544
1545// ==========================================================================
1546// Version parsing
1547// ==========================================================================
1548
1549bool SCH_IO_GEDA::parseVersionLine( const wxString& aLine )
1550{
1551 wxStringTokenizer tokenizer( aLine );
1552
1553 if( tokenizer.CountTokens() < 2 )
1554 return false;
1555
1556 wxString v = tokenizer.GetNextToken();
1557
1558 if( v != wxT( "v" ) )
1559 return false;
1560
1561 wxString dateStr = tokenizer.GetNextToken();
1562 dateStr.ToLong( &m_releaseVersion );
1563
1564 if( m_releaseVersion < 19700101 )
1565 return false;
1566
1567 // File format version is optional in very old files
1568 if( tokenizer.HasMoreTokens() )
1569 tokenizer.GetNextToken().ToLong( &m_fileFormatVersion );
1570 else
1572
1573 return true;
1574}
1575
1576
1577// ==========================================================================
1578// Attribute parsing
1579// ==========================================================================
1580
1581std::vector<SCH_IO_GEDA::GEDA_ATTR> SCH_IO_GEDA::parseAttributes( wxTextFile& aFile,
1582 size_t& aLineIdx )
1583{
1584 std::vector<GEDA_ATTR> attrs;
1585
1586 while( aLineIdx < aFile.GetLineCount() )
1587 {
1588 wxString line = aFile.GetLine( aLineIdx );
1589
1590 if( line.Trim() == wxT( "}" ) )
1591 {
1592 aLineIdx++;
1593 break;
1594 }
1595
1596 if( line.StartsWith( wxT( "T " ) ) )
1597 {
1598 // T x y color size visibility show_name_value angle alignment num_lines
1599 wxStringTokenizer tok( line );
1600 tok.GetNextToken(); // skip 'T'
1601
1602 long x = 0, y = 0, color = 0, size = 0, vis = 0, showNV = 0, angle = 0, align = 0;
1603 long numLines = 1;
1604
1605 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
1606 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
1607 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
1608 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &size );
1609 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &vis );
1610 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &showNV );
1611 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &angle );
1612 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &align );
1613 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &numLines );
1614
1615 aLineIdx++;
1616
1617 wxString textContent;
1618
1619 for( long i = 0; i < numLines && aLineIdx < aFile.GetLineCount(); i++ )
1620 {
1621 if( i > 0 )
1622 textContent += wxT( "\n" );
1623
1624 textContent += aFile.GetLine( aLineIdx );
1625 aLineIdx++;
1626 }
1627
1628 int eqPos = textContent.Find( '=' );
1629
1630 if( eqPos != wxNOT_FOUND )
1631 {
1632 GEDA_ATTR attr;
1633 attr.name = textContent.Left( eqPos );
1634 attr.value = textContent.Mid( eqPos + 1 );
1635
1636 // Strip surrounding double quotes from attribute values
1637 if( attr.value.length() >= 2
1638 && attr.value.StartsWith( wxT( "\"" ) )
1639 && attr.value.EndsWith( wxT( "\"" ) ) )
1640 {
1641 attr.value = attr.value.Mid( 1, attr.value.length() - 2 );
1642 }
1643
1644 attr.x = static_cast<int>( x );
1645 attr.y = static_cast<int>( y );
1646 attr.size = static_cast<int>( size );
1647 attr.angle = static_cast<int>( angle );
1648 attr.align = static_cast<int>( align );
1649 attr.visible = ( vis == 1 );
1650 attr.showNV = static_cast<int>( showNV );
1651 attrs.push_back( attr );
1652 }
1653
1654 continue;
1655 }
1656
1657 aLineIdx++;
1658 }
1659
1660 return attrs;
1661}
1662
1663
1664wxString SCH_IO_GEDA::findAttr( const std::vector<GEDA_ATTR>& aAttrs,
1665 const wxString& aName ) const
1666{
1667 for( const GEDA_ATTR& attr : aAttrs )
1668 {
1669 if( attr.name == aName )
1670 return attr.value;
1671 }
1672
1673 return wxEmptyString;
1674}
1675
1676
1677const SCH_IO_GEDA::GEDA_ATTR* SCH_IO_GEDA::findAttrStruct( const std::vector<GEDA_ATTR>& aAttrs,
1678 const wxString& aName ) const
1679{
1680 for( const GEDA_ATTR& attr : aAttrs )
1681 {
1682 if( attr.name == aName )
1683 return &attr;
1684 }
1685
1686 return nullptr;
1687}
1688
1689
1690std::vector<SCH_IO_GEDA::GEDA_ATTR> SCH_IO_GEDA::maybeParseAttributes( wxTextFile& aFile,
1691 size_t& aLineIdx )
1692{
1693 if( aLineIdx < aFile.GetLineCount() && aFile.GetLine( aLineIdx ).Trim() == wxT( "{" ) )
1694 {
1695 aLineIdx++;
1696 return parseAttributes( aFile, aLineIdx );
1697 }
1698
1699 return {};
1700}
1701
1702
1703// ==========================================================================
1704// Style mapping
1705// ==========================================================================
1706
1708{
1709 switch( aDashStyle )
1710 {
1711 case 1: return LINE_STYLE::DOT;
1712 case 2: return LINE_STYLE::DASH;
1713 case 3: return LINE_STYLE::DASHDOT;
1714 case 4: return LINE_STYLE::DASHDOTDOT;
1715 default: return LINE_STYLE::DEFAULT;
1716 }
1717}
1718
1719
1721{
1722 switch( aFillType )
1723 {
1724 case 1: return FILL_T::FILLED_SHAPE;
1725 case 2: return FILL_T::CROSS_HATCH;
1726 case 3: return FILL_T::HATCH;
1727 default: return FILL_T::NO_FILL;
1728 }
1729}
1730
1731
1732// ==========================================================================
1733// Orientation mapping
1734// ==========================================================================
1735
1736int SCH_IO_GEDA::toKiCadOrientation( int aAngle, int aMirror ) const
1737{
1738 int orientation = SYM_ORIENT_0;
1739
1740 switch( aAngle )
1741 {
1742 case 0: orientation = SYM_ORIENT_0; break;
1743 case 90: orientation = SYM_ORIENT_90; break;
1744 case 180: orientation = SYM_ORIENT_180; break;
1745 case 270: orientation = SYM_ORIENT_270; break;
1746 default: orientation = SYM_ORIENT_0; break;
1747 }
1748
1749 if( aMirror )
1750 orientation |= SYM_MIRROR_Y;
1751
1752 return orientation;
1753}
1754
1755
1757{
1758 wxString libName;
1759
1760 if( m_schematic )
1761 libName = m_schematic->Project().GetProjectName();
1762
1763 if( libName.IsEmpty() )
1764 libName = m_filename.GetName();
1765
1766 if( libName.IsEmpty() )
1767 libName = wxT( "noname" );
1768
1769 libName += wxT( "-geda-import" );
1770 libName = LIB_ID::FixIllegalChars( libName, true ).wx_str();
1771 return libName;
1772}
1773
1774
1775// ==========================================================================
1776// Object parsers - connectivity
1777// ==========================================================================
1778
1779void SCH_IO_GEDA::parseComponent( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
1780{
1781 // Flush any previous pending component before starting a new one
1783
1784 // C x y selectable angle mirror basename.sym
1785 wxStringTokenizer tok( aLine );
1786 tok.GetNextToken(); // skip 'C'
1787
1788 long x = 0, y = 0, selectable = 0, angle = 0, mirror = 0;
1789
1790 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
1791 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
1792 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &selectable );
1793 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &angle );
1794 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &mirror );
1795
1796 wxString basename;
1797
1798 if( tok.HasMoreTokens() )
1799 basename = tok.GetNextToken();
1800
1801 // gEDA prefixes embedded component basenames with "EMBEDDED"
1802 if( basename.StartsWith( wxT( "EMBEDDED" ) ) )
1803 basename = basename.Mid( 8 );
1804
1805 m_pendingComp = std::make_unique<PENDING_COMPONENT>();
1806 m_pendingComp->basename = basename;
1807 m_pendingComp->x = static_cast<int>( x );
1808 m_pendingComp->y = static_cast<int>( y );
1809 m_pendingComp->selectable = static_cast<int>( selectable );
1810 m_pendingComp->angle = static_cast<int>( angle );
1811 m_pendingComp->mirror = static_cast<int>( mirror );
1812
1813 m_pendingComp->attrs = maybeParseAttributes( aFile, aLineIdx );
1814}
1815
1816
1817void SCH_IO_GEDA::parseNet( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
1818{
1819 // N x1 y1 x2 y2 color
1820 wxStringTokenizer tok( aLine );
1821 tok.GetNextToken(); // skip 'N'
1822
1823 long x1 = 0, y1 = 0, x2 = 0, y2 = 0;
1824
1825 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x1 );
1826 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y1 );
1827 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x2 );
1828 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y2 );
1829
1830 VECTOR2I start = toKiCad( static_cast<int>( x1 ), static_cast<int>( y1 ) );
1831 VECTOR2I end = toKiCad( static_cast<int>( x2 ), static_cast<int>( y2 ) );
1832
1833 auto wire = std::make_unique<SCH_LINE>( start, LAYER_WIRE );
1834 wire->SetEndPoint( end );
1835 m_screen->Append( wire.release() );
1836
1837 trackEndpoint( static_cast<int>( x1 ), static_cast<int>( y1 ) );
1838 trackEndpoint( static_cast<int>( x2 ), static_cast<int>( y2 ) );
1839
1840 std::vector<GEDA_ATTR> attrs = maybeParseAttributes( aFile, aLineIdx );
1841 wxString netname = findAttr( attrs, wxT( "netname" ) );
1842
1843 if( !netname.IsEmpty() )
1844 {
1845 auto label = std::make_unique<SCH_LABEL>( start, netname );
1846 int textSize = toKiCadDist( GEDA_DEFAULT_TEXT_SIZE_MILS / 2 );
1847 label->SetTextSize( VECTOR2I( textSize, textSize ) );
1848 m_screen->Append( label.release() );
1849 }
1850}
1851
1852
1853void SCH_IO_GEDA::parseBus( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
1854{
1855 // U x1 y1 x2 y2 color [ripperdir]
1856 wxStringTokenizer tok( aLine );
1857 tok.GetNextToken(); // skip 'U'
1858
1859 long x1 = 0, y1 = 0, x2 = 0, y2 = 0, color = 0, ripperDir = 0;
1860
1861 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x1 );
1862 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y1 );
1863 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x2 );
1864 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y2 );
1865 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
1866
1867 if( m_releaseVersion > 20020825 )
1868 {
1869 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &ripperDir );
1870 }
1871
1872 VECTOR2I start = toKiCad( static_cast<int>( x1 ), static_cast<int>( y1 ) );
1873 VECTOR2I end = toKiCad( static_cast<int>( x2 ), static_cast<int>( y2 ) );
1874
1875 auto line = std::make_unique<SCH_LINE>( start, LAYER_BUS );
1876 line->SetEndPoint( end );
1877 m_screen->Append( line.release() );
1878
1879 m_busSegments.push_back( { start, end, static_cast<int>( ripperDir ) } );
1880
1881 maybeParseAttributes( aFile, aLineIdx );
1882}
1883
1884
1885void SCH_IO_GEDA::parsePin( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
1886{
1887 // P x1 y1 x2 y2 color [pintype whichend]
1888 wxStringTokenizer tok( aLine );
1889 tok.GetNextToken(); // skip 'P'
1890
1891 long x1 = 0, y1 = 0, x2 = 0, y2 = 0, color = 0, pintype = 0, whichend = 0;
1892
1893 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x1 );
1894 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y1 );
1895 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x2 );
1896 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y2 );
1897 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
1898
1899 if( m_releaseVersion > 20020825 )
1900 {
1901 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pintype );
1902 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &whichend );
1903 }
1904
1905 VECTOR2I start = toKiCad( static_cast<int>( x1 ), static_cast<int>( y1 ) );
1906 VECTOR2I end = toKiCad( static_cast<int>( x2 ), static_cast<int>( y2 ) );
1907
1908 // In schematic context, pin lines become wire stubs for connectivity
1909 auto wire = std::make_unique<SCH_LINE>( start, LAYER_WIRE );
1910 wire->SetEndPoint( end );
1911 m_screen->Append( wire.release() );
1912
1913 VECTOR2I connPt = ( whichend == 0 ) ? start : end;
1914 int connX = ( whichend == 0 ) ? static_cast<int>( x1 ) : static_cast<int>( x2 );
1915 int connY = ( whichend == 0 ) ? static_cast<int>( y1 ) : static_cast<int>( y2 );
1916 trackEndpoint( connX, connY );
1917
1918 std::vector<GEDA_ATTR> attrs = maybeParseAttributes( aFile, aLineIdx );
1919
1920 wxString pinlabel = findAttr( attrs, wxT( "pinlabel" ) );
1921
1922 if( !pinlabel.IsEmpty() )
1923 {
1924 auto label = std::make_unique<SCH_LABEL>( connPt, pinlabel );
1925 int textSize = toKiCadDist( GEDA_DEFAULT_TEXT_SIZE_MILS / 2 );
1926 label->SetTextSize( VECTOR2I( textSize, textSize ) );
1927 m_screen->Append( label.release() );
1928 }
1929}
1930
1931
1932void SCH_IO_GEDA::parseEmbeddedComponent( wxTextFile& aFile, size_t& aLineIdx )
1933{
1934 if( !m_pendingComp )
1935 return;
1936
1937 auto libSym = std::make_unique<LIB_SYMBOL>( m_pendingComp->basename );
1938
1939 // Parse until ] closing bracket
1940 while( aLineIdx < aFile.GetLineCount() )
1941 {
1942 wxString line = aFile.GetLine( aLineIdx );
1943
1944 if( line.Trim() == wxT( "]" ) )
1945 {
1946 aLineIdx++;
1947 break;
1948 }
1949
1950 if( line.IsEmpty() )
1951 {
1952 aLineIdx++;
1953 continue;
1954 }
1955
1956 wxChar type = line[0];
1957 aLineIdx++;
1958
1959 switch( type )
1960 {
1961 case 'P':
1962 {
1963 wxStringTokenizer tok( line );
1964 tok.GetNextToken(); // skip 'P'
1965 long px1 = 0, py1 = 0, px2 = 0, py2 = 0, pc = 0, pt = 0, pw = 0;
1966
1967 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &px1 );
1968 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &py1 );
1969 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &px2 );
1970 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &py2 );
1971 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pc );
1972 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pt );
1973 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pw );
1974
1975 std::vector<GEDA_ATTR> pinAttrs = maybeParseAttributes( aFile, aLineIdx );
1976 addSymbolPin( *libSym, static_cast<int>( px1 ), static_cast<int>( py1 ),
1977 static_cast<int>( px2 ), static_cast<int>( py2 ),
1978 static_cast<int>( pw ), pinAttrs );
1979 break;
1980 }
1981
1982 case 'L':
1983 case 'B':
1984 case 'V':
1985 case 'A':
1986 case 'H':
1987 addSymbolGraphic( *libSym, line, aFile, aLineIdx, type );
1988 break;
1989
1990 case 'T':
1991 {
1992 // Skip text lines in embedded symbols (they're attribute text)
1993 wxStringTokenizer tok( line );
1994 tok.GetNextToken(); // skip 'T'
1995 long numLines = 1;
1996
1997 // Parse enough to get numLines (9th field)
1998 for( int i = 0; i < 8 && tok.HasMoreTokens(); i++ )
1999 {
2000 if( i == 7 )
2001 tok.GetNextToken().ToLong( &numLines );
2002 else
2003 tok.GetNextToken();
2004 }
2005
2006 for( long i = 0; i < numLines && aLineIdx < aFile.GetLineCount(); i++ )
2007 aLineIdx++;
2008
2009 maybeParseAttributes( aFile, aLineIdx );
2010 break;
2011 }
2012
2013 case '{':
2014 {
2015 // Skip nested attribute block
2016 while( aLineIdx < aFile.GetLineCount() )
2017 {
2018 if( aFile.GetLine( aLineIdx ).Trim() == wxT( "}" ) )
2019 {
2020 aLineIdx++;
2021 break;
2022 }
2023
2024 aLineIdx++;
2025 }
2026
2027 break;
2028 }
2029
2030 case 'v':
2031 default:
2032 break;
2033 }
2034 }
2035
2036 m_pendingComp->embedded = true;
2037 m_pendingComp->embeddedSym = std::move( libSym );
2038}
2039
2040
2041// ==========================================================================
2042// Object parsers - graphics
2043// ==========================================================================
2044
2045void SCH_IO_GEDA::parseText( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2046{
2047 // T x y color size visibility show_name_value angle alignment num_lines
2048 // Old formats may omit alignment and/or num_lines fields
2049 wxStringTokenizer tok( aLine );
2050 tok.GetNextToken(); // skip 'T'
2051
2052 long x = 0, y = 0, color = 0, size = 0, vis = 0, showNV = 0, angle = 0, align = 0;
2053 long numLines = 1;
2054
2055 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
2056 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
2057 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2058 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &size );
2059 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &vis );
2060 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &showNV );
2061 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &angle );
2062
2063 if( m_fileFormatVersion >= 1 )
2064 {
2065 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &align );
2066 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &numLines );
2067 }
2068 else if( m_releaseVersion >= 20000220 )
2069 {
2070 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &align );
2071
2072 numLines = 1;
2073 }
2074 else
2075 {
2076 align = 0;
2077 numLines = 1;
2078 }
2079
2080 wxString textContent;
2081
2082 for( long i = 0; i < numLines && aLineIdx < aFile.GetLineCount(); i++ )
2083 {
2084 if( i > 0 )
2085 textContent += wxT( "\n" );
2086
2087 textContent += aFile.GetLine( aLineIdx );
2088 aLineIdx++;
2089 }
2090
2091 textContent = convertOverbars( textContent );
2092
2093 if( vis == 0 )
2094 {
2095 maybeParseAttributes( aFile, aLineIdx );
2096 return;
2097 }
2098
2099 // Handle attribute text (contains '=') with show_name_value field
2100 int eqPos = textContent.Find( '=' );
2101
2102 if( eqPos != wxNOT_FOUND )
2103 {
2104 wxString name = textContent.Left( eqPos );
2105 wxString value = textContent.Mid( eqPos + 1 );
2106
2107 switch( static_cast<int>( showNV ) )
2108 {
2109 case 1: textContent = value; break;
2110 case 2: textContent = name; break;
2111 default: textContent = name + wxT( "=" ) + value; break;
2112 }
2113 }
2114
2115 VECTOR2I pos = toKiCad( static_cast<int>( x ), static_cast<int>( y ) );
2116
2117 auto text = std::make_unique<SCH_TEXT>( pos, textContent );
2118
2119 int textSize = toKiCadDist( static_cast<int>( size ) * 10 );
2120
2121 if( textSize < toKiCadDist( 10 ) )
2122 textSize = toKiCadDist( 10 );
2123
2124 text->SetTextSize( VECTOR2I( textSize, textSize ) );
2125
2126 // gEDA allows arbitrary angles but KiCad text only supports orthogonal.
2127 // Snap to the nearest 90-degree increment.
2128 int normAngle = ( ( static_cast<int>( angle ) % 360 ) + 360 ) % 360;
2129 int snapped = ( ( normAngle + 45 ) / 90 ) * 90;
2130
2131 if( snapped == 360 )
2132 snapped = 0;
2133
2134 switch( snapped )
2135 {
2136 case 90: text->SetTextAngle( ANGLE_90 ); break;
2137 case 180: text->SetTextAngle( ANGLE_180 ); break;
2138 case 270: text->SetTextAngle( ANGLE_270 ); break;
2139 default: break;
2140 }
2141
2142 // Horizontal alignment: gEDA align / 3 gives column (0=left, 1=center, 2=right)
2143 switch( static_cast<int>( align ) / 3 )
2144 {
2145 case 1: text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER ); break;
2146 case 2: text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT ); break;
2147 default: text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT ); break;
2148 }
2149
2150 // Vertical alignment: gEDA align % 3 gives row (0=bottom, 1=middle, 2=top)
2151 switch( static_cast<int>( align ) % 3 )
2152 {
2153 case 1: text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER ); break;
2154 case 2: text->SetVertJustify( GR_TEXT_V_ALIGN_TOP ); break;
2155 default: text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM ); break;
2156 }
2157
2158 m_screen->Append( text.release() );
2159 maybeParseAttributes( aFile, aLineIdx );
2160}
2161
2162
2163void SCH_IO_GEDA::parseLine( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2164{
2165 // L x1 y1 x2 y2 color [width capstyle dashstyle dashlength dashspace]
2166 wxStringTokenizer tok( aLine );
2167 tok.GetNextToken(); // skip 'L'
2168
2169 long x1 = 0, y1 = 0, x2 = 0, y2 = 0, color = 0, width = 0;
2170 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
2171
2172 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x1 );
2173 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y1 );
2174 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x2 );
2175 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y2 );
2176 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2177
2178 if( m_releaseVersion > 20000704 )
2179 {
2180 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &width );
2181 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
2182 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
2183 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
2184 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
2185 }
2186
2187 VECTOR2I start = toKiCad( static_cast<int>( x1 ), static_cast<int>( y1 ) );
2188 VECTOR2I end = toKiCad( static_cast<int>( x2 ), static_cast<int>( y2 ) );
2189
2190 auto line = std::make_unique<SCH_LINE>( start, LAYER_NOTES );
2191 line->SetEndPoint( end );
2192 line->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( width ) ),
2193 toLineStyle( static_cast<int>( dashstyle ) ) ) );
2194 m_screen->Append( line.release() );
2195 maybeParseAttributes( aFile, aLineIdx );
2196}
2197
2198
2199void SCH_IO_GEDA::parseBox( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2200{
2201 // B x y width height color [linewidth capstyle dashstyle dashlength dashspace
2202 // filltype fillwidth a1 p1 a2 p2]
2203 wxStringTokenizer tok( aLine );
2204 tok.GetNextToken(); // skip 'B'
2205
2206 long x = 0, y = 0, w = 0, h = 0, color = 0, linewidth = 0;
2207 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
2208 long filltype = 0, fillwidth = 0, a1 = 0, p1 = 0, a2 = 0, p2 = 0;
2209
2210 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
2211 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
2212 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &w );
2213 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &h );
2214 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2215
2216 if( m_releaseVersion > 20000704 )
2217 {
2218 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
2219 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
2220 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
2221 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
2222 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
2223 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
2224 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &fillwidth );
2225 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a1 );
2226 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p1 );
2227 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a2 );
2228 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p2 );
2229 }
2230
2231 VECTOR2I topLeft = toKiCad( static_cast<int>( x ), static_cast<int>( y + h ) );
2232 VECTOR2I botRight = toKiCad( static_cast<int>( x + w ), static_cast<int>( y ) );
2233
2234 auto rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_NOTES );
2235 rect->SetStart( topLeft );
2236 rect->SetEnd( botRight );
2237 rect->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
2238 toLineStyle( static_cast<int>( dashstyle ) ) ) );
2239
2240 FILL_T fill = toFillType( static_cast<int>( filltype ) );
2241
2242 if( fill != FILL_T::NO_FILL )
2243 {
2244 rect->SetFilled( true );
2245 rect->SetFillMode( fill );
2246 }
2247
2248 m_screen->Append( rect.release() );
2249 maybeParseAttributes( aFile, aLineIdx );
2250}
2251
2252
2253void SCH_IO_GEDA::parseCircle( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2254{
2255 // V cx cy radius color [linewidth capstyle dashstyle dashlength dashspace
2256 // filltype fillwidth a1 p1 a2 p2]
2257 wxStringTokenizer tok( aLine );
2258 tok.GetNextToken(); // skip 'V'
2259
2260 long cx = 0, cy = 0, radius = 0, color = 0, linewidth = 0;
2261 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
2262 long filltype = 0, fillwidth = 0, a1 = 0, p1 = 0, a2 = 0, p2 = 0;
2263
2264 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cx );
2265 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cy );
2266 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &radius );
2267 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2268
2269 if( m_releaseVersion > 20000704 )
2270 {
2271 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
2272 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
2273 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
2274 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
2275 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
2276 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
2277 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &fillwidth );
2278 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a1 );
2279 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p1 );
2280 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a2 );
2281 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p2 );
2282 }
2283
2284 VECTOR2I center = toKiCad( static_cast<int>( cx ), static_cast<int>( cy ) );
2285
2286 auto circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_NOTES );
2287 circle->SetCenter( center );
2288 circle->SetEnd( VECTOR2I( center.x + toKiCadDist( static_cast<int>( radius ) ), center.y ) );
2289 circle->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
2290 toLineStyle( static_cast<int>( dashstyle ) ) ) );
2291
2292 FILL_T fill = toFillType( static_cast<int>( filltype ) );
2293
2294 if( fill != FILL_T::NO_FILL )
2295 {
2296 circle->SetFilled( true );
2297 circle->SetFillMode( fill );
2298 }
2299
2300 m_screen->Append( circle.release() );
2301 maybeParseAttributes( aFile, aLineIdx );
2302}
2303
2304
2305void SCH_IO_GEDA::parseArc( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2306{
2307 // A cx cy radius startangle sweepangle color [linewidth capstyle dashstyle dashlength dashspace]
2308 wxStringTokenizer tok( aLine );
2309 tok.GetNextToken(); // skip 'A'
2310
2311 long cx = 0, cy = 0, radius = 0, startAngle = 0, sweepAngle = 0;
2312 long color = 0, linewidth = 0, capstyle = 0, dashstyle = 0;
2313 long dashlength = 0, dashspace = 0;
2314
2315 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cx );
2316 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cy );
2317 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &radius );
2318 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &startAngle );
2319 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &sweepAngle );
2320 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2321
2322 if( m_releaseVersion > 20000704 )
2323 {
2324 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
2325 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
2326 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
2327 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
2328 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
2329 }
2330
2331 VECTOR2I center = toKiCad( static_cast<int>( cx ), static_cast<int>( cy ) );
2332 int r = toKiCadDist( static_cast<int>( radius ) );
2333
2334 double startRad = DEG2RAD( static_cast<double>( startAngle ) );
2335 double endRad = DEG2RAD( static_cast<double>( startAngle + sweepAngle ) );
2336
2337 // Y-flip negates the Y component of angle calculations
2338 VECTOR2I arcStart( center.x + KiROUND( r * cos( startRad ) ),
2339 center.y - KiROUND( r * sin( startRad ) ) );
2340 VECTOR2I arcEnd( center.x + KiROUND( r * cos( endRad ) ),
2341 center.y - KiROUND( r * sin( endRad ) ) );
2342
2343 auto arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_NOTES );
2344 arc->SetCenter( center );
2345
2346 // Y-flip reverses angular sweep direction, so swap start and end
2347 // to preserve the original arc visual appearance.
2348 arc->SetStart( arcEnd );
2349 arc->SetEnd( arcStart );
2350
2351 arc->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
2352 toLineStyle( static_cast<int>( dashstyle ) ) ) );
2353 m_screen->Append( arc.release() );
2354 maybeParseAttributes( aFile, aLineIdx );
2355}
2356
2357
2358void SCH_IO_GEDA::parsePath( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2359{
2360 // H color width capstyle dashstyle dashlength dashspace
2361 // filltype fillwidth a1 p1 a2 p2 numlines
2362 // followed by numlines of SVG-style path data
2363 wxStringTokenizer tok( aLine );
2364 tok.GetNextToken(); // skip 'H'
2365
2366 long color = 0, width = 0, capstyle = 0, dashstyle = 0;
2367 long dashlength = 0, dashspace = 0, filltype = 0, fillwidth = 0;
2368 long a1 = 0, p1 = 0, a2 = 0, p2 = 0, numlines = 0;
2369
2370 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
2371 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &width );
2372 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
2373 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
2374 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
2375 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
2376 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
2377 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &fillwidth );
2378 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a1 );
2379 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p1 );
2380 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a2 );
2381 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p2 );
2382 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &numlines );
2383
2384 // Collect all path data lines
2385 wxString pathData;
2386
2387 for( long i = 0; i < numlines && aLineIdx < aFile.GetLineCount(); i++ )
2388 {
2389 if( i > 0 )
2390 pathData += wxT( " " );
2391
2392 pathData += aFile.GetLine( aLineIdx ).Trim();
2393 aLineIdx++;
2394 }
2395
2396 VECTOR2I startPt;
2397 VECTOR2I currentPt;
2398 STROKE_PARAMS stroke( toKiCadDist( static_cast<int>( width ) ),
2399 toLineStyle( static_cast<int>( dashstyle ) ) );
2400
2401 auto emitSeg = [&]( const VECTOR2I& aFrom, const VECTOR2I& aTo )
2402 {
2403 auto seg = std::make_unique<SCH_LINE>( aFrom, LAYER_NOTES );
2404 seg->SetEndPoint( aTo );
2405 seg->SetStroke( stroke );
2406 m_screen->Append( seg.release() );
2407 };
2408
2409 wxStringTokenizer pathTok( pathData, wxT( " ,\t" ) );
2410
2411 // For relative (lowercase) SVG commands, deltas are in gEDA mils (Y-up).
2412 // Convert to KiCad IU (Y-down) by scaling X and negating+scaling Y.
2413 auto relToAbs = [&]( long dx, long dy ) -> VECTOR2I
2414 {
2415 return currentPt + VECTOR2I( toKiCadDist( static_cast<int>( dx ) ),
2416 -toKiCadDist( static_cast<int>( dy ) ) );
2417 };
2418
2419 while( pathTok.HasMoreTokens() )
2420 {
2421 wxString token = pathTok.GetNextToken();
2422
2423 if( token == wxT( "M" ) )
2424 {
2425 long px = 0, py = 0;
2426
2427 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
2428 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
2429 {
2430 currentPt = toKiCad( static_cast<int>( px ), static_cast<int>( py ) );
2431 startPt = currentPt;
2432 }
2433 }
2434 else if( token == wxT( "m" ) )
2435 {
2436 long dx = 0, dy = 0;
2437
2438 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dx )
2439 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dy ) )
2440 {
2441 currentPt = relToAbs( dx, dy );
2442 startPt = currentPt;
2443 }
2444 }
2445 else if( token == wxT( "L" ) )
2446 {
2447 long px = 0, py = 0;
2448
2449 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
2450 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
2451 {
2452 VECTOR2I pt = toKiCad( static_cast<int>( px ), static_cast<int>( py ) );
2453 emitSeg( currentPt, pt );
2454 currentPt = pt;
2455 }
2456 }
2457 else if( token == wxT( "l" ) )
2458 {
2459 long dx = 0, dy = 0;
2460
2461 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dx )
2462 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dy ) )
2463 {
2464 VECTOR2I pt = relToAbs( dx, dy );
2465 emitSeg( currentPt, pt );
2466 currentPt = pt;
2467 }
2468 }
2469 else if( token == wxT( "C" ) )
2470 {
2471 long cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, px = 0, py = 0;
2472
2473 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx1 )
2474 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy1 )
2475 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx2 )
2476 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy2 )
2477 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
2478 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
2479 {
2480 VECTOR2I cp1 = toKiCad( static_cast<int>( cx1 ), static_cast<int>( cy1 ) );
2481 VECTOR2I cp2 = toKiCad( static_cast<int>( cx2 ), static_cast<int>( cy2 ) );
2482 VECTOR2I endPt = toKiCad( static_cast<int>( px ), static_cast<int>( py ) );
2483
2484 auto bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_NOTES );
2485 bezier->SetStart( currentPt );
2486 bezier->SetBezierC1( cp1 );
2487 bezier->SetBezierC2( cp2 );
2488 bezier->SetEnd( endPt );
2489 bezier->SetStroke( stroke );
2490 bezier->RebuildBezierToSegmentsPointsList(
2491 schIUScale.mmToIU( ARC_LOW_DEF_MM ) );
2492 m_screen->Append( bezier.release() );
2493 currentPt = endPt;
2494 }
2495 }
2496 else if( token == wxT( "c" ) )
2497 {
2498 long cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, dx = 0, dy = 0;
2499
2500 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx1 )
2501 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy1 )
2502 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx2 )
2503 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy2 )
2504 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dx )
2505 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &dy ) )
2506 {
2507 VECTOR2I cp1 = relToAbs( cx1, cy1 );
2508 VECTOR2I cp2 = relToAbs( cx2, cy2 );
2509 VECTOR2I endPt = relToAbs( dx, dy );
2510
2511 auto bezier = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_NOTES );
2512 bezier->SetStart( currentPt );
2513 bezier->SetBezierC1( cp1 );
2514 bezier->SetBezierC2( cp2 );
2515 bezier->SetEnd( endPt );
2516 bezier->SetStroke( stroke );
2517 bezier->RebuildBezierToSegmentsPointsList(
2518 schIUScale.mmToIU( ARC_LOW_DEF_MM ) );
2519 m_screen->Append( bezier.release() );
2520 currentPt = endPt;
2521 }
2522 }
2523 else if( token == wxT( "Z" ) || token == wxT( "z" ) )
2524 {
2525 if( currentPt != startPt )
2526 {
2527 emitSeg( currentPt, startPt );
2528 currentPt = startPt;
2529 }
2530 }
2531 }
2532
2533 maybeParseAttributes( aFile, aLineIdx );
2534}
2535
2536
2537void SCH_IO_GEDA::parsePicture( const wxString& aLine, wxTextFile& aFile, size_t& aLineIdx )
2538{
2539 // G x y width height angle mirrored embedded
2540 wxStringTokenizer tok( aLine );
2541 tok.GetNextToken(); // skip 'G'
2542
2543 long x = 0, y = 0, w = 0, h = 0, angle = 0, mirror = 0, embedded = 0;
2544
2545 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
2546 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
2547 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &w );
2548 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &h );
2549 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &angle );
2550 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &mirror );
2551 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &embedded );
2552
2553 wxString filename;
2554
2555 if( aLineIdx < aFile.GetLineCount() )
2556 {
2557 filename = aFile.GetLine( aLineIdx ).Trim().Trim( false );
2558 aLineIdx++;
2559 }
2560
2561 wxString base64Data;
2562
2563 if( embedded )
2564 {
2565 while( aLineIdx < aFile.GetLineCount() )
2566 {
2567 wxString dataLine = aFile.GetLine( aLineIdx );
2568 aLineIdx++;
2569
2570 if( dataLine.Trim() == wxT( "." ) )
2571 break;
2572
2573 base64Data += dataLine.Trim();
2574 }
2575 }
2576
2577 auto bitmap = std::make_unique<SCH_BITMAP>();
2578 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
2579 bool loaded = false;
2580
2581 int savedLoadFlags = wxImage::GetDefaultLoadFlags();
2582 wxImage::SetDefaultLoadFlags( savedLoadFlags & ~wxImage::Load_Verbose );
2583
2584 if( embedded && !base64Data.IsEmpty() )
2585 {
2586 wxMemoryBuffer buf = wxBase64Decode( base64Data );
2587
2588 if( buf.GetDataLen() > 0 )
2589 loaded = refImage.ReadImageFile( buf );
2590 }
2591 else if( !filename.IsEmpty() )
2592 {
2593 wxFileName imgFile( filename );
2594
2595 if( !imgFile.IsAbsolute() && m_filename.IsOk() )
2596 imgFile.MakeAbsolute( m_filename.GetPath() );
2597
2598 if( imgFile.FileExists() )
2599 loaded = refImage.ReadImageFile( imgFile.GetFullPath() );
2600 }
2601
2602 wxImage::SetDefaultLoadFlags( savedLoadFlags );
2603
2604 if( !loaded )
2605 return;
2606
2607 // gEDA specifies x,y as the lower-left corner of the picture bounding box.
2608 // Compute center position from the lower-left corner + size.
2609 int gCenterX = static_cast<int>( x ) + static_cast<int>( w ) / 2;
2610 int gCenterY = static_cast<int>( y ) + static_cast<int>( h ) / 2;
2611 VECTOR2I center = toKiCad( gCenterX, gCenterY );
2612
2613 int targetWidth = toKiCadDist( static_cast<int>( w ) );
2614 VECTOR2I imgSize = refImage.GetSize();
2615
2616 if( imgSize.x > 0 )
2617 {
2618 double scaleFactor = static_cast<double>( targetWidth ) / imgSize.x;
2619 refImage.SetImageScale( scaleFactor );
2620 }
2621
2622 bitmap->SetPosition( center );
2623
2624 if( mirror )
2625 bitmap->MirrorHorizontally( center.x );
2626
2627 for( long a = angle; a > 0; a -= 90 )
2628 bitmap->Rotate( center, false );
2629
2630 m_maxY = std::max( m_maxY, static_cast<int>( y + h ) );
2631 m_screen->Append( bitmap.release() );
2632}
2633
2634
2635// ==========================================================================
2636// Symbol loading
2637// ==========================================================================
2638
2640{
2642 return;
2643
2645
2646 // Check GEDADATA environment variable first (highest priority for system symbols)
2647 wxString gedaData;
2648
2649 if( wxGetEnv( wxT( "GEDADATA" ), &gedaData ) && !gedaData.IsEmpty() )
2650 {
2651 wxString envSymDir = gedaData + wxFileName::GetPathSeparator() + wxT( "sym" );
2652
2653 if( wxDir::Exists( envSymDir ) )
2654 scanSymbolDir( envSymDir );
2655 }
2656
2657 // Scan standard gEDA and Lepton-EDA symbol directories
2658 static const wxString defaultPaths[] = {
2659 wxT( "/usr/share/gEDA/sym" ),
2660 wxT( "/usr/share/geda-symbols" ),
2661 wxT( "/usr/local/share/gEDA/sym" ),
2662 wxT( "/usr/local/share/geda-symbols" ),
2663 wxT( "/usr/share/lepton-eda/sym" ),
2664 wxT( "/usr/local/share/lepton-eda/sym" ),
2665 };
2666
2667 for( const wxString& dir : defaultPaths )
2668 {
2669 if( wxDir::Exists( dir ) )
2670 scanSymbolDir( dir );
2671 }
2672
2673 // Scan XDG data directories for Lepton-EDA symbols.
2674 // $XDG_DATA_DIRS defaults to /usr/local/share:/usr/share per the spec.
2675 wxString xdgDataDirs;
2676
2677 if( wxGetEnv( wxT( "XDG_DATA_DIRS" ), &xdgDataDirs ) && !xdgDataDirs.IsEmpty() )
2678 {
2679 wxStringTokenizer xdgTok( xdgDataDirs, wxT( ":" ) );
2680
2681 while( xdgTok.HasMoreTokens() )
2682 {
2683 wxString dataDir = xdgTok.GetNextToken();
2684
2685 if( dataDir.IsEmpty() )
2686 continue;
2687
2688 wxString leptonSymDir = dataDir + wxFileName::GetPathSeparator()
2689 + wxT( "lepton-eda" ) + wxFileName::GetPathSeparator()
2690 + wxT( "sym" );
2691
2692 if( wxDir::Exists( leptonSymDir ) )
2693 scanSymbolDir( leptonSymDir );
2694
2695 wxString gedaSymDir = dataDir + wxFileName::GetPathSeparator()
2696 + wxT( "gEDA" ) + wxFileName::GetPathSeparator()
2697 + wxT( "sym" );
2698
2699 if( wxDir::Exists( gedaSymDir ) )
2700 scanSymbolDir( gedaSymDir );
2701 }
2702 }
2703
2704 // Scan relative to the schematic file location.
2705 // Properties-based paths are handled at the end regardless of whether
2706 // a valid schematic path exists.
2707 wxString schDir;
2708
2709 if( m_filename.IsOk() )
2710 schDir = m_filename.GetPath();
2711
2712 // Scan user-level RC files for additional library paths
2713 wxString homeDir = wxGetHomeDir();
2714
2715 if( !homeDir.IsEmpty() )
2716 {
2717 wxString userGedaDir = homeDir + wxFileName::GetPathSeparator() + wxT( ".gEDA" );
2718
2719 parseRcFileForLibraries( userGedaDir + wxFileName::GetPathSeparator() + wxT( "gafrc" ),
2720 userGedaDir );
2721 parseRcFileForLibraries( userGedaDir + wxFileName::GetPathSeparator() + wxT( "gschemrc" ),
2722 userGedaDir );
2723
2724 wxString leptonDir;
2725 wxString sep( wxFileName::GetPathSeparator() );
2726
2727 if( wxGetEnv( wxT( "XDG_CONFIG_HOME" ), &leptonDir ) && !leptonDir.IsEmpty() )
2728 leptonDir += sep + wxT( "lepton-eda" );
2729 else
2730 leptonDir = homeDir + sep + wxT( ".config" ) + sep + wxT( "lepton-eda" );
2731
2732 parseRcFileForLibraries( leptonDir + wxFileName::GetPathSeparator() + wxT( "gafrc" ),
2733 leptonDir );
2734 }
2735
2736 // Parse project-level RC files and scan schematic-relative directories
2737 if( !schDir.IsEmpty() )
2738 {
2739 parseRcFileForLibraries( schDir + wxFileName::GetPathSeparator() + wxT( "gafrc" ),
2740 schDir );
2741 parseRcFileForLibraries( schDir + wxFileName::GetPathSeparator() + wxT( "gschemrc" ),
2742 schDir );
2743
2744 // Parse Lepton-EDA INI-style config files (lepton.conf or geda.conf)
2745 // for [libs] section with component-library entries
2746 static const wxString configNames[] = {
2747 wxT( "lepton.conf" ),
2748 wxT( "geda.conf" ),
2749 };
2750
2751 for( const wxString& configName : configNames )
2752 {
2753 wxString confPath = schDir + wxFileName::GetPathSeparator() + configName;
2754
2755 if( !wxFileExists( confPath ) )
2756 continue;
2757
2758 wxTextFile confFile;
2759
2760 if( !confFile.Open( confPath ) )
2761 continue;
2762
2763 bool inLibsSection = false;
2764
2765 for( size_t i = 0; i < confFile.GetLineCount(); ++i )
2766 {
2767 wxString line = confFile.GetLine( i );
2768 line.Trim( true );
2769 line.Trim( false );
2770
2771 if( line.IsEmpty() || line[0] == '#' || line[0] == ';' )
2772 continue;
2773
2774 if( line[0] == '[' )
2775 {
2776 inLibsSection = ( line.CmpNoCase( wxT( "[libs]" ) ) == 0 );
2777 continue;
2778 }
2779
2780 if( !inLibsSection )
2781 continue;
2782
2783 if( line.StartsWith( wxT( "component-library" ) ) )
2784 {
2785 int eqPos = line.Find( '=' );
2786
2787 if( eqPos == wxNOT_FOUND )
2788 continue;
2789
2790 wxString libPath = line.Mid( eqPos + 1 ).Trim( false ).Trim( true );
2791 wxFileName libDir( libPath );
2792
2793 if( !libDir.IsAbsolute() )
2794 {
2795 libDir.MakeAbsolute( schDir );
2796 libPath = libDir.GetFullPath();
2797 }
2798
2799 if( wxDir::Exists( libPath ) )
2800 scanSymbolDir( libPath );
2801 }
2802 }
2803
2804 break;
2805 }
2806
2807 scanSymbolDir( schDir );
2808
2809 wxString symDir = schDir + wxFileName::GetPathSeparator() + wxT( "sym" );
2810
2811 if( wxDir::Exists( symDir ) )
2812 scanSymbolDir( symDir );
2813
2814 wxFileName parentDir( schDir );
2815 parentDir.RemoveLastDir();
2816 wxString parentSymDir =
2817 parentDir.GetPath() + wxFileName::GetPathSeparator() + wxT( "sym" );
2818
2819 if( wxDir::Exists( parentSymDir ) )
2820 scanSymbolDir( parentSymDir );
2821 }
2822
2823 if( m_properties )
2824 {
2825 auto it = m_properties->find( "sym_search_paths" );
2826
2827 if( it != m_properties->end() )
2828 {
2829 wxString paths = it->second;
2830 wxStringTokenizer tok( paths, wxT( "\n;" ) );
2831
2832 while( tok.HasMoreTokens() )
2833 {
2834 wxString dir = tok.GetNextToken().Trim().Trim( false );
2835
2836 if( !dir.IsEmpty() && wxDir::Exists( dir ) )
2837 scanSymbolDir( dir );
2838 }
2839 }
2840 }
2841}
2842
2843
2844void SCH_IO_GEDA::scanSymbolDir( const wxString& aDir, int aDepth )
2845{
2846 if( aDepth > 20 )
2847 return;
2848
2849 wxDir dir( aDir );
2850
2851 if( !dir.IsOpened() )
2852 return;
2853
2854 wxString filename;
2855 bool cont = dir.GetFirst( &filename );
2856
2857 while( cont )
2858 {
2859 wxString fullPath = aDir + wxFileName::GetPathSeparator() + filename;
2860
2861 if( wxDir::Exists( fullPath ) )
2862 {
2863 scanSymbolDir( fullPath, aDepth + 1 );
2864 }
2865 else if( filename.EndsWith( wxT( ".sym" ) ) )
2866 {
2867 SYM_CACHE_ENTRY entry;
2868 entry.path = fullPath;
2869 m_symLibrary[filename] = std::move( entry );
2870 }
2871
2872 cont = dir.GetNext( &filename );
2873 }
2874}
2875
2876
2877void SCH_IO_GEDA::parseRcFileForLibraries( const wxString& aPath, const wxString& aBaseDir )
2878{
2879 if( !wxFileExists( aPath ) )
2880 return;
2881
2882 wxTextFile rcFile;
2883
2884 if( !rcFile.Open( aPath ) )
2885 return;
2886
2887 for( size_t i = 0; i < rcFile.GetLineCount(); ++i )
2888 {
2889 wxString line = rcFile.GetLine( i );
2890 line.Trim( true );
2891 line.Trim( false );
2892
2893 if( !line.StartsWith( wxT( "(component-library-search " ) )
2894 && !line.StartsWith( wxT( "(component-library " ) ) )
2895 {
2896 continue;
2897 }
2898
2899 int firstQuote = line.Find( '"' );
2900
2901 if( firstQuote == wxNOT_FOUND )
2902 continue;
2903
2904 // Find the closing quote of the first argument. The two-argument
2905 // form (component-library "path" "name") has a second quoted
2906 // string that we ignore.
2907 int secondQuote = line.Mid( firstQuote + 1 ).Find( '"' );
2908
2909 if( secondQuote != wxNOT_FOUND )
2910 secondQuote += firstQuote + 1;
2911
2912 if( secondQuote == wxNOT_FOUND || secondQuote <= firstQuote )
2913 continue;
2914
2915 wxString libPath = line.Mid( firstQuote + 1, secondQuote - firstQuote - 1 );
2916 wxFileName libDir( libPath );
2917
2918 if( !libDir.IsAbsolute() )
2919 {
2920 libDir.MakeAbsolute( aBaseDir );
2921 libPath = libDir.GetFullPath();
2922 }
2923
2924 if( wxDir::Exists( libPath ) )
2925 scanSymbolDir( libPath );
2926 }
2927}
2928
2929
2930std::unique_ptr<LIB_SYMBOL> SCH_IO_GEDA::loadSymbolFile( const wxString& aPath,
2931 wxString* aSymversion,
2932 wxString* aNetAttr )
2933{
2934 wxTextFile file;
2935
2936 if( !file.Open( aPath ) )
2937 return nullptr;
2938
2939 if( file.GetLineCount() == 0 )
2940 return nullptr;
2941
2942 // Validate version line
2943 wxString firstLine = file.GetFirstLine().Trim( false );
2944
2945 if( !firstLine.StartsWith( wxT( "v " ) ) )
2946 return nullptr;
2947
2948 wxFileName fn( aPath );
2949 wxString symName = fn.GetName();
2950 auto libSym = std::make_unique<LIB_SYMBOL>( symName );
2951 libSym->SetShowPinNumbers( true );
2952 libSym->SetShowPinNames( true );
2953
2954 size_t lineIdx = 1; // Skip version line
2955
2956 while( lineIdx < file.GetLineCount() )
2957 {
2958 wxString line = file.GetLine( lineIdx );
2959
2960 if( line.IsEmpty() )
2961 {
2962 lineIdx++;
2963 continue;
2964 }
2965
2966 wxChar type = line[0];
2967 lineIdx++;
2968
2969 switch( type )
2970 {
2971 case 'P':
2972 {
2973 wxStringTokenizer tok( line );
2974 tok.GetNextToken(); // skip 'P'
2975 long px1 = 0, py1 = 0, px2 = 0, py2 = 0, pc = 0, pt = 0, pw = 0;
2976
2977 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &px1 );
2978 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &py1 );
2979 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &px2 );
2980 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &py2 );
2981 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pc );
2982 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pt );
2983 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &pw );
2984
2985 std::vector<GEDA_ATTR> pinAttrs = maybeParseAttributes( file, lineIdx );
2986 addSymbolPin( *libSym, static_cast<int>( px1 ), static_cast<int>( py1 ),
2987 static_cast<int>( px2 ), static_cast<int>( py2 ),
2988 static_cast<int>( pw ), pinAttrs );
2989 break;
2990 }
2991
2992 case 'L':
2993 case 'B':
2994 case 'V':
2995 case 'A':
2996 case 'H':
2997 addSymbolGraphic( *libSym, line, file, lineIdx, type );
2998 break;
2999
3000 case 'T':
3001 {
3002 // T x y color size vis show_nv angle align num_lines
3003 wxStringTokenizer tok( line );
3004 tok.GetNextToken(); // skip 'T'
3005 long numLines = 1;
3006
3007 for( int i = 0; i < 9 && tok.HasMoreTokens(); i++ )
3008 {
3009 wxString fieldVal = tok.GetNextToken();
3010
3011 if( i == 8 )
3012 fieldVal.ToLong( &numLines );
3013 }
3014
3015 for( long i = 0; i < numLines && lineIdx < file.GetLineCount(); i++ )
3016 {
3017 wxString textLine = file.GetLine( lineIdx );
3018
3019 if( aSymversion && textLine.StartsWith( wxT( "symversion=" ) ) )
3020 *aSymversion = textLine.Mid( 11 );
3021
3022 if( aNetAttr && textLine.StartsWith( wxT( "net=" ) ) )
3023 *aNetAttr = textLine.Mid( 4 );
3024
3025 lineIdx++;
3026 }
3027
3028 maybeParseAttributes( file, lineIdx );
3029 break;
3030 }
3031
3032 case '{':
3033 {
3034 while( lineIdx < file.GetLineCount() )
3035 {
3036 if( file.GetLine( lineIdx ).Trim() == wxT( "}" ) )
3037 {
3038 lineIdx++;
3039 break;
3040 }
3041
3042 lineIdx++;
3043 }
3044
3045 break;
3046 }
3047
3048 case 'v':
3049 default:
3050 break;
3051 }
3052 }
3053
3054 return libSym;
3055}
3056
3057
3058void SCH_IO_GEDA::addSymbolPin( LIB_SYMBOL& aSymbol, int aX1, int aY1, int aX2, int aY2,
3059 int aWhichEnd, const std::vector<GEDA_ATTR>& aAttrs )
3060{
3061 // whichend determines which endpoint is the connection point (active end)
3062 // 0 = (x1,y1) is connection point, 1 = (x2,y2) is connection point
3063 int connX = 0, connY = 0, otherX = 0, otherY = 0;
3064
3065 if( aWhichEnd == 0 )
3066 {
3067 connX = aX1; connY = aY1;
3068 otherX = aX2; otherY = aY2;
3069 }
3070 else
3071 {
3072 connX = aX2; connY = aY2;
3073 otherX = aX1; otherY = aY1;
3074 }
3075
3076 // Symbol coordinates use the same mil system but are relative to the symbol origin.
3077 // The connection point goes at the tip, the other end is toward the symbol body.
3078 VECTOR2I connPt( toKiCadDist( connX ), -toKiCadDist( connY ) );
3079 VECTOR2I bodyPt( toKiCadDist( otherX ), -toKiCadDist( otherY ) );
3080
3081 // PIN_ORIENTATION describes which direction the pin body extends FROM the
3082 // connection point, so the delta must point from connection toward body.
3083 int dx = otherX - connX;
3084 int dy = otherY - connY;
3085 int length = toKiCadDist( KiROUND( std::hypot( dx, dy ) ) );
3086
3088
3089 if( dx > 0 )
3091 else if( dx < 0 )
3093 else if( dy > 0 )
3094 orient = PIN_ORIENTATION::PIN_UP;
3095 else if( dy < 0 )
3097
3098 wxString pinnumber = findAttr( aAttrs, wxT( "pinnumber" ) );
3099 wxString pinlabel = findAttr( aAttrs, wxT( "pinlabel" ) );
3100 wxString pintypeStr = findAttr( aAttrs, wxT( "pintype" ) );
3101
3102 if( pinnumber.IsEmpty() )
3103 pinnumber = wxT( "?" );
3104
3105 if( pinlabel.IsEmpty() )
3106 pinlabel = pinnumber;
3107
3109
3110 if( pintypeStr == wxT( "in" ) )
3112 else if( pintypeStr == wxT( "out" ) )
3114 else if( pintypeStr == wxT( "io" ) )
3115 elecType = ELECTRICAL_PINTYPE::PT_BIDI;
3116 else if( pintypeStr == wxT( "oc" ) )
3118 else if( pintypeStr == wxT( "oe" ) )
3120 else if( pintypeStr == wxT( "pas" ) )
3122 else if( pintypeStr == wxT( "tp" ) )
3124 else if( pintypeStr == wxT( "tri" ) )
3126 else if( pintypeStr == wxT( "clk" ) )
3128 else if( pintypeStr == wxT( "pwr" ) )
3130
3131 SCH_PIN* pin = new SCH_PIN( &aSymbol );
3132 pin->SetPosition( connPt );
3133 pin->SetLength( length );
3134 pin->SetOrientation( orient );
3135 pin->SetNumber( pinnumber );
3136 pin->SetName( pinlabel );
3137 pin->SetType( elecType );
3138
3139 aSymbol.AddDrawItem( pin );
3140}
3141
3142
3143void SCH_IO_GEDA::addSymbolGraphic( LIB_SYMBOL& aSymbol, const wxString& aLine,
3144 wxTextFile& aFile, size_t& aLineIdx, wxChar aType )
3145{
3146 switch( aType )
3147 {
3148 case 'L':
3149 {
3150 wxStringTokenizer tok( aLine );
3151 tok.GetNextToken(); // skip 'L'
3152 long x1 = 0, y1 = 0, x2 = 0, y2 = 0, color = 0, width = 0;
3153 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
3154
3155 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x1 );
3156 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y1 );
3157 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x2 );
3158 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y2 );
3159 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
3160 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &width );
3161 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
3162 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
3163 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
3164 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
3165
3166 VECTOR2I start( toKiCadDist( static_cast<int>( x1 ) ),
3167 -toKiCadDist( static_cast<int>( y1 ) ) );
3168 VECTOR2I end( toKiCadDist( static_cast<int>( x2 ) ),
3169 -toKiCadDist( static_cast<int>( y2 ) ) );
3170
3172 shape->AddPoint( start );
3173 shape->AddPoint( end );
3174 shape->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( width ) ),
3175 toLineStyle( static_cast<int>( dashstyle ) ) ) );
3176 aSymbol.AddDrawItem( shape );
3177 break;
3178 }
3179
3180 case 'B':
3181 {
3182 wxStringTokenizer tok( aLine );
3183 tok.GetNextToken(); // skip 'B'
3184 long x = 0, y = 0, w = 0, h = 0, color = 0, linewidth = 0;
3185 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
3186 long filltype = 0;
3187
3188 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &x );
3189 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &y );
3190 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &w );
3191 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &h );
3192 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
3193 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
3194 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
3195 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
3196 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
3197 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
3198 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
3199
3200 VECTOR2I topLeft( toKiCadDist( static_cast<int>( x ) ),
3201 -toKiCadDist( static_cast<int>( y + h ) ) );
3202 VECTOR2I botRight( toKiCadDist( static_cast<int>( x + w ) ),
3203 -toKiCadDist( static_cast<int>( y ) ) );
3204
3206 rect->SetStart( topLeft );
3207 rect->SetEnd( botRight );
3208 rect->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
3209 toLineStyle( static_cast<int>( dashstyle ) ) ) );
3210
3211 FILL_T fill = toFillType( static_cast<int>( filltype ) );
3212
3213 if( fill != FILL_T::NO_FILL )
3214 {
3215 rect->SetFilled( true );
3216 rect->SetFillMode( fill );
3217 }
3218
3219 aSymbol.AddDrawItem( rect );
3220 break;
3221 }
3222
3223 case 'V':
3224 {
3225 wxStringTokenizer tok( aLine );
3226 tok.GetNextToken(); // skip 'V'
3227 long cx = 0, cy = 0, r = 0, color = 0, linewidth = 0;
3228 long capstyle = 0, dashstyle = 0, dashlength = 0, dashspace = 0;
3229 long filltype = 0;
3230
3231 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cx );
3232 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cy );
3233 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &r );
3234 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
3235 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
3236 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
3237 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
3238 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
3239 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
3240 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
3241
3242 VECTOR2I center( toKiCadDist( static_cast<int>( cx ) ),
3243 -toKiCadDist( static_cast<int>( cy ) ) );
3244
3246 circle->SetCenter( center );
3247 circle->SetEnd( VECTOR2I( center.x + toKiCadDist( static_cast<int>( r ) ), center.y ) );
3248 circle->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
3249 toLineStyle( static_cast<int>( dashstyle ) ) ) );
3250
3251 FILL_T fill = toFillType( static_cast<int>( filltype ) );
3252
3253 if( fill != FILL_T::NO_FILL )
3254 {
3255 circle->SetFilled( true );
3256 circle->SetFillMode( fill );
3257 }
3258
3259 aSymbol.AddDrawItem( circle );
3260 break;
3261 }
3262
3263 case 'A':
3264 {
3265 wxStringTokenizer tok( aLine );
3266 tok.GetNextToken(); // skip 'A'
3267 long cx = 0, cy = 0, r = 0, sa = 0, da = 0;
3268 long color = 0, linewidth = 0;
3269
3270 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cx );
3271 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &cy );
3272 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &r );
3273 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &sa );
3274 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &da );
3275 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
3276 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &linewidth );
3277
3278 VECTOR2I center( toKiCadDist( static_cast<int>( cx ) ),
3279 -toKiCadDist( static_cast<int>( cy ) ) );
3280 int radius = toKiCadDist( static_cast<int>( r ) );
3281
3282 double startRad = DEG2RAD( static_cast<double>( sa ) );
3283 double endRad = DEG2RAD( static_cast<double>( sa + da ) );
3284
3285 VECTOR2I arcStart( center.x + KiROUND( radius * cos( startRad ) ),
3286 center.y - KiROUND( radius * sin( startRad ) ) );
3287 VECTOR2I arcEnd( center.x + KiROUND( radius * cos( endRad ) ),
3288 center.y - KiROUND( radius * sin( endRad ) ) );
3289
3291 arc->SetCenter( center );
3292
3293 // Symbol coordinates use negative-Y, which reverses angular sweep
3294 // direction. Swap start/end to preserve the correct arc extent.
3295 arc->SetStart( arcEnd );
3296 arc->SetEnd( arcStart );
3297 arc->SetStroke( STROKE_PARAMS( toKiCadDist( static_cast<int>( linewidth ) ),
3299 aSymbol.AddDrawItem( arc );
3300 break;
3301 }
3302
3303 case 'H':
3304 {
3305 wxStringTokenizer tok( aLine );
3306 tok.GetNextToken(); // skip 'H'
3307
3308 long color = 0, width = 0, capstyle = 0, dashstyle = 0;
3309 long dashlength = 0, dashspace = 0, filltype = 0, fillwidth = 0;
3310 long a1 = 0, p1 = 0, a2 = 0, p2 = 0, numlines = 0;
3311
3312 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &color );
3313 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &width );
3314 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &capstyle );
3315 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashstyle );
3316 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashlength );
3317 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &dashspace );
3318 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &filltype );
3319 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &fillwidth );
3320 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a1 );
3321 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p1 );
3322 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &a2 );
3323 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &p2 );
3324 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &numlines );
3325
3326 wxString pathData;
3327
3328 for( long i = 0; i < numlines && aLineIdx < aFile.GetLineCount(); i++ )
3329 {
3330 if( i > 0 )
3331 pathData += wxT( " " );
3332
3333 pathData += aFile.GetLine( aLineIdx ).Trim();
3334 aLineIdx++;
3335 }
3336
3337 std::vector<VECTOR2I> polyPts;
3338 VECTOR2I startPt;
3339 VECTOR2I currentPt;
3340 STROKE_PARAMS symStroke( toKiCadDist( static_cast<int>( width ) ),
3341 toLineStyle( static_cast<int>( dashstyle ) ) );
3342 FILL_T symFill = toFillType( static_cast<int>( filltype ) );
3343
3344 auto flushPoly = [&]()
3345 {
3346 if( polyPts.size() < 2 )
3347 return;
3348
3350
3351 for( const VECTOR2I& pt : polyPts )
3352 poly->AddPoint( pt );
3353
3354 poly->SetStroke( symStroke );
3355
3356 if( symFill != FILL_T::NO_FILL )
3357 {
3358 poly->SetFilled( true );
3359 poly->SetFillMode( symFill );
3360 }
3361
3362 aSymbol.AddDrawItem( poly );
3363 polyPts.clear();
3364 };
3365
3366 wxStringTokenizer pathTok( pathData, wxT( " ,\t" ) );
3367
3368 while( pathTok.HasMoreTokens() )
3369 {
3370 wxString pathToken = pathTok.GetNextToken();
3371
3372 // Symbol path coordinates are in mils (Y-up). Convert to IU (Y-down).
3373 auto symAbsPt = [this]( long aX, long aY ) -> VECTOR2I
3374 {
3375 return VECTOR2I( toKiCadDist( static_cast<int>( aX ) ),
3376 -toKiCadDist( static_cast<int>( aY ) ) );
3377 };
3378
3379 auto symRelPt = [this, &currentPt]( long aDx, long aDy ) -> VECTOR2I
3380 {
3381 return currentPt + VECTOR2I( toKiCadDist( static_cast<int>( aDx ) ),
3382 -toKiCadDist( static_cast<int>( aDy ) ) );
3383 };
3384
3385 bool isRelative = ( pathToken.IsAscii() && islower( pathToken[0u] ) );
3386
3387 if( pathToken == wxT( "M" ) || pathToken == wxT( "m" ) )
3388 {
3389 long px = 0, py = 0;
3390
3391 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
3392 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
3393 {
3394 currentPt = isRelative ? symRelPt( px, py ) : symAbsPt( px, py );
3395 startPt = currentPt;
3396
3397 if( polyPts.empty() )
3398 polyPts.push_back( currentPt );
3399 }
3400 }
3401 else if( pathToken == wxT( "L" ) || pathToken == wxT( "l" ) )
3402 {
3403 long px = 0, py = 0;
3404
3405 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
3406 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
3407 {
3408 currentPt = isRelative ? symRelPt( px, py ) : symAbsPt( px, py );
3409 polyPts.push_back( currentPt );
3410 }
3411 }
3412 else if( pathToken == wxT( "C" ) || pathToken == wxT( "c" ) )
3413 {
3414 long cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, px = 0, py = 0;
3415
3416 if( pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx1 )
3417 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy1 )
3418 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cx2 )
3419 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &cy2 )
3420 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &px )
3421 && pathTok.HasMoreTokens() && pathTok.GetNextToken().ToLong( &py ) )
3422 {
3423 flushPoly();
3424
3425 VECTOR2I cp1 = isRelative ? symRelPt( cx1, cy1 ) : symAbsPt( cx1, cy1 );
3426 VECTOR2I cp2 = isRelative ? symRelPt( cx2, cy2 ) : symAbsPt( cx2, cy2 );
3427 VECTOR2I endPt = isRelative ? symRelPt( px, py ) : symAbsPt( px, py );
3428
3430 bezier->SetStart( currentPt );
3431 bezier->SetBezierC1( cp1 );
3432 bezier->SetBezierC2( cp2 );
3433 bezier->SetEnd( endPt );
3434 bezier->SetStroke( symStroke );
3436 schIUScale.mmToIU( ARC_LOW_DEF_MM ) );
3437
3438 if( symFill != FILL_T::NO_FILL )
3439 {
3440 bezier->SetFilled( true );
3441 bezier->SetFillMode( symFill );
3442 }
3443
3444 aSymbol.AddDrawItem( bezier );
3445 currentPt = endPt;
3446 polyPts.push_back( currentPt );
3447 }
3448 }
3449 else if( pathToken == wxT( "Z" ) || pathToken == wxT( "z" ) )
3450 {
3451 if( currentPt != startPt )
3452 {
3453 polyPts.push_back( startPt );
3454 currentPt = startPt;
3455 }
3456 }
3457 }
3458
3459 flushPoly();
3460
3461 break;
3462 }
3463
3464 default:
3465 break;
3466 }
3467
3468 maybeParseAttributes( aFile, aLineIdx );
3469}
3470
3471
3475static int editDistance( const wxString& a, const wxString& b )
3476{
3477 size_t m = a.length();
3478 size_t n = b.length();
3479
3480 std::vector<int> prev( n + 1 );
3481 std::vector<int> curr( n + 1 );
3482
3483 for( size_t j = 0; j <= n; ++j )
3484 prev[j] = static_cast<int>( j );
3485
3486 for( size_t i = 1; i <= m; ++i )
3487 {
3488 curr[0] = static_cast<int>( i );
3489
3490 for( size_t j = 1; j <= n; ++j )
3491 {
3492 int cost = ( a[i - 1] == b[j - 1] ) ? 0 : 1;
3493 curr[j] = std::min( { prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost } );
3494 }
3495
3496 std::swap( prev, curr );
3497 }
3498
3499 return prev[n];
3500}
3501
3502
3503std::unique_ptr<LIB_SYMBOL> SCH_IO_GEDA::loadBuiltinSymbol( const wxString& aBasename )
3504{
3505 const auto& builtins = getBuiltinSymbols();
3506 auto it = builtins.find( aBasename );
3507
3508 if( it == builtins.end() )
3509 return nullptr;
3510
3511 wxString tempPath = wxFileName::CreateTempFileName( wxT( "geda_sym_" ) );
3512
3513 if( tempPath.IsEmpty() )
3514 return nullptr;
3515
3516 {
3517 wxFile temp( tempPath, wxFile::write );
3518
3519 if( !temp.IsOpened() || !temp.Write( it->second ) )
3520 {
3521 wxRemoveFile( tempPath );
3522 return nullptr;
3523 }
3524 }
3525
3526 wxString netAttr;
3527 auto result = loadSymbolFile( tempPath, nullptr, &netAttr );
3528 wxRemoveFile( tempPath );
3529
3530 if( result )
3531 {
3532 wxString symName = aBasename;
3533
3534 if( symName.EndsWith( wxT( ".sym" ) ) )
3535 symName = symName.Left( symName.length() - 4 );
3536
3537 result->SetName( symName );
3538
3539 if( !netAttr.IsEmpty() )
3540 {
3541 SYM_CACHE_ENTRY& entry = m_symLibrary[aBasename];
3542 entry.netAttr = netAttr;
3543 }
3544 }
3545
3546 return result;
3547}
3548
3549
3550LIB_SYMBOL* SCH_IO_GEDA::getOrLoadSymbol( const wxString& aBasename )
3551{
3552 // Check if already loaded
3553 auto it = m_libSymbols.find( aBasename );
3554
3555 if( it != m_libSymbols.end() )
3556 return it->second.get();
3557
3558 // Try the library cache
3560
3561 auto cacheIt = m_symLibrary.find( aBasename );
3562
3563 if( cacheIt != m_symLibrary.end() )
3564 {
3565 if( !cacheIt->second.symbol )
3566 {
3567 cacheIt->second.symbol = loadSymbolFile( cacheIt->second.path,
3568 &cacheIt->second.symversion,
3569 &cacheIt->second.netAttr );
3570
3571 // When a project-local symbol overrides a builtin power symbol but lacks a
3572 // net= attribute, inherit the builtin's net= so power detection still works.
3573 // This handles projects that provide custom graphics for standard power symbols.
3574 if( cacheIt->second.symbol && cacheIt->second.netAttr.IsEmpty() )
3575 {
3576 const auto& builtins = getBuiltinSymbols();
3577 auto builtinIt = builtins.find( aBasename );
3578
3579 if( builtinIt != builtins.end() )
3580 {
3581 // Scan the builtin symbol text for a net= attribute line.
3582 // In gEDA .sym format, net= always starts a line.
3583 const wxString& src = builtinIt->second;
3584 int pos = src.Find( wxT( "\nnet=" ) );
3585
3586 if( pos != wxNOT_FOUND )
3587 {
3588 wxString rest = src.Mid( pos + 5 );
3589 int eol = rest.Find( '\n' );
3590
3591 cacheIt->second.netAttr = ( eol != wxNOT_FOUND )
3592 ? rest.Left( eol ).Trim()
3593 : rest.Trim();
3594 }
3595 }
3596 }
3597 }
3598
3599 if( cacheIt->second.symbol )
3600 {
3601 m_libSymbols[aBasename] = std::make_unique<LIB_SYMBOL>( *cacheIt->second.symbol );
3602 return m_libSymbols[aBasename].get();
3603 }
3604 }
3605
3606 // Try built-in standard symbols before falling back to a placeholder
3607 auto builtin = loadBuiltinSymbol( aBasename );
3608
3609 if( builtin )
3610 {
3611 m_libSymbols[aBasename] = std::move( builtin );
3612 return m_libSymbols[aBasename].get();
3613 }
3614
3615 // Not found anywhere - create a fallback placeholder
3616 m_libSymbols[aBasename] = createFallbackSymbol( aBasename );
3617
3618 if( m_reporter )
3619 {
3620 wxString suggestion;
3621 int bestDist = INT_MAX;
3622
3623 for( const auto& [name, entry] : m_symLibrary )
3624 {
3625 int dist = editDistance( aBasename, name );
3626
3627 if( dist < bestDist && dist <= 3 )
3628 {
3629 bestDist = dist;
3630 suggestion = name;
3631 }
3632 }
3633
3634 // Also check builtins, which may have a closer match than the library cache
3635 for( const auto& [name, content] : getBuiltinSymbols() )
3636 {
3637 int dist = editDistance( aBasename, name );
3638
3639 if( dist < bestDist && dist <= 3 )
3640 {
3641 bestDist = dist;
3642 suggestion = name;
3643 }
3644 }
3645
3646 wxString msg = wxString::Format( _( "Symbol '%s' not found in gEDA libraries." ),
3647 aBasename );
3648
3649 if( !suggestion.IsEmpty() )
3650 msg += wxString::Format( _( " Did you mean '%s'?" ), suggestion );
3651
3652 m_reporter->Report( msg );
3653 }
3654
3655 return m_libSymbols[aBasename].get();
3656}
3657
3658
3659std::unique_ptr<LIB_SYMBOL> SCH_IO_GEDA::createFallbackSymbol( const wxString& aBasename )
3660{
3661 wxString symName = aBasename;
3662
3663 if( symName.EndsWith( wxT( ".sym" ) ) )
3664 symName = symName.Left( symName.length() - 4 );
3665
3666 auto libSymbol = std::make_unique<LIB_SYMBOL>( symName );
3667 libSymbol->SetShowPinNumbers( true );
3668 libSymbol->SetShowPinNames( true );
3669
3670 int halfSize = toKiCadDist( DEFAULT_SYMBOL_SIZE_MILS ) / 2;
3671
3673 rect->SetStart( VECTOR2I( -halfSize, -halfSize ) );
3674 rect->SetEnd( VECTOR2I( halfSize, halfSize ) );
3676 libSymbol->AddDrawItem( rect );
3677
3678 return libSymbol;
3679}
3680
3681
3683{
3684 if( !m_pendingComp )
3685 return;
3686
3687 // Skip title block symbols
3688 if( m_pendingComp->basename.StartsWith( wxT( "title-" ) ) )
3689 {
3690 m_pendingComp.reset();
3691 return;
3692 }
3693
3694 // Detect no-connect symbols (nc-right-1.sym, nc-left-1.sym, etc.) and emit
3695 // SCH_NO_CONNECT at the pin connection point rather than the component origin.
3696 wxString ncValue = findAttr( m_pendingComp->attrs, wxT( "value" ) );
3697 bool isNoConnect = m_pendingComp->basename.StartsWith( wxT( "nc-" ) )
3698 && ncValue == wxT( "NoConnection" );
3699
3700 if( isNoConnect )
3701 {
3702 LIB_SYMBOL* ncSym = getOrLoadSymbol( m_pendingComp->basename );
3703 int pinLocalX = 0;
3704 int pinLocalY = 0;
3705
3706 if( ncSym )
3707 {
3708 const std::vector<SCH_PIN*>& pins = ncSym->GetPins();
3709
3710 if( !pins.empty() )
3711 {
3712 VECTOR2I pinPos = pins[0]->GetPosition();
3713 pinLocalX = pinPos.x / MILS_TO_IU;
3714 pinLocalY = -pinPos.y / MILS_TO_IU;
3715 }
3716 }
3717
3718 // Apply gEDA component rotation/mirror to the pin offset
3719 int rx = pinLocalX;
3720 int ry = pinLocalY;
3721
3722 switch( m_pendingComp->angle )
3723 {
3724 case 90: rx = -pinLocalY; ry = pinLocalX; break;
3725 case 180: rx = -pinLocalX; ry = -pinLocalY; break;
3726 case 270: rx = pinLocalY; ry = -pinLocalX; break;
3727 default: break;
3728 }
3729
3730 if( m_pendingComp->mirror )
3731 rx = -rx;
3732
3733 int gedaX = m_pendingComp->x + rx;
3734 int gedaY = m_pendingComp->y + ry;
3735
3736 m_screen->Append( new SCH_NO_CONNECT( toKiCad( gedaX, gedaY ) ) );
3737 m_pendingComp.reset();
3738 return;
3739 }
3740
3741 // Check for hierarchical sheet reference. In gEDA, a source= attribute
3742 // pointing to a .sch file indicates a hierarchical sub-schematic.
3743 wxString sourceAttr = findAttr( m_pendingComp->attrs, wxT( "source" ) );
3744
3745 if( !sourceAttr.IsEmpty() && sourceAttr.EndsWith( wxT( ".sch" ) ) )
3746 {
3747 importHierarchicalSheet( sourceAttr );
3748 m_pendingComp.reset();
3749 return;
3750 }
3751
3752 LIB_SYMBOL* libSym = nullptr;
3753
3754 if( m_pendingComp->embedded && m_pendingComp->embeddedSym )
3755 {
3756 wxString basename = m_pendingComp->basename;
3757 m_libSymbols[basename] = std::move( m_pendingComp->embeddedSym );
3758 libSym = m_libSymbols[basename].get();
3759 }
3760 else
3761 {
3762 libSym = getOrLoadSymbol( m_pendingComp->basename );
3763 }
3764
3765 if( !libSym )
3766 {
3767 m_pendingComp.reset();
3768 return;
3769 }
3770
3771 // Compare component symversion against the loaded symbol's symversion.
3772 // A mismatch in the major version number indicates the symbol has changed
3773 // in an incompatible way since the schematic was saved.
3774 wxString compSymver = findAttr( m_pendingComp->attrs, wxT( "symversion" ) );
3775
3776 if( !compSymver.IsEmpty() && m_reporter )
3777 {
3778 wxString symSymver;
3779 auto cacheIt = m_symLibrary.find( m_pendingComp->basename );
3780
3781 if( cacheIt != m_symLibrary.end() )
3782 symSymver = cacheIt->second.symversion;
3783
3784 if( !symSymver.IsEmpty() )
3785 {
3786 long compMajor = 0;
3787 long symMajor = 0;
3788 compSymver.BeforeFirst( '.' ).ToLong( &compMajor );
3789 symSymver.BeforeFirst( '.' ).ToLong( &symMajor );
3790
3791 if( compMajor != symMajor )
3792 {
3793 wxString refdes = findAttr( m_pendingComp->attrs, wxT( "refdes" ) );
3794 wxString label = refdes.IsEmpty() ? m_pendingComp->basename : refdes;
3795
3796 m_reporter->Report(
3797 wxString::Format( _( "Symbol version mismatch for '%s' (%s): "
3798 "schematic has symversion %s, "
3799 "library symbol has symversion %s." ),
3800 label, m_pendingComp->basename,
3801 compSymver, symSymver ),
3803 }
3804 }
3805 }
3806
3807 // Detect power symbols via the net= attribute in the .sym definition.
3808 // gEDA power symbols (gnd-1.sym, vcc-1.sym, etc.) carry a net=NETNAME:PIN
3809 // attribute that identifies them as implicit power connections.
3810 wxString symNetAttr;
3811 auto cacheEntry = m_symLibrary.find( m_pendingComp->basename );
3812
3813 if( cacheEntry != m_symLibrary.end() )
3814 symNetAttr = cacheEntry->second.netAttr;
3815
3816 bool isPowerSym = !symNetAttr.IsEmpty();
3817 wxString powerNetName;
3818 std::unique_ptr<LIB_SYMBOL> powerCopy;
3819
3820 if( isPowerSym )
3821 {
3822 // Schematic-level net= overrides the .sym-level default (e.g. generic-power.sym
3823 // has net=Vcc:1 but instances use net=5V:1 or net=9V:1).
3824 wxString schematicNet = findAttr( m_pendingComp->attrs, wxT( "net" ) );
3825 wxString effectiveNet = schematicNet.IsEmpty() ? symNetAttr : schematicNet;
3826
3827 int colonPos = effectiveNet.Find( ':' );
3828
3829 if( colonPos != wxNOT_FOUND )
3830 powerNetName = effectiveNet.Left( colonPos );
3831 else
3832 powerNetName = effectiveNet;
3833
3834 // Work on a copy so the shared cached symbol is not modified.
3835 // Each power instance may have a different net name.
3836 powerCopy = std::make_unique<LIB_SYMBOL>( *libSym );
3837 libSym = powerCopy.get();
3838
3839 libSym->SetGlobalPower();
3840 libSym->SetShowPinNames( false );
3841 libSym->SetShowPinNumbers( false );
3842 libSym->GetReferenceField().SetText( wxT( "#PWR" ) );
3843 libSym->GetReferenceField().SetVisible( false );
3844 libSym->GetValueField().SetText( powerNetName );
3845 libSym->GetValueField().SetVisible( true );
3846
3847 for( SCH_PIN* pin : libSym->GetPins() )
3848 {
3849 pin->SetName( powerNetName );
3851 }
3852 }
3853
3854 LIB_ID libId( getLibName(), libSym->GetName() );
3856
3857 auto symbol = std::make_unique<SCH_SYMBOL>( *libSym, libId, &m_schematic->CurrentSheet(),
3858 1, 0, pos );
3859
3860 int orient = toKiCadOrientation( m_pendingComp->angle, m_pendingComp->mirror );
3861 symbol->SetOrientation( orient );
3862
3863 if( isPowerSym )
3864 {
3865 wxString pwrRef = wxString::Format( wxT( "#PWR%04d" ), ++m_powerCounter );
3866 symbol->SetRef( &m_schematic->CurrentSheet(), pwrRef );
3867 symbol->GetField( FIELD_T::REFERENCE )->SetText( pwrRef );
3868 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
3869 symbol->GetField( FIELD_T::VALUE )->SetText( powerNetName );
3870 symbol->GetField( FIELD_T::VALUE )->SetVisible( true );
3871 }
3872
3873 // Mark graphical-only symbols as excluded from BOM, board, and simulation
3874 wxString graphicalAttr = findAttr( m_pendingComp->attrs, wxT( "graphical" ) );
3875
3876 if( graphicalAttr == wxT( "1" ) )
3877 {
3878 symbol->SetExcludedFromBOM( true );
3879 symbol->SetExcludedFromBoard( true );
3880 symbol->SetExcludedFromSim( true );
3881 }
3882
3883 wxString refdes = findAttr( m_pendingComp->attrs, wxT( "refdes" ) );
3884 wxString value = findAttr( m_pendingComp->attrs, wxT( "value" ) );
3885 wxString footprint = findAttr( m_pendingComp->attrs, wxT( "footprint" ) );
3886
3887 if( !isPowerSym && !refdes.IsEmpty() )
3888 {
3889 symbol->SetRef( &m_schematic->CurrentSheet(), refdes );
3890
3891 SCH_FIELD* refField = symbol->GetField( FIELD_T::REFERENCE );
3892 const GEDA_ATTR* refAttr = findAttrStruct( m_pendingComp->attrs, wxT( "refdes" ) );
3893 refField->SetText( refdes );
3894
3895 if( refAttr )
3896 {
3897 refField->SetPosition( toKiCad( refAttr->x, refAttr->y ) );
3898 int textSize = toKiCadDist( refAttr->size * 10 );
3899 refField->SetTextSize( VECTOR2I( textSize, textSize ) );
3900 refField->SetVisible( refAttr->visible );
3901 }
3902 }
3903
3904 if( !isPowerSym && !value.IsEmpty() )
3905 {
3906 SCH_FIELD* valField = symbol->GetField( FIELD_T::VALUE );
3907 const GEDA_ATTR* valAttr = findAttrStruct( m_pendingComp->attrs, wxT( "value" ) );
3908 valField->SetText( value );
3909
3910 if( valAttr )
3911 {
3912 valField->SetPosition( toKiCad( valAttr->x, valAttr->y ) );
3913 int textSize = toKiCadDist( valAttr->size * 10 );
3914 valField->SetTextSize( VECTOR2I( textSize, textSize ) );
3915 valField->SetVisible( valAttr->visible );
3916 }
3917 }
3918
3919 if( !footprint.IsEmpty() )
3920 symbol->SetFootprintFieldText( footprint );
3921
3922 // Map documentation attribute to KiCad's native DATASHEET field
3923 wxString documentation = findAttr( m_pendingComp->attrs, wxT( "documentation" ) );
3924
3925 if( !documentation.IsEmpty() )
3926 {
3927 SCH_FIELD* dsField = symbol->GetField( FIELD_T::DATASHEET );
3928 dsField->SetText( documentation );
3929 }
3930
3931 // Map description attribute to KiCad's native DESCRIPTION field
3932 const GEDA_ATTR* descAttr = findAttrStruct( m_pendingComp->attrs, wxT( "description" ) );
3933
3934 if( descAttr && !descAttr->value.IsEmpty() )
3935 {
3936 SCH_FIELD* descField = symbol->GetField( FIELD_T::DESCRIPTION );
3937 descField->SetText( descAttr->value );
3938 descField->SetVisible( descAttr->visible );
3939
3940 if( descAttr->visible )
3941 {
3942 descField->SetPosition( toKiCad( descAttr->x, descAttr->y ) );
3943 int textSize = toKiCadDist( descAttr->size * 10 );
3944 descField->SetTextSize( VECTOR2I( textSize, textSize ) );
3945 }
3946 }
3947
3948 // Collect net= attributes for post-processing. Power symbols already establish
3949 // their net connection through the pin name, so skip global label generation.
3950 if( !isPowerSym )
3951 {
3952 for( const GEDA_ATTR& attr : m_pendingComp->attrs )
3953 {
3954 if( attr.name == wxT( "net" ) )
3955 {
3956 int colonPos = attr.value.Find( ':' );
3957
3958 if( colonPos != wxNOT_FOUND )
3959 {
3960 NET_ATTR_RECORD rec;
3961 rec.netname = attr.value.Left( colonPos );
3962 rec.pinnumber = attr.value.Mid( colonPos + 1 );
3963 rec.symbol = symbol.get();
3964 m_netAttrRecords.push_back( rec );
3965 }
3966 }
3967 }
3968 }
3969
3970 // Store additional attributes as custom fields (skip those already handled)
3971 for( const GEDA_ATTR& attr : m_pendingComp->attrs )
3972 {
3973 if( attr.name == wxT( "refdes" ) || attr.name == wxT( "value" )
3974 || attr.name == wxT( "footprint" ) || attr.name == wxT( "net" )
3975 || attr.name == wxT( "device" ) || attr.name == wxT( "symversion" )
3976 || attr.name == wxT( "documentation" ) || attr.name == wxT( "description" )
3977 || attr.name == wxT( "graphical" )
3978 || attr.name == wxT( "slot" ) || attr.name == wxT( "numslots" )
3979 || attr.name == wxT( "slotdef" ) )
3980 {
3981 continue;
3982 }
3983
3984 SCH_FIELD* field = symbol->AddField(
3985 SCH_FIELD( symbol.get(), FIELD_T::USER, attr.name ) );
3986 field->SetText( attr.value );
3987 field->SetVisible( attr.visible );
3988 field->SetPosition( pos );
3989 }
3990
3991 // Track pin connection points for junction detection. Reverse-map the
3992 // KiCad IU positions back to gEDA coordinates to match the wire endpoints.
3993 for( SCH_PIN* pin : libSym->GetPins() )
3994 {
3995 VECTOR2I pinPos = symbol->GetPinPhysicalPosition( pin );
3996 int gedaX = pinPos.x / MILS_TO_IU;
3997 int gedaY = m_maxY - pinPos.y / MILS_TO_IU;
3998 trackEndpoint( gedaX, gedaY );
3999 }
4000
4001 symbol->SetLibSymbol( new LIB_SYMBOL( *libSym ) );
4002
4003 // Apply multi-slot pin remapping on the symbol's PRIVATE copy of the lib symbol.
4004 // gEDA slotdef format is "N:pin1,pin2,pin3" where N is the slot number
4005 // and each pin corresponds to a pinseq-ordered pin in the symbol.
4006 // Operating on the private copy prevents corrupting the shared cached symbol.
4007 wxString slotStr = findAttr( m_pendingComp->attrs, wxT( "slot" ) );
4008 long slotNum = 0;
4009
4010 if( !slotStr.IsEmpty() && slotStr.ToLong( &slotNum ) && slotNum > 0 )
4011 {
4012 wxString targetSlotDef;
4013
4014 for( const GEDA_ATTR& attr : m_pendingComp->attrs )
4015 {
4016 if( attr.name != wxT( "slotdef" ) )
4017 continue;
4018
4019 int colonPos = attr.value.Find( ':' );
4020
4021 if( colonPos == wxNOT_FOUND )
4022 continue;
4023
4024 long defSlot = 0;
4025
4026 if( attr.value.Left( colonPos ).ToLong( &defSlot ) && defSlot == slotNum )
4027 {
4028 targetSlotDef = attr.value.Mid( colonPos + 1 );
4029 break;
4030 }
4031 }
4032
4033 if( !targetSlotDef.IsEmpty() )
4034 {
4035 wxArrayString slotPins;
4036 wxStringTokenizer slotTok( targetSlotDef, wxT( "," ) );
4037
4038 while( slotTok.HasMoreTokens() )
4039 slotPins.Add( slotTok.GetNextToken() );
4040
4041 LIB_SYMBOL* privateSym = symbol->GetLibSymbolRef().get();
4042
4043 // Build pinseq-ordered list from the private copy's pins.
4044 // Pins are added in file order which matches pinseq order.
4045 std::map<long, SCH_PIN*> pinsBySeq;
4046 int autoSeq = 1;
4047
4048 for( SCH_PIN* pin : privateSym->GetPins() )
4049 {
4050 pinsBySeq[autoSeq] = pin;
4051 autoSeq++;
4052 }
4053
4054 for( size_t i = 0; i < slotPins.size() && i < pinsBySeq.size(); i++ )
4055 {
4056 auto seqIt = pinsBySeq.find( static_cast<long>( i + 1 ) );
4057
4058 if( seqIt != pinsBySeq.end() )
4059 seqIt->second->SetNumber( slotPins[i].Trim() );
4060 }
4061 }
4062 }
4063
4064 m_screen->Append( symbol.release() );
4065 m_pendingComp.reset();
4066}
4067
4068
4069// ==========================================================================
4070// Hierarchical sheet import
4071// ==========================================================================
4072
4073void SCH_IO_GEDA::importHierarchicalSheet( const wxString& aSourceFile )
4074{
4075 wxFileName sourceFileName( aSourceFile );
4076
4077 // Resolve relative paths against the directory of the current schematic
4078 if( !sourceFileName.IsAbsolute() )
4079 sourceFileName.SetPath( m_filename.GetPath() );
4080
4081 wxString fullPath = sourceFileName.GetFullPath();
4082
4083 if( !wxFileExists( fullPath ) )
4084 {
4085 if( m_reporter )
4086 {
4087 m_reporter->Report(
4088 wxString::Format( _( "Hierarchical source '%s' not found, "
4089 "creating empty sheet." ),
4090 aSourceFile ),
4092 }
4093 }
4094
4095 if( m_importStack.count( fullPath ) )
4096 {
4097 if( m_reporter )
4098 {
4099 m_reporter->Report(
4100 wxString::Format( _( "Circular hierarchy detected for '%s', skipping." ),
4101 aSourceFile ),
4103 }
4104
4105 return;
4106 }
4107
4109
4110 // Use a default sheet size. We'll resize after loading the sub-schematic content.
4111 VECTOR2I sheetSize( schIUScale.MilsToIU( 2000 ), schIUScale.MilsToIU( 1500 ) );
4112
4113 auto sheet = std::make_unique<SCH_SHEET>( m_rootSheet, pos, sheetSize );
4114
4115 wxString refdes = findAttr( m_pendingComp->attrs, wxT( "refdes" ) );
4116
4117 if( !refdes.IsEmpty() )
4118 {
4119 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( refdes );
4120 }
4121 else
4122 {
4123 sheet->GetField( FIELD_T::SHEET_NAME )->SetText(
4124 sourceFileName.GetName() );
4125 }
4126
4127 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( aSourceFile );
4128 sheet->SetFileName( aSourceFile );
4129
4130 SCH_SHEET* sheetPtr = sheet.get();
4131 m_screen->Append( sheet.release() );
4132
4133 if( wxFileExists( fullPath ) )
4134 {
4135 m_deferredSheets.push_back( { sheetPtr, fullPath } );
4136 }
4137}
4138
4139
4140// ==========================================================================
4141// Endpoint tracking
4142// ==========================================================================
4143
4144void SCH_IO_GEDA::trackEndpoint( int aGedaX, int aGedaY )
4145{
4146 m_netEndpoints[std::make_pair( aGedaX, aGedaY )]++;
4147}
4148
4149
4150// ==========================================================================
4151// Post-processing
4152// ==========================================================================
4153
4162
4163
4165{
4166 for( const NET_ATTR_RECORD& rec : m_netAttrRecords )
4167 {
4168 if( !rec.symbol )
4169 continue;
4170
4171 const LIB_SYMBOL* libSym = rec.symbol->GetLibSymbolRef().get();
4172
4173 if( !libSym )
4174 continue;
4175
4176 // Find the pin with matching number
4177 for( SCH_PIN* pin : libSym->GetPins() )
4178 {
4179 if( pin->GetNumber() == rec.pinnumber )
4180 {
4181 VECTOR2I pinPos = rec.symbol->GetPinPhysicalPosition( pin );
4182
4183 auto label = std::make_unique<SCH_GLOBALLABEL>( pinPos, rec.netname );
4184 int textSize = toKiCadDist( GEDA_DEFAULT_TEXT_SIZE_MILS / 2 );
4185 label->SetTextSize( VECTOR2I( textSize, textSize ) );
4186
4187 // Build a unit direction vector for the pin in library space,
4188 // then transform it through the symbol's rotation/mirror matrix
4189 // to get the world-space pin direction.
4190 VECTOR2I pinDir( 0, 0 );
4191
4192 switch( pin->GetOrientation() )
4193 {
4194 case PIN_ORIENTATION::PIN_RIGHT: pinDir = VECTOR2I( 1, 0 ); break;
4195 case PIN_ORIENTATION::PIN_LEFT: pinDir = VECTOR2I( -1, 0 ); break;
4196 case PIN_ORIENTATION::PIN_UP: pinDir = VECTOR2I( 0, -1 ); break;
4197 case PIN_ORIENTATION::PIN_DOWN: pinDir = VECTOR2I( 0, 1 ); break;
4198 default: pinDir = VECTOR2I( 1, 0 ); break;
4199 }
4200
4201 VECTOR2I worldDir = rec.symbol->GetTransform().TransformCoordinate( pinDir );
4202
4203 // Orient the label away from the transformed pin direction
4204 if( std::abs( worldDir.x ) >= std::abs( worldDir.y ) )
4205 {
4206 label->SetSpinStyle( worldDir.x > 0 ? SPIN_STYLE::LEFT
4208 }
4209 else
4210 {
4211 label->SetSpinStyle( worldDir.y > 0 ? SPIN_STYLE::UP
4213 }
4214
4215 m_screen->Append( label.release() );
4216 break;
4217 }
4218 }
4219 }
4220}
4221
4222
4224{
4225 // Collect all wire segments and pin positions from the screen.
4226 std::vector<std::pair<VECTOR2I, VECTOR2I>> wireSegs;
4227 std::set<std::pair<int, int>> junctionPts;
4228
4229 for( SCH_ITEM* item : m_screen->Items() )
4230 {
4231 if( item->Type() != SCH_LINE_T )
4232 continue;
4233
4234 SCH_LINE* wire = static_cast<SCH_LINE*>( item );
4235
4236 if( wire->GetLayer() != LAYER_WIRE )
4237 continue;
4238
4239 wireSegs.emplace_back( wire->GetStartPoint(), wire->GetEndPoint() );
4240 }
4241
4242 // Place junctions where 3+ endpoints coincide at the same point.
4243 for( const auto& [pos, count] : m_netEndpoints )
4244 {
4245 if( count >= 3 )
4246 junctionPts.insert( { pos.first, pos.second } );
4247 }
4248
4249 // Detect T-junctions: a wire endpoint that lands on the interior of
4250 // another wire segment. Work in gEDA coordinates for exact integer match,
4251 // then convert when placing the junction.
4252 for( const auto& [pos, count] : m_netEndpoints )
4253 {
4254 if( junctionPts.count( pos ) )
4255 continue;
4256
4257 VECTOR2I pt = toKiCad( pos.first, pos.second );
4258
4259 for( const auto& [segStart, segEnd] : wireSegs )
4260 {
4261 if( pt == segStart || pt == segEnd )
4262 continue;
4263
4264 bool isHorizontal = ( segStart.y == segEnd.y );
4265 bool isVertical = ( segStart.x == segEnd.x );
4266
4267 if( !isHorizontal && !isVertical )
4268 continue;
4269
4270 bool onSeg = false;
4271
4272 if( isHorizontal && pt.y == segStart.y )
4273 {
4274 int minX = std::min( segStart.x, segEnd.x );
4275 int maxX = std::max( segStart.x, segEnd.x );
4276 onSeg = ( pt.x > minX && pt.x < maxX );
4277 }
4278 else if( isVertical && pt.x == segStart.x )
4279 {
4280 int minY = std::min( segStart.y, segEnd.y );
4281 int maxY = std::max( segStart.y, segEnd.y );
4282 onSeg = ( pt.y > minY && pt.y < maxY );
4283 }
4284
4285 if( onSeg )
4286 {
4287 junctionPts.insert( pos );
4288 break;
4289 }
4290 }
4291 }
4292
4293 for( const auto& [gX, gY] : junctionPts )
4294 {
4295 VECTOR2I kicadPos = toKiCad( gX, gY );
4296 m_screen->Append( std::make_unique<SCH_JUNCTION>( kicadPos ).release() );
4297 }
4298}
4299
4300
4302{
4303 if( m_busSegments.empty() )
4304 return;
4305
4306 // Collect all net (wire) endpoints from the screen
4307 std::vector<std::pair<VECTOR2I, SCH_LINE*>> wireEndpoints;
4308
4309 for( SCH_ITEM* item : m_screen->Items() )
4310 {
4311 if( item->Type() != SCH_LINE_T )
4312 continue;
4313
4314 SCH_LINE* wire = static_cast<SCH_LINE*>( item );
4315
4316 if( wire->GetLayer() != LAYER_WIRE )
4317 continue;
4318
4319 wireEndpoints.emplace_back( wire->GetStartPoint(), wire );
4320 wireEndpoints.emplace_back( wire->GetEndPoint(), wire );
4321 }
4322
4323 int entrySize = schIUScale.MilsToIU( DEFAULT_SCH_ENTRY_SIZE );
4324
4325 for( const BUS_SEGMENT& bus : m_busSegments )
4326 {
4327 bool busIsHorizontal = ( bus.start.y == bus.end.y );
4328 bool busIsVertical = ( bus.start.x == bus.end.x );
4329
4330 if( !busIsHorizontal && !busIsVertical )
4331 continue;
4332
4333 for( const auto& [pt, wire] : wireEndpoints )
4334 {
4335 bool onBus = false;
4336
4337 if( busIsHorizontal )
4338 {
4339 int minX = std::min( bus.start.x, bus.end.x );
4340 int maxX = std::max( bus.start.x, bus.end.x );
4341 onBus = ( pt.y == bus.start.y && pt.x >= minX && pt.x <= maxX );
4342 }
4343 else
4344 {
4345 int minY = std::min( bus.start.y, bus.end.y );
4346 int maxY = std::max( bus.start.y, bus.end.y );
4347 onBus = ( pt.x == bus.start.x && pt.y >= minY && pt.y <= maxY );
4348 }
4349
4350 if( !onBus )
4351 continue;
4352
4353 // Determine which direction the wire goes away from the bus
4354 VECTOR2I otherEnd = ( wire->GetStartPoint() == pt ) ? wire->GetEndPoint()
4355 : wire->GetStartPoint();
4356 int dx = 0;
4357 int dy = 0;
4358
4359 if( busIsHorizontal )
4360 {
4361 dy = ( otherEnd.y < pt.y ) ? -entrySize : entrySize;
4362 int sign = bus.ripperDir;
4363
4364 if( sign == 0 )
4365 {
4366 // Auto-detect from wire position relative to bus center
4367 int busMidX = ( bus.start.x + bus.end.x ) / 2;
4368 sign = ( pt.x <= busMidX ) ? 1 : -1;
4369 }
4370
4371 dx = sign * entrySize;
4372 }
4373 else
4374 {
4375 dx = ( otherEnd.x < pt.x ) ? -entrySize : entrySize;
4376 int sign = bus.ripperDir;
4377
4378 if( sign == 0 )
4379 {
4380 int busMidY = ( bus.start.y + bus.end.y ) / 2;
4381 sign = ( pt.y <= busMidY ) ? 1 : -1;
4382 }
4383
4384 dy = sign * entrySize;
4385 }
4386
4387 // Bus entry position is on the bus; its end (pos + size) is on the wire side
4388 auto entry = std::make_unique<SCH_BUS_WIRE_ENTRY>( pt );
4389 entry->SetSize( VECTOR2I( dx, dy ) );
4390
4391 // Shorten the wire so it starts at the bus entry tip instead of the bus
4392 VECTOR2I entryTip = pt + VECTOR2I( dx, dy );
4393
4394 if( wire->GetStartPoint() == pt )
4395 wire->SetStartPoint( entryTip );
4396 else
4397 wire->SetEndPoint( entryTip );
4398
4399 m_screen->Append( entry.release() );
4400 }
4401 }
4402}
4403
4404
4406{
4407 for( const DEFERRED_SHEET& deferred : m_deferredSheets )
4408 {
4409 SCH_SHEET* sheet = deferred.sheet;
4410
4411 SCH_SCREEN* subScreen = new SCH_SCREEN( m_schematic );
4412 subScreen->SetFileName( deferred.sourceFile );
4413 sheet->SetScreen( subScreen );
4414
4415 // Use a fresh importer instance for each sub-schematic to avoid
4416 // clobbering parse state. Share the import stack for recursion detection
4417 // and the symbol library cache to avoid redundant filesystem scanning.
4418 SCH_IO_GEDA subImporter;
4419 subImporter.m_importStack = m_importStack;
4420 subImporter.m_importStack.insert( m_filename.GetFullPath() );
4421 for( const auto& [name, entry] : m_symLibrary )
4422 {
4423 SYM_CACHE_ENTRY subEntry;
4424 subEntry.path = entry.path;
4425 subImporter.m_symLibrary[name] = std::move( subEntry );
4426 }
4427
4429
4430 try
4431 {
4432 subImporter.LoadSchematicFile( deferred.sourceFile, m_schematic,
4433 sheet, m_properties );
4434
4435 // Merge any newly-discovered symbols back into the parent cache
4436 // so subsequent sub-schematics benefit from them.
4437 for( auto& [name, entry] : subImporter.m_symLibrary )
4438 {
4439 if( m_symLibrary.find( name ) == m_symLibrary.end() )
4440 m_symLibrary[name] = std::move( entry );
4441 }
4442 }
4443 catch( const IO_ERROR& e )
4444 {
4445 if( m_reporter )
4446 {
4447 m_reporter->Report(
4448 wxString::Format( _( "Failed to load sub-schematic '%s': %s" ),
4449 deferred.sourceFile, e.What() ),
4451 }
4452 }
4453 }
4454}
4455
4456
4458{
4459 BOX2I bbox;
4460
4461 for( SCH_ITEM* item : m_screen->Items() )
4462 bbox.Merge( item->GetBoundingBox() );
4463
4464 if( bbox.GetWidth() == 0 || bbox.GetHeight() == 0 )
4465 return;
4466
4467 VECTOR2I targetSize = bbox.GetSize();
4468 targetSize += VECTOR2I( schIUScale.MilsToIU( 1500 ), schIUScale.MilsToIU( 1500 ) );
4469
4470 PAGE_INFO pageInfo = m_screen->GetPageSettings();
4471 VECTOR2I pageSizeIU = pageInfo.GetSizeIU( schIUScale.IU_PER_MILS );
4472
4473 if( pageSizeIU.x < targetSize.x )
4474 pageInfo.SetWidthMils( schIUScale.IUToMils( targetSize.x ) );
4475
4476 if( pageSizeIU.y < targetSize.y )
4477 pageInfo.SetHeightMils( schIUScale.IUToMils( targetSize.y ) );
4478
4479 m_screen->SetPageSettings( pageInfo );
4480
4481 pageSizeIU = m_screen->GetPageSettings().GetSizeIU( schIUScale.IU_PER_MILS );
4482 VECTOR2I sheetCentre( pageSizeIU.x / 2, pageSizeIU.y / 2 );
4483 VECTOR2I itemsCentre = bbox.Centre();
4484
4485 VECTOR2I translation = sheetCentre - itemsCentre;
4486 translation.x = translation.x - translation.x % schIUScale.MilsToIU( 100 );
4487 translation.y = translation.y - translation.y % schIUScale.MilsToIU( 100 );
4488
4489 std::vector<SCH_ITEM*> allItems;
4490 std::copy( m_screen->Items().begin(), m_screen->Items().end(),
4491 std::back_inserter( allItems ) );
4492
4493 for( SCH_ITEM* item : allItems )
4494 {
4495 item->SetPosition( item->GetPosition() + translation );
4496 item->ClearFlags();
4497 m_screen->Update( item );
4498 }
4499}
4500
4501
4502// ==========================================================================
4503// Main entry point
4504// ==========================================================================
4505
4506SCH_SHEET* SCH_IO_GEDA::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
4507 SCH_SHEET* aAppendToMe,
4508 const std::map<std::string, UTF8>* aProperties )
4509{
4510 wxASSERT( !aFileName.IsEmpty() && aSchematic );
4511
4512 m_properties = aProperties;
4513 m_schematic = aSchematic;
4514 m_filename = wxFileName( aFileName );
4515 m_libSymbols.clear();
4516 m_netEndpoints.clear();
4517 m_netAttrRecords.clear();
4518 m_busSegments.clear();
4519 m_deferredSheets.clear();
4520 m_pendingComp.reset();
4522 m_symLibrary.clear();
4523
4524 m_maxY = 0;
4525 m_releaseVersion = 0;
4527 m_powerCounter = 0;
4528
4529 if( aAppendToMe )
4530 {
4531 wxCHECK_MSG( aSchematic->IsValid(), nullptr,
4532 "Can't append to a schematic with no root!" );
4533 m_rootSheet = aAppendToMe;
4534 }
4535 else
4536 {
4537 m_rootSheet = new SCH_SHEET( aSchematic );
4538 m_rootSheet->SetFileName( aFileName );
4539 aSchematic->SetTopLevelSheets( { m_rootSheet } );
4540 }
4541
4542 if( !m_rootSheet->GetScreen() )
4543 {
4544 SCH_SCREEN* screen = new SCH_SCREEN( aSchematic );
4545 screen->SetFileName( aFileName );
4546 m_rootSheet->SetScreen( screen );
4547 const_cast<KIID&>( m_rootSheet->m_Uuid ) = screen->GetUuid();
4548 }
4549
4550 m_screen = m_rootSheet->GetScreen();
4551
4552 wxTextFile file;
4553
4554 if( !file.Open( aFileName ) )
4555 THROW_IO_ERROR( wxString::Format( _( "Cannot open file '%s'." ), aFileName ) );
4556
4557 if( file.GetLineCount() == 0 )
4558 THROW_IO_ERROR( wxString::Format( _( "File '%s' is empty." ), aFileName ) );
4559
4560 // First pass: scan for max Y coordinate to set up the Y-flip transform.
4561 // We need this before creating objects because coordinates are transformed during creation.
4562 for( size_t i = 0; i < file.GetLineCount(); i++ )
4563 {
4564 wxString line = file.GetLine( i );
4565 wxChar type = line.IsEmpty() ? '\0' : line[0];
4566
4567 // Skip embedded component blocks entirely. Their coordinates are
4568 // symbol-local and must not affect the schematic-level Y extent.
4569 if( type == '[' )
4570 {
4571 while( ++i < file.GetLineCount() )
4572 {
4573 if( file.GetLine( i ).Trim() == wxT( "]" ) )
4574 break;
4575 }
4576
4577 continue;
4578 }
4579
4580 if( type == 'C' || type == 'N' || type == 'U' || type == 'T' || type == 'L'
4581 || type == 'B' || type == 'V' || type == 'A' || type == 'P' || type == 'G' )
4582 {
4583 wxStringTokenizer tok( line );
4584 tok.GetNextToken(); // skip type
4585
4586 long val = 0;
4587
4588 if( tok.HasMoreTokens() ) tok.GetNextToken(); // x or x1
4589
4590 if( tok.HasMoreTokens() )
4591 {
4592 tok.GetNextToken().ToLong( &val );
4593
4594 if( val > m_maxY )
4595 m_maxY = static_cast<int>( val );
4596 }
4597
4598 if( type == 'N' || type == 'U' || type == 'L' || type == 'P' )
4599 {
4600 if( tok.HasMoreTokens() ) tok.GetNextToken(); // x2
4601
4602 if( tok.HasMoreTokens() )
4603 {
4604 tok.GetNextToken().ToLong( &val );
4605
4606 if( val > m_maxY )
4607 m_maxY = static_cast<int>( val );
4608 }
4609 }
4610
4611 if( type == 'B' )
4612 {
4613 long bw = 0, bh = 0;
4614
4615 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &bw );
4616 if( tok.HasMoreTokens() ) tok.GetNextToken().ToLong( &bh );
4617
4618 long top = val + bh;
4619
4620 if( top > m_maxY )
4621 m_maxY = static_cast<int>( top );
4622 }
4623
4624 if( type == 'V' || type == 'A' )
4625 {
4626 // V: cx cy radius ... A: cx cy radius ...
4627 // After reading cy into val, the next token is radius
4628 long radius = 0;
4629
4630 if( tok.HasMoreTokens() )
4631 tok.GetNextToken().ToLong( &radius );
4632
4633 long extent = val + radius;
4634
4635 if( extent > m_maxY )
4636 m_maxY = static_cast<int>( extent );
4637 }
4638
4639 // T lines have content lines that follow the header. Skip them
4640 // to avoid misinterpreting text as object lines.
4641 // Format: T x y color size vis show_nv angle align num_lines
4642 // tok has consumed: type, x (skipped), y (read). Skip remaining 6
4643 // fields to reach num_lines.
4644 if( type == 'T' )
4645 {
4646 long numTextLines = 1;
4647
4648 for( int j = 0; j < 6 && tok.HasMoreTokens(); j++ )
4649 tok.GetNextToken();
4650
4651 if( tok.HasMoreTokens() )
4652 tok.GetNextToken().ToLong( &numTextLines );
4653
4654 i += static_cast<size_t>( numTextLines );
4655 }
4656 }
4657 else if( type == 'H' )
4658 {
4659 // Scan H path lines for Y coordinates in subsequent lines
4660 wxStringTokenizer tok( line );
4661 tok.GetNextToken(); // skip 'H'
4662 long numlines = 0;
4663
4664 for( int j = 0; j < 12 && tok.HasMoreTokens(); j++ )
4665 tok.GetNextToken();
4666
4667 if( tok.HasMoreTokens() )
4668 tok.GetNextToken().ToLong( &numlines );
4669
4670 for( long j = 0; j < numlines && ( i + 1 ) < file.GetLineCount(); j++ )
4671 {
4672 i++;
4673 wxString pathLine = file.GetLine( i );
4674 wxStringTokenizer ptok( pathLine, wxT( " ,\t" ) );
4675
4676 // Path data uses SVG-like commands: M x,y L x,y C x1,y1 x2,y2 x3,y3 z
4677 // Coordinate pairs alternate X then Y. Command letters reset to X.
4678 bool nextIsY = false;
4679
4680 while( ptok.HasMoreTokens() )
4681 {
4682 wxString token = ptok.GetNextToken();
4683
4684 if( token.length() == 1 && wxIsalpha( token[0] ) )
4685 {
4686 nextIsY = false;
4687 continue;
4688 }
4689
4690 long pval = 0;
4691
4692 if( token.ToLong( &pval ) )
4693 {
4694 if( nextIsY && pval > m_maxY )
4695 m_maxY = static_cast<int>( pval );
4696
4697 nextIsY = !nextIsY;
4698 }
4699 }
4700 }
4701 }
4702 }
4703
4704 m_maxY += 1000;
4705
4706 // Parse version line
4707 size_t lineIdx = 0;
4708 wxString firstLine = file.GetLine( lineIdx );
4709 lineIdx++;
4710
4711 if( !parseVersionLine( firstLine ) )
4712 {
4713 THROW_IO_ERROR( wxString::Format( _( "File '%s' is not a valid gEDA schematic." ),
4714 aFileName ) );
4715 }
4716
4717 // Main parse loop
4718 while( lineIdx < file.GetLineCount() )
4719 {
4720 wxString line = file.GetLine( lineIdx );
4721 lineIdx++;
4722
4723 if( line.IsEmpty() )
4724 continue;
4725
4726 wxChar type = line[0];
4727
4728 switch( type )
4729 {
4730 case 'C':
4731 parseComponent( line, file, lineIdx );
4732 break;
4733
4734 case 'N':
4735 parseNet( line, file, lineIdx );
4736 break;
4737
4738 case 'U':
4739 parseBus( line, file, lineIdx );
4740 break;
4741
4742 case 'T':
4743 parseText( line, file, lineIdx );
4744 break;
4745
4746 case 'L':
4747 parseLine( line, file, lineIdx );
4748 break;
4749
4750 case 'B':
4751 parseBox( line, file, lineIdx );
4752 break;
4753
4754 case 'V':
4755 parseCircle( line, file, lineIdx );
4756 break;
4757
4758 case 'A':
4759 parseArc( line, file, lineIdx );
4760 break;
4761
4762 case 'H':
4763 parsePath( line, file, lineIdx );
4764 break;
4765
4766 case 'G':
4767 parsePicture( line, file, lineIdx );
4768 break;
4769
4770 case 'P':
4771 parsePin( line, file, lineIdx );
4772 break;
4773
4774 case '[':
4775 parseEmbeddedComponent( file, lineIdx );
4776 break;
4777
4778 case '{':
4779 {
4780 while( lineIdx < file.GetLineCount() )
4781 {
4782 if( file.GetLine( lineIdx ).Trim() == wxT( "}" ) )
4783 {
4784 lineIdx++;
4785 break;
4786 }
4787
4788 lineIdx++;
4789 }
4790
4791 break;
4792 }
4793
4794 case '#':
4795 case 'v':
4796 default:
4797 break;
4798 }
4799 }
4800
4801 postProcess();
4802
4803 // Size the page to fit the imported content and center it, following
4804 // the same approach as the Eagle importer.
4806
4807 // Multi-page support: when the project handler passes additional schematic
4808 // files, create sub-sheets for each and load them into the hierarchy.
4809 if( aProperties )
4810 {
4811 auto it = aProperties->find( "additional_schematics" );
4812
4813 if( it != aProperties->end() )
4814 {
4815 wxString additionalFiles = wxString::FromUTF8( it->second.c_str() );
4816 wxStringTokenizer tok( additionalFiles, wxT( ";" ) );
4817 int sheetY = schIUScale.MilsToIU( 500 );
4818 int sheetSpacing = schIUScale.MilsToIU( 2000 );
4819 int pageNum = 2;
4820
4821 while( tok.HasMoreTokens() )
4822 {
4823 wxString filePath = tok.GetNextToken();
4824 wxFileName fn( filePath );
4825
4826 VECTOR2I pos( schIUScale.MilsToIU( 500 ), sheetY );
4827 VECTOR2I size( schIUScale.MilsToIU( 2000 ), schIUScale.MilsToIU( 1500 ) );
4828
4829 auto subSheet = std::make_unique<SCH_SHEET>( m_rootSheet, pos, size );
4830
4831 subSheet->GetField( FIELD_T::SHEET_NAME )->SetText(
4832 wxString::Format( wxT( "Page %d" ), pageNum ) );
4833 subSheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( fn.GetFullName() );
4834 subSheet->SetFileName( fn.GetFullName() );
4835
4836 SCH_SHEET* subSheetPtr = subSheet.get();
4837 m_screen->Append( subSheet.release() );
4838
4839 if( fn.FileExists() )
4840 {
4841 SCH_SCREEN* subScreen = new SCH_SCREEN( m_schematic );
4842 subScreen->SetFileName( fn.GetFullPath() );
4843 subSheetPtr->SetScreen( subScreen );
4844
4845 SCH_IO_GEDA subImporter;
4846
4847 for( const auto& [name, entry] : m_symLibrary )
4848 {
4850 copy.path = entry.path;
4851 copy.symversion = entry.symversion;
4852 copy.netAttr = entry.netAttr;
4853 subImporter.m_symLibrary[name] = std::move( copy );
4854 }
4855
4857
4858 try
4859 {
4860 subImporter.LoadSchematicFile( fn.GetFullPath(), m_schematic,
4861 subSheetPtr, nullptr );
4862 }
4863 catch( const IO_ERROR& e )
4864 {
4865 if( m_reporter )
4866 {
4867 m_reporter->Report(
4868 wxString::Format( _( "Failed to load page '%s': %s" ),
4869 fn.GetFullName(), e.What() ),
4871 }
4872 }
4873 }
4874
4875 sheetY += sheetSpacing;
4876 pageNum++;
4877 }
4878 }
4879 }
4880
4881 return m_rootSheet;
4882}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
constexpr double ARC_LOW_DEF_MM
Definition base_units.h:118
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr Vec Centre() const
Definition box2.h:97
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:658
constexpr size_type GetHeight() const
Definition box2.h:215
constexpr const SizeVec & GetSize() const
Definition box2.h:206
void SetBezierC2(const VECTOR2I &aPt)
Definition eda_shape.h:258
void SetCenter(const VECTOR2I &aCenter)
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:178
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:220
void SetBezierC1(const VECTOR2I &aPt)
Definition eda_shape.h:255
void SetFillMode(FILL_T aFill)
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:546
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:400
REPORTER * m_reporter
Reporter to log errors/warnings to, may be nullptr.
Definition io_base.h:237
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()
Definition kiid.h:49
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:192
Define a library symbol object.
Definition lib_symbol.h:83
void SetGlobalPower()
wxString GetName() const override
Definition lib_symbol.h:145
SCH_FIELD & GetValueField()
Return reference to the value field.
Definition lib_symbol.h:332
std::vector< SCH_PIN * > GetPins() const override
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
SCH_FIELD & GetReferenceField()
Return reference to the reference designator field.
Definition lib_symbol.h:336
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:79
void SetHeightMils(double aHeightInMils)
const VECTOR2D GetSizeIU(double aIUScale) const
Gets the page size in internal units.
Definition page_info.h:177
void SetWidthMils(double aWidthInMils)
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
VECTOR2I GetSize() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Holds all the data relating to one schematic.
Definition schematic.h:88
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:172
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
std::unique_ptr< PENDING_COMPONENT > m_pendingComp
Pending component awaiting symbol resolution.
void addBusEntries()
Create SCH_BUS_WIRE_ENTRY objects where net endpoints touch bus segments.
void importHierarchicalSheet(const wxString &aSourceFile)
Import a gEDA hierarchical sub-schematic as a KiCad SCH_SHEET.
int toKiCadDist(int aMils) const
Convert a gEDA distance in mils to KiCad IU.
SCHEMATIC * m_schematic
void addSymbolGraphic(LIB_SYMBOL &aSymbol, const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx, wxChar aType)
Add a graphical item (line/box/circle/arc/path) to a LIB_SYMBOL.
void parseBus(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void fitPageToContent()
Enlarge the page if needed and center all content on the sheet.
std::vector< GEDA_ATTR > maybeParseAttributes(wxTextFile &aFile, size_t &aLineIdx)
Check for and consume a { } attribute block at the current line position.
wxFileName m_filename
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load information from some input file format that this SCH_IO implementation knows about,...
bool m_symLibraryInitialized
const GEDA_ATTR * findAttrStruct(const std::vector< GEDA_ATTR > &aAttrs, const wxString &aName) const
Find a GEDA_ATTR struct by name, returns nullptr if not found.
void parseEmbeddedComponent(wxTextFile &aFile, size_t &aLineIdx)
void addJunctions()
Place junctions where 3+ net/pin endpoints coincide.
void parseNet(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void postProcess()
Run the full post-processing pipeline after parsing is complete:
long m_fileFormatVersion
std::map< std::pair< int, int >, int > m_netEndpoints
Net endpoint positions in raw gEDA coordinates for junction detection.
int m_powerCounter
Sequential counter for auto-generated #PWR references.
SCH_SHEET * m_rootSheet
static constexpr int MILS_TO_IU
gEDA coordinates are mils with Y-up.
SCH_SCREEN * m_screen
int m_maxY
The maximum Y coordinate seen during parsing (gEDA coords, before flip).
std::vector< DEFERRED_SHEET > m_deferredSheets
std::unique_ptr< LIB_SYMBOL > loadBuiltinSymbol(const wxString &aBasename)
Try loading a symbol from the built-in standard library embedded in the importer.
LIB_SYMBOL * getOrLoadSymbol(const wxString &aBasename)
Get or load a cached symbol by gEDA basename.
void parseText(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void parseComponent(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void parsePin(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
int toKiCadOrientation(int aAngle, int aMirror) const
Map gEDA angle (0/90/180/270) + mirror to KiCad symbol orientation.
void parseLine(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void parseCircle(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
std::map< wxString, std::unique_ptr< LIB_SYMBOL > > m_libSymbols
Loaded symbols for this import session, keyed by basename.
void parsePath(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void scanSymbolDir(const wxString &aDir, int aDepth=0)
Recursively scan a directory for .sym files and populate m_symLibrary.
std::set< wxString > m_importStack
Set of fully-resolved file paths currently in the import call stack, used to detect and prevent circu...
wxString getLibName() const
Derive the KiCad import library name from the schematic file.
void flushPendingComponent()
Instantiate the pending component: look up or load the symbol, create SCH_SYMBOL, apply transforms an...
std::unique_ptr< LIB_SYMBOL > createFallbackSymbol(const wxString &aBasename)
Create a fallback rectangular symbol when the .sym file is not found.
static const std::map< wxString, wxString > & getBuiltinSymbols()
Return the map of built-in gEDA symbol definitions (symbol name -> .sym content).
VECTOR2I toKiCad(int aGedaX, int aGedaY) const
Apply the Y-flip and scale to transform gEDA coords to KiCad.
static LINE_STYLE toLineStyle(int aDashStyle)
Map gEDA dashstyle (0-4) to KiCad LINE_STYLE.
void parseBox(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
std::vector< NET_ATTR_RECORD > m_netAttrRecords
wxString findAttr(const std::vector< GEDA_ATTR > &aAttrs, const wxString &aName) const
Find an attribute by name, returns empty string if not found.
std::vector< BUS_SEGMENT > m_busSegments
void parseArc(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
bool CanReadSchematicFile(const wxString &aFileName) const override
Checks if this SCH_IO can read the specified schematic file.
std::unique_ptr< LIB_SYMBOL > loadSymbolFile(const wxString &aPath, wxString *aSymversion=nullptr, wxString *aNetAttr=nullptr)
Load a .sym file and return a LIB_SYMBOL.
void processNetAttributes()
For each component with net= attributes, create global labels at pin positions.
void initSymbolLibrary()
Ensure the symbol library hash is built by scanning gEDA library dirs.
std::map< wxString, SYM_CACHE_ENTRY > m_symLibrary
Symbol library cache: gEDA basename -> cache entry.
long m_releaseVersion
gEDA file version fields from the "v YYYYMMDD N" header line.
~SCH_IO_GEDA() override
void parsePicture(const wxString &aLine, wxTextFile &aFile, size_t &aLineIdx)
void parseRcFileForLibraries(const wxString &aPath, const wxString &aBaseDir)
Parse an RC file (gafrc or gschemrc) for component-library directives.
void trackEndpoint(int aGedaX, int aGedaY)
Track a point as a net or pin endpoint for junction detection.
bool parseVersionLine(const wxString &aLine)
Parse the "v YYYYMMDD N" version line and validate.
std::vector< GEDA_ATTR > parseAttributes(wxTextFile &aFile, size_t &aLineIdx)
Read a { } attribute block starting after the '{' line.
const std::map< std::string, UTF8 > * m_properties
Properties passed from the import framework (search paths, etc.)
void addSymbolPin(LIB_SYMBOL &aSymbol, int aX1, int aY1, int aX2, int aY2, int aWhichEnd, const std::vector< GEDA_ATTR > &aAttrs)
Create a pin on a LIB_SYMBOL from gEDA P line data and its attributes.
void loadDeferredSheets()
Load sub-schematics for any SCH_SHEET objects created during parsing.
static FILL_T toFillType(int aFillType)
Map gEDA filltype (0-4) to KiCad FILL_T.
SCH_IO(const wxString &aName)
Definition sch_io.h:375
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:168
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:341
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
const KIID & GetUuid() const
Definition sch_screen.h:532
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
void SetFilled(bool aFilled) override
Definition sch_shape.cpp:69
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:63
void AddPoint(const VECTOR2I &aPosition)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Simple container to manage line stroke parameters.
virtual void SetShowPinNumbers(bool aShow)
Set or clear the pin number visibility flag.
Definition symbol.h:174
virtual void SetShowPinNames(bool aShow)
Set or clear the pin name visibility flag.
Definition symbol.h:168
wxString wx_str() const
Definition utf8.cpp:45
static REPORTER & GetInstance()
Definition reporter.cpp:195
#define DEFAULT_SCH_ENTRY_SIZE
The default text size in mils. (can be changed in preference menu)
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_270
Definition eda_angle.h:416
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
FILL_T
Definition eda_shape.h:56
@ NO_FILL
Definition eda_shape.h:57
@ HATCH
Definition eda_shape.h:61
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:58
@ CROSS_HATCH
Definition eda_shape.h:63
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
@ LAYER_DEVICE
Definition layer_ids.h:466
@ LAYER_WIRE
Definition layer_ids.h:452
@ LAYER_NOTES
Definition layer_ids.h:467
@ LAYER_BUS
Definition layer_ids.h:453
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:36
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:37
@ PT_OUTPUT
usual output
Definition pin_type.h:38
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:40
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:39
@ PT_OPENEMITTER
pin type open emitter
Definition pin_type.h:49
@ PT_OPENCOLLECTOR
pin type open collector
Definition pin_type.h:48
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:46
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:43
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:105
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:127
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:111
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:118
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:135
@ RPT_SEVERITY_WARNING
static constexpr int GEDA_DEFAULT_TEXT_SIZE_MILS
static wxString convertOverbars(const wxString &aInput)
Convert gEDA overbar markup to KiCad syntax.
static constexpr int DEFAULT_SYMBOL_SIZE_MILS
static int editDistance(const wxString &a, const wxString &b)
Compute the Levenshtein edit distance between two strings.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
LINE_STYLE
Dashed line types.
Parsed bus segment in KiCad coordinates with gEDA ripper direction.
Deferred hierarchical sheet loads.
Parsed attribute from a gEDA T line inside a { } block.
int showNV
0=name+value, 1=value, 2=name
Components with net= attributes that need post-processing.
Entry in the symbol library search cache.
wxString netAttr
net= attribute (e.g. "GND:1") identifying power symbols
wxString path
Full path to .sym file.
@ SYM_ORIENT_270
Definition symbol.h:42
@ SYM_MIRROR_Y
Definition symbol.h:44
@ SYM_ORIENT_180
Definition symbol.h:41
@ SYM_ORIENT_90
Definition symbol.h:40
@ SYM_ORIENT_0
Definition symbol.h:39
@ USER
The field ID hasn't been set yet; field is invalid.
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
KIBIS top(path, &reporter)
KIBIS_PIN * pin
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
double DEG2RAD(double deg)
Definition trigo.h:166
@ SCH_LINE_T
Definition typeinfo.h:167
constexpr int sign(T val)
Definition util.h:145
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
Definition of file extensions used in Kicad.