You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

579 lines
16 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2010 jean-pierre.charras
  5. * Copyright (C) 1992-2023 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. #include <bitmap2component.h>
  25. #include <bitmap2cmp_frame.h>
  26. #include <bitmap2cmp_panel.h>
  27. #include <bitmap2cmp_settings.h>
  28. #include <bitmap_io.h>
  29. #include <bitmaps.h>
  30. #include <common.h>
  31. #include <id.h>
  32. #include <kiface_base.h>
  33. #include <pgm_base.h>
  34. #include <widgets/wx_menubar.h>
  35. #include <wildcards_and_files_ext.h>
  36. #include <file_history.h>
  37. #include <tool/tool_manager.h>
  38. #include <tool/tool_dispatcher.h>
  39. #include <tool/common_control.h>
  40. #include <bitmap2cmp_control.h>
  41. #include <tool/actions.h>
  42. #include <wx/filedlg.h>
  43. #include <wx/msgdlg.h>
  44. #define DEFAULT_DPI 300 // the image DPI used in formats that do not define a DPI
  45. IMAGE_SIZE::IMAGE_SIZE()
  46. {
  47. m_outputSize = 0.0;
  48. m_originalDPI = DEFAULT_DPI;
  49. m_originalSizePixels = 0;
  50. m_unit = EDA_UNITS::MILLIMETRES;
  51. }
  52. void IMAGE_SIZE::SetOutputSizeFromInitialImageSize()
  53. {
  54. // Safety-check to guarantee no divide-by-zero
  55. m_originalDPI = std::max( 1, m_originalDPI );
  56. // Set the m_outputSize value from the m_originalSizePixels and the selected unit
  57. if( m_unit == EDA_UNITS::MILLIMETRES )
  58. m_outputSize = (double)GetOriginalSizePixels() / m_originalDPI * 25.4;
  59. else if( m_unit == EDA_UNITS::INCHES )
  60. m_outputSize = (double)GetOriginalSizePixels() / m_originalDPI;
  61. else
  62. m_outputSize = m_originalDPI;
  63. }
  64. int IMAGE_SIZE::GetOutputDPI()
  65. {
  66. int outputDPI;
  67. if( m_unit == EDA_UNITS::MILLIMETRES )
  68. outputDPI = GetOriginalSizePixels() / ( m_outputSize / 25.4 );
  69. else if( m_unit == EDA_UNITS::INCHES )
  70. outputDPI = GetOriginalSizePixels() / m_outputSize;
  71. else
  72. outputDPI = KiROUND( m_outputSize );
  73. // Zero is not a DPI, and may cause divide-by-zero errors...
  74. outputDPI = std::max( 1, outputDPI );
  75. return outputDPI;
  76. }
  77. void IMAGE_SIZE::SetUnit( EDA_UNITS aUnit )
  78. {
  79. // Set the unit used for m_outputSize, and convert the old m_outputSize value
  80. // to the value in new unit
  81. if( aUnit == m_unit )
  82. return;
  83. // Convert m_outputSize to mm:
  84. double size_mm;
  85. if( m_unit == EDA_UNITS::MILLIMETRES )
  86. {
  87. size_mm = m_outputSize;
  88. }
  89. else if( m_unit == EDA_UNITS::INCHES )
  90. {
  91. size_mm = m_outputSize * 25.4;
  92. }
  93. else
  94. {
  95. // m_outputSize is the DPI, not an image size
  96. // the image size is m_originalSizePixels / m_outputSize (in inches)
  97. if( m_outputSize )
  98. size_mm = m_originalSizePixels / m_outputSize * 25.4;
  99. else
  100. size_mm = 0;
  101. }
  102. // Convert m_outputSize to new value:
  103. if( aUnit == EDA_UNITS::MILLIMETRES )
  104. {
  105. m_outputSize = size_mm;
  106. }
  107. else if( aUnit == EDA_UNITS::INCHES )
  108. {
  109. m_outputSize = size_mm / 25.4;
  110. }
  111. else
  112. {
  113. if( size_mm )
  114. m_outputSize = m_originalSizePixels / size_mm * 25.4;
  115. else
  116. m_outputSize = 0;
  117. }
  118. m_unit = aUnit;
  119. }
  120. BEGIN_EVENT_TABLE( BITMAP2CMP_FRAME, EDA_BASE_FRAME )
  121. EVT_MENU( wxID_CLOSE, BITMAP2CMP_FRAME::OnExit )
  122. EVT_MENU( wxID_EXIT, BITMAP2CMP_FRAME::OnExit )
  123. EVT_MENU_RANGE( ID_FILE1, ID_FILEMAX, BITMAP2CMP_FRAME::OnFileHistory )
  124. EVT_MENU( ID_FILE_LIST_CLEAR, BITMAP2CMP_FRAME::OnClearFileHistory )
  125. END_EVENT_TABLE()
  126. BITMAP2CMP_FRAME::BITMAP2CMP_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
  127. KIWAY_PLAYER( aKiway, aParent, FRAME_BM2CMP, _( "Image Converter" ), wxDefaultPosition,
  128. wxDefaultSize, wxDEFAULT_FRAME_STYLE, wxT( "bitmap2cmp" ), unityScale ),
  129. m_panel( nullptr ),
  130. m_statusBar( nullptr )
  131. {
  132. m_aboutTitle = _HKI( "KiCad Image Converter" );
  133. // Give an icon
  134. wxIcon icon;
  135. wxIconBundle icon_bundle;
  136. icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_bitmap2component, 48 ) );
  137. icon_bundle.AddIcon( icon );
  138. icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_bitmap2component, 128 ) );
  139. icon_bundle.AddIcon( icon );
  140. icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_bitmap2component, 256 ) );
  141. icon_bundle.AddIcon( icon );
  142. icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_bitmap2component_32 ) );
  143. icon_bundle.AddIcon( icon );
  144. icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_bitmap2component_16 ) );
  145. icon_bundle.AddIcon( icon );
  146. SetIcons( icon_bundle );
  147. wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
  148. SetSizer( mainSizer );
  149. m_panel = new BITMAP2CMP_PANEL( this );
  150. mainSizer->Add( m_panel, 1, wxEXPAND, 5 );
  151. m_statusBar = this->CreateStatusBar( 1, wxSTB_SIZEGRIP, wxID_ANY );
  152. LoadSettings( config() );
  153. m_toolManager = new TOOL_MANAGER;
  154. m_toolManager->SetEnvironment( nullptr, nullptr, nullptr, config(), this );
  155. m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager );
  156. // Register tools
  157. m_toolManager->RegisterTool( new COMMON_CONTROL );
  158. m_toolManager->RegisterTool( new BITMAP2CMP_CONTROL );
  159. m_toolManager->InitTools();
  160. ReCreateMenuBar();
  161. GetSizer()->SetSizeHints( this );
  162. SetSize( m_framePos.x, m_framePos.y, m_frameSize.x, m_frameSize.y );
  163. if ( m_framePos == wxDefaultPosition )
  164. Centre();
  165. }
  166. BITMAP2CMP_FRAME::~BITMAP2CMP_FRAME()
  167. {
  168. SaveSettings( config() );
  169. /*
  170. * This needed for OSX: avoids further OnDraw processing after this
  171. * destructor and before the native window is destroyed
  172. */
  173. Freeze();
  174. }
  175. void BITMAP2CMP_FRAME::OnExit( wxCommandEvent& aEvent )
  176. {
  177. // Just generate a wxCloseEvent
  178. Close( false );
  179. }
  180. wxWindow* BITMAP2CMP_FRAME::GetToolCanvas() const
  181. {
  182. return m_panel->GetCurrentPage();
  183. }
  184. void BITMAP2CMP_FRAME::OnFileHistory( wxCommandEvent& event )
  185. {
  186. wxString fn = GetFileFromHistory( event.GetId(), _( "Image files" ) );
  187. if( !fn.IsEmpty() )
  188. {
  189. OpenProjectFiles( std::vector<wxString>( 1, fn ) );
  190. Refresh();
  191. }
  192. }
  193. void BITMAP2CMP_FRAME::OnClearFileHistory( wxCommandEvent& aEvent )
  194. {
  195. ClearFileHistory();
  196. }
  197. void BITMAP2CMP_FRAME::doReCreateMenuBar()
  198. {
  199. COMMON_CONTROL* tool = m_toolManager->GetTool<COMMON_CONTROL>();
  200. EDA_BASE_FRAME* base_frame = dynamic_cast<EDA_BASE_FRAME*>( this );
  201. // base_frame == nullptr should not happen, but it makes Coverity happy
  202. wxCHECK( base_frame, /* void */ );
  203. // wxWidgets handles the OSX Application menu behind the scenes, but that means
  204. // we always have to start from scratch with a new wxMenuBar.
  205. wxMenuBar* oldMenuBar = base_frame->GetMenuBar();
  206. WX_MENUBAR* menuBar = new WX_MENUBAR();
  207. //-- File menu -----------------------------------------------------------
  208. //
  209. ACTION_MENU* fileMenu = new ACTION_MENU( false, tool );
  210. fileMenu->Add( ACTIONS::open );
  211. static ACTION_MENU* openRecentMenu;
  212. FILE_HISTORY& fileHistory = GetFileHistory();
  213. // Create the menu if it does not exist. Adding a file to/from the history
  214. // will automatically refresh the menu.
  215. if( !openRecentMenu )
  216. {
  217. openRecentMenu = new ACTION_MENU( false, tool );
  218. openRecentMenu->SetIcon( BITMAPS::recent );
  219. fileHistory.UseMenu( openRecentMenu );
  220. fileHistory.AddFilesToMenu();
  221. }
  222. // Ensure the title is up to date after changing language
  223. openRecentMenu->SetTitle( _( "Open Recent" ) );
  224. fileHistory.UpdateClearText( openRecentMenu, _( "Clear Recent Files" ) );
  225. wxMenuItem* item = fileMenu->Add( openRecentMenu->Clone() );
  226. // Add the file menu condition here since it needs the item ID for the submenu
  227. ACTION_CONDITIONS cond;
  228. cond.Enable( FILE_HISTORY::FileHistoryNotEmpty( fileHistory ) );
  229. RegisterUIUpdateHandler( item->GetId(), cond );
  230. fileMenu->AppendSeparator();
  231. fileMenu->AddQuit( _( "Image Converter" ) );
  232. //-- Preferences menu -----------------------------------------------
  233. //
  234. ACTION_MENU* prefsMenu = new ACTION_MENU( false, tool );
  235. prefsMenu->Add( ACTIONS::openPreferences );
  236. prefsMenu->AppendSeparator();
  237. AddMenuLanguageList( prefsMenu, tool );
  238. //-- Menubar -------------------------------------------------------------
  239. //
  240. menuBar->Append( fileMenu, _( "&File" ) );
  241. menuBar->Append( prefsMenu, _( "&Preferences" ) );
  242. base_frame->AddStandardHelpMenu( menuBar );
  243. base_frame->SetMenuBar( menuBar );
  244. delete oldMenuBar;
  245. }
  246. void BITMAP2CMP_FRAME::ShowChangedLanguage()
  247. {
  248. EDA_BASE_FRAME::ShowChangedLanguage();
  249. UpdateTitle();
  250. SaveSettings( config() );
  251. IMAGE_SIZE imageSizeX = m_panel->GetOutputSizeX();
  252. IMAGE_SIZE imageSizeY = m_panel->GetOutputSizeY();
  253. Freeze();
  254. wxSizer* mainSizer = m_panel->GetContainingSizer();
  255. mainSizer->Detach( m_panel );
  256. m_panel->Destroy();
  257. m_panel = new BITMAP2CMP_PANEL( this );
  258. mainSizer->Add( m_panel, 1, wxEXPAND, 5 );
  259. Layout();
  260. if( !m_bitmapFileName.IsEmpty() )
  261. OpenProjectFiles( std::vector<wxString>( 1, m_bitmapFileName ) );
  262. LoadSettings( config() );
  263. m_panel->SetOutputSize( imageSizeX, imageSizeY );
  264. Thaw();
  265. Refresh();
  266. }
  267. void BITMAP2CMP_FRAME::UpdateTitle()
  268. {
  269. wxString title;
  270. if( !m_bitmapFileName.IsEmpty() )
  271. {
  272. wxFileName filename( m_bitmapFileName );
  273. title = filename.GetFullName() + wxT( " \u2014 " );
  274. }
  275. title += _( "Image Converter" );
  276. SetTitle( title );
  277. }
  278. void BITMAP2CMP_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
  279. {
  280. EDA_BASE_FRAME::LoadSettings( aCfg );
  281. BITMAP2CMP_SETTINGS* cfg = dynamic_cast<BITMAP2CMP_SETTINGS*>( aCfg );
  282. if( cfg )
  283. {
  284. m_bitmapFileName = cfg->m_BitmapFileName;
  285. m_convertedFileName = cfg->m_ConvertedFileName;
  286. m_panel->LoadSettings( cfg );
  287. }
  288. }
  289. void BITMAP2CMP_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
  290. {
  291. EDA_BASE_FRAME::SaveSettings( aCfg );
  292. BITMAP2CMP_SETTINGS* cfg = dynamic_cast<BITMAP2CMP_SETTINGS*>( aCfg );
  293. if( cfg )
  294. {
  295. cfg->m_BitmapFileName = m_bitmapFileName;
  296. cfg->m_ConvertedFileName = m_convertedFileName;
  297. m_panel->SaveSettings( cfg );
  298. }
  299. }
  300. void BITMAP2CMP_FRAME::OnLoadFile()
  301. {
  302. wxFileName fn( m_bitmapFileName );
  303. wxString path = fn.GetPath();
  304. if( path.IsEmpty() || !wxDirExists( path ) )
  305. path = m_mruPath;
  306. wxFileDialog fileDlg( this, _( "Choose Image" ), path, wxEmptyString,
  307. _( "Image Files" ) + wxS( " " )+ wxImage::GetImageExtWildcard(),
  308. wxFD_OPEN | wxFD_FILE_MUST_EXIST );
  309. int diag = fileDlg.ShowModal();
  310. if( diag != wxID_OK )
  311. return;
  312. wxString fullFilename = fileDlg.GetPath();
  313. if( !OpenProjectFiles( std::vector<wxString>( 1, fullFilename ) ) )
  314. return;
  315. fn = fullFilename;
  316. m_mruPath = fn.GetPath();
  317. SetStatusText( fullFilename );
  318. UpdateTitle();
  319. Refresh();
  320. }
  321. bool BITMAP2CMP_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
  322. {
  323. m_bitmapFileName = aFileSet[0];
  324. if( m_panel->OpenProjectFiles( aFileSet, aCtl ) )
  325. {
  326. UpdateFileHistory( m_bitmapFileName );
  327. return true;
  328. }
  329. return false;
  330. }
  331. void BITMAP2CMP_FRAME::ExportDrawingSheetFormat()
  332. {
  333. wxFileName fn( m_convertedFileName );
  334. wxString path = fn.GetPath();
  335. if( path.IsEmpty() || !wxDirExists(path) )
  336. path = ::wxGetCwd();
  337. wxFileDialog fileDlg( this, _( "Create Drawing Sheet File" ), path, wxEmptyString,
  338. FILEEXT::DrawingSheetFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  339. int diag = fileDlg.ShowModal();
  340. if( diag != wxID_OK )
  341. return;
  342. fn = fileDlg.GetPath();
  343. fn.SetExt( FILEEXT::DrawingSheetFileExtension );
  344. m_convertedFileName = fn.GetFullPath();
  345. FILE* outfile;
  346. outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
  347. if( outfile == nullptr )
  348. {
  349. wxString msg;
  350. msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
  351. wxMessageBox( msg );
  352. return;
  353. }
  354. std::string buffer;
  355. m_panel->ExportToBuffer( buffer, KICAD_WKS_LOGO );
  356. fputs( buffer.c_str(), outfile );
  357. fclose( outfile );
  358. }
  359. void BITMAP2CMP_FRAME::ExportPostScriptFormat()
  360. {
  361. wxFileName fn( m_convertedFileName );
  362. wxString path = fn.GetPath();
  363. if( path.IsEmpty() || !wxDirExists( path ) )
  364. path = ::wxGetCwd();
  365. wxFileDialog fileDlg( this, _( "Create PostScript File" ), path, wxEmptyString,
  366. FILEEXT::PSFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  367. if( fileDlg.ShowModal() != wxID_OK )
  368. return;
  369. fn = fileDlg.GetPath();
  370. fn.SetExt( wxT( "ps" ) );
  371. m_convertedFileName = fn.GetFullPath();
  372. FILE* outfile;
  373. outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
  374. if( outfile == nullptr )
  375. {
  376. wxString msg;
  377. msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
  378. wxMessageBox( msg );
  379. return;
  380. }
  381. std::string buffer;
  382. m_panel->ExportToBuffer( buffer, POSTSCRIPT_FMT );
  383. fputs( buffer.c_str(), outfile );
  384. fclose( outfile );
  385. }
  386. void BITMAP2CMP_FRAME::ExportEeschemaFormat()
  387. {
  388. wxFileName fn( m_convertedFileName );
  389. wxString path = fn.GetPath();
  390. if( path.IsEmpty() || !wxDirExists(path) )
  391. path = ::wxGetCwd();
  392. wxFileDialog fileDlg( this, _( "Create Symbol Library" ), path, wxEmptyString,
  393. FILEEXT::KiCadSymbolLibFileWildcard(),
  394. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  395. if( fileDlg.ShowModal() != wxID_OK )
  396. return;
  397. fn = EnsureFileExtension( fileDlg.GetPath(), FILEEXT::KiCadSymbolLibFileExtension );
  398. m_convertedFileName = fn.GetFullPath();
  399. FILE* outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
  400. if( outfile == nullptr )
  401. {
  402. wxString msg;
  403. msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
  404. wxMessageBox( msg );
  405. return;
  406. }
  407. std::string buffer;
  408. m_panel->ExportToBuffer( buffer, EESCHEMA_FMT );
  409. fputs( buffer.c_str(), outfile );
  410. fclose( outfile );
  411. }
  412. void BITMAP2CMP_FRAME::ExportPcbnewFormat()
  413. {
  414. wxFileName fn( m_convertedFileName );
  415. wxString path = fn.GetPath();
  416. if( path.IsEmpty() || !wxDirExists( path ) )
  417. path = m_mruPath;
  418. wxFileDialog fileDlg( this, _( "Create Footprint Library" ), path, wxEmptyString,
  419. FILEEXT::KiCadFootprintLibFileWildcard(),
  420. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  421. if( fileDlg.ShowModal() != wxID_OK )
  422. return;
  423. fn = EnsureFileExtension( fileDlg.GetPath(), FILEEXT::KiCadFootprintFileExtension );
  424. m_convertedFileName = fn.GetFullPath();
  425. FILE* outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
  426. if( outfile == nullptr )
  427. {
  428. wxString msg;
  429. msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
  430. wxMessageBox( msg );
  431. return;
  432. }
  433. std::string buffer;
  434. m_panel->ExportToBuffer( buffer, PCBNEW_KICAD_MOD );
  435. fputs( buffer.c_str(), outfile );
  436. fclose( outfile );
  437. m_mruPath = fn.GetPath();
  438. }