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.

580 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, KIWAY_PLAYER )
  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. setupUIConditions();
  162. GetSizer()->SetSizeHints( this );
  163. SetSize( m_framePos.x, m_framePos.y, m_frameSize.x, m_frameSize.y );
  164. if ( m_framePos == wxDefaultPosition )
  165. Centre();
  166. }
  167. BITMAP2CMP_FRAME::~BITMAP2CMP_FRAME()
  168. {
  169. SaveSettings( config() );
  170. /*
  171. * This needed for OSX: avoids further OnDraw processing after this
  172. * destructor and before the native window is destroyed
  173. */
  174. Freeze();
  175. }
  176. void BITMAP2CMP_FRAME::OnExit( wxCommandEvent& aEvent )
  177. {
  178. // Just generate a wxCloseEvent
  179. Close( false );
  180. }
  181. wxWindow* BITMAP2CMP_FRAME::GetToolCanvas() const
  182. {
  183. return m_panel->GetCurrentPage();
  184. }
  185. void BITMAP2CMP_FRAME::OnFileHistory( wxCommandEvent& event )
  186. {
  187. wxString fn = GetFileFromHistory( event.GetId(), _( "Image files" ) );
  188. if( !fn.IsEmpty() )
  189. {
  190. OpenProjectFiles( std::vector<wxString>( 1, fn ) );
  191. Refresh();
  192. }
  193. }
  194. void BITMAP2CMP_FRAME::OnClearFileHistory( wxCommandEvent& aEvent )
  195. {
  196. ClearFileHistory();
  197. }
  198. void BITMAP2CMP_FRAME::doReCreateMenuBar()
  199. {
  200. COMMON_CONTROL* tool = m_toolManager->GetTool<COMMON_CONTROL>();
  201. EDA_BASE_FRAME* base_frame = dynamic_cast<EDA_BASE_FRAME*>( this );
  202. // base_frame == nullptr should not happen, but it makes Coverity happy
  203. wxCHECK( base_frame, /* void */ );
  204. // wxWidgets handles the OSX Application menu behind the scenes, but that means
  205. // we always have to start from scratch with a new wxMenuBar.
  206. wxMenuBar* oldMenuBar = base_frame->GetMenuBar();
  207. WX_MENUBAR* menuBar = new WX_MENUBAR();
  208. //-- File menu -----------------------------------------------------------
  209. //
  210. ACTION_MENU* fileMenu = new ACTION_MENU( false, tool );
  211. fileMenu->Add( ACTIONS::open );
  212. static ACTION_MENU* openRecentMenu;
  213. FILE_HISTORY& fileHistory = GetFileHistory();
  214. // Create the menu if it does not exist. Adding a file to/from the history
  215. // will automatically refresh the menu.
  216. if( !openRecentMenu )
  217. {
  218. openRecentMenu = new ACTION_MENU( false, tool );
  219. openRecentMenu->SetIcon( BITMAPS::recent );
  220. fileHistory.UseMenu( openRecentMenu );
  221. fileHistory.AddFilesToMenu();
  222. }
  223. // Ensure the title is up to date after changing language
  224. openRecentMenu->SetTitle( _( "Open Recent" ) );
  225. fileHistory.UpdateClearText( openRecentMenu, _( "Clear Recent Files" ) );
  226. wxMenuItem* item = fileMenu->Add( openRecentMenu->Clone() );
  227. // Add the file menu condition here since it needs the item ID for the submenu
  228. ACTION_CONDITIONS cond;
  229. cond.Enable( FILE_HISTORY::FileHistoryNotEmpty( fileHistory ) );
  230. RegisterUIUpdateHandler( item->GetId(), cond );
  231. fileMenu->AppendSeparator();
  232. fileMenu->AddQuit( _( "Image Converter" ) );
  233. //-- Preferences menu -----------------------------------------------
  234. //
  235. ACTION_MENU* prefsMenu = new ACTION_MENU( false, tool );
  236. prefsMenu->Add( ACTIONS::openPreferences );
  237. prefsMenu->AppendSeparator();
  238. AddMenuLanguageList( prefsMenu, tool );
  239. //-- Menubar -------------------------------------------------------------
  240. //
  241. menuBar->Append( fileMenu, _( "&File" ) );
  242. menuBar->Append( prefsMenu, _( "&Preferences" ) );
  243. base_frame->AddStandardHelpMenu( menuBar );
  244. base_frame->SetMenuBar( menuBar );
  245. delete oldMenuBar;
  246. }
  247. void BITMAP2CMP_FRAME::ShowChangedLanguage()
  248. {
  249. EDA_BASE_FRAME::ShowChangedLanguage();
  250. UpdateTitle();
  251. SaveSettings( config() );
  252. IMAGE_SIZE imageSizeX = m_panel->GetOutputSizeX();
  253. IMAGE_SIZE imageSizeY = m_panel->GetOutputSizeY();
  254. Freeze();
  255. wxSizer* mainSizer = m_panel->GetContainingSizer();
  256. mainSizer->Detach( m_panel );
  257. m_panel->Destroy();
  258. m_panel = new BITMAP2CMP_PANEL( this );
  259. mainSizer->Add( m_panel, 1, wxEXPAND, 5 );
  260. Layout();
  261. if( !m_bitmapFileName.IsEmpty() )
  262. OpenProjectFiles( std::vector<wxString>( 1, m_bitmapFileName ) );
  263. LoadSettings( config() );
  264. m_panel->SetOutputSize( imageSizeX, imageSizeY );
  265. Thaw();
  266. Refresh();
  267. }
  268. void BITMAP2CMP_FRAME::UpdateTitle()
  269. {
  270. wxString title;
  271. if( !m_bitmapFileName.IsEmpty() )
  272. {
  273. wxFileName filename( m_bitmapFileName );
  274. title = filename.GetFullName() + wxT( " \u2014 " );
  275. }
  276. title += _( "Image Converter" );
  277. SetTitle( title );
  278. }
  279. void BITMAP2CMP_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
  280. {
  281. EDA_BASE_FRAME::LoadSettings( aCfg );
  282. BITMAP2CMP_SETTINGS* cfg = dynamic_cast<BITMAP2CMP_SETTINGS*>( aCfg );
  283. if( cfg )
  284. {
  285. m_bitmapFileName = cfg->m_BitmapFileName;
  286. m_convertedFileName = cfg->m_ConvertedFileName;
  287. m_panel->LoadSettings( cfg );
  288. }
  289. }
  290. void BITMAP2CMP_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
  291. {
  292. EDA_BASE_FRAME::SaveSettings( aCfg );
  293. BITMAP2CMP_SETTINGS* cfg = dynamic_cast<BITMAP2CMP_SETTINGS*>( aCfg );
  294. if( cfg )
  295. {
  296. cfg->m_BitmapFileName = m_bitmapFileName;
  297. cfg->m_ConvertedFileName = m_convertedFileName;
  298. m_panel->SaveSettings( cfg );
  299. }
  300. }
  301. void BITMAP2CMP_FRAME::OnLoadFile()
  302. {
  303. wxFileName fn( m_bitmapFileName );
  304. wxString path = fn.GetPath();
  305. if( path.IsEmpty() || !wxDirExists( path ) )
  306. path = m_mruPath;
  307. wxFileDialog fileDlg( this, _( "Choose Image" ), path, wxEmptyString,
  308. _( "Image Files" ) + wxS( " " )+ wxImage::GetImageExtWildcard(),
  309. wxFD_OPEN | wxFD_FILE_MUST_EXIST );
  310. int diag = fileDlg.ShowModal();
  311. if( diag != wxID_OK )
  312. return;
  313. wxString fullFilename = fileDlg.GetPath();
  314. if( !OpenProjectFiles( std::vector<wxString>( 1, fullFilename ) ) )
  315. return;
  316. fn = fullFilename;
  317. m_mruPath = fn.GetPath();
  318. SetStatusText( fullFilename );
  319. UpdateTitle();
  320. Refresh();
  321. }
  322. bool BITMAP2CMP_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
  323. {
  324. m_bitmapFileName = aFileSet[0];
  325. if( m_panel->OpenProjectFiles( aFileSet, aCtl ) )
  326. {
  327. UpdateFileHistory( m_bitmapFileName );
  328. return true;
  329. }
  330. return false;
  331. }
  332. void BITMAP2CMP_FRAME::ExportDrawingSheetFormat()
  333. {
  334. wxFileName fn( m_convertedFileName );
  335. wxString path = fn.GetPath();
  336. if( path.IsEmpty() || !wxDirExists(path) )
  337. path = ::wxGetCwd();
  338. wxFileDialog fileDlg( this, _( "Create Drawing Sheet File" ), path, wxEmptyString,
  339. FILEEXT::DrawingSheetFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  340. int diag = fileDlg.ShowModal();
  341. if( diag != wxID_OK )
  342. return;
  343. fn = fileDlg.GetPath();
  344. fn.SetExt( FILEEXT::DrawingSheetFileExtension );
  345. m_convertedFileName = fn.GetFullPath();
  346. FILE* outfile;
  347. outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
  348. if( outfile == nullptr )
  349. {
  350. wxString msg;
  351. msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
  352. wxMessageBox( msg );
  353. return;
  354. }
  355. std::string buffer;
  356. m_panel->ExportToBuffer( buffer, KICAD_WKS_LOGO );
  357. fputs( buffer.c_str(), outfile );
  358. fclose( outfile );
  359. }
  360. void BITMAP2CMP_FRAME::ExportPostScriptFormat()
  361. {
  362. wxFileName fn( m_convertedFileName );
  363. wxString path = fn.GetPath();
  364. if( path.IsEmpty() || !wxDirExists( path ) )
  365. path = ::wxGetCwd();
  366. wxFileDialog fileDlg( this, _( "Create PostScript File" ), path, wxEmptyString,
  367. FILEEXT::PSFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  368. if( fileDlg.ShowModal() != wxID_OK )
  369. return;
  370. fn = fileDlg.GetPath();
  371. fn.SetExt( wxT( "ps" ) );
  372. m_convertedFileName = fn.GetFullPath();
  373. FILE* outfile;
  374. outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
  375. if( outfile == nullptr )
  376. {
  377. wxString msg;
  378. msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
  379. wxMessageBox( msg );
  380. return;
  381. }
  382. std::string buffer;
  383. m_panel->ExportToBuffer( buffer, POSTSCRIPT_FMT );
  384. fputs( buffer.c_str(), outfile );
  385. fclose( outfile );
  386. }
  387. void BITMAP2CMP_FRAME::ExportEeschemaFormat()
  388. {
  389. wxFileName fn( m_convertedFileName );
  390. wxString path = fn.GetPath();
  391. if( path.IsEmpty() || !wxDirExists(path) )
  392. path = ::wxGetCwd();
  393. wxFileDialog fileDlg( this, _( "Create Symbol Library" ), path, wxEmptyString,
  394. FILEEXT::KiCadSymbolLibFileWildcard(),
  395. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  396. if( fileDlg.ShowModal() != wxID_OK )
  397. return;
  398. fn = EnsureFileExtension( fileDlg.GetPath(), FILEEXT::KiCadSymbolLibFileExtension );
  399. m_convertedFileName = fn.GetFullPath();
  400. FILE* outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
  401. if( outfile == nullptr )
  402. {
  403. wxString msg;
  404. msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
  405. wxMessageBox( msg );
  406. return;
  407. }
  408. std::string buffer;
  409. m_panel->ExportToBuffer( buffer, EESCHEMA_FMT );
  410. fputs( buffer.c_str(), outfile );
  411. fclose( outfile );
  412. }
  413. void BITMAP2CMP_FRAME::ExportPcbnewFormat()
  414. {
  415. wxFileName fn( m_convertedFileName );
  416. wxString path = fn.GetPath();
  417. if( path.IsEmpty() || !wxDirExists( path ) )
  418. path = m_mruPath;
  419. wxFileDialog fileDlg( this, _( "Create Footprint Library" ), path, wxEmptyString,
  420. FILEEXT::KiCadFootprintLibFileWildcard(),
  421. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  422. if( fileDlg.ShowModal() != wxID_OK )
  423. return;
  424. fn = EnsureFileExtension( fileDlg.GetPath(), FILEEXT::KiCadFootprintFileExtension );
  425. m_convertedFileName = fn.GetFullPath();
  426. FILE* outfile = wxFopen( m_convertedFileName, wxT( "w" ) );
  427. if( outfile == nullptr )
  428. {
  429. wxString msg;
  430. msg.Printf( _( "File '%s' could not be created." ), m_convertedFileName );
  431. wxMessageBox( msg );
  432. return;
  433. }
  434. std::string buffer;
  435. m_panel->ExportToBuffer( buffer, PCBNEW_KICAD_MOD );
  436. fputs( buffer.c_str(), outfile );
  437. fclose( outfile );
  438. m_mruPath = fn.GetPath();
  439. }