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.

333 lines
10 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 CERN
  5. * Copyright (C) 2019-2020 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 <sch_component.h>
  25. #include <id.h>
  26. #include <kiway.h>
  27. #include <confirm.h>
  28. #include <tool/conditional_menu.h>
  29. #include <tool/selection_conditions.h>
  30. #include <tools/ee_actions.h>
  31. #include <tools/ee_inspection_tool.h>
  32. #include <tools/ee_selection_tool.h>
  33. #include <tools/ee_selection.h>
  34. #include <search_stack.h>
  35. #include <sim/sim_plot_frame.h>
  36. #include <sch_edit_frame.h>
  37. #include <lib_edit_frame.h>
  38. #include <lib_view_frame.h>
  39. #include <eda_doc.h>
  40. #include <invoke_sch_dialog.h>
  41. #include <project.h>
  42. #include <dialogs/dialog_display_info_HTML_base.h>
  43. #include <math/util.h> // for KiROUND
  44. EE_INSPECTION_TOOL::EE_INSPECTION_TOOL()
  45. : EE_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.InspectionTool" )
  46. {
  47. }
  48. bool EE_INSPECTION_TOOL::Init()
  49. {
  50. EE_TOOL_BASE::Init();
  51. auto singleMarkerCondition = SELECTION_CONDITIONS::OnlyType( SCH_MARKER_T )
  52. && SELECTION_CONDITIONS::Count( 1 );
  53. // Add inspection actions to the selection tool menu
  54. //
  55. CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
  56. selToolMenu.AddItem( EE_ACTIONS::showDatasheet, EE_CONDITIONS::SingleSymbol && EE_CONDITIONS::Idle, 220 );
  57. return true;
  58. }
  59. int EE_INSPECTION_TOOL::RunERC( const TOOL_EVENT& aEvent )
  60. {
  61. if( m_frame->IsType( FRAME_SCH_LIB_EDITOR ) )
  62. {
  63. checkPart( static_cast<LIB_EDIT_FRAME*>( m_frame )->GetCurPart() );
  64. }
  65. else if( m_frame->IsType( FRAME_SCH ) )
  66. {
  67. wxWindow* erc = wxWindow::FindWindowById( ID_DIALOG_ERC, m_frame );
  68. if( erc )
  69. {
  70. // Needed at least on Windows. Raise() is not enough
  71. erc->Show( true );
  72. // Bring it to the top if already open. Dual monitor users need this.
  73. erc->Raise();
  74. }
  75. else
  76. InvokeDialogERC( static_cast<SCH_EDIT_FRAME*>( m_frame ) );
  77. }
  78. return 0;
  79. }
  80. // helper function to sort pins by pin num
  81. bool sort_by_pin_number( const LIB_PIN* ref, const LIB_PIN* tst )
  82. {
  83. // Use number as primary key
  84. int test = ref->GetNumber().Cmp( tst->GetNumber() );
  85. // Use DeMorgan variant as secondary key
  86. if( test == 0 )
  87. test = ref->GetConvert() - tst->GetConvert();
  88. // Use unit as tertiary key
  89. if( test == 0 )
  90. test = ref->GetUnit() - tst->GetUnit();
  91. return test < 0;
  92. }
  93. void EE_INSPECTION_TOOL::checkPart( LIB_PART* aPart )
  94. {
  95. if( !aPart )
  96. return;
  97. wxString msg;
  98. const int min_grid_size = 25;
  99. const int grid_size = KiROUND( getView()->GetGAL()->GetGridSize().x );
  100. const int clamped_grid_size = ( grid_size < min_grid_size ) ? min_grid_size : grid_size;
  101. LIB_PINS pinList;
  102. aPart->GetPins( pinList );
  103. if( pinList.empty() )
  104. {
  105. DisplayInfoMessage( m_frame, _( "No pins!" ) );
  106. return;
  107. }
  108. // Sort pins by pin num, so 2 duplicate pins
  109. // (pins with the same number) will be consecutive in list
  110. sort( pinList.begin(), pinList.end(), sort_by_pin_number );
  111. // Test for duplicates:
  112. DIALOG_DISPLAY_HTML_TEXT_BASE error_display( m_frame, wxID_ANY, _( "Marker Information" ),
  113. wxDefaultPosition, wxSize( 750, 600 ) );
  114. std::vector<wxString> messages;
  115. int dup_error = 0;
  116. for( unsigned ii = 1; ii < pinList.size(); ii++ )
  117. {
  118. LIB_PIN* pin = pinList[ii - 1];
  119. LIB_PIN* next = pinList[ii];
  120. if( pin->GetNumber() != next->GetNumber() || pin->GetConvert() != next->GetConvert() )
  121. continue;
  122. dup_error++;
  123. /* TODO I dare someone to find a way to make happy translators on this thing! Lorenzo */
  124. msg = wxString::Format( _( "<b>Duplicate pin %s</b> \"%s\" at location <b>(%.3f, %.3f)</b>"
  125. " conflicts with pin %s \"%s\" at location <b>(%.3f, %.3f)</b>" ),
  126. next->GetNumber(),
  127. next->GetName(),
  128. next->GetPosition().x / 1000.0, -next->GetPosition().y / 1000.0,
  129. pin->GetNumber(),
  130. pin->GetName(),
  131. pin->GetPosition().x / 1000.0, -pin->GetPosition().y / 1000.0 );
  132. if( aPart->GetUnitCount() > 1 )
  133. {
  134. msg += wxString::Format( _( " in units %c and %c" ),
  135. 'A' + next->GetUnit() - 1,
  136. 'A' + pin->GetUnit() - 1 );
  137. }
  138. if( aPart->HasConversion() )
  139. {
  140. if( next->GetConvert() )
  141. msg += _( " of converted" );
  142. else
  143. msg += _( " of normal" );
  144. }
  145. msg += wxT( ".<br>" );
  146. messages.push_back( msg );
  147. }
  148. // Test for off grid pins:
  149. int offgrid_error = 0;
  150. for( LIB_PIN* pin : pinList )
  151. {
  152. if( ( (pin->GetPosition().x % clamped_grid_size) == 0 ) &&
  153. ( (pin->GetPosition().y % clamped_grid_size) == 0 ) )
  154. continue;
  155. // "pin" is off grid here.
  156. offgrid_error++;
  157. msg = wxString::Format( _( "<b>Off grid pin %s</b> \"%s\" at location <b>(%.3f, %.3f)</b>" ),
  158. pin->GetNumber(),
  159. pin->GetName(),
  160. pin->GetPosition().x / 1000.0, -pin->GetPosition().y / 1000.0 );
  161. if( aPart->GetUnitCount() > 1 )
  162. msg += wxString::Format( _( " in symbol %c" ), 'A' + pin->GetUnit() - 1 );
  163. if( aPart->HasConversion() )
  164. {
  165. if( pin->GetConvert() )
  166. msg += _( " of converted" );
  167. else
  168. msg += _( " of normal" );
  169. }
  170. msg += wxT( ".<br>" );
  171. messages.push_back( msg );
  172. }
  173. if( !dup_error && !offgrid_error )
  174. DisplayInfoMessage( m_frame, _( "No off grid or duplicate pins were found." ) );
  175. else
  176. {
  177. wxColour bgcolor = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
  178. wxColour fgcolor = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
  179. wxString outmsg = wxString::Format( "<html><body bgcolor='%s' text='%s'>",
  180. bgcolor.GetAsString( wxC2S_HTML_SYNTAX ),
  181. fgcolor.GetAsString( wxC2S_HTML_SYNTAX ) );
  182. for( auto& msgPart : messages )
  183. outmsg += msgPart;
  184. outmsg += "</body></html>";
  185. error_display.m_htmlWindow->SetPage( outmsg );
  186. error_display.ShowModal();
  187. }
  188. }
  189. int EE_INSPECTION_TOOL::RunSimulation( const TOOL_EVENT& aEvent )
  190. {
  191. #ifdef KICAD_SPICE
  192. SIM_PLOT_FRAME* simFrame = (SIM_PLOT_FRAME*) m_frame->Kiway().Player( FRAME_SIMULATOR, true );
  193. simFrame->Show( true );
  194. // On Windows, Raise() does not bring the window on screen, when iconized
  195. if( simFrame->IsIconized() )
  196. simFrame->Iconize( false );
  197. simFrame->Raise();
  198. #endif /* KICAD_SPICE */
  199. return 0;
  200. }
  201. int EE_INSPECTION_TOOL::ShowDatasheet( const TOOL_EVENT& aEvent )
  202. {
  203. wxString datasheet;
  204. if( m_frame->IsType( FRAME_SCH_LIB_EDITOR ) )
  205. {
  206. LIB_PART* part = static_cast<LIB_EDIT_FRAME*>( m_frame )->GetCurPart();
  207. if( !part )
  208. return 0;
  209. datasheet = part->GetDatasheetField().GetText();
  210. }
  211. else if( m_frame->IsType( FRAME_SCH_VIEWER ) || m_frame->IsType( FRAME_SCH_VIEWER_MODAL ) )
  212. {
  213. LIB_PART* entry = static_cast<LIB_VIEW_FRAME*>( m_frame )->GetSelectedSymbol();
  214. if( !entry )
  215. return 0;
  216. datasheet = entry->GetDatasheetField().GetText();
  217. }
  218. else if( m_frame->IsType( FRAME_SCH ) )
  219. {
  220. EE_SELECTION& selection = m_selectionTool->RequestSelection( EE_COLLECTOR::ComponentsOnly );
  221. if( selection.Empty() )
  222. return 0;
  223. SCH_COMPONENT* component = (SCH_COMPONENT*) selection.Front();
  224. datasheet = component->GetField( DATASHEET )->GetText();
  225. }
  226. if( !datasheet.IsEmpty() && datasheet != wxT( "~" ) )
  227. GetAssociatedDocument( m_frame, datasheet, &m_frame->Prj() );
  228. return 0;
  229. }
  230. int EE_INSPECTION_TOOL::UpdateMessagePanel( const TOOL_EVENT& aEvent )
  231. {
  232. EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
  233. EE_SELECTION& selection = selTool->GetSelection();
  234. if( selection.GetSize() == 1 )
  235. {
  236. EDA_ITEM* item = (EDA_ITEM*) selection.Front();
  237. MSG_PANEL_ITEMS msgItems;
  238. item->GetMsgPanelInfo( m_frame, msgItems );
  239. m_frame->SetMsgPanel( msgItems );
  240. }
  241. else
  242. {
  243. m_frame->ClearMsgPanel();
  244. }
  245. if( SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
  246. editFrame->UpdateNetHighlightStatus();
  247. return 0;
  248. }
  249. void EE_INSPECTION_TOOL::setTransitions()
  250. {
  251. Go( &EE_INSPECTION_TOOL::RunERC, EE_ACTIONS::runERC.MakeEvent() );
  252. Go( &EE_INSPECTION_TOOL::RunSimulation, EE_ACTIONS::runSimulation.MakeEvent() );
  253. Go( &EE_INSPECTION_TOOL::ShowDatasheet, EE_ACTIONS::showDatasheet.MakeEvent() );
  254. Go( &EE_INSPECTION_TOOL::UpdateMessagePanel, EVENTS::SelectedEvent );
  255. Go( &EE_INSPECTION_TOOL::UpdateMessagePanel, EVENTS::UnselectedEvent );
  256. Go( &EE_INSPECTION_TOOL::UpdateMessagePanel, EVENTS::ClearedEvent );
  257. Go( &EE_INSPECTION_TOOL::UpdateMessagePanel, EVENTS::SelectedItemsModified );
  258. }