From b3259a1c42a1cd2667d1e284690d786d90adff8f Mon Sep 17 00:00:00 2001
From: Eric Zhuang <840064358@qq.com>
Date: Sun, 29 Dec 2024 17:01:27 +0000
Subject: [PATCH] ODB++:Support choice compress format and specify output
 filename.

---
 common/jobs/job_export_pcb_odb.cpp          |   1 +
 common/jobs/job_export_pcb_odb.h            |   1 +
 kicad/cli/command_pcb_export_odb.cpp        |   8 +-
 pcbnew/dialogs/dialog_export_odbpp.cpp      | 301 +++++++++++++++-----
 pcbnew/dialogs/dialog_export_odbpp.fbp      | 130 ++++++---
 pcbnew/dialogs/dialog_export_odbpp.h        |   7 +-
 pcbnew/dialogs/dialog_export_odbpp_base.cpp |  50 ++--
 pcbnew/dialogs/dialog_export_odbpp_base.h   |   8 +-
 pcbnew/files.cpp                            |   4 +-
 pcbnew/pcb_io/odbpp/pcb_io_odbpp.cpp        |   2 +-
 pcbnew/pcbnew_jobs_handler.cpp              |  36 ++-
 pcbnew/pcbnew_settings.h                    |   2 +-
 12 files changed, 393 insertions(+), 157 deletions(-)

diff --git a/common/jobs/job_export_pcb_odb.cpp b/common/jobs/job_export_pcb_odb.cpp
index 99fb89d892..ec2db7d80f 100644
--- a/common/jobs/job_export_pcb_odb.cpp
+++ b/common/jobs/job_export_pcb_odb.cpp
@@ -32,6 +32,7 @@ NLOHMANN_JSON_SERIALIZE_ENUM( JOB_EXPORT_PCB_ODB::ODB_COMPRESSION,
                               {
                                       { JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::NONE, "none" },
                                       { JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::ZIP, "zip" },
+                                      { JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::TGZ, "tgz" },
                               } )
 
 
diff --git a/common/jobs/job_export_pcb_odb.h b/common/jobs/job_export_pcb_odb.h
index de216d0c51..b469495475 100644
--- a/common/jobs/job_export_pcb_odb.h
+++ b/common/jobs/job_export_pcb_odb.h
@@ -42,6 +42,7 @@ public:
     {
         NONE,
         ZIP,
+        TGZ,
     };
 
     wxString m_filename;
diff --git a/kicad/cli/command_pcb_export_odb.cpp b/kicad/cli/command_pcb_export_odb.cpp
index 41d85c21c0..c1709da44f 100644
--- a/kicad/cli/command_pcb_export_odb.cpp
+++ b/kicad/cli/command_pcb_export_odb.cpp
@@ -49,7 +49,7 @@ CLI::PCB_EXPORT_ODB_COMMAND::PCB_EXPORT_ODB_COMMAND() :
     m_argParser.add_argument( ARG_COMPRESS )
             .default_value( std::string( "zip" ) )
             .help( std::string( "Compression mode" ) )
-            .choices( "none", "zip" );
+            .choices( "none", "zip", "tgz" );
 
     m_argParser.add_argument( ARG_UNITS )
             .default_value( std::string( "mm" ) )
@@ -87,11 +87,13 @@ int CLI::PCB_EXPORT_ODB_COMMAND::doPerform( KIWAY& aKiway )
     else if( units == "in" )
         job->m_units = JOB_EXPORT_PCB_ODB::ODB_UNITS::INCHES;
 
-    wxString compression = From_UTF8( m_argParser.get<std::string>( ARG_COMPRESS ).c_str() );
+    wxString compression = From_UTF8( m_argParser.get<std::string>( ARG_COMPRESS ).c_str() ).Lower();
 
     if( compression == "zip" )
         job->m_compressionMode = JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::ZIP;
-    else
+    else if( compression == "tgz" )
+        job->m_compressionMode = JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::TGZ;
+    else if( compression == "none" )
         job->m_compressionMode = JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::NONE;
 
     LOCALE_IO dummy;
diff --git a/pcbnew/dialogs/dialog_export_odbpp.cpp b/pcbnew/dialogs/dialog_export_odbpp.cpp
index ec952d9bcb..3f3ddcd44f 100644
--- a/pcbnew/dialogs/dialog_export_odbpp.cpp
+++ b/pcbnew/dialogs/dialog_export_odbpp.cpp
@@ -42,16 +42,16 @@
 #include <jobs/job_export_pcb_odb.h>
 #include <pcb_io/pcb_io_mgr.h>
 #include <wx/dir.h>
-#include <wx/dirdlg.h>
+#include <wx/filedlg.h>
 #include <wx/wfstream.h>
 #include <wx/zipstrm.h>
+#include <wx/tarstrm.h>
+#include <wx/zstream.h>
 
 static wxString s_oemColumn = wxEmptyString;
 
 DIALOG_EXPORT_ODBPP::DIALOG_EXPORT_ODBPP( PCB_EDIT_FRAME* aParent ) :
-        DIALOG_EXPORT_ODBPP_BASE( aParent ),
-        m_parent( aParent ),
-        m_job( nullptr )
+        DIALOG_EXPORT_ODBPP_BASE( aParent ), m_parent( aParent ), m_job( nullptr )
 {
     m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
 
@@ -62,7 +62,10 @@ DIALOG_EXPORT_ODBPP::DIALOG_EXPORT_ODBPP( PCB_EDIT_FRAME* aParent ) :
     if( path.IsEmpty() )
     {
         wxFileName brdFile( m_parent->GetBoard()->GetFileName() );
-        path = brdFile.GetPath();
+        wxFileName odbFile( brdFile.GetPath(),
+                            wxString::Format( wxS( "%s-odb" ), brdFile.GetName() ),
+                            FILEEXT::ArchiveFileExtension );
+        path = odbFile.GetFullPath();
     }
 
     m_outputFileName->SetValue( path );
@@ -78,9 +81,7 @@ DIALOG_EXPORT_ODBPP::DIALOG_EXPORT_ODBPP( PCB_EDIT_FRAME* aParent ) :
 
 DIALOG_EXPORT_ODBPP::DIALOG_EXPORT_ODBPP( JOB_EXPORT_PCB_ODB* aJob, PCB_EDIT_FRAME* aEditFrame,
                                           wxWindow* aParent ) :
-        DIALOG_EXPORT_ODBPP_BASE( aParent ),
-        m_parent( aEditFrame ),
-        m_job( aJob )
+        DIALOG_EXPORT_ODBPP_BASE( aParent ), m_parent( aEditFrame ), m_job( aJob )
 {
     m_browseButton->Hide();
 
@@ -99,23 +100,128 @@ DIALOG_EXPORT_ODBPP::DIALOG_EXPORT_ODBPP( JOB_EXPORT_PCB_ODB* aJob, PCB_EDIT_FRA
 
 void DIALOG_EXPORT_ODBPP::onBrowseClicked( wxCommandEvent& event )
 {
+    // clang-format off
+    wxString filter = _( "zip files" )
+                      + AddFileExtListToFilter( { FILEEXT::ArchiveFileExtension } ) + "|"
+                      + _( "tgz files" )
+                      + AddFileExtListToFilter( { "tgz" } );
+    // clang-format on
+
     // Build the absolute path of current output directory to preselect it in the file browser.
     wxString   path = ExpandEnvVarSubstitutions( m_outputFileName->GetValue(), &Prj() );
     wxFileName fn( Prj().AbsolutePath( path ) );
 
-    wxDirDialog dlg( this, _( "Export ODB++ File" ), fn.GetPath() );
+    wxFileName brdFile( m_parent->GetBoard()->GetFileName() );
+
+    wxString fileDialogName( wxString::Format( wxS( "%s-odb" ), brdFile.GetName() ) );
+
+    wxFileDialog dlg( this, _( "Export ODB++ File" ), fn.GetPath(), fileDialogName, filter,
+                      wxFD_SAVE );
 
     if( dlg.ShowModal() == wxID_CANCEL )
         return;
 
-    m_outputFileName->SetValue( dlg.GetPath() );
+    path = dlg.GetPath();
+
+    fn = wxFileName( path );
+
+    if( fn.GetExt().Lower() == "zip" )
+    {
+        m_choiceCompress->SetSelection(
+                static_cast<int>( JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::ZIP ) );
+    }
+    else if( fn.GetExt().Lower() == "tgz" )
+    {
+        m_choiceCompress->SetSelection(
+                static_cast<int>( JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::TGZ ) );
+    }
+    else if( path.EndsWith( "/" ) || path.EndsWith( "\\" ) )
+    {
+        m_choiceCompress->SetSelection(
+                static_cast<int>( JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::NONE ) );
+    }
+    else
+    {
+        wxString msg;
+        msg.Printf( _( "The selected output file name is not a supported archive format." ) );
+        DisplayErrorMessage( this, msg );
+        return;
+    }
+
+    m_outputFileName->SetValue( path );
 }
 
+void DIALOG_EXPORT_ODBPP::onFormatChoice( wxCommandEvent& event )
+{
+    OnFmtChoiceOptionChanged();
+}
+
+
+void DIALOG_EXPORT_ODBPP::OnFmtChoiceOptionChanged()
+{
+    wxString fn = m_outputFileName->GetValue();
+
+    wxFileName fileName( fn );
+
+    auto compressionMode =
+            static_cast<JOB_EXPORT_PCB_ODB::ODB_COMPRESSION>( m_choiceCompress->GetSelection() );
+
+    int sepIdx = std::max( fn.Find( '/', true ), fn.Find( '\\', true ) );
+    int dotIdx = fn.Find( '.', true );
+
+    if( fileName.IsDir() )
+        fn = fn.Mid( 0, sepIdx );
+    else if( sepIdx < dotIdx )
+        fn = fn.Mid( 0, dotIdx );
+
+    switch( compressionMode )
+    {
+    case JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::ZIP:
+        fn = fn + '.' + FILEEXT::ArchiveFileExtension;
+        break;
+    case JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::TGZ: fn += ".tgz"; break;
+    case JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::NONE: fn = wxFileName( fn, "" ).GetFullPath(); break;
+    default: break;
+    };
+
+    m_outputFileName->SetValue( fn );
+}
 
 void DIALOG_EXPORT_ODBPP::onOKClick( wxCommandEvent& event )
 {
     if( !m_job )
+    {
+        wxString fn = m_outputFileName->GetValue();
+
+        if( fn.IsEmpty() )
+        {
+            wxString msg;
+            msg.Printf( _( "Output file name cannot be empty." ) );
+            DisplayErrorMessage( this, msg );
+            return;
+        }
+
+        auto compressionMode = static_cast<JOB_EXPORT_PCB_ODB::ODB_COMPRESSION>(
+                m_choiceCompress->GetSelection() );
+
+        wxFileName fileName( fn );
+        bool       isDirectory = fileName.IsDir();
+        wxString   extension = fileName.GetExt();
+
+        if( ( compressionMode == JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::NONE && !isDirectory )
+            || ( compressionMode == JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::ZIP && extension != "zip" )
+            || ( compressionMode == JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::TGZ
+                 && extension != "tgz" ) )
+        {
+            wxString msg;
+            msg.Printf(
+                    _( "The output file name conflicts with the selected compression format." ) );
+            DisplayErrorMessage( this, msg );
+            return;
+        }
+
         m_parent->SetLastPath( LAST_PATH_ODBPP, m_outputFileName->GetValue() );
+    }
 
     event.Skip();
 }
@@ -130,13 +236,13 @@ bool DIALOG_EXPORT_ODBPP::Init()
     {
         m_choiceUnits->SetSelection( cfg->m_ExportODBPP.units );
         m_precision->SetValue( cfg->m_ExportODBPP.precision );
-        m_cbCompress->SetValue( cfg->m_ExportODBPP.compress );
+        m_choiceCompress->SetSelection( cfg->m_ExportODBPP.compressFormat );
     }
     else
     {
         m_choiceUnits->SetSelection( static_cast<int>( m_job->m_units ) );
         m_precision->SetValue( m_job->m_precision );
-        m_cbCompress->SetValue( m_job->m_compressionMode == JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::ZIP );
+        m_choiceCompress->SetSelection( static_cast<int>( m_job->m_compressionMode ) );
     }
 
     return true;
@@ -152,17 +258,17 @@ bool DIALOG_EXPORT_ODBPP::TransferDataFromWindow()
 
         cfg->m_ExportODBPP.units = m_choiceUnits->GetSelection();
         cfg->m_ExportODBPP.precision = m_precision->GetValue();
-        cfg->m_ExportODBPP.compress = m_cbCompress->GetValue();
+        cfg->m_ExportODBPP.compressFormat = m_choiceCompress->GetSelection();
     }
     else
     {
         m_job->SetOutputPath( m_outputFileName->GetValue() );
 
         m_job->m_precision = m_precision->GetValue();
-        m_job->m_units = static_cast<JOB_EXPORT_PCB_ODB::ODB_UNITS>( m_choiceUnits->GetSelection() );
-        m_job->m_compressionMode = m_cbCompress->GetValue()
-                                           ? JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::ZIP
-                                           : JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::NONE;
+        m_job->m_units =
+                static_cast<JOB_EXPORT_PCB_ODB::ODB_UNITS>( m_choiceUnits->GetSelection() );
+        m_job->m_compressionMode = static_cast<JOB_EXPORT_PCB_ODB::ODB_COMPRESSION>(
+                m_choiceCompress->GetSelection() );
     }
 
     return true;
@@ -170,9 +276,9 @@ bool DIALOG_EXPORT_ODBPP::TransferDataFromWindow()
 
 
 void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BOARD* aBoard,
-                                              PCB_EDIT_FRAME* aParentFrame,
+                                              PCB_EDIT_FRAME*    aParentFrame,
                                               PROGRESS_REPORTER* aProgressReporter,
-                                              REPORTER* aReporter )
+                                              REPORTER*          aReporter )
 {
     wxCHECK( aBoard, /* void */ );
     wxString outputPath = aJob.GetOutputPath();
@@ -190,6 +296,20 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
 
     wxString msg;
 
+    if( !wxFileName::DirExists( pcbFileName.GetPath() ) )
+    {
+        // Make every directory provided when the provided path doesn't exist
+        if( !wxFileName::Mkdir( pcbFileName.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
+        {
+            msg.Printf( _( "Cannot create output directory '%s'." ), pcbFileName.GetFullPath() );
+
+            if( aReporter )
+                aReporter->Report( msg, RPT_SEVERITY_ERROR );
+
+            return;
+        }
+    }
+
     if( pcbFileName.IsDir() && !pcbFileName.IsDirWritable() )
     {
         msg.Printf( _( "Insufficient permissions to folder '%s'." ), pcbFileName.GetPath() );
@@ -211,35 +331,17 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
         return;
     }
 
-    if( !wxFileName::DirExists( pcbFileName.GetFullPath() ) )
-    {
-        // Make every directory provided when the provided path doesn't exist
-        if( !wxFileName::Mkdir( pcbFileName.GetFullPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
-        {
-            msg.Printf( _( "Cannot create output directory '%s'." ), pcbFileName.GetFullPath() );
-
-            if( aReporter )
-                aReporter->Report( msg, RPT_SEVERITY_ERROR );
-
-            return;
-        }
-    }
-
-    wxFileName zipFileName( pcbFileName.GetFullPath(),
-                            wxString::Format( wxS( "%s-odb.zip" ),
-                                              aBoard->GetProject()->GetProjectName() ) );
-
-    wxFileName tempFile( pcbFileName.GetFullPath(), "" );
+    wxFileName tempFile( pcbFileName.GetFullPath() );
 
     if( aJob.m_compressionMode != JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::NONE )
     {
-        if( zipFileName.Exists() )
+        if( pcbFileName.Exists() )
         {
             if( aParentFrame )
             {
                 msg = wxString::Format( _( "Output files '%s' already exists. "
                                            "Do you want to overwrite it?" ),
-                                        zipFileName.GetFullPath() );
+                                        pcbFileName.GetFullPath() );
 
                 KIDIALOG errorDlg( aParentFrame, msg, _( "Confirmation" ),
                                    wxOK | wxCANCEL | wxICON_WARNING );
@@ -248,10 +350,10 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
                 if( errorDlg.ShowModal() != wxID_OK )
                     return;
 
-                if( !wxRemoveFile( zipFileName.GetFullPath() ) )
+                if( !wxRemoveFile( pcbFileName.GetFullPath() ) )
                 {
                     msg.Printf( _( "Cannot remove existing output file '%s'." ),
-                                zipFileName.GetFullPath() );
+                                pcbFileName.GetFullPath() );
                     DisplayErrorMessage( aParentFrame, msg );
                     return;
                 }
@@ -259,7 +361,7 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
             else
             {
                 msg = wxString::Format( _( "Output file '%s' already exists." ),
-                                        zipFileName.GetFullPath() );
+                                        pcbFileName.GetFullPath() );
 
                 if( aReporter )
                     aReporter->Report( msg, RPT_SEVERITY_ERROR );
@@ -284,10 +386,8 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
     }
     else
     {
-        // Plugin will create the 'odb' subdirectory for us, so test for it here
-        wxFileName odbDir( tempFile );
-        odbDir.AppendDir( "odb" );
-        wxDir testDir( odbDir.GetFullPath() );
+        // Test for the output directory of tempFile
+        wxDir testDir( tempFile.GetFullPath() );
 
         if( testDir.IsOpened() && ( testDir.HasFiles() || testDir.HasSubDirs() ) )
         {
@@ -295,7 +395,7 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
             {
                 msg = wxString::Format( _( "Output directory '%s' already exists and is not empty. "
                                            "Do you want to overwrite it?" ),
-                                        odbDir.GetFullPath() );
+                                        tempFile.GetFullPath() );
 
                 KIDIALOG errorDlg( aParentFrame, msg, _( "Confirmation" ),
                                    wxOK | wxCANCEL | wxICON_WARNING );
@@ -304,10 +404,10 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
                 if( errorDlg.ShowModal() != wxID_OK )
                     return;
 
-                if( !odbDir.Rmdir( wxPATH_RMDIR_RECURSIVE ) )
+                if( !tempFile.Rmdir( wxPATH_RMDIR_RECURSIVE ) )
                 {
                     msg.Printf( _( "Cannot remove existing output directory '%s'." ),
-                                odbDir.GetFullPath() );
+                                tempFile.GetFullPath() );
                     DisplayErrorMessage( aParentFrame, msg );
                     return;
                 }
@@ -315,7 +415,7 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
             else
             {
                 msg = wxString::Format( _( "Output directory '%s' already exists." ),
-                                        odbDir.GetFullPath() );
+                                        tempFile.GetFullPath() );
 
                 if( aReporter )
                     aReporter->Report( msg, RPT_SEVERITY_ERROR );
@@ -332,31 +432,30 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
     props["units"] = aJob.m_units == JOB_EXPORT_PCB_ODB::ODB_UNITS::MILLIMETERS ? "mm" : "inch";
     props["sigfig"] = wxString::Format( "%d", aJob.m_precision );
 
-    auto saveFile =
-            [&]() -> bool
+    auto saveFile = [&]() -> bool
+    {
+        try
+        {
+            IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( PCB_IO_MGR::ODBPP ) );
+            pi->SetReporter( aReporter );
+            pi->SetProgressReporter( aProgressReporter );
+            pi->SaveBoard( tempFile.GetFullPath(), aBoard, &props );
+            return true;
+        }
+        catch( const IO_ERROR& ioe )
+        {
+            if( aReporter )
             {
-                try
-                {
-                    IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( PCB_IO_MGR::ODBPP ) );
-                    pi->SetReporter( aReporter );
-                    pi->SetProgressReporter( aProgressReporter );
-                    pi->SaveBoard( tempFile.GetFullPath(), aBoard, &props );
-                    return true;
-                }
-                catch( const IO_ERROR& ioe )
-                {
-                    if( aReporter )
-                    {
-                        msg = wxString::Format( _( "Error generating ODBPP files '%s'.\n%s" ),
-                                                tempFile.GetFullPath(), ioe.What() );
-                        aReporter->Report( msg, RPT_SEVERITY_ERROR );
-                    }
+                msg = wxString::Format( _( "Error generating ODBPP files '%s'.\n%s" ),
+                                        tempFile.GetFullPath(), ioe.What() );
+                aReporter->Report( msg, RPT_SEVERITY_ERROR );
+            }
 
-                    // In case we started a file but didn't fully write it, clean up
-                    wxFileName::Rmdir( tempFile.GetFullPath() );
-                    return false;
-                }
-            };
+            // In case we started a file but didn't fully write it, clean up
+            wxFileName::Rmdir( tempFile.GetFullPath() );
+            return false;
+        }
+    };
 
     thread_pool& tp = GetKiCadThreadPool();
     auto         ret = tp.submit( saveFile );
@@ -365,7 +464,7 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
 
     while( status != std::future_status::ready )
     {
-        if( aProgressReporter)
+        if( aProgressReporter )
             aProgressReporter->KeepRefreshing();
 
         status = ret.wait_for( std::chrono::milliseconds( 250 ) );
@@ -387,12 +486,12 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
         return;
     }
 
-    if( aJob.m_compressionMode != JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::NONE )
+    if( aJob.m_compressionMode == JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::ZIP )
     {
         if( aProgressReporter )
             aProgressReporter->AdvancePhase( _( "Compressing output" ) );
 
-        wxFFileOutputStream fnout( zipFileName.GetFullPath() );
+        wxFFileOutputStream fnout( pcbFileName.GetFullPath() );
         wxZipOutputStream   zipStream( fnout );
 
         std::function<void( const wxString&, const wxString& )> addDirToZip =
@@ -434,6 +533,54 @@ void DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BO
 
         tempFile.Rmdir( wxPATH_RMDIR_RECURSIVE );
     }
+    else if( aJob.m_compressionMode == JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::TGZ )
+    {
+        wxFFileOutputStream fnout( pcbFileName.GetFullPath() );
+        wxZlibOutputStream  zlibStream( fnout, -1, wxZLIB_GZIP );
+        wxTarOutputStream   tarStream( zlibStream );
+
+        std::function<void( const wxString&, const wxString& )> addDirToTar =
+                [&]( const wxString& dirPath, const wxString& parentPath )
+        {
+            wxDir    dir( dirPath );
+            wxString fileName;
+
+            bool cont = dir.GetFirst( &fileName, wxEmptyString, wxDIR_DEFAULT );
+            while( cont )
+            {
+                wxFileName fileInTar( dirPath, fileName );
+                wxString   relativePath =
+                        parentPath.IsEmpty()
+                                  ? fileName
+                                  : parentPath + wxString( wxFileName::GetPathSeparator() )
+                                          + fileName;
+
+                if( wxFileName::DirExists( fileInTar.GetFullPath() ) )
+                {
+                    tarStream.PutNextDirEntry( relativePath );
+                    addDirToTar( fileInTar.GetFullPath(), relativePath );
+                }
+                else
+                {
+                    wxFFileInputStream fileStream( fileInTar.GetFullPath() );
+                    tarStream.PutNextEntry( relativePath, wxDateTime::Now(),
+                                            fileStream.GetLength() );
+                    fileStream.Read( tarStream );
+                }
+                cont = dir.GetNext( &fileName );
+            }
+        };
+
+        addDirToTar(
+                tempFile.GetFullPath(),
+                tempFile.GetPath( wxPATH_NO_SEPARATOR ).AfterLast( tempFile.GetPathSeparator() ) );
+
+        tarStream.Close();
+        zlibStream.Close();
+        fnout.Close();
+
+        tempFile.Rmdir( wxPATH_RMDIR_RECURSIVE );
+    }
 
     if( aProgressReporter )
         aProgressReporter->SetCurrentProgress( 1 );
diff --git a/pcbnew/dialogs/dialog_export_odbpp.fbp b/pcbnew/dialogs/dialog_export_odbpp.fbp
index 55715e666b..4e1dfb513f 100644
--- a/pcbnew/dialogs/dialog_export_odbpp.fbp
+++ b/pcbnew/dialogs/dialog_export_odbpp.fbp
@@ -47,10 +47,10 @@
       <property name="hidden">0</property>
       <property name="id">wxID_ANY</property>
       <property name="maximum_size"></property>
-      <property name="minimum_size"></property>
+      <property name="minimum_size">380,265</property>
       <property name="name">DIALOG_EXPORT_ODBPP_BASE</property>
       <property name="pos"></property>
-      <property name="size">380,300</property>
+      <property name="size">-1,-1</property>
       <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property>
       <property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
       <property name="title">Export ODB++</property>
@@ -58,15 +58,15 @@
       <property name="two_step_creation">0</property>
       <property name="window_extra_style"></property>
       <property name="window_name"></property>
-      <property name="window_style"></property>
+      <property name="window_style">wxBORDER_DEFAULT</property>
       <object class="wxBoxSizer" expanded="true">
         <property name="minimum_size"></property>
         <property name="name">bMainSizer</property>
         <property name="orient">wxVERTICAL</property>
         <property name="permission">none</property>
         <object class="sizeritem" expanded="true">
-          <property name="border">15</property>
-          <property name="flag">wxBOTTOM|wxEXPAND|wxTOP</property>
+          <property name="border">5</property>
+          <property name="flag">wxALL|wxEXPAND</property>
           <property name="proportion">0</property>
           <object class="wxBoxSizer" expanded="true">
             <property name="minimum_size"></property>
@@ -75,7 +75,7 @@
             <property name="permission">protected</property>
             <object class="sizeritem" expanded="false">
               <property name="border">5</property>
-              <property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT</property>
+              <property name="flag">wxALIGN_CENTER_VERTICAL</property>
               <property name="proportion">0</property>
               <object class="wxStaticText" expanded="false">
                 <property name="BottomDockable">1</property>
@@ -106,7 +106,7 @@
                 <property name="gripper">0</property>
                 <property name="hidden">0</property>
                 <property name="id">wxID_ANY</property>
-                <property name="label">Folder:</property>
+                <property name="label">Output File:</property>
                 <property name="markup">0</property>
                 <property name="max_size"></property>
                 <property name="maximize_button">0</property>
@@ -137,7 +137,7 @@
             </object>
             <object class="sizeritem" expanded="false">
               <property name="border">5</property>
-              <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+              <property name="flag">wxALL|wxEXPAND</property>
               <property name="proportion">1</property>
               <object class="wxTextCtrl" expanded="false">
                 <property name="BottomDockable">1</property>
@@ -278,7 +278,7 @@
           </object>
         </object>
         <object class="sizeritem" expanded="true">
-          <property name="border">5</property>
+          <property name="border">10</property>
           <property name="flag">wxEXPAND</property>
           <property name="proportion">0</property>
           <object class="wxBoxSizer" expanded="true">
@@ -286,11 +286,11 @@
             <property name="name">bSizer3</property>
             <property name="orient">wxHORIZONTAL</property>
             <property name="permission">none</property>
-            <object class="sizeritem" expanded="true">
+            <object class="sizeritem" expanded="false">
               <property name="border">10</property>
-              <property name="flag">wxEXPAND|wxLEFT|wxRIGHT|wxTOP</property>
+              <property name="flag">wxEXPAND|wxALL</property>
               <property name="proportion">1</property>
-              <object class="wxStaticBoxSizer" expanded="true">
+              <object class="wxStaticBoxSizer" expanded="false">
                 <property name="id">wxID_ANY</property>
                 <property name="label">File Format</property>
                 <property name="minimum_size"></property>
@@ -300,12 +300,12 @@
                 <property name="permission">none</property>
                 <object class="sizeritem" expanded="false">
                   <property name="border">5</property>
-                  <property name="flag">wxEXPAND|wxALL</property>
+                  <property name="flag">wxEXPAND|wxLEFT</property>
                   <property name="proportion">3</property>
                   <object class="wxFlexGridSizer" expanded="false">
                     <property name="cols">2</property>
                     <property name="flexible_direction">wxBOTH</property>
-                    <property name="growablecols">1</property>
+                    <property name="growablecols">0,1</property>
                     <property name="growablerows"></property>
                     <property name="hgap">0</property>
                     <property name="minimum_size"></property>
@@ -316,7 +316,7 @@
                     <property name="vgap">0</property>
                     <object class="sizeritem" expanded="false">
                       <property name="border">5</property>
-                      <property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
+                      <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                       <property name="proportion">0</property>
                       <object class="wxStaticText" expanded="false">
                         <property name="BottomDockable">1</property>
@@ -378,7 +378,7 @@
                     </object>
                     <object class="sizeritem" expanded="false">
                       <property name="border">5</property>
-                      <property name="flag">wxALIGN_RIGHT|wxALL</property>
+                      <property name="flag">wxEXPAND|wxALL</property>
                       <property name="proportion">0</property>
                       <object class="wxChoice" expanded="false">
                         <property name="BottomDockable">1</property>
@@ -415,7 +415,7 @@
                         <property name="maximum_size"></property>
                         <property name="min_size"></property>
                         <property name="minimize_button">0</property>
-                        <property name="minimum_size"></property>
+                        <property name="minimum_size">-1,-1</property>
                         <property name="moveable">1</property>
                         <property name="name">m_choiceUnits</property>
                         <property name="pane_border">1</property>
@@ -427,7 +427,7 @@
                         <property name="resize">Resizable</property>
                         <property name="selection">0</property>
                         <property name="show">1</property>
-                        <property name="size">130,30</property>
+                        <property name="size">-1,-1</property>
                         <property name="style"></property>
                         <property name="subclass">; ; forward_declare</property>
                         <property name="toolbar_pane">0</property>
@@ -443,7 +443,7 @@
                     </object>
                     <object class="sizeritem" expanded="false">
                       <property name="border">5</property>
-                      <property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
+                      <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                       <property name="proportion">0</property>
                       <object class="wxStaticText" expanded="false">
                         <property name="BottomDockable">1</property>
@@ -505,7 +505,7 @@
                     </object>
                     <object class="sizeritem" expanded="false">
                       <property name="border">5</property>
-                      <property name="flag">wxALIGN_RIGHT|wxALL</property>
+                      <property name="flag">wxALL|wxEXPAND</property>
                       <property name="proportion">0</property>
                       <object class="wxSpinCtrl" expanded="false">
                         <property name="BottomDockable">1</property>
@@ -536,7 +536,7 @@
                         <property name="gripper">0</property>
                         <property name="hidden">0</property>
                         <property name="id">wxID_ANY</property>
-                        <property name="initial">7</property>
+                        <property name="initial">6</property>
                         <property name="max">16</property>
                         <property name="max_size"></property>
                         <property name="maximize_button">0</property>
@@ -555,7 +555,7 @@
                         <property name="pos"></property>
                         <property name="resize">Resizable</property>
                         <property name="show">1</property>
-                        <property name="size">130,30</property>
+                        <property name="size">-1,-1</property>
                         <property name="style">wxSP_ARROW_KEYS</property>
                         <property name="subclass"></property>
                         <property name="toolbar_pane">0</property>
@@ -568,9 +568,9 @@
                     </object>
                     <object class="sizeritem" expanded="false">
                       <property name="border">5</property>
-                      <property name="flag">wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND</property>
+                      <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                       <property name="proportion">0</property>
-                      <object class="wxCheckBox" expanded="false">
+                      <object class="wxStaticText" expanded="false">
                         <property name="BottomDockable">1</property>
                         <property name="LeftDockable">1</property>
                         <property name="RightDockable">1</property>
@@ -584,7 +584,6 @@
                         <property name="caption"></property>
                         <property name="caption_visible">1</property>
                         <property name="center_pane">0</property>
-                        <property name="checked">0</property>
                         <property name="close_button">1</property>
                         <property name="context_help"></property>
                         <property name="context_menu">1</property>
@@ -600,7 +599,8 @@
                         <property name="gripper">0</property>
                         <property name="hidden">0</property>
                         <property name="id">wxID_ANY</property>
-                        <property name="label">Compress output</property>
+                        <property name="label">Compression Format:</property>
+                        <property name="markup">0</property>
                         <property name="max_size"></property>
                         <property name="maximize_button">0</property>
                         <property name="maximum_size"></property>
@@ -608,7 +608,7 @@
                         <property name="minimize_button">0</property>
                         <property name="minimum_size"></property>
                         <property name="moveable">1</property>
-                        <property name="name">m_cbCompress</property>
+                        <property name="name">m_lblCompress</property>
                         <property name="pane_border">1</property>
                         <property name="pane_position"></property>
                         <property name="pane_size"></property>
@@ -619,9 +619,71 @@
                         <property name="show">1</property>
                         <property name="size"></property>
                         <property name="style"></property>
+                        <property name="subclass"></property>
+                        <property name="toolbar_pane">0</property>
+                        <property name="tooltip">Select the format to compress the output ODB++ files</property>
+                        <property name="window_extra_style"></property>
+                        <property name="window_name"></property>
+                        <property name="window_style"></property>
+                        <property name="wrap">-1</property>
+                      </object>
+                    </object>
+                    <object class="sizeritem" expanded="false">
+                      <property name="border">5</property>
+                      <property name="flag">wxALL|wxEXPAND</property>
+                      <property name="proportion">0</property>
+                      <object class="wxChoice" expanded="false">
+                        <property name="BottomDockable">1</property>
+                        <property name="LeftDockable">1</property>
+                        <property name="RightDockable">1</property>
+                        <property name="TopDockable">1</property>
+                        <property name="aui_layer">0</property>
+                        <property name="aui_name"></property>
+                        <property name="aui_position">0</property>
+                        <property name="aui_row">0</property>
+                        <property name="best_size"></property>
+                        <property name="bg"></property>
+                        <property name="caption"></property>
+                        <property name="caption_visible">1</property>
+                        <property name="center_pane">0</property>
+                        <property name="choices">&quot;None&quot; &quot;ZIP&quot; &quot;TGZ&quot;</property>
+                        <property name="close_button">1</property>
+                        <property name="context_help"></property>
+                        <property name="context_menu">1</property>
+                        <property name="default_pane">0</property>
+                        <property name="dock">Dock</property>
+                        <property name="dock_fixed">0</property>
+                        <property name="docking">Left</property>
+                        <property name="drag_accept_files">0</property>
+                        <property name="enabled">1</property>
+                        <property name="fg"></property>
+                        <property name="floatable">1</property>
+                        <property name="font"></property>
+                        <property name="gripper">0</property>
+                        <property name="hidden">0</property>
+                        <property name="id">wxID_ANY</property>
+                        <property name="max_size"></property>
+                        <property name="maximize_button">0</property>
+                        <property name="maximum_size"></property>
+                        <property name="min_size"></property>
+                        <property name="minimize_button">0</property>
+                        <property name="minimum_size">-1,-1</property>
+                        <property name="moveable">1</property>
+                        <property name="name">m_choiceCompress</property>
+                        <property name="pane_border">1</property>
+                        <property name="pane_position"></property>
+                        <property name="pane_size"></property>
+                        <property name="permission">protected</property>
+                        <property name="pin_button">1</property>
+                        <property name="pos"></property>
+                        <property name="resize">Resizable</property>
+                        <property name="selection">1</property>
+                        <property name="show">1</property>
+                        <property name="size">-1,-1</property>
+                        <property name="style"></property>
                         <property name="subclass">; ; forward_declare</property>
                         <property name="toolbar_pane">0</property>
-                        <property name="tooltip">Compress output into &apos;zip&apos; file</property>
+                        <property name="tooltip"></property>
                         <property name="validator_data_type"></property>
                         <property name="validator_style">wxFILTER_NONE</property>
                         <property name="validator_type">wxDefaultValidator</property>
@@ -629,7 +691,7 @@
                         <property name="window_extra_style"></property>
                         <property name="window_name"></property>
                         <property name="window_style"></property>
-                        <event name="OnCheckBox">onCompressCheck</event>
+                        <event name="OnChoice">onFormatChoice</event>
                       </object>
                     </object>
                   </object>
@@ -638,16 +700,6 @@
             </object>
           </object>
         </object>
-        <object class="sizeritem" expanded="false">
-          <property name="border">5</property>
-          <property name="flag">wxEXPAND</property>
-          <property name="proportion">1</property>
-          <object class="spacer" expanded="false">
-            <property name="height">0</property>
-            <property name="permission">protected</property>
-            <property name="width">0</property>
-          </object>
-        </object>
         <object class="sizeritem" expanded="false">
           <property name="border">5</property>
           <property name="flag">wxALL|wxEXPAND</property>
diff --git a/pcbnew/dialogs/dialog_export_odbpp.h b/pcbnew/dialogs/dialog_export_odbpp.h
index 1e2cc76c5b..c76d2abc66 100644
--- a/pcbnew/dialogs/dialog_export_odbpp.h
+++ b/pcbnew/dialogs/dialog_export_odbpp.h
@@ -45,8 +45,7 @@ public:
 
     int GetPrecision() const { return m_precision->GetValue(); }
 
-
-    bool GetCompress() const { return m_cbCompress->GetValue(); }
+    int GetCompressFormat() const { return m_choiceCompress->GetSelection(); }
 
     // Runs the actual generation process; shared between GUI and CLI system
     static void GenerateODBPPFiles( const JOB_EXPORT_PCB_ODB& aJob, BOARD* aBoard,
@@ -56,8 +55,12 @@ public:
 
 private:
     void onBrowseClicked( wxCommandEvent& event ) override;
+    void onFormatChoice( wxCommandEvent& event ) override;
     void onOKClick( wxCommandEvent& event ) override;
 
+    void OnFmtChoiceOptionChanged();
+
+
     bool Init();
     bool TransferDataFromWindow() override;
 
diff --git a/pcbnew/dialogs/dialog_export_odbpp_base.cpp b/pcbnew/dialogs/dialog_export_odbpp_base.cpp
index fc924e30c6..38edd881e2 100644
--- a/pcbnew/dialogs/dialog_export_odbpp_base.cpp
+++ b/pcbnew/dialogs/dialog_export_odbpp_base.cpp
@@ -13,28 +13,28 @@
 
 DIALOG_EXPORT_ODBPP_BASE::DIALOG_EXPORT_ODBPP_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style )
 {
-	this->SetSizeHints( wxDefaultSize, wxDefaultSize );
+	this->SetSizeHints( wxSize( 380,265 ), wxDefaultSize );
 
 	wxBoxSizer* bMainSizer;
 	bMainSizer = new wxBoxSizer( wxVERTICAL );
 
 	bSizerTop = new wxBoxSizer( wxHORIZONTAL );
 
-	m_lblBrdFile = new wxStaticText( this, wxID_ANY, _("Folder:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_lblBrdFile = new wxStaticText( this, wxID_ANY, _("Output File:"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_lblBrdFile->Wrap( -1 );
-	bSizerTop->Add( m_lblBrdFile, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
+	bSizerTop->Add( m_lblBrdFile, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 	m_outputFileName = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
 	m_outputFileName->SetToolTip( _("Enter a filename if you do not want to use default file names\nCan be used only when printing the current sheet") );
 	m_outputFileName->SetMinSize( wxSize( 350,-1 ) );
 
-	bSizerTop->Add( m_outputFileName, 1, wxALIGN_CENTER_VERTICAL, 5 );
+	bSizerTop->Add( m_outputFileName, 1, wxALL|wxEXPAND, 5 );
 
 	m_browseButton = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW|0 );
 	bSizerTop->Add( m_browseButton, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
 
 
-	bMainSizer->Add( bSizerTop, 0, wxBOTTOM|wxEXPAND|wxTOP, 15 );
+	bMainSizer->Add( bSizerTop, 0, wxALL|wxEXPAND, 5 );
 
 	wxBoxSizer* bSizer3;
 	bSizer3 = new wxBoxSizer( wxHORIZONTAL );
@@ -44,47 +44,52 @@ DIALOG_EXPORT_ODBPP_BASE::DIALOG_EXPORT_ODBPP_BASE( wxWindow* parent, wxWindowID
 
 	wxFlexGridSizer* fgSizer;
 	fgSizer = new wxFlexGridSizer( 0, 2, 0, 0 );
+	fgSizer->AddGrowableCol( 0 );
 	fgSizer->AddGrowableCol( 1 );
 	fgSizer->SetFlexibleDirection( wxBOTH );
 	fgSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
 
 	m_lblUnits = new wxStaticText( sbSizer1->GetStaticBox(), wxID_ANY, _("Units:"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_lblUnits->Wrap( -1 );
-	fgSizer->Add( m_lblUnits, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
+	fgSizer->Add( m_lblUnits, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 	wxString m_choiceUnitsChoices[] = { _("Millimeters"), _("Inches") };
 	int m_choiceUnitsNChoices = sizeof( m_choiceUnitsChoices ) / sizeof( wxString );
-	m_choiceUnits = new wxChoice( sbSizer1->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxSize( 130,30 ), m_choiceUnitsNChoices, m_choiceUnitsChoices, 0 );
+	m_choiceUnits = new wxChoice( sbSizer1->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), m_choiceUnitsNChoices, m_choiceUnitsChoices, 0 );
 	m_choiceUnits->SetSelection( 0 );
-	fgSizer->Add( m_choiceUnits, 0, wxALIGN_RIGHT|wxALL, 5 );
+	fgSizer->Add( m_choiceUnits, 0, wxEXPAND|wxALL, 5 );
 
 	m_lblPrecision = new wxStaticText( sbSizer1->GetStaticBox(), wxID_ANY, _("Precision:"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_lblPrecision->Wrap( -1 );
 	m_lblPrecision->SetToolTip( _("The number of values following the decimal separator") );
 
-	fgSizer->Add( m_lblPrecision, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
+	fgSizer->Add( m_lblPrecision, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
-	m_precision = new wxSpinCtrl( sbSizer1->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 130,30 ), wxSP_ARROW_KEYS, 2, 16, 7 );
+	m_precision = new wxSpinCtrl( sbSizer1->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), wxSP_ARROW_KEYS, 2, 16, 6 );
 	m_precision->SetToolTip( _("The number of values following the decimal separator") );
 
-	fgSizer->Add( m_precision, 0, wxALIGN_RIGHT|wxALL, 5 );
+	fgSizer->Add( m_precision, 0, wxALL|wxEXPAND, 5 );
 
-	m_cbCompress = new wxCheckBox( sbSizer1->GetStaticBox(), wxID_ANY, _("Compress output"), wxDefaultPosition, wxDefaultSize, 0 );
-	m_cbCompress->SetToolTip( _("Compress output into 'zip' file") );
+	m_lblCompress = new wxStaticText( sbSizer1->GetStaticBox(), wxID_ANY, _("Compression Format:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_lblCompress->Wrap( -1 );
+	m_lblCompress->SetToolTip( _("Select the format to compress the output ODB++ files") );
 
-	fgSizer->Add( m_cbCompress, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
+	fgSizer->Add( m_lblCompress, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+	wxString m_choiceCompressChoices[] = { _("None"), _("ZIP"), _("TGZ") };
+	int m_choiceCompressNChoices = sizeof( m_choiceCompressChoices ) / sizeof( wxString );
+	m_choiceCompress = new wxChoice( sbSizer1->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), m_choiceCompressNChoices, m_choiceCompressChoices, 0 );
+	m_choiceCompress->SetSelection( 1 );
+	fgSizer->Add( m_choiceCompress, 0, wxALL|wxEXPAND, 5 );
 
 
-	sbSizer1->Add( fgSizer, 3, wxEXPAND|wxALL, 5 );
+	sbSizer1->Add( fgSizer, 3, wxEXPAND|wxLEFT, 5 );
 
 
-	bSizer3->Add( sbSizer1, 1, wxEXPAND|wxLEFT|wxRIGHT|wxTOP, 10 );
+	bSizer3->Add( sbSizer1, 1, wxEXPAND|wxALL, 10 );
 
 
-	bMainSizer->Add( bSizer3, 0, wxEXPAND, 5 );
-
-
-	bMainSizer->Add( 0, 0, 1, wxEXPAND, 5 );
+	bMainSizer->Add( bSizer3, 0, wxEXPAND, 10 );
 
 	m_stdButtons = new wxStdDialogButtonSizer();
 	m_stdButtonsOK = new wxButton( this, wxID_OK );
@@ -98,12 +103,13 @@ DIALOG_EXPORT_ODBPP_BASE::DIALOG_EXPORT_ODBPP_BASE( wxWindow* parent, wxWindowID
 
 	this->SetSizer( bMainSizer );
 	this->Layout();
+	bMainSizer->Fit( this );
 
 	this->Centre( wxBOTH );
 
 	// Connect Events
 	m_browseButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onBrowseClicked ), NULL, this );
-	m_cbCompress->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onCompressCheck ), NULL, this );
+	m_choiceCompress->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onFormatChoice ), NULL, this );
 	m_stdButtonsOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onOKClick ), NULL, this );
 }
 
@@ -111,7 +117,7 @@ DIALOG_EXPORT_ODBPP_BASE::~DIALOG_EXPORT_ODBPP_BASE()
 {
 	// Disconnect Events
 	m_browseButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onBrowseClicked ), NULL, this );
-	m_cbCompress->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onCompressCheck ), NULL, this );
+	m_choiceCompress->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onFormatChoice ), NULL, this );
 	m_stdButtonsOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onOKClick ), NULL, this );
 
 }
diff --git a/pcbnew/dialogs/dialog_export_odbpp_base.h b/pcbnew/dialogs/dialog_export_odbpp_base.h
index 0d6a044203..ff40c3bf3a 100644
--- a/pcbnew/dialogs/dialog_export_odbpp_base.h
+++ b/pcbnew/dialogs/dialog_export_odbpp_base.h
@@ -28,7 +28,6 @@ class STD_BITMAP_BUTTON;
 #include <wx/sizer.h>
 #include <wx/choice.h>
 #include <wx/spinctrl.h>
-#include <wx/checkbox.h>
 #include <wx/statbox.h>
 #include <wx/dialog.h>
 
@@ -50,20 +49,21 @@ class DIALOG_EXPORT_ODBPP_BASE : public DIALOG_SHIM
 		wxChoice* m_choiceUnits;
 		wxStaticText* m_lblPrecision;
 		wxSpinCtrl* m_precision;
-		wxCheckBox* m_cbCompress;
+		wxStaticText* m_lblCompress;
+		wxChoice* m_choiceCompress;
 		wxStdDialogButtonSizer* m_stdButtons;
 		wxButton* m_stdButtonsOK;
 		wxButton* m_stdButtonsCancel;
 
 		// Virtual event handlers, override them in your derived class
 		virtual void onBrowseClicked( wxCommandEvent& event ) { event.Skip(); }
-		virtual void onCompressCheck( wxCommandEvent& event ) { event.Skip(); }
+		virtual void onFormatChoice( wxCommandEvent& event ) { event.Skip(); }
 		virtual void onOKClick( wxCommandEvent& event ) { event.Skip(); }
 
 
 	public:
 
-		DIALOG_EXPORT_ODBPP_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Export ODB++"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 380,300 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
+		DIALOG_EXPORT_ODBPP_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Export ODB++"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxBORDER_DEFAULT );
 
 		~DIALOG_EXPORT_ODBPP_BASE();
 
diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp
index ad4fb46301..8cf674d5c1 100644
--- a/pcbnew/files.cpp
+++ b/pcbnew/files.cpp
@@ -1420,8 +1420,8 @@ void PCB_EDIT_FRAME::GenODBPPFiles( wxCommandEvent& event )
 
     job.SetOutputPath( dlg.GetOutputPath() );
     job.m_filename = GetBoard()->GetFileName();
-    job.m_compressionMode = dlg.GetCompress() ? JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::ZIP
-                                              : JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::NONE;
+    job.m_compressionMode = static_cast<JOB_EXPORT_PCB_ODB::ODB_COMPRESSION>( dlg.GetCompressFormat() );
+
     job.m_precision = dlg.GetPrecision();
     job.m_units = dlg.GetUnitsString() == "mm" ? JOB_EXPORT_PCB_ODB::ODB_UNITS::MILLIMETERS
                                                : JOB_EXPORT_PCB_ODB::ODB_UNITS::INCHES;
diff --git a/pcbnew/pcb_io/odbpp/pcb_io_odbpp.cpp b/pcbnew/pcb_io/odbpp/pcb_io_odbpp.cpp
index ee9129df24..a49747c0b6 100644
--- a/pcbnew/pcb_io/odbpp/pcb_io_odbpp.cpp
+++ b/pcbnew/pcb_io/odbpp/pcb_io_odbpp.cpp
@@ -90,7 +90,7 @@ bool PCB_IO_ODBPP::ExportODB( const wxString& aFileName )
     try
     {
         std::shared_ptr<ODB_TREE_WRITER> writer =
-                std::make_shared<ODB_TREE_WRITER>( aFileName, "odb" );
+                std::make_shared<ODB_TREE_WRITER>( aFileName );
         writer->SetRootPath( writer->GetCurrentPath() );
         
         if( m_progressReporter )
diff --git a/pcbnew/pcbnew_jobs_handler.cpp b/pcbnew/pcbnew_jobs_handler.cpp
index c89a663842..60c6c89fb0 100644
--- a/pcbnew/pcbnew_jobs_handler.cpp
+++ b/pcbnew/pcbnew_jobs_handler.cpp
@@ -1842,15 +1842,39 @@ int PCBNEW_JOBS_HANDLER::JobExportOdb( JOB* aJob )
     if( !brd )
         return CLI::EXIT_CODES::ERR_INVALID_INPUT_FILE;
 
-    if( job->OutputPathFullSpecified() )
-    {
-        wxFileName fn = brd->GetFileName();
-        fn.SetName( fn.GetName() );
-        fn.SetExt( "zip" );
+    wxFileName fn( brd->GetFileName() );
+    wxString   path = job->GetOutputPath();
 
-        job->SetOutputPath( fn.GetName() );
+    if( path.IsEmpty() )
+    {
+        wxFileName outputfn( fn.GetPath(), wxString::Format( wxS( "%s-odb" ), fn.GetName() ) );
+        path = outputfn.GetFullPath();
     }
 
+    wxFileName fileName( path );
+
+    int sepIdx = std::max( path.Find( '/', true ), path.Find( '\\', true ) );
+    int dotIdx = path.Find( '.', true );
+
+    if( fileName.IsDir() && path.EndsWith( wxFileName::GetPathSeparator() ) )
+        path = path.Mid( 0, sepIdx );
+    else if( sepIdx < dotIdx )
+        path = path.Mid( 0, dotIdx );
+
+    switch( job->m_compressionMode )
+    {
+    case JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::ZIP:
+        path = path + '.' + FILEEXT::ArchiveFileExtension;
+        break;
+    case JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::TGZ: path += ".tgz"; break;
+    case JOB_EXPORT_PCB_ODB::ODB_COMPRESSION::NONE:
+        path = wxFileName( path, "" ).GetFullPath();
+        break;
+    default: break;
+    };
+
+    job->SetOutputPath( path );
+
     DIALOG_EXPORT_ODBPP::GenerateODBPPFiles( *job, brd, nullptr, m_progressReporter, m_reporter );
 
     return CLI::EXIT_CODES::SUCCESS;
diff --git a/pcbnew/pcbnew_settings.h b/pcbnew/pcbnew_settings.h
index 345ae1aa29..0319844c02 100644
--- a/pcbnew/pcbnew_settings.h
+++ b/pcbnew/pcbnew_settings.h
@@ -206,7 +206,7 @@ public:
     {
         int    precision;
         int    units;
-        bool   compress;
+        int    compressFormat;
     };
 
     struct DIALOG_EXPORT_SVG