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.

250 lines
8.2 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or (at your
  9. * option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <board.h>
  20. #include <confirm.h>
  21. #include <pcb_view.h>
  22. #include <pcb_screen.h>
  23. #include <gal/gal_display_options.h>
  24. #include <gal/graphics_abstraction_layer.h>
  25. #include <math/vector2wx.h>
  26. #include <design_block_lib_table.h>
  27. #include <design_block.h>
  28. #include <footprint_preview_panel.h>
  29. #include <pgm_base.h>
  30. #include <pcb_painter.h>
  31. #include <pcb_edit_frame.h>
  32. #include <pcb_field.h>
  33. #include <pcb_io/pcb_io.h>
  34. #include <pcb_io/pcb_io_mgr.h>
  35. #include <project_pcb.h>
  36. #include <pcbnew_settings.h>
  37. //#include <pcb_helpers.h>
  38. #include <settings/settings_manager.h>
  39. #include <widgets/pcb_design_block_preview_widget.h>
  40. #include <widgets/wx_progress_reporters.h>
  41. #include <wx/log.h>
  42. #include <wx/stattext.h>
  43. #include <wx/panel.h>
  44. PCB_DESIGN_BLOCK_PREVIEW_WIDGET::PCB_DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, PCB_EDIT_FRAME* aFrame ) :
  45. DESIGN_BLOCK_PREVIEW_WIDGET( aParent ), m_preview( nullptr ), m_status( nullptr ), m_statusPanel( nullptr ),
  46. m_statusSizer( nullptr ), m_previewItem( nullptr )
  47. {
  48. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  49. COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
  50. PCBNEW_SETTINGS* app_settings = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" );
  51. m_galDisplayOptions.ReadConfig( *common_settings, app_settings->m_Window, this );
  52. m_galDisplayOptions.m_forceDisplayCursor = false;
  53. m_preview = FOOTPRINT_PREVIEW_PANEL::New( &aFrame->Kiway(), this, aFrame );
  54. m_preview->SetStealsFocus( false );
  55. m_preview->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_NEVER );
  56. m_preview->GetGAL()->SetAxesEnabled( false );
  57. // Do not display the grid: the look is not good for a small canvas area.
  58. // But mainly, due to some strange bug I (JPC) was unable to fix, the grid creates
  59. // strange artifacts on Windows when Pcb is run from KiCad manager (but not in
  60. // stand alone...).
  61. m_preview->GetGAL()->SetGridVisibility( true );
  62. // Early initialization of the canvas background color,
  63. // before any OnPaint event is fired for the canvas using a wrong bg color
  64. KIGFX::VIEW* view = m_preview->GetView();
  65. auto settings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() );
  66. if( auto* theme = Pgm().GetSettingsManager().GetColorSettings( app_settings->m_ColorTheme ) )
  67. settings->LoadColors( theme );
  68. const COLOR4D& backgroundColor = settings->GetBackgroundColor();
  69. const COLOR4D& foregroundColor = settings->GetCursorColor();
  70. m_preview->GetGAL()->SetClearColor( backgroundColor );
  71. m_outerSizer = new wxBoxSizer( wxVERTICAL );
  72. m_statusPanel = new wxPanel( this );
  73. m_statusPanel->SetBackgroundColour( backgroundColor.ToColour() );
  74. m_status = new wxStaticText( m_statusPanel, wxID_ANY, wxEmptyString );
  75. m_status->SetForegroundColour( settings->GetLayerColor( LAYER_REFERENCEPART ).ToColour() );
  76. m_statusSizer = new wxBoxSizer( wxVERTICAL );
  77. m_statusSizer->Add( 0, 0, 1 ); // add a spacer
  78. m_statusSizer->Add( m_status, 0, wxALIGN_CENTER );
  79. m_statusSizer->Add( 0, 0, 1 ); // add a spacer
  80. m_statusPanel->SetSizer( m_statusSizer );
  81. // Give the status panel the same color scheme as the canvas so it isn't jarring when
  82. // switched to.
  83. m_statusPanel->SetBackgroundColour( backgroundColor.ToColour() );
  84. m_statusPanel->SetForegroundColour( foregroundColor.ToColour() );
  85. // Give the preview panel a small top border to align its top with the status panel,
  86. // and give the status panel a small bottom border to align its bottom with the preview
  87. // panel.
  88. m_outerSizer->Add( m_preview, 1, wxTOP | wxEXPAND, 5 );
  89. m_outerSizer->Add( m_statusPanel, 1, wxBOTTOM | wxEXPAND, 5 );
  90. // Hide the status panel to start
  91. m_statusPanel->Hide();
  92. SetSizer( m_outerSizer );
  93. Layout();
  94. Connect( wxEVT_SIZE, wxSizeEventHandler( PCB_DESIGN_BLOCK_PREVIEW_WIDGET::onSize ), nullptr, this );
  95. }
  96. PCB_DESIGN_BLOCK_PREVIEW_WIDGET::~PCB_DESIGN_BLOCK_PREVIEW_WIDGET()
  97. {
  98. if( m_previewItem )
  99. m_preview->GetView()->Remove( m_previewItem );
  100. delete m_previewItem;
  101. }
  102. void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::SetStatusText( wxString const& aText )
  103. {
  104. wxCHECK( m_statusPanel, /* void */ );
  105. m_status->SetLabel( aText );
  106. m_preview->Hide();
  107. m_statusPanel->Show();
  108. Layout();
  109. }
  110. void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent )
  111. {
  112. if( m_previewItem )
  113. {
  114. fitOnDrawArea();
  115. m_preview->ForceRefresh();
  116. }
  117. aEvent.Skip();
  118. }
  119. void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea()
  120. {
  121. if( !m_previewItem )
  122. return;
  123. // set the view scale to fit the item on screen
  124. KIGFX::VIEW* view = m_preview->GetView();
  125. // Calculate the drawing area size, in internal units, for a scaling factor = 1.0
  126. view->SetScale( 1.0 );
  127. VECTOR2D clientSize = view->ToWorld( ToVECTOR2D( m_preview->GetClientSize() ), false );
  128. // Calculate the draw scale to fit the drawing area
  129. double scale =
  130. std::min( fabs( clientSize.x / m_itemBBox.GetWidth() ), fabs( clientSize.y / m_itemBBox.GetHeight() ) );
  131. // Above calculation will yield an exact fit; add a bit of whitespace around block
  132. scale /= 1.2;
  133. // Now fix the best scale
  134. view->SetScale( scale );
  135. view->SetCenter( m_itemBBox.Centre() );
  136. }
  137. void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock )
  138. {
  139. KIGFX::VIEW* view = m_preview->GetView();
  140. if( m_previewItem )
  141. {
  142. view->Clear();
  143. delete m_previewItem;
  144. m_previewItem = nullptr;
  145. }
  146. if( aDesignBlock && wxFileExists( aDesignBlock->GetBoardFile() ) )
  147. {
  148. try
  149. {
  150. IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( PCB_IO_MGR::KICAD_SEXP ) );
  151. WX_PROGRESS_REPORTER progressReporter( this, _( "Load PCB" ), 1, PR_CAN_ABORT );
  152. pi->SetProgressReporter( &progressReporter );
  153. m_previewItem = pi->LoadBoard( aDesignBlock->GetBoardFile(), nullptr );
  154. }
  155. catch( const IO_ERROR& ioe )
  156. {
  157. // You wouldn't think boardFn.GetFullPath() would throw, but we get a stack buffer
  158. // underflow from ASAN. While it's probably an ASAN error, a second try/catch doesn't
  159. // cost us much.
  160. try
  161. {
  162. if( ioe.Problem() != wxT( "CANCEL" ) )
  163. {
  164. wxString msg =
  165. wxString::Format( _( "Error loading board file:\n%s" ), aDesignBlock->GetBoardFile() );
  166. DisplayErrorMessage( this, msg, ioe.What() );
  167. }
  168. }
  169. catch( ... )
  170. {
  171. // That was already our best-efforts
  172. }
  173. }
  174. BOX2I bBox;
  175. if( m_previewItem )
  176. {
  177. for( BOARD_ITEM* item : m_previewItem->GetItemSet() )
  178. {
  179. view->Add( item );
  180. if( item->Type() == PCB_FIELD_T )
  181. {
  182. if( !static_cast<const PCB_FIELD*>( item )->IsVisible() )
  183. continue;
  184. }
  185. bBox.Merge( item->GetBoundingBox() );
  186. }
  187. }
  188. m_itemBBox = bBox;
  189. if( !m_preview->IsShownOnScreen() )
  190. {
  191. m_preview->Show();
  192. if( m_statusPanel )
  193. m_statusPanel->Hide();
  194. Layout(); // Ensure panel size is up to date.
  195. }
  196. // Calculate the draw scale to fit the drawing area
  197. fitOnDrawArea();
  198. }
  199. m_preview->ForceRefresh();
  200. m_preview->Show();
  201. }