1 /**
2 * @file gerbview.cpp
3 * @brief GERBVIEW main file.
4 */
5
6 /*
7 * This program source code file is part of KiCad, a free EDA CAD application.
8 *
9 * Copyright (C) 1992-2021 KiCad Developers, see change_log.txt for contributors.
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, you may find one here:
23 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24 * or you may search the http://www.gnu.org website for the version 2 license,
25 * or you may write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27 */
28
29 #include <gerbview.h>
30 #include <gerbview_frame.h>
31 #include <gerbview_settings.h>
32 #include <gestfich.h>
33 #include <kiface_base.h>
34 #include <macros.h>
35 #include <nlohmann/json.hpp>
36 #include <pgm_base.h>
37 #include <settings/settings_manager.h>
38 #include <wildcards_and_files_ext.h>
39 #include <wx/ffile.h>
40
41 using json = nlohmann::json;
42
43
44 namespace GERBV {
45
46 static struct IFACE : public KIFACE_BASE
47 {
48 // Of course all are virtual overloads, implementations of the KIFACE.
49
IFACEGERBV::IFACE50 IFACE( const char* aName, KIWAY::FACE_T aType ) :
51 KIFACE_BASE( aName, aType )
52 {}
53
54 bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override;
55
56 void OnKifaceEnd() override;
57
CreateWindowGERBV::IFACE58 wxWindow* CreateWindow( wxWindow* aParent, int aClassId, KIWAY* aKiway,
59 int aCtlBits = 0 ) override
60 {
61 switch( aClassId )
62 {
63 case FRAME_GERBER:
64 {
65 GERBVIEW_FRAME* frame = new GERBVIEW_FRAME( aKiway, aParent );
66 return frame;
67 }
68 break;
69
70 default:
71 ;
72 }
73
74 return nullptr;
75 }
76
77 /**
78 * Return a pointer to the requested object.
79 *
80 * The safest way to use this is to retrieve a pointer to a static instance of an interface,
81 * similar to how the KIFACE interface is exported. But if you know what you are doing use
82 * it to retrieve anything you want.
83 *
84 * @param aDataId identifies which object you want the address of.
85 * @return the object requested and must be cast into the know type.
86 */
IfaceOrAddressGERBV::IFACE87 void* IfaceOrAddress( int aDataId ) override
88 {
89 return nullptr;
90 }
91
92 /**
93 * Saving a file under a different name is delegated to the various KIFACEs because
94 * the project doesn't know the internal format of the various files (which may have
95 * paths in them that need updating).
96 */
97 void SaveFileAs( const wxString& aProjectBasePath, const wxString& aProjectName,
98 const wxString& aNewProjectBasePath, const wxString& aNewProjectName,
99 const wxString& aSrcFilePath, wxString& aErrors ) override;
100
101 } kiface( "gerbview", KIWAY::FACE_GERBVIEW );
102
103 } // namespace
104
105
106 using namespace GERBV;
107
108
109 static PGM_BASE* process;
110
111
Kiface()112 KIFACE_BASE& Kiface() { return kiface; }
113
114
115 // KIFACE_GETTER's actual spelling is a substitution macro found in kiway.h.
116 // KIFACE_GETTER will not have name mangling due to declaration in kiway.h.
KIFACE_GETTER(int * aKIFACEversion,int aKiwayVersion,PGM_BASE * aProgram)117 MY_API( KIFACE* ) KIFACE_GETTER( int* aKIFACEversion, int aKiwayVersion, PGM_BASE* aProgram )
118 {
119 process = aProgram;
120 return &kiface;
121 }
122
123
Pgm()124 PGM_BASE& Pgm()
125 {
126 wxASSERT( process ); // KIFACE_GETTER has already been called.
127 return *process;
128 }
129
130
131 // Similar to PGM_BASE& Pgm(), but return nullptr when a *.ki_face is run from a python script.
PgmOrNull()132 PGM_BASE* PgmOrNull()
133 {
134 return process;
135 }
136
137
OnKifaceStart(PGM_BASE * aProgram,int aCtlBits)138 bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
139 {
140 InitSettings( new GERBVIEW_SETTINGS );
141 aProgram->GetSettingsManager().RegisterSettings( KifaceSettings() );
142 start_common( aCtlBits );
143 return true;
144 }
145
146
OnKifaceEnd()147 void IFACE::OnKifaceEnd()
148 {
149 end_common();
150 }
151
152
SaveFileAs(const wxString & aProjectBasePath,const wxString & aProjectName,const wxString & aNewProjectBasePath,const wxString & aNewProjectName,const wxString & aSrcFilePath,wxString & aErrors)153 void IFACE::SaveFileAs( const wxString& aProjectBasePath, const wxString& aProjectName,
154 const wxString& aNewProjectBasePath, const wxString& aNewProjectName,
155 const wxString& aSrcFilePath, wxString& aErrors )
156 {
157 wxFileName destFile( aSrcFilePath );
158 wxString destPath = destFile.GetPathWithSep();
159 wxUniChar pathSep = wxFileName::GetPathSeparator();
160 wxString ext = destFile.GetExt();
161
162 if( destPath.StartsWith( aProjectBasePath + pathSep ) )
163 {
164 destPath.Replace( aProjectBasePath, aNewProjectBasePath, false );
165 destFile.SetPath( destPath );
166 }
167
168 if( ext == "gbr" || IsProtelExtension( ext ) )
169 {
170 wxString destFileName = destFile.GetName();
171
172 if( destFileName.StartsWith( aProjectName + "-" ) )
173 {
174 destFileName.Replace( aProjectName, aNewProjectName, false );
175 destFile.SetName( destFileName );
176 }
177
178 KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
179 }
180 else if( ext == "gbrjob" )
181 {
182 if( destFile.GetName() == aProjectName + "-job" )
183 destFile.SetName( aNewProjectName + "-job" );
184
185 FILE_LINE_READER jobfileReader( aSrcFilePath );
186
187 char* line;
188 wxString data;
189
190 while( ( line = jobfileReader.ReadLine() ) )
191 data << line << '\n';
192
193 // detect the file format: old (deprecated) gerber format or official JSON format
194 if( !data.Contains( "{" ) )
195 {
196 KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
197 return;
198 }
199
200 bool success = false;
201
202 try
203 {
204 // Will throw on parse error
205 json js = json::parse( TO_UTF8( data ) );
206
207 for( auto& entry : js["FilesAttributes"] )
208 {
209 wxString path = wxString( entry["Path"].get<std::string>() );
210
211 if( path.StartsWith( aProjectName + "-" ) )
212 {
213 path.Replace( aProjectName, aNewProjectName, false );
214 entry["Path"] = path.ToStdString();
215 }
216 }
217
218 wxFFile destJobFile( destFile.GetFullPath(), "wb" );
219
220 if( destJobFile.IsOpened() )
221 success = destJobFile.Write( js.dump( 0 ) );
222
223 // wxFFile dtor will close the file
224 }
225 catch( ... )
226 {
227 success = false;
228 }
229
230 if( !success )
231 {
232 wxString msg;
233
234 if( !aErrors.empty() )
235 aErrors += "\n";
236
237 msg.Printf( _( "Cannot copy file '%s'." ), destFile.GetFullPath() );
238 aErrors += msg;
239 }
240 }
241 else if( ext == "drl" )
242 {
243 wxString destFileName = destFile.GetName();
244
245 if( destFileName == aProjectName )
246 destFileName = aNewProjectName;
247 else if( destFileName.StartsWith( aProjectName + "-" ) )
248 destFileName.Replace( aProjectName, aNewProjectName, false );
249
250 destFile.SetName( destFileName );
251
252 KiCopyFile( aSrcFilePath, destFile.GetFullPath(), aErrors );
253 }
254 else
255 {
256 wxFAIL_MSG( "Unexpected filetype for GerbView::SaveFileAs()" );
257 }
258 }
259
260