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.

574 lines
16 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018-2019 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <kiway.h>
  24. #include <kiway_player.h>
  25. #include <dialog_shim.h>
  26. #include <fields_grid_table.h>
  27. #include <sch_base_frame.h>
  28. #include <sch_field.h>
  29. #include <sch_validators.h>
  30. #include <validators.h>
  31. #include <class_library.h>
  32. #include <template_fieldnames.h>
  33. #include <widgets/grid_icon_text_helpers.h>
  34. #include <widgets/grid_text_button_helpers.h>
  35. #include "eda_doc.h"
  36. enum
  37. {
  38. MYID_SELECT_FOOTPRINT = 991, // must be within GRID_TRICKS' enum range
  39. MYID_SHOW_DATASHEET
  40. };
  41. template <class T>
  42. FIELDS_GRID_TABLE<T>::FIELDS_GRID_TABLE( DIALOG_SHIM* aDialog, SCH_BASE_FRAME* aFrame,
  43. LIB_PART* aPart ) :
  44. m_frame( aFrame ),
  45. m_userUnits( aDialog->GetUserUnits() ),
  46. m_part( aPart ),
  47. m_fieldNameValidator( aFrame->IsType( FRAME_SCH_LIB_EDITOR ), FIELD_NAME ),
  48. m_referenceValidator( aFrame->IsType( FRAME_SCH_LIB_EDITOR ), REFERENCE ),
  49. m_valueValidator( aFrame->IsType( FRAME_SCH_LIB_EDITOR ), VALUE ),
  50. m_libIdValidator( LIB_ID::ID_PCB ),
  51. m_urlValidator( aFrame->IsType( FRAME_SCH_LIB_EDITOR ), DATASHEET ),
  52. m_nonUrlValidator( aFrame->IsType( FRAME_SCH_LIB_EDITOR ), DATASHEET )
  53. {
  54. // Build the various grid cell attributes.
  55. // NOTE: validators and cellAttrs are member variables to get the destruction order
  56. // right. wxGrid is VERY cranky about this.
  57. m_readOnlyAttr = new wxGridCellAttr;
  58. m_readOnlyAttr->SetReadOnly( true );
  59. m_fieldNameAttr = new wxGridCellAttr;
  60. GRID_CELL_TEXT_EDITOR* nameEditor = new GRID_CELL_TEXT_EDITOR();
  61. nameEditor->SetValidator( m_fieldNameValidator );
  62. m_fieldNameAttr->SetEditor( nameEditor );
  63. m_referenceAttr = new wxGridCellAttr;
  64. GRID_CELL_TEXT_EDITOR* referenceEditor = new GRID_CELL_TEXT_EDITOR();
  65. referenceEditor->SetValidator( m_referenceValidator );
  66. m_referenceAttr->SetEditor( referenceEditor );
  67. m_valueAttr = new wxGridCellAttr;
  68. GRID_CELL_TEXT_EDITOR* valueEditor = new GRID_CELL_TEXT_EDITOR();
  69. valueEditor->SetValidator( m_valueValidator );
  70. m_valueAttr->SetEditor( valueEditor );
  71. m_footprintAttr = new wxGridCellAttr;
  72. GRID_CELL_FOOTPRINT_ID_EDITOR* fpIdEditor = new GRID_CELL_FOOTPRINT_ID_EDITOR( aDialog );
  73. fpIdEditor->SetValidator( m_libIdValidator );
  74. m_footprintAttr->SetEditor( fpIdEditor );
  75. m_urlAttr = new wxGridCellAttr;
  76. GRID_CELL_URL_EDITOR* urlEditor = new GRID_CELL_URL_EDITOR( aDialog );
  77. urlEditor->SetValidator( m_urlValidator );
  78. m_urlAttr->SetEditor( urlEditor );
  79. m_nonUrlAttr = new wxGridCellAttr;
  80. GRID_CELL_TEXT_EDITOR* nonUrlEditor = new GRID_CELL_TEXT_EDITOR();
  81. nonUrlEditor->SetValidator( m_nonUrlValidator );
  82. m_nonUrlAttr->SetEditor( nonUrlEditor );
  83. m_boolAttr = new wxGridCellAttr;
  84. m_boolAttr->SetRenderer( new wxGridCellBoolRenderer() );
  85. m_boolAttr->SetEditor( new wxGridCellBoolEditor() );
  86. m_boolAttr->SetAlignment( wxALIGN_CENTER, wxALIGN_BOTTOM );
  87. wxArrayString vAlignNames;
  88. vAlignNames.Add( _( "Top" ) );
  89. vAlignNames.Add( _( "Center" ) );
  90. vAlignNames.Add( _( "Bottom" ) );
  91. m_vAlignAttr = new wxGridCellAttr;
  92. m_vAlignAttr->SetEditor( new wxGridCellChoiceEditor( vAlignNames ) );
  93. m_vAlignAttr->SetAlignment( wxALIGN_CENTER, wxALIGN_BOTTOM );
  94. wxArrayString hAlignNames;
  95. hAlignNames.Add( _( "Left" ) );
  96. hAlignNames.Add(_( "Center" ) );
  97. hAlignNames.Add(_( "Right" ) );
  98. m_hAlignAttr = new wxGridCellAttr;
  99. m_hAlignAttr->SetEditor( new wxGridCellChoiceEditor( hAlignNames ) );
  100. m_hAlignAttr->SetAlignment( wxALIGN_CENTER, wxALIGN_BOTTOM );
  101. wxArrayString orientationNames;
  102. orientationNames.Add( _( "Horizontal" ) );
  103. orientationNames.Add(_( "Vertical" ) );
  104. m_orientationAttr = new wxGridCellAttr;
  105. m_orientationAttr->SetEditor( new wxGridCellChoiceEditor( orientationNames ) );
  106. m_orientationAttr->SetAlignment( wxALIGN_CENTER, wxALIGN_BOTTOM );
  107. }
  108. template <class T>
  109. FIELDS_GRID_TABLE<T>::~FIELDS_GRID_TABLE()
  110. {
  111. m_readOnlyAttr->DecRef();
  112. m_fieldNameAttr->DecRef();
  113. m_boolAttr->DecRef();
  114. m_referenceAttr->DecRef();
  115. m_valueAttr->DecRef();
  116. m_footprintAttr->DecRef();
  117. m_urlAttr->DecRef();
  118. m_nonUrlAttr->DecRef();
  119. m_vAlignAttr->DecRef();
  120. m_hAlignAttr->DecRef();
  121. m_orientationAttr->DecRef();
  122. }
  123. template <class T>
  124. wxString FIELDS_GRID_TABLE<T>::GetColLabelValue( int aCol )
  125. {
  126. switch( aCol )
  127. {
  128. case FDC_NAME: return _( "Name" );
  129. case FDC_VALUE: return _( "Value" );
  130. case FDC_SHOWN: return _( "Show" );
  131. case FDC_H_ALIGN: return _( "H Align" );
  132. case FDC_V_ALIGN: return _( "V Align" );
  133. case FDC_ITALIC: return _( "Italic" );
  134. case FDC_BOLD: return _( "Bold" );
  135. case FDC_TEXT_SIZE: return _( "Text Size" );
  136. case FDC_ORIENTATION: return _( "Orientation" );
  137. case FDC_POSX: return _( "X Position" );
  138. case FDC_POSY: return _( "Y Position" );
  139. default: wxFAIL; return wxEmptyString;
  140. }
  141. }
  142. template <class T>
  143. bool FIELDS_GRID_TABLE<T>::CanGetValueAs( int aRow, int aCol, const wxString& aTypeName )
  144. {
  145. switch( aCol )
  146. {
  147. case FDC_NAME:
  148. case FDC_VALUE:
  149. case FDC_H_ALIGN:
  150. case FDC_V_ALIGN:
  151. case FDC_TEXT_SIZE:
  152. case FDC_ORIENTATION:
  153. case FDC_POSX:
  154. case FDC_POSY:
  155. return aTypeName == wxGRID_VALUE_STRING;
  156. case FDC_SHOWN:
  157. case FDC_ITALIC:
  158. case FDC_BOLD:
  159. return aTypeName == wxGRID_VALUE_BOOL;
  160. default:
  161. wxFAIL;
  162. return false;
  163. }
  164. }
  165. template <class T>
  166. bool FIELDS_GRID_TABLE<T>::CanSetValueAs( int aRow, int aCol, const wxString& aTypeName )
  167. {
  168. return CanGetValueAs( aRow, aCol, aTypeName );
  169. }
  170. template <class T>
  171. wxGridCellAttr* FIELDS_GRID_TABLE<T>::GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind )
  172. {
  173. switch( aCol )
  174. {
  175. case FDC_NAME:
  176. if( aRow < MANDATORY_FIELDS )
  177. {
  178. m_readOnlyAttr->IncRef();
  179. return m_readOnlyAttr;
  180. }
  181. else
  182. {
  183. m_fieldNameAttr->IncRef();
  184. return m_fieldNameAttr;
  185. }
  186. case FDC_VALUE:
  187. if( aRow == REFERENCE )
  188. {
  189. m_referenceAttr->IncRef();
  190. return m_referenceAttr;
  191. }
  192. else if( aRow == VALUE )
  193. {
  194. // For power symbols, the value is not editable, because value and pin name must
  195. // be the same and can be edited only in library editor.
  196. if( m_part && m_part->IsPower() && ! m_frame->IsType( FRAME_SCH_LIB_EDITOR ) )
  197. {
  198. m_readOnlyAttr->IncRef();
  199. return m_readOnlyAttr;
  200. }
  201. else
  202. {
  203. m_valueAttr->IncRef();
  204. return m_valueAttr;
  205. }
  206. }
  207. else if( aRow == FOOTPRINT )
  208. {
  209. m_footprintAttr->IncRef();
  210. return m_footprintAttr;
  211. }
  212. else if( aRow == DATASHEET )
  213. {
  214. m_urlAttr->IncRef();
  215. return m_urlAttr;
  216. }
  217. else
  218. {
  219. wxString fieldname = GetValue( aRow, FDC_NAME );
  220. const TEMPLATE_FIELDNAME* templateFn = m_frame->GetTemplateFieldName( fieldname );
  221. if( templateFn && templateFn->m_URL )
  222. {
  223. m_urlAttr->IncRef();
  224. return m_urlAttr;
  225. }
  226. else
  227. {
  228. m_nonUrlAttr->IncRef();
  229. return m_nonUrlAttr;
  230. }
  231. }
  232. return nullptr;
  233. case FDC_TEXT_SIZE:
  234. case FDC_POSX:
  235. case FDC_POSY:
  236. return nullptr;
  237. case FDC_H_ALIGN:
  238. m_hAlignAttr->IncRef();
  239. return m_hAlignAttr;
  240. case FDC_V_ALIGN:
  241. m_vAlignAttr->IncRef();
  242. return m_vAlignAttr;
  243. case FDC_ORIENTATION:
  244. m_orientationAttr->IncRef();
  245. return m_orientationAttr;
  246. case FDC_SHOWN:
  247. case FDC_ITALIC:
  248. case FDC_BOLD:
  249. m_boolAttr->IncRef();
  250. return m_boolAttr;
  251. default:
  252. wxFAIL;
  253. return nullptr;
  254. }
  255. }
  256. template <class T>
  257. wxString FIELDS_GRID_TABLE<T>::GetValue( int aRow, int aCol )
  258. {
  259. wxCHECK( aRow < GetNumberRows(), wxEmptyString );
  260. const T& field = this->at( (size_t) aRow );
  261. switch( aCol )
  262. {
  263. case FDC_NAME:
  264. // Use default field name for mandatory fields, because they are translated
  265. // according to the current locale
  266. if( aRow < MANDATORY_FIELDS )
  267. return TEMPLATE_FIELDNAME::GetDefaultFieldName( aRow );
  268. else
  269. return field.GetName( false );
  270. case FDC_VALUE:
  271. return field.GetText();
  272. case FDC_SHOWN:
  273. return StringFromBool( field.IsVisible() );
  274. case FDC_H_ALIGN:
  275. switch ( field.GetHorizJustify() )
  276. {
  277. case GR_TEXT_HJUSTIFY_LEFT: return _( "Left" );
  278. case GR_TEXT_HJUSTIFY_CENTER: return _( "Center" );
  279. case GR_TEXT_HJUSTIFY_RIGHT: return _( "Right" );
  280. }
  281. break;
  282. case FDC_V_ALIGN:
  283. switch ( field.GetVertJustify() )
  284. {
  285. case GR_TEXT_VJUSTIFY_TOP: return _( "Top" );
  286. case GR_TEXT_VJUSTIFY_CENTER: return _( "Center" );
  287. case GR_TEXT_VJUSTIFY_BOTTOM: return _( "Bottom" );
  288. }
  289. break;
  290. case FDC_ITALIC:
  291. return StringFromBool( field.IsItalic() );
  292. case FDC_BOLD:
  293. return StringFromBool( field.IsBold() );
  294. case FDC_TEXT_SIZE:
  295. return StringFromValue( m_userUnits, field.GetTextSize().GetHeight(), true, true );
  296. case FDC_ORIENTATION:
  297. switch ( (int) field.GetTextAngle() )
  298. {
  299. case TEXT_ANGLE_HORIZ: return _( "Horizontal" );
  300. case TEXT_ANGLE_VERT: return _( "Vertical" );
  301. }
  302. break;
  303. case FDC_POSX:
  304. return StringFromValue( m_userUnits, field.GetTextPos().x, true );
  305. case FDC_POSY:
  306. return StringFromValue( m_userUnits, field.GetTextPos().y, true );
  307. default:
  308. // we can't assert here because wxWidgets sometimes calls this without checking
  309. // the column type when trying to see if there's an overflow
  310. break;
  311. }
  312. return wxT( "bad wxWidgets!" );
  313. }
  314. template <class T>
  315. bool FIELDS_GRID_TABLE<T>::GetValueAsBool( int aRow, int aCol )
  316. {
  317. wxCHECK( aRow < GetNumberRows(), false );
  318. const T& field = this->at( (size_t) aRow );
  319. switch( aCol )
  320. {
  321. case FDC_SHOWN: return field.IsVisible();
  322. case FDC_ITALIC: return field.IsItalic();
  323. case FDC_BOLD: return field.IsBold();
  324. default:
  325. wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a bool value" ), aCol ) );
  326. return false;
  327. }
  328. }
  329. template <class T>
  330. void FIELDS_GRID_TABLE<T>::SetValue( int aRow, int aCol, const wxString &aValue )
  331. {
  332. wxCHECK( aRow < GetNumberRows(), /*void*/ );
  333. T& field = this->at( (size_t) aRow );
  334. wxPoint pos;
  335. switch( aCol )
  336. {
  337. case FDC_NAME:
  338. field.SetName( aValue );
  339. break;
  340. case FDC_VALUE:
  341. field.SetText( aValue );
  342. break;
  343. case FDC_SHOWN:
  344. field.SetVisible( BoolFromString( aValue ) );
  345. break;
  346. case FDC_H_ALIGN:
  347. if( aValue == _( "Left" ) )
  348. field.SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  349. else if( aValue == _( "Center" ) )
  350. field.SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
  351. else if( aValue == _( "Right" ) )
  352. field.SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
  353. else
  354. wxFAIL_MSG( wxT( "unknown horizontal alignment: " ) + aValue );
  355. break;
  356. case FDC_V_ALIGN:
  357. if( aValue == _( "Top" ) )
  358. field.SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
  359. else if( aValue == _( "Center" ) )
  360. field.SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
  361. else if( aValue == _( "Bottom" ) )
  362. field.SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
  363. else
  364. wxFAIL_MSG( wxT( "unknown vertical alignment: " ) + aValue);
  365. break;
  366. case FDC_ITALIC:
  367. field.SetItalic( BoolFromString( aValue ) );
  368. break;
  369. case FDC_BOLD:
  370. field.SetBold( BoolFromString( aValue ) );
  371. break;
  372. case FDC_TEXT_SIZE:
  373. field.SetTextSize( wxSize( ValueFromString( m_userUnits, aValue ),
  374. ValueFromString( m_userUnits, aValue ) ) );
  375. break;
  376. case FDC_ORIENTATION:
  377. if( aValue == _( "Horizontal" ) )
  378. field.SetTextAngle( TEXT_ANGLE_HORIZ );
  379. else if( aValue == _( "Vertical" ) )
  380. field.SetTextAngle( TEXT_ANGLE_VERT );
  381. else
  382. wxFAIL_MSG( wxT( "unknown orientation: " ) + aValue );
  383. break;
  384. case FDC_POSX:
  385. case FDC_POSY:
  386. pos = field.GetTextPos();
  387. if( aCol == FDC_POSX )
  388. pos.x = ValueFromString( m_userUnits, aValue );
  389. else
  390. pos.y = ValueFromString( m_userUnits, aValue );
  391. field.SetTextPos( pos );
  392. break;
  393. default:
  394. wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a string value" ), aCol ) );
  395. break;
  396. }
  397. GetView()->Refresh();
  398. }
  399. template <class T>
  400. void FIELDS_GRID_TABLE<T>::SetValueAsBool( int aRow, int aCol, bool aValue )
  401. {
  402. wxCHECK( aRow < GetNumberRows(), /*void*/ );
  403. T& field = this->at( (size_t) aRow );
  404. switch( aCol )
  405. {
  406. case FDC_SHOWN:
  407. field.SetVisible( aValue );
  408. break;
  409. case FDC_ITALIC:
  410. field.SetItalic( aValue );
  411. break;
  412. case FDC_BOLD:
  413. field.SetBold( aValue );
  414. break;
  415. default:
  416. wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a bool value" ), aCol ) );
  417. break;
  418. }
  419. }
  420. // Explicit Instantiations
  421. template class FIELDS_GRID_TABLE<SCH_FIELD>;
  422. template class FIELDS_GRID_TABLE<LIB_FIELD>;
  423. void FIELDS_GRID_TRICKS::showPopupMenu( wxMenu& menu )
  424. {
  425. if( m_grid->GetGridCursorRow() == FOOTPRINT && m_grid->GetGridCursorCol() == FDC_VALUE )
  426. {
  427. menu.Append( MYID_SELECT_FOOTPRINT, _( "Select Footprint..." ),
  428. _( "Browse for footprint" ) );
  429. menu.AppendSeparator();
  430. }
  431. else if( m_grid->GetGridCursorRow() == DATASHEET && m_grid->GetGridCursorCol() == FDC_VALUE )
  432. {
  433. menu.Append( MYID_SHOW_DATASHEET, _( "Show Datasheet" ),
  434. _( "Show datasheet in browser" ) );
  435. menu.AppendSeparator();
  436. }
  437. GRID_TRICKS::showPopupMenu( menu );
  438. }
  439. void FIELDS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
  440. {
  441. if( event.GetId() == MYID_SELECT_FOOTPRINT )
  442. {
  443. // pick a footprint using the footprint picker.
  444. wxString fpid = m_grid->GetCellValue( FOOTPRINT, FDC_VALUE );
  445. KIWAY_PLAYER* frame = m_dlg->Kiway().Player( FRAME_PCB_MODULE_VIEWER_MODAL, true, m_dlg );
  446. if( frame->ShowModal( &fpid, m_dlg ) )
  447. m_grid->SetCellValue( FOOTPRINT, FDC_VALUE, fpid );
  448. frame->Destroy();
  449. }
  450. else if (event.GetId() == MYID_SHOW_DATASHEET )
  451. {
  452. wxString datasheet_uri = m_grid->GetCellValue( DATASHEET, FDC_VALUE );
  453. GetAssociatedDocument( m_dlg, datasheet_uri );
  454. }
  455. else
  456. {
  457. GRID_TRICKS::doPopupSelection( event );
  458. }
  459. }
  460. template <class T>
  461. wxString FIELDS_GRID_TABLE<T>::StringFromBool( bool aValue )
  462. {
  463. if( aValue )
  464. return wxT( "1" );
  465. else
  466. return wxT( "0" );
  467. }
  468. template <class T>
  469. bool FIELDS_GRID_TABLE<T>::BoolFromString( wxString aValue )
  470. {
  471. if( aValue == "1" )
  472. {
  473. return true;
  474. }
  475. else if( aValue == "0" )
  476. {
  477. return false;
  478. }
  479. else
  480. {
  481. wxFAIL_MSG( wxString::Format( "string \"%s\" can't be converted to boolean "
  482. "correctly, it will have been perceived as FALSE", aValue ) );
  483. return false;
  484. }
  485. }