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