576 lines
16 KiB
C++
576 lines
16 KiB
C++
/*
|
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 1992-2010 jean-pierre.charras
|
|
* Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, you may find one here:
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <bitmap2component.h>
|
|
#include <bitmap2cmp_frame.h>
|
|
#include <bitmap2cmp_panel.h>
|
|
#include <bitmap2cmp_settings.h>
|
|
#include <bitmap_io.h>
|
|
#include <bitmaps.h>
|
|
#include <common.h>
|
|
#include <id.h>
|
|
#include <kiface_base.h>
|
|
#include <pgm_base.h>
|
|
#include <widgets/wx_menubar.h>
|
|
#include <wildcards_and_files_ext.h>
|
|
#include <file_history.h>
|
|
#include <tool/tool_manager.h>
|
|
#include <tool/tool_dispatcher.h>
|
|
#include <tool/common_control.h>
|
|
#include <bitmap2cmp_control.h>
|
|
#include <tool/actions.h>
|
|
|
|
#include <wx/filedlg.h>
|
|
#include <wx/msgdlg.h>
|
|
|
|
|
|
#define DEFAULT_DPI 300 // the image DPI used in formats that do not define a DPI
|
|
|
|
IMAGE_SIZE::IMAGE_SIZE()
|
|
{
|
|
m_outputSize = 0.0;
|
|
m_originalDPI = DEFAULT_DPI;
|
|
m_originalSizePixels = 0;
|
|
m_unit = EDA_UNITS::MILLIMETRES;
|
|
}
|
|
|
|
|
|
void IMAGE_SIZE::SetOutputSizeFromInitialImageSize()
|
|
{
|
|
// Safety-check to guarantee no divide-by-zero
|
|
m_originalDPI = std::max( 1, m_originalDPI );
|
|
|
|
// Set the m_outputSize value from the m_originalSizePixels and the selected unit
|
|
if( m_unit == EDA_UNITS::MILLIMETRES )
|
|
m_outputSize = (double)GetOriginalSizePixels() / m_originalDPI * 25.4;
|
|
else if( m_unit == EDA_UNITS::INCHES )
|
|
m_outputSize = (double)GetOriginalSizePixels() / m_originalDPI;
|
|
else
|
|
m_outputSize = m_originalDPI;
|
|
}
|
|
|
|
|
|
int IMAGE_SIZE::GetOutputDPI()
|
|
{
|
|
int outputDPI;
|
|
|
|
if( m_unit == EDA_UNITS::MILLIMETRES )
|
|
outputDPI = GetOriginalSizePixels() / ( m_outputSize / 25.4 );
|
|
else if( m_unit == EDA_UNITS::INCHES )
|
|
outputDPI = GetOriginalSizePixels() / m_outputSize;
|
|
else
|
|
outputDPI = KiROUND( m_outputSize );
|
|
|
|
// Zero is not a DPI, and may cause divide-by-zero errors...
|
|
outputDPI = std::max( 1, outputDPI );
|
|
|
|
return outputDPI;
|
|
}
|
|
|
|
|
|
void IMAGE_SIZE::SetUnit( EDA_UNITS aUnit )
|
|
{
|
|
// Set the unit used for m_outputSize, and convert the old m_outputSize value
|
|
// to the value in new unit
|
|
if( aUnit == m_unit )
|
|
return;
|
|
|
|
// Convert m_outputSize to mm:
|
|
double size_mm;
|
|
|
|
if( m_unit == EDA_UNITS::MILLIMETRES )
|
|
{
|
|
size_mm = m_outputSize;
|
|
}
|
|
else if( m_unit == EDA_UNITS::INCHES )
|
|
{
|
|
size_mm = m_outputSize * 25.4;
|
|
}
|
|
else
|
|
{
|
|
// m_outputSize is the DPI, not an image size
|
|
// the image size is m_originalSizePixels / m_outputSize (in inches)
|
|
if( m_outputSize )
|
|
size_mm = m_originalSizePixels / m_outputSize * 25.4;
|
|
else
|
|
size_mm = 0;
|
|
}
|
|
|
|
// Convert m_outputSize to new value:
|
|
if( aUnit == EDA_UNITS::MILLIMETRES )
|
|
{
|
|
m_outputSize = size_mm;
|
|
}
|
|
else if( aUnit == EDA_UNITS::INCHES )
|
|
{
|
|
m_outputSize = size_mm / 25.4;
|
|
}
|
|
else
|
|
{
|
|
if( size_mm )
|
|
m_outputSize = m_originalSizePixels / size_mm * 25.4;
|
|
else
|
|
m_outputSize = 0;
|
|
}
|
|
|
|
m_unit = aUnit;
|
|
}
|
|
|
|
|
|
BEGIN_EVENT_TABLE( BITMAP2CMP_FRAME, EDA_BASE_FRAME )
|
|
EVT_MENU( wxID_CLOSE, BITMAP2CMP_FRAME::OnExit )
|
|
EVT_MENU( wxID_EXIT, BITMAP2CMP_FRAME::OnExit )
|
|
|
|
EVT_MENU_RANGE( ID_FILE1, ID_FILEMAX, BITMAP2CMP_FRAME::OnFileHistory )
|
|
EVT_MENU( ID_FILE_LIST_CLEAR, BITMAP2CMP_FRAME::OnClearFileHistory )
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
BITMAP2CMP_FRAME::BITMAP2CMP_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
|
|
KIWAY_PLAYER( aKiway, aParent, FRAME_BM2CMP, _( "Image Converter" ), wxDefaultPosition,
|
|
wxDefaultSize, wxDEFAULT_FRAME_STYLE, wxT( "bitmap2cmp" ), unityScale ),
|
|
m_panel( nullptr ),
|
|
m_statusBar( nullptr )
|
|
{
|
|
m_aboutTitle = _HKI( "KiCad Image Converter" );
|
|
|
|
// Give an icon
|
|
wxIcon icon;
|
|
wxIconBundle icon_bundle;
|
|
|
|
icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_bitmap2component ) );
|
|
icon_bundle.AddIcon( icon );
|
|
icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_bitmap2component_32 ) );
|
|
icon_bundle.AddIcon( icon );
|
|
icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_bitmap2component_16 ) );
|
|
icon_bundle.AddIcon( icon );
|
|
|
|
SetIcons( icon_bundle );
|
|
|
|
wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
|
|
SetSizer( mainSizer );
|
|
|
|
m_panel = new BITMAP2CMP_PANEL( this );
|
|
mainSizer->Add( m_panel, 1, wxEXPAND, 5 );
|
|
|
|
m_statusBar = this->CreateStatusBar( 1, wxSTB_SIZEGRIP, wxID_ANY );
|
|
|
|
LoadSettings( config() );
|
|
|
|
m_toolManager = new TOOL_MANAGER;
|
|
m_toolManager->SetEnvironment( nullptr, nullptr, nullptr, config(), this );
|
|
|
|
m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager );
|
|
|
|
// Register tools
|
|
m_toolManager->RegisterTool( new COMMON_CONTROL );
|
|
m_toolManager->RegisterTool( new BITMAP2CMP_CONTROL );
|
|
m_toolManager->InitTools();
|
|
|
|
ReCreateMenuBar();
|
|
|
|
GetSizer()->SetSizeHints( this );
|
|
|
|
SetSize( m_framePos.x, m_framePos.y, m_frameSize.x, m_frameSize.y );
|
|
|
|
if ( m_framePos == wxDefaultPosition )
|
|
Centre();
|
|
}
|
|
|
|
|
|
BITMAP2CMP_FRAME::~BITMAP2CMP_FRAME()
|
|
{
|
|
SaveSettings( config() );
|
|
/*
|
|
* This needed for OSX: avoids further OnDraw processing after this
|
|
* destructor and before the native window is destroyed
|
|
*/
|
|
Freeze();
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::OnExit( wxCommandEvent& aEvent )
|
|
{
|
|
// Just generate a wxCloseEvent
|
|
Close( false );
|
|
}
|
|
|
|
|
|
wxWindow* BITMAP2CMP_FRAME::GetToolCanvas() const
|
|
{
|
|
return m_panel->GetCurrentPage();
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::OnFileHistory( wxCommandEvent& event )
|
|
{
|
|
wxString fn = GetFileFromHistory( event.GetId(), _( "Image files" ) );
|
|
|
|
if( !fn.IsEmpty() )
|
|
{
|
|
OpenProjectFiles( std::vector<wxString>( 1, fn ) );
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::OnClearFileHistory( wxCommandEvent& aEvent )
|
|
{
|
|
ClearFileHistory();
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::doReCreateMenuBar()
|
|
{
|
|
COMMON_CONTROL* tool = m_toolManager->GetTool<COMMON_CONTROL>();
|
|
EDA_BASE_FRAME* base_frame = dynamic_cast<EDA_BASE_FRAME*>( this );
|
|
|
|
// base_frame == nullptr should not happen, but it makes Coverity happy
|
|
wxCHECK( base_frame, /* void */ );
|
|
|
|
// wxWidgets handles the OSX Application menu behind the scenes, but that means
|
|
// we always have to start from scratch with a new wxMenuBar.
|
|
wxMenuBar* oldMenuBar = base_frame->GetMenuBar();
|
|
WX_MENUBAR* menuBar = new WX_MENUBAR();
|
|
|
|
//-- File menu -----------------------------------------------------------
|
|
//
|
|
ACTION_MENU* fileMenu = new ACTION_MENU( false, tool );
|
|
|
|
fileMenu->Add( ACTIONS::open );
|
|
|
|
static ACTION_MENU* openRecentMenu;
|
|
FILE_HISTORY& fileHistory = GetFileHistory();
|
|
|
|
// Create the menu if it does not exist. Adding a file to/from the history
|
|
// will automatically refresh the menu.
|
|
if( !openRecentMenu )
|
|
{
|
|
openRecentMenu = new ACTION_MENU( false, tool );
|
|
openRecentMenu->SetIcon( BITMAPS::recent );
|
|
|
|
fileHistory.UseMenu( openRecentMenu );
|
|
fileHistory.AddFilesToMenu();
|
|
}
|
|
|
|
// Ensure the title is up to date after changing language
|
|
openRecentMenu->SetTitle( _( "Open Recent" ) );
|
|
fileHistory.UpdateClearText( openRecentMenu, _( "Clear Recent Files" ) );
|
|
|
|
wxMenuItem* item = fileMenu->Add( openRecentMenu->Clone() );
|
|
|
|
// Add the file menu condition here since it needs the item ID for the submenu
|
|
ACTION_CONDITIONS cond;
|
|
cond.Enable( FILE_HISTORY::FileHistoryNotEmpty( fileHistory ) );
|
|
RegisterUIUpdateHandler( item->GetId(), cond );
|
|
|
|
fileMenu->AppendSeparator();
|
|
fileMenu->AddQuit( _( "Image Converter" ) );
|
|
|
|
//-- Preferences menu -----------------------------------------------
|
|
//
|
|
ACTION_MENU* prefsMenu = new ACTION_MENU( false, tool );
|
|
|
|
prefsMenu->Add( ACTIONS::openPreferences );
|
|
|
|
prefsMenu->AppendSeparator();
|
|
AddMenuLanguageList( prefsMenu, tool );
|
|
|
|
|
|
//-- Menubar -------------------------------------------------------------
|
|
//
|
|
menuBar->Append( fileMenu, _( "&File" ) );
|
|
menuBar->Append( prefsMenu, _( "&Preferences" ) );
|
|
base_frame->AddStandardHelpMenu( menuBar );
|
|
|
|
base_frame->SetMenuBar( menuBar );
|
|
delete oldMenuBar;
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::ShowChangedLanguage()
|
|
{
|
|
EDA_BASE_FRAME::ShowChangedLanguage();
|
|
|
|
UpdateTitle();
|
|
|
|
SaveSettings( config() );
|
|
IMAGE_SIZE imageSizeX = m_panel->GetOutputSizeX();
|
|
IMAGE_SIZE imageSizeY = m_panel->GetOutputSizeY();
|
|
Freeze();
|
|
|
|
wxSizer* mainSizer = m_panel->GetContainingSizer();
|
|
mainSizer->Detach( m_panel );
|
|
m_panel->Destroy();
|
|
|
|
m_panel = new BITMAP2CMP_PANEL( this );
|
|
mainSizer->Add( m_panel, 1, wxEXPAND, 5 );
|
|
Layout();
|
|
|
|
if( !m_bitmapFileName.IsEmpty() )
|
|
OpenProjectFiles( std::vector<wxString>( 1, m_bitmapFileName ) );
|
|
|
|
LoadSettings( config() );
|
|
m_panel->SetOutputSize( imageSizeX, imageSizeY );
|
|
|
|
Thaw();
|
|
Refresh();
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::UpdateTitle()
|
|
{
|
|
wxString title;
|
|
|
|
if( !m_bitmapFileName.IsEmpty() )
|
|
{
|
|
wxFileName filename( m_bitmapFileName );
|
|
title = filename.GetFullName() + wxT( " \u2014 " );
|
|
}
|
|
|
|
title += _( "Image Converter" );
|
|
|
|
SetTitle( title );
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
|
|
{
|
|
EDA_BASE_FRAME::LoadSettings( aCfg );
|
|
|
|
BITMAP2CMP_SETTINGS* cfg = dynamic_cast<BITMAP2CMP_SETTINGS*>( aCfg );
|
|
|
|
if( cfg )
|
|
{
|
|
m_bitmapFileName = cfg->m_BitmapFileName;
|
|
m_convertedFileName = cfg->m_ConvertedFileName;
|
|
m_panel->LoadSettings( cfg );
|
|
}
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
|
|
{
|
|
EDA_BASE_FRAME::SaveSettings( aCfg );
|
|
|
|
BITMAP2CMP_SETTINGS* cfg = dynamic_cast<BITMAP2CMP_SETTINGS*>( aCfg );
|
|
|
|
if( cfg )
|
|
{
|
|
cfg->m_BitmapFileName = m_bitmapFileName;
|
|
cfg->m_ConvertedFileName = m_convertedFileName;
|
|
m_panel->SaveSettings( cfg );
|
|
}
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::OnLoadFile()
|
|
{
|
|
wxFileName fn( m_bitmapFileName );
|
|
wxString path = fn.GetPath();
|
|
|
|
if( path.IsEmpty() || !wxDirExists( path ) )
|
|
path = m_mruPath;
|
|
|
|
wxFileDialog fileDlg( this, _( "Choose Image" ), path, wxEmptyString,
|
|
_( "Image Files" ) + wxS( " " )+ wxImage::GetImageExtWildcard(),
|
|
wxFD_OPEN | wxFD_FILE_MUST_EXIST );
|
|
|
|
int diag = fileDlg.ShowModal();
|
|
|
|
if( diag != wxID_OK )
|
|
return;
|
|
|
|
wxString fullFilename = fileDlg.GetPath();
|
|
|
|
if( !OpenProjectFiles( std::vector<wxString>( 1, fullFilename ) ) )
|
|
return;
|
|
|
|
fn = fullFilename;
|
|
m_mruPath = fn.GetPath();
|
|
SetStatusText( fullFilename );
|
|
UpdateTitle();
|
|
Refresh();
|
|
}
|
|
|
|
|
|
bool BITMAP2CMP_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
|
|
{
|
|
m_bitmapFileName = aFileSet[0];
|
|
|
|
if( m_panel->OpenProjectFiles( aFileSet, aCtl ) )
|
|
{
|
|
UpdateFileHistory( m_bitmapFileName );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::ExportDrawingSheetFormat()
|
|
{
|
|
wxFileName fn( m_convertedFileName );
|
|
wxString path = fn.GetPath();
|
|
|
|
if( path.IsEmpty() || !wxDirExists(path) )
|
|
path = ::wxGetCwd();
|
|
|
|
wxFileDialog fileDlg( this, _( "Create Drawing Sheet File" ), path, wxEmptyString,
|
|
FILEEXT::DrawingSheetFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
|
|
int diag = fileDlg.ShowModal();
|
|
|
|
if( diag != wxID_OK )
|
|
return;
|
|
|
|
fn = fileDlg.GetPath();
|
|
fn.SetExt( FILEEXT::DrawingSheetFileExtension );
|
|
m_convertedFileName = fn.GetFullPath();
|
|
|
|
FILE* outfile;
|
|
outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
|
|
|
|
if( outfile == nullptr )
|
|
{
|
|
wxString msg;
|
|
msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
|
|
wxMessageBox( msg );
|
|
return;
|
|
}
|
|
|
|
std::string buffer;
|
|
m_panel->ExportToBuffer( buffer, KICAD_WKS_LOGO );
|
|
fputs( buffer.c_str(), outfile );
|
|
fclose( outfile );
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::ExportPostScriptFormat()
|
|
{
|
|
wxFileName fn( m_convertedFileName );
|
|
wxString path = fn.GetPath();
|
|
|
|
if( path.IsEmpty() || !wxDirExists( path ) )
|
|
path = ::wxGetCwd();
|
|
|
|
wxFileDialog fileDlg( this, _( "Create PostScript File" ), path, wxEmptyString,
|
|
FILEEXT::PSFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
|
|
|
|
if( fileDlg.ShowModal() != wxID_OK )
|
|
return;
|
|
|
|
fn = fileDlg.GetPath();
|
|
fn.SetExt( wxT( "ps" ) );
|
|
m_convertedFileName = fn.GetFullPath();
|
|
|
|
FILE* outfile;
|
|
outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
|
|
|
|
if( outfile == nullptr )
|
|
{
|
|
wxString msg;
|
|
msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
|
|
wxMessageBox( msg );
|
|
return;
|
|
}
|
|
|
|
std::string buffer;
|
|
m_panel->ExportToBuffer( buffer, POSTSCRIPT_FMT );
|
|
fputs( buffer.c_str(), outfile );
|
|
fclose( outfile );
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::ExportEeschemaFormat()
|
|
{
|
|
wxFileName fn( m_convertedFileName );
|
|
wxString path = fn.GetPath();
|
|
|
|
if( path.IsEmpty() || !wxDirExists(path) )
|
|
path = ::wxGetCwd();
|
|
|
|
wxFileDialog fileDlg( this, _( "Create Symbol Library" ), path, wxEmptyString,
|
|
FILEEXT::KiCadSymbolLibFileWildcard(),
|
|
wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
|
|
|
|
if( fileDlg.ShowModal() != wxID_OK )
|
|
return;
|
|
|
|
fn = EnsureFileExtension( fileDlg.GetPath(), FILEEXT::KiCadSymbolLibFileExtension );
|
|
m_convertedFileName = fn.GetFullPath();
|
|
|
|
FILE* outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
|
|
|
|
if( outfile == nullptr )
|
|
{
|
|
wxString msg;
|
|
msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
|
|
wxMessageBox( msg );
|
|
return;
|
|
}
|
|
|
|
std::string buffer;
|
|
m_panel->ExportToBuffer( buffer, EESCHEMA_FMT );
|
|
fputs( buffer.c_str(), outfile );
|
|
fclose( outfile );
|
|
}
|
|
|
|
|
|
void BITMAP2CMP_FRAME::ExportPcbnewFormat()
|
|
{
|
|
wxFileName fn( m_convertedFileName );
|
|
wxString path = fn.GetPath();
|
|
|
|
if( path.IsEmpty() || !wxDirExists( path ) )
|
|
path = m_mruPath;
|
|
|
|
wxFileDialog fileDlg( this, _( "Create Footprint Library" ), path, wxEmptyString,
|
|
FILEEXT::KiCadFootprintLibFileWildcard(),
|
|
wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
|
|
|
|
if( fileDlg.ShowModal() != wxID_OK )
|
|
return;
|
|
|
|
fn = EnsureFileExtension( fileDlg.GetPath(), FILEEXT::KiCadFootprintFileExtension );
|
|
m_convertedFileName = fn.GetFullPath();
|
|
|
|
FILE* outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
|
|
|
|
if( outfile == nullptr )
|
|
{
|
|
wxString msg;
|
|
msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
|
|
wxMessageBox( msg );
|
|
return;
|
|
}
|
|
|
|
std::string buffer;
|
|
m_panel->ExportToBuffer( buffer, PCBNEW_KICAD_MOD );
|
|
fputs( buffer.c_str(), outfile );
|
|
fclose( outfile );
|
|
m_mruPath = fn.GetPath();
|
|
}
|
|
|
|
|