1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
23  */
24 
25 #include <wx/filedlg.h>
26 #include <wx/wfstream.h>
27 #include <wx/zipstrm.h>
28 #include <reporter.h>
29 #include <dialogs/html_message_box.h>
30 #include <gerbview_frame.h>
31 #include <gerbview_id.h>
32 #include <gerber_file_image.h>
33 #include <gerber_file_image_list.h>
34 #include <excellon_image.h>
35 #include <wildcards_and_files_ext.h>
36 #include <view/view.h>
37 #include <widgets/wx_progress_reporters.h>
38 #include "widgets/gerbview_layer_widget.h"
39 
40 // HTML Messages used more than one time:
41 #define MSG_NO_MORE_LAYER _( "<b>No more available layers</b> in GerbView to load files" )
42 #define MSG_NOT_LOADED _( "\n<b>Not loaded:</b> <i>%s</i>" )
43 #define MSG_OOM _( "\n<b>Memory was exhausted reading:</b> <i>%s</i>" )
44 
45 
OnGbrFileHistory(wxCommandEvent & event)46 void GERBVIEW_FRAME::OnGbrFileHistory( wxCommandEvent& event )
47 {
48     wxString fn;
49 
50     fn = GetFileFromHistory( event.GetId(), _( "Gerber files" ) );
51 
52     if( !fn.IsEmpty() )
53     {
54         Erase_Current_DrawLayer( false );
55         LoadGerberFiles( fn );
56     }
57 }
58 
OnClearGbrFileHistory(wxCommandEvent & aEvent)59 void GERBVIEW_FRAME::OnClearGbrFileHistory( wxCommandEvent& aEvent )
60 {
61     ClearFileHistory();
62 }
63 
64 
OnDrlFileHistory(wxCommandEvent & event)65 void GERBVIEW_FRAME::OnDrlFileHistory( wxCommandEvent& event )
66 {
67     wxString fn;
68 
69     fn = GetFileFromHistory( event.GetId(), _( "Drill files" ), &m_drillFileHistory );
70 
71     if( !fn.IsEmpty() )
72     {
73         Erase_Current_DrawLayer( false );
74         LoadExcellonFiles( fn );
75     }
76 }
77 
78 
OnClearDrlFileHistory(wxCommandEvent & aEvent)79 void GERBVIEW_FRAME::OnClearDrlFileHistory( wxCommandEvent& aEvent )
80 {
81     ClearFileHistory( &m_drillFileHistory );
82 }
83 
84 
OnZipFileHistory(wxCommandEvent & event)85 void GERBVIEW_FRAME::OnZipFileHistory( wxCommandEvent& event )
86 {
87     wxString filename;
88     filename = GetFileFromHistory( event.GetId(), _( "Zip files" ), &m_zipFileHistory );
89 
90     if( !filename.IsEmpty() )
91     {
92         Erase_Current_DrawLayer( false );
93         LoadZipArchiveFile( filename );
94     }
95 }
96 
97 
OnClearZipFileHistory(wxCommandEvent & aEvent)98 void GERBVIEW_FRAME::OnClearZipFileHistory( wxCommandEvent& aEvent )
99 {
100     ClearFileHistory( &m_zipFileHistory );
101 }
102 
103 
OnJobFileHistory(wxCommandEvent & event)104 void GERBVIEW_FRAME::OnJobFileHistory( wxCommandEvent& event )
105 {
106     wxString filename = GetFileFromHistory( event.GetId(), _( "Job files" ), &m_jobFileHistory );
107 
108     if( !filename.IsEmpty() )
109         LoadGerberJobFile( filename );
110 }
111 
112 
OnClearJobFileHistory(wxCommandEvent & aEvent)113 void GERBVIEW_FRAME::OnClearJobFileHistory( wxCommandEvent& aEvent )
114 {
115     ClearFileHistory( &m_jobFileHistory );
116 }
117 
118 
LoadGerberFiles(const wxString & aFullFileName)119 bool GERBVIEW_FRAME::LoadGerberFiles( const wxString& aFullFileName )
120 {
121     static int lastGerberFileWildcard = 0;
122     wxString   filetypes;
123     wxArrayString filenamesList;
124     wxFileName filename = aFullFileName;
125     wxString currentPath;
126 
127     if( !filename.IsOk() )
128     {
129         /* Standard gerber filetypes
130          * (See http://en.wikipedia.org/wiki/Gerber_File)
131          * The .gbr (.pho in legacy files) extension is the default used in Pcbnew; however
132          * there are a lot of other extensions used for gerber files.  Because the first letter
133          * is usually g, we accept g* as extension.
134          * (Mainly internal copper layers do not have specific extension, and filenames are like
135          * *.g1, *.g2 *.gb1 ...)
136          * Now (2014) Ucamco (the company which manages the Gerber format) encourages use of .gbr
137          * only and the Gerber X2 file format.
138          */
139         filetypes = _( "Gerber files (.g* .lgr .pho)" );
140         filetypes << wxT("|");
141         filetypes += wxT("*.g*;*.G*;*.pho;*.PHO" );
142         filetypes << wxT("|");
143 
144         /* Special gerber filetypes */
145         filetypes += _( "Top layer" ) + AddFileExtListToFilter( { "GTL" } ) + wxT( "|" );
146         filetypes += _( "Bottom layer" ) + AddFileExtListToFilter( { "GBL" } ) + wxT( "|" );
147         filetypes += _( "Bottom solder resist" ) + AddFileExtListToFilter( { "GBS" } ) + wxT( "|" );
148         filetypes += _( "Top solder resist" ) + AddFileExtListToFilter( { "GTS" } ) + wxT( "|" );
149         filetypes += _( "Bottom overlay" ) + AddFileExtListToFilter( { "GBO" } ) + wxT( "|" );
150         filetypes += _( "Top overlay" ) + AddFileExtListToFilter( { "GTO" } ) + wxT( "|" );
151         filetypes += _( "Bottom paste" ) + AddFileExtListToFilter( { "GBP" } ) + wxT( "|" );
152         filetypes += _( "Top paste" ) + AddFileExtListToFilter( { "GTP" } ) + wxT( "|" );
153         filetypes += _( "Keep-out layer" ) + AddFileExtListToFilter( { "GKO" } ) + wxT( "|" );
154         filetypes += _( "Mechanical layers" ) + AddFileExtListToFilter( { "GM1", "GM2", "GM3", "GM4", "GM5", "GM6", "GM7", "GM8", "GM9" } ) + wxT( "|" );
155         filetypes += _( "Top Pad Master" ) + AddFileExtListToFilter( { "GPT" } ) + wxT( "|" );
156         filetypes += _( "Bottom Pad Master" ) + AddFileExtListToFilter( { "GPB" } ) + wxT( "|" );
157 
158         // All filetypes
159         filetypes += AllFilesWildcard();
160 
161         // Use the current working directory if the file name path does not exist.
162         if( filename.DirExists() )
163             currentPath = filename.GetPath();
164         else
165         {
166             currentPath = m_mruPath;
167 
168             // On wxWidgets 3.1 (bug?) the path in wxFileDialog is ignored when
169             // finishing by the dir separator. Remove it if any:
170             if( currentPath.EndsWith( '\\' ) || currentPath.EndsWith( '/' ) )
171                 currentPath.RemoveLast();
172         }
173 
174         wxFileDialog dlg( this, _( "Open Gerber File(s)" ), currentPath, filename.GetFullName(),
175                           filetypes,
176                           wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE | wxFD_CHANGE_DIR );
177         dlg.SetFilterIndex( lastGerberFileWildcard );
178 
179         if( dlg.ShowModal() == wxID_CANCEL )
180             return false;
181 
182         lastGerberFileWildcard = dlg.GetFilterIndex();
183         dlg.GetPaths( filenamesList );
184         m_mruPath = currentPath = dlg.GetDirectory();
185     }
186     else
187     {
188         filenamesList.Add( aFullFileName );
189         m_mruPath = currentPath = filename.GetPath();
190     }
191 
192     Erase_Current_DrawLayer( false );
193 
194     // Set the busy cursor
195     wxBusyCursor wait;
196 
197     bool isFirstFile = GetImagesList()->GetLoadedImageCount() == 0;
198 
199     bool success = LoadListOfGerberAndDrillFiles( currentPath, filenamesList );
200 
201     // Auto zoom is only applied if there is only one file loaded
202     if( isFirstFile )
203     {
204         Zoom_Automatique( false );
205     }
206 
207     return success;
208 }
209 
210 
LoadListOfGerberAndDrillFiles(const wxString & aPath,const wxArrayString & aFilenameList,const std::vector<int> * aFileType)211 bool GERBVIEW_FRAME::LoadListOfGerberAndDrillFiles( const wxString& aPath,
212                                             const wxArrayString& aFilenameList,
213                                             const std::vector<int>* aFileType )
214 {
215     wxFileName filename;
216 
217     // Read gerber files: each file is loaded on a new GerbView layer
218     bool success = true;
219     int layer = GetActiveLayer();
220     LSET visibility = GetVisibleLayers();
221 
222     // Manage errors when loading files
223     wxString msg;
224     WX_STRING_REPORTER reporter( &msg );
225 
226     // Create progress dialog (only used if more than 1 file to load
227     std::unique_ptr<WX_PROGRESS_REPORTER> progress = nullptr;
228 
229     for( unsigned ii = 0; ii < aFilenameList.GetCount(); ii++ )
230     {
231         filename = aFilenameList[ii];
232 
233         if( !filename.IsAbsolute() )
234             filename.SetPath( aPath );
235 
236         // Check for non existing files, to avoid creating broken or useless data
237         // and report all in one error list:
238         if( !filename.FileExists() )
239         {
240             wxString warning;
241             warning << "<b>" << _( "File not found:" ) << "</b><br>"
242                     << filename.GetFullPath() << "<br>";
243             reporter.Report( warning, RPT_SEVERITY_WARNING );
244             success = false;
245             continue;
246         }
247 
248         m_lastFileName = filename.GetFullPath();
249 
250         if( !progress && ( aFilenameList.GetCount() > 1 ) )
251         {
252             progress = std::make_unique<WX_PROGRESS_REPORTER>( this,
253                                                                _( "Loading Gerber files..." ), 1,
254                                                                false );
255             progress->SetMaxProgress( aFilenameList.GetCount() - 1 );
256             progress->Report( wxString::Format( _("Loading %u/%zu %s..." ),
257                                                 ii+1,
258                                                 aFilenameList.GetCount(),
259                                                 m_lastFileName ) );
260         }
261         else if( progress )
262         {
263             progress->Report( wxString::Format( _("Loading %u/%zu %s..." ),
264                                                 ii+1,
265                                                 aFilenameList.GetCount(),
266                                                 m_lastFileName ) );
267             progress->KeepRefreshing();
268         }
269 
270         SetActiveLayer( layer, false );
271 
272         visibility[ layer ] = true;
273 
274         try
275         {
276             if( aFileType && ( *aFileType )[ii] == 1 )
277             {
278                 LoadExcellonFiles( filename.GetFullPath() );
279                 layer = GetActiveLayer(); // Loading NC drill file changes the active layer
280             }
281             else
282             {
283                 if( filename.GetExt() == GerberJobFileExtension.c_str() )
284                 {
285                     //We cannot read a gerber job file as a gerber plot file: skip it
286                     wxString txt;
287                     txt.Printf( _( "<b>A gerber job file cannot be loaded as a plot file</b> "
288                                    "<i>%s</i>" ),
289                                 filename.GetFullName() );
290                     success = false;
291                     reporter.Report( txt, RPT_SEVERITY_ERROR );
292                 }
293                 else if( Read_GERBER_File( filename.GetFullPath() ) )
294                 {
295                     UpdateFileHistory( m_lastFileName );
296 
297                     GetCanvas()->GetView()->SetLayerHasNegatives(
298                             GERBER_DRAW_LAYER( layer ), GetGbrImage( layer )->HasNegativeItems() );
299 
300                     layer = getNextAvailableLayer( layer );
301 
302                     if( layer == NO_AVAILABLE_LAYERS && ii < aFilenameList.GetCount() - 1 )
303                     {
304                         success = false;
305                         reporter.Report( MSG_NO_MORE_LAYER, RPT_SEVERITY_ERROR );
306 
307                         // Report the name of not loaded files:
308                         ii += 1;
309                         while( ii < aFilenameList.GetCount() )
310                         {
311                             filename = aFilenameList[ii++];
312                             wxString txt =
313                                     wxString::Format( MSG_NOT_LOADED, filename.GetFullName() );
314                             reporter.Report( txt, RPT_SEVERITY_ERROR );
315                         }
316                         break;
317                     }
318 
319                     SetActiveLayer( layer, false );
320                 }
321             }
322         }
323         catch( const std::bad_alloc& )
324         {
325             wxString txt = wxString::Format( MSG_OOM, filename.GetFullName() );
326             reporter.Report( txt, RPT_SEVERITY_ERROR );
327             success = false;
328             continue;
329         }
330 
331         if( progress )
332             progress->AdvanceProgress();
333     }
334 
335     if( !success )
336     {
337         wxSafeYield();  // Allows slice of time to redraw the screen
338                         // to refresh widgets, before displaying messages
339         HTML_MESSAGE_BOX mbox( this, _( "Errors" ) );
340         mbox.ListSet( msg );
341         mbox.ShowModal();
342     }
343 
344     SetVisibleLayers( visibility );
345 
346     // Synchronize layers tools with actual active layer:
347     ReFillLayerWidget();
348 
349     // TODO: it would be nice if we could set the active layer to one of the
350     // ones that was just loaded, but to maintain the previous user experience
351     // we need to set it to a blank layer in case they load another file.
352     // We can't start with the next available layer when loading files because
353     // some users expect the behavior of overwriting the active layer on load.
354     SetActiveLayer( getNextAvailableLayer( layer ), true );
355 
356     m_LayersManager->UpdateLayerIcons();
357     syncLayerBox( true );
358 
359     GetCanvas()->Refresh();
360 
361     return success;
362 }
363 
364 
LoadExcellonFiles(const wxString & aFullFileName)365 bool GERBVIEW_FRAME::LoadExcellonFiles( const wxString& aFullFileName )
366 {
367     wxString   filetypes;
368     wxArrayString filenamesList;
369     wxFileName filename = aFullFileName;
370     wxString currentPath;
371 
372     if( !filename.IsOk() )
373     {
374         filetypes = DrillFileWildcard();
375         filetypes << wxT( "|" );
376 
377         /* All filetypes */
378         filetypes += AllFilesWildcard();
379 
380         /* Use the current working directory if the file name path does not exist. */
381         if( filename.DirExists() )
382             currentPath = filename.GetPath();
383         else
384             currentPath = m_mruPath;
385 
386         wxFileDialog dlg( this, _( "Open NC (Excellon) Drill File(s)" ),
387                           currentPath, filename.GetFullName(), filetypes,
388                           wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE | wxFD_CHANGE_DIR );
389 
390         if( dlg.ShowModal() == wxID_CANCEL )
391             return false;
392 
393         dlg.GetPaths( filenamesList );
394         currentPath = wxGetCwd();
395         m_mruPath = currentPath;
396     }
397     else
398     {
399         filenamesList.Add( aFullFileName );
400         currentPath = filename.GetPath();
401         m_mruPath = currentPath;
402     }
403 
404     // Read Excellon drill files: each file is loaded on a new GerbView layer
405     bool success = true;
406     int layer = GetActiveLayer();
407 
408     // Manage errors when loading files
409     wxString msg;
410     WX_STRING_REPORTER reporter( &msg );
411 
412     for( unsigned ii = 0; ii < filenamesList.GetCount(); ii++ )
413     {
414         filename = filenamesList[ii];
415 
416         if( !filename.IsAbsolute() )
417             filename.SetPath( currentPath );
418 
419         m_lastFileName = filename.GetFullPath();
420 
421         SetActiveLayer( layer, false );
422 
423         if( Read_EXCELLON_File( filename.GetFullPath() ) )
424         {
425             // Update the list of recent drill files.
426             UpdateFileHistory( filename.GetFullPath(), &m_drillFileHistory );
427 
428             layer = getNextAvailableLayer( layer );
429 
430             if( layer == NO_AVAILABLE_LAYERS && ii < filenamesList.GetCount()-1 )
431             {
432                 success = false;
433                 reporter.Report( MSG_NO_MORE_LAYER, RPT_SEVERITY_ERROR );
434 
435                 // Report the name of not loaded files:
436                 ii += 1;
437                 while( ii < filenamesList.GetCount() )
438                 {
439                     filename = filenamesList[ii++];
440                     wxString txt = wxString::Format( MSG_NOT_LOADED, filename.GetFullName() );
441                     reporter.Report( txt, RPT_SEVERITY_ERROR );
442                 }
443                 break;
444             }
445 
446             SetActiveLayer( layer, false );
447         }
448     }
449 
450     if( !success )
451     {
452         HTML_MESSAGE_BOX mbox( this, _( "Errors" ) );
453         mbox.ListSet( msg );
454         mbox.ShowModal();
455     }
456 
457     Zoom_Automatique( false );
458 
459     // Synchronize layers tools with actual active layer:
460     ReFillLayerWidget();
461     SetActiveLayer( GetActiveLayer() );
462     m_LayersManager->UpdateLayerIcons();
463     syncLayerBox();
464 
465     return success;
466 }
467 
468 
unarchiveFiles(const wxString & aFullFileName,REPORTER * aReporter)469 bool GERBVIEW_FRAME::unarchiveFiles( const wxString& aFullFileName, REPORTER* aReporter )
470 {
471     wxString msg;
472 
473     // Extract the path of aFullFileName. We use it to store temporary files
474     wxFileName fn( aFullFileName );
475     wxString unzipDir = fn.GetPath();
476 
477     wxFFileInputStream zipFile( aFullFileName );
478 
479     if( !zipFile.IsOk() )
480     {
481         if( aReporter )
482         {
483             msg.Printf( _( "Zip file '%s' cannot be opened." ), aFullFileName );
484             aReporter->Report( msg, RPT_SEVERITY_ERROR );
485         }
486 
487         return false;
488     }
489 
490     // Update the list of recent zip files.
491     UpdateFileHistory( aFullFileName, &m_zipFileHistory );
492 
493     // The unzipped file in only a temporary file. Give it a filename
494     // which cannot conflict with an usual filename.
495     // TODO: make Read_GERBER_File() and Read_EXCELLON_File() able to
496     // accept a stream, and avoid using a temp file.
497     wxFileName temp_fn( "$tempfile.tmp" );
498     temp_fn.MakeAbsolute( unzipDir );
499     wxString unzipped_tempfile = temp_fn.GetFullPath();
500 
501 
502     bool success = true;
503     wxZipInputStream zipArchive( zipFile );
504     wxZipEntry* entry;
505     bool reported_no_more_layer = false;
506 
507     while( ( entry = zipArchive.GetNextEntry() ) )
508     {
509         wxString fname = entry->GetName();
510         wxFileName uzfn = fname;
511         wxString curr_ext = uzfn.GetExt().Lower();
512 
513         // The archive contains Gerber and/or Excellon drill files. Use the right loader.
514         // However it can contain a few other files (reports, pdf files...),
515         // which will be skipped.
516         // Gerber files ext is usually "gbr", but can be also another value, starting by "g"
517         // old gerber files ext from kicad is .pho
518         // drill files do not have a well defined ext
519         // It is .drl in kicad, but .txt in Altium for instance
520         // Allows only .drl for drill files.
521         if( curr_ext[0] != 'g' && curr_ext != "pho" && curr_ext != "drl" )
522         {
523             if( aReporter )
524             {
525                 msg.Printf( _( "Skipped file '%s' (unknown type).\n" ), entry->GetName() );
526                 aReporter->Report( msg, RPT_SEVERITY_WARNING );
527             }
528 
529             continue;
530         }
531 
532         if( curr_ext == GerberJobFileExtension.c_str() )
533         {
534             //We cannot read a gerber job file as a gerber plot file: skip it
535             if( aReporter )
536             {
537                 msg.Printf( _( "Skipped file '%s' (gerber job file).\n" ), entry->GetName() );
538                 aReporter->Report( msg, RPT_SEVERITY_WARNING );
539             }
540 
541             continue;
542         }
543 
544         int layer = GetActiveLayer();
545 
546         if( layer == NO_AVAILABLE_LAYERS )
547         {
548             success = false;
549 
550             if( aReporter )
551             {
552                 if( !reported_no_more_layer )
553                     aReporter->Report( MSG_NO_MORE_LAYER,  RPT_SEVERITY_ERROR );
554 
555                 reported_no_more_layer = true;
556 
557                 // Report the name of not loaded files:
558                 msg.Printf( MSG_NOT_LOADED, entry->GetName() );
559                 aReporter->Report( msg, RPT_SEVERITY_ERROR );
560             }
561 
562             delete entry;
563             continue;
564         }
565 
566         // Create the unzipped temporary file:
567         {
568             wxFFileOutputStream temporary_ofile( unzipped_tempfile );
569 
570             if( temporary_ofile.Ok() )
571                 temporary_ofile.Write( zipArchive );
572             else
573             {
574                 success = false;
575 
576                 if( aReporter )
577                 {
578                     msg.Printf( _( "<b>Unable to create temporary file '%s'.</b>\n"),
579                                 unzipped_tempfile );
580                     aReporter->Report( msg, RPT_SEVERITY_ERROR );
581                 }
582             }
583         }
584 
585         bool read_ok = true;
586 
587         if( curr_ext[0] == 'g' || curr_ext == "pho" )
588         {
589             // Read gerber files: each file is loaded on a new GerbView layer
590             read_ok = Read_GERBER_File( unzipped_tempfile );
591 
592             if( read_ok )
593                 GetCanvas()->GetView()->SetLayerHasNegatives(
594                         GERBER_DRAW_LAYER( layer ), GetGbrImage( layer )->HasNegativeItems() );
595         }
596         else // if( curr_ext == "drl" )
597         {
598             read_ok = Read_EXCELLON_File( unzipped_tempfile );
599         }
600 
601         delete entry;
602 
603         // The unzipped file is only a temporary file, delete it.
604         wxRemoveFile( unzipped_tempfile );
605 
606         if( !read_ok )
607         {
608             success = false;
609 
610             if( aReporter )
611             {
612                 msg.Printf( _("<b>unzipped file %s read error</b>\n"), unzipped_tempfile );
613                 aReporter->Report( msg, RPT_SEVERITY_ERROR );
614             }
615         }
616         else
617         {
618             GERBER_FILE_IMAGE* gerber_image = GetGbrImage( layer );
619 
620             if( gerber_image )
621                 gerber_image->m_FileName = fname;
622 
623             layer = getNextAvailableLayer( layer );
624             SetActiveLayer( layer, false );
625         }
626     }
627 
628     return success;
629 }
630 
631 
LoadZipArchiveFile(const wxString & aFullFileName)632 bool GERBVIEW_FRAME::LoadZipArchiveFile( const wxString& aFullFileName )
633 {
634 #define ZipFileExtension "zip"
635 
636     wxFileName filename = aFullFileName;
637     wxString currentPath;
638 
639     if( !filename.IsOk() )
640     {
641         // Use the current working directory if the file name path does not exist.
642         if( filename.DirExists() )
643             currentPath = filename.GetPath();
644         else
645             currentPath = m_mruPath;
646 
647         wxFileDialog dlg( this, _( "Open Zip File" ), currentPath, filename.GetFullName(),
648                           ZipFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR );
649 
650         if( dlg.ShowModal() == wxID_CANCEL )
651             return false;
652 
653         filename = dlg.GetPath();
654         currentPath = wxGetCwd();
655         m_mruPath = currentPath;
656     }
657     else
658     {
659         currentPath = filename.GetPath();
660         m_mruPath = currentPath;
661     }
662 
663     wxString msg;
664     WX_STRING_REPORTER reporter( &msg );
665 
666     if( filename.IsOk() )
667         unarchiveFiles( filename.GetFullPath(), &reporter );
668 
669     Zoom_Automatique( false );
670 
671     // Synchronize layers tools with actual active layer:
672     ReFillLayerWidget();
673     SetActiveLayer( GetActiveLayer() );
674     m_LayersManager->UpdateLayerIcons();
675     syncLayerBox();
676 
677     if( !msg.IsEmpty() )
678     {
679         wxSafeYield();  // Allows slice of time to redraw the screen
680                         // to refresh widgets, before displaying messages
681         HTML_MESSAGE_BOX mbox( this, _( "Messages" ) );
682         mbox.ListSet( msg );
683         mbox.ShowModal();
684     }
685 
686     return true;
687 }
688