KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drill_chart_template.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <algorithm>
23#include <fstream>
24
25#include <i18n_utility.h>
26#include <json_common.h>
27
28#include <wx/translation.h>
29
30
32{
33 // Headings are _HKI() literals, translated only when DefaultColumn() hands one to a UI
34 // or a fresh chart, so regenerating a chart under a different language cannot alter an
35 // approved drawing already on disk
36 static const GENERATED_TABLE_SCHEMA schema(
37 { { (int) DRILL_CHART_COLUMN_ID::SYMBOL, "symbol", _HKI( "SYM" ), GENERATED_TABLE_ALIGN::CENTER, true,
38 _HKI( "Symbol" ) },
39 { (int) DRILL_CHART_COLUMN_ID::DRILL_DIAMETER, "drill_diameter", _HKI( "DRILL DIA" ),
40 GENERATED_TABLE_ALIGN::RIGHT, true, _HKI( "Drill diameter" ) },
41 { (int) DRILL_CHART_COLUMN_ID::SLOT_SIZE, "slot_size", _HKI( "SLOT W x L" ), GENERATED_TABLE_ALIGN::RIGHT,
42 true, _HKI( "Slot size" ) },
43 { (int) DRILL_CHART_COLUMN_ID::PLATING, "plating", _HKI( "PLATED" ), GENERATED_TABLE_ALIGN::CENTER, true,
44 _HKI( "Plating" ) },
45 { (int) DRILL_CHART_COLUMN_ID::OPERATION_COUNT, "operation_count", _HKI( "OPS" ),
46 GENERATED_TABLE_ALIGN::RIGHT, true, _HKI( "Operation count" ) },
47 { (int) DRILL_CHART_COLUMN_ID::SITE_COUNT, "site_count", _HKI( "SITE COUNT" ),
48 GENERATED_TABLE_ALIGN::RIGHT, false, _HKI( "Site count" ) },
49 { (int) DRILL_CHART_COLUMN_ID::LAYER_SPAN, "layer_span", _HKI( "FROM / TO" ),
50 GENERATED_TABLE_ALIGN::CENTER, true, _HKI( "Layer span" ) },
51 { (int) DRILL_CHART_COLUMN_ID::OPERATION, "operation", _HKI( "OPERATION" ), GENERATED_TABLE_ALIGN::LEFT,
52 false, _HKI( "Operation" ) },
53 { (int) DRILL_CHART_COLUMN_ID::PROTECTION, "protection", _HKI( "PROTECTION" ),
54 GENERATED_TABLE_ALIGN::LEFT, false, _HKI( "Protection" ) },
55 { (int) DRILL_CHART_COLUMN_ID::BACKDRILL_STUB, "backdrill_stub", _HKI( "BACKDRILL STUB" ),
56 GENERATED_TABLE_ALIGN::RIGHT, false, _HKI( "Backdrill stub" ) },
57 { (int) DRILL_CHART_COLUMN_ID::ASPECT_RATIO, "aspect_ratio", _HKI( "ASPECT RATIO" ),
58 GENERATED_TABLE_ALIGN::RIGHT, false, _HKI( "Aspect ratio" ) },
59 { (int) DRILL_CHART_COLUMN_ID::DESCRIPTION, "description", _HKI( "DESCRIPTION" ),
60 GENERATED_TABLE_ALIGN::LEFT, false, _HKI( "Description" ) } },
62
63 return schema;
64}
65
66
68{
69 return m_Plated == aOther.m_Plated && m_NonPlated == aOther.m_NonPlated
70 && m_Vias == aOther.m_Vias && m_Slots == aOther.m_Slots
71 && m_Backdrills == aOther.m_Backdrills && m_Castellated == aOther.m_Castellated;
72}
73
74
76 m_version( 1 ),
77 m_units( DrillChartSchema().DefaultUnits() ),
78 m_precision( DrillChartSchema().DefaultPrecision() ),
79 m_showTotals( true )
80{
81}
82
83
85{
87
88 tmpl.SetName( wxT( "Default" ) );
90
91 return tmpl;
92}
93
94
95bool DRILL_CHART_TEMPLATE::SaveToFile( const wxString& aPath, wxString* aError ) const
96{
97 nlohmann::json js;
98 js["name"] = m_name.ToUTF8();
99 js["version"] = m_version;
100 js["units"] = GeneratedTableUnitsToken( m_units );
101 js["precision"] = m_precision;
102 js["show_totals"] = m_showTotals;
103 js["columns"] = nlohmann::json::array();
104
105 for( const GENERATED_TABLE_COLUMN& col : m_columns )
106 {
107 nlohmann::json entry;
108 entry["id"] = DrillChartSchema().Token( col.m_Id );
109 entry["heading"] = col.m_Heading.ToUTF8();
110 entry["align"] = GeneratedTableAlignToken( col.m_Align );
111 entry["width"] = col.m_Width;
112 js["columns"].push_back( entry );
113 }
114
115 try
116 {
117 std::ofstream out( aPath.fn_str() );
118
119 if( !out.is_open() )
120 {
121 if( aError )
122 *aError = wxString::Format( _( "Cannot write '%s'." ), aPath );
123
124 return false;
125 }
126
127 out << js.dump( 2 ) << std::endl;
128
129 // failbit does not throw by default, so a full disk would otherwise leave a
130 // truncated template and report success
131 if( !out.good() )
132 {
133 if( aError )
134 *aError = wxString::Format( _( "Error writing '%s'." ), aPath );
135
136 return false;
137 }
138 }
139 catch( const std::exception& e )
140 {
141 if( aError )
142 *aError = wxString::FromUTF8( e.what() );
143
144 return false;
145 }
146
147 return true;
148}
149
150
151bool DRILL_CHART_TEMPLATE::LoadFromFile( const wxString& aPath, wxString* aError )
152{
154
155 // Typed accessors inside the try. Nlohmann throws on a wrong-typed field and it would
156 // otherwise escape through a dialog handler
157 try
158 {
159 std::ifstream in( aPath.fn_str() );
160
161 if( !in.is_open() )
162 {
163 if( aError )
164 *aError = wxString::Format( _( "Cannot read '%s'." ), aPath );
165
166 return false;
167 }
168
169 nlohmann::json js;
170 in >> js;
171
172 if( !js.is_object() )
173 {
174 if( aError )
175 *aError = _( "Template is not a JSON object." );
176
177 return false;
178 }
179
180 if( js.contains( "name" ) && js["name"].is_string() )
181 loaded.m_name = wxString::FromUTF8( js["name"].get<std::string>() );
182
183 if( js.contains( "version" ) && js["version"].is_number_integer() )
184 loaded.m_version = js["version"].get<int>();
185
186 if( js.contains( "units" ) && js["units"].is_string() )
187 {
188 GeneratedTableUnitsFromToken( wxString::FromUTF8( js["units"].get<std::string>() ),
189 loaded.m_units );
190 }
191
192 if( js.contains( "precision" ) && js["precision"].is_number_integer() )
193 loaded.m_precision = std::clamp( js["precision"].get<int>(), 0, 6 );
194
195 if( js.contains( "show_totals" ) && js["show_totals"].is_boolean() )
196 loaded.m_showTotals = js["show_totals"].get<bool>();
197
198 if( js.contains( "columns" ) && js["columns"].is_array() )
199 {
200 for( const nlohmann::json& entry : js["columns"] )
201 {
202 if( !entry.is_object() || !entry.contains( "id" ) || !entry["id"].is_string() )
203 continue;
204
205 // Skipped rather than defaulted, so a template written by a newer KiCad does
206 // not silently turn an unknown column into the symbol column
208 wxString::FromUTF8( entry["id"].get<std::string>() ) );
209
210 if( !def )
211 continue;
212
214 col.m_Id = def->m_Id;
215
216 if( entry.contains( "heading" ) && entry["heading"].is_string() )
217 col.m_Heading = wxString::FromUTF8( entry["heading"].get<std::string>() );
218
219 if( entry.contains( "align" ) && entry["align"].is_string() )
220 {
221 GeneratedTableAlignFromToken( wxString::FromUTF8( entry["align"].get<std::string>() ),
222 col.m_Align );
223 }
224
225 if( entry.contains( "width" ) && entry["width"].is_number_integer() )
226 col.m_Width = entry["width"].get<int>();
227
228 loaded.m_columns.push_back( col );
229 }
230 }
231 }
232 catch( const std::exception& e )
233 {
234 if( aError )
235 *aError = wxString::FromUTF8( e.what() );
236
237 return false;
238 }
239
240 if( !DrillChartSchema().Validate( loaded.m_columns ) )
241 {
242 if( aError )
243 *aError = _( "Template has no usable columns." );
244
245 return false;
246 }
247
248 *this = std::move( loaded );
249 return true;
250}
bool SaveToFile(const wxString &aPath, wxString *aError) const
Read and write a template as JSON.
void SetName(const wxString &aName)
wxString m_name
Identify the template file to whoever opens it.
std::vector< GENERATED_TABLE_COLUMN > & Columns()
bool LoadFromFile(const wxString &aPath, wxString *aError)
GENERATED_TABLE_UNITS m_units
std::vector< GENERATED_TABLE_COLUMN > m_columns
static DRILL_CHART_TEMPLATE MakeDefault()
The columns one generator (the drill chart, a stackup table, ...) can produce, and the units and prec...
const char * Token(int aId) const
The file token for a known id, or a stable placeholder for one the schema does not recognize,...
std::vector< GENERATED_TABLE_COLUMN > Defaults() const
const GENERATED_TABLE_COLUMN_DEF * FindToken(const wxString &aToken) const
const GENERATED_TABLE_SCHEMA & DrillChartSchema()
Every column the drill chart schema knows how to build, in the order the schema lists them.
const GENERATED_TABLE_SCHEMA & DrillChartSchema()
Every column the drill chart schema knows how to build, in the order the schema lists them.
#define _(s)
bool GeneratedTableUnitsFromToken(const wxString &aToken, GENERATED_TABLE_UNITS &aUnits)
const char * GeneratedTableUnitsToken(GENERATED_TABLE_UNITS aUnits)
Stable file tokens, independent of enum ordering so inserting a value later cannot change what an exi...
bool GeneratedTableAlignFromToken(const wxString &aToken, GENERATED_TABLE_ALIGN &aAlign)
const char * GeneratedTableAlignToken(GENERATED_TABLE_ALIGN aAlign)
Some functions to handle hotkeys in KiCad.
#define _HKI(x)
Definition page_info.cpp:40
Which holes a chart reports on.
bool operator==(const DRILL_CHART_FILTER &aOther) const
The column a schema knows how to build, independent of whether any particular table shows it.
A column as a generated table actually carries it: what a row's cell in that column holds and how it ...
GENERATED_TABLE_ALIGN m_Align