KiCad PCB EDA Suite
dialog_edit_components_libid.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright 2017 Jean-Pierre Charras, jp.charras@wanadoo.fr
5  * Copyright 1992-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 
31 #include <fctsys.h>
32 #include <confirm.h>
33 #include <sch_edit_frame.h>
34 #include <sch_draw_panel.h>
35 #include <sch_component.h>
36 #include <sch_reference_list.h>
37 #include <schematic.h>
38 #include <pgm_base.h>
39 #include <symbol_lib_table.h>
40 #include <trace_helpers.h>
41 #include <widgets/wx_grid.h>
42 
44 #include <wx/tokenzr.h>
45 #include <grid_tricks.h>
47 
48 #define COL_REFS 0
49 #define COL_CURR_LIBID 1
50 #define COL_NEW_LIBID 2
51 
52 // a re-implementation of wxGridCellAutoWrapStringRenderer to allow workaround to autorowsize bug
53 class GRIDCELL_AUTOWRAP_STRINGRENDERER : public wxGridCellAutoWrapStringRenderer
54 {
55 public:
56  int GetHeight( wxDC& aDC, wxGrid* aGrid, int aRow, int aCol );
57 
58  wxGridCellRenderer *Clone() const override
59  { return new GRIDCELL_AUTOWRAP_STRINGRENDERER; }
60 
61 private:
62  // HELPER ROUTINES UNCHANGED FROM wxWidgets IMPLEMENTATION
63 
64  wxArrayString GetTextLines( wxGrid& grid,
65  wxDC& dc,
66  const wxGridCellAttr& attr,
67  const wxRect& rect,
68  int row, int col);
69 
70  // Helper methods of GetTextLines()
71 
72  // Break a single logical line of text into several physical lines, all of
73  // which are added to the lines array. The lines are broken at maxWidth and
74  // the dc is used for measuring text extent only.
75  void BreakLine(wxDC& dc,
76  const wxString& logicalLine,
77  wxCoord maxWidth,
78  wxArrayString& lines);
79 
80  // Break a word, which is supposed to be wider than maxWidth, into several
81  // lines, which are added to lines array and the last, incomplete, of which
82  // is returned in line output parameter.
83  //
84  // Returns the width of the last line.
85  wxCoord BreakWord(wxDC& dc,
86  const wxString& word,
87  wxCoord maxWidth,
88  wxArrayString& lines,
89  wxString& line);
90 };
91 
92 
93 // PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
94 wxArrayString
96  wxDC& dc,
97  const wxGridCellAttr& attr,
98  const wxRect& rect,
99  int row, int col)
100 {
101  dc.SetFont(attr.GetFont());
102  const wxCoord maxWidth = rect.GetWidth();
103 
104  // Transform logical lines into physical ones, wrapping the longer ones.
105  const wxArrayString
106  logicalLines = wxSplit(grid.GetCellValue(row, col), '\n', '\0');
107 
108  // Trying to do anything if the column is hidden anyhow doesn't make sense
109  // and we run into problems in BreakLine() in this case.
110  if ( maxWidth <= 0 )
111  return logicalLines;
112 
113  wxArrayString physicalLines;
114  for ( wxArrayString::const_iterator it = logicalLines.begin();
115  it != logicalLines.end();
116  ++it )
117  {
118  const wxString& line = *it;
119 
120  if ( dc.GetTextExtent(line).x > maxWidth )
121  {
122  // Line does not fit, break it up.
123  BreakLine(dc, line, maxWidth, physicalLines);
124  }
125  else // The entire line fits as is
126  {
127  physicalLines.push_back(line);
128  }
129  }
130 
131  return physicalLines;
132 }
133 
134 
135 // PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
136 void
138  const wxString& logicalLine,
139  wxCoord maxWidth,
140  wxArrayString& lines)
141 {
142  wxCoord lineWidth = 0;
143  wxString line;
144 
145  // For each word
146  wxStringTokenizer wordTokenizer(logicalLine, wxS(" \t"), wxTOKEN_RET_DELIMS);
147  while ( wordTokenizer.HasMoreTokens() )
148  {
149  const wxString word = wordTokenizer.GetNextToken();
150  const wxCoord wordWidth = dc.GetTextExtent(word).x;
151  if ( lineWidth + wordWidth < maxWidth )
152  {
153  // Word fits, just add it to this line.
154  line += word;
155  lineWidth += wordWidth;
156  }
157  else
158  {
159  // Word does not fit, check whether the word is itself wider that
160  // available width
161  if ( wordWidth < maxWidth )
162  {
163  // Word can fit in a new line, put it at the beginning
164  // of the new line.
165  lines.push_back(line);
166  line = word;
167  lineWidth = wordWidth;
168  }
169  else // Word cannot fit in available width at all.
170  {
171  if ( !line.empty() )
172  {
173  lines.push_back(line);
174  line.clear();
175  lineWidth = 0;
176  }
177 
178  // Break it up in several lines.
179  lineWidth = BreakWord(dc, word, maxWidth, lines, line);
180  }
181  }
182  }
183 
184  if ( !line.empty() )
185  lines.push_back(line);
186 }
187 
188 
189 // PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
190 wxCoord
192  const wxString& word,
193  wxCoord maxWidth,
194  wxArrayString& lines,
195  wxString& line)
196 {
197  wxArrayInt widths;
198  dc.GetPartialTextExtents(word, widths);
199 
200  // TODO: Use binary search to find the first element > maxWidth.
201  const unsigned count = widths.size();
202  unsigned n;
203  for ( n = 0; n < count; n++ )
204  {
205  if ( widths[n] > maxWidth )
206  break;
207  }
208 
209  if ( n == 0 )
210  {
211  // This is a degenerate case: the first character of the word is
212  // already wider than the available space, so we just can't show it
213  // completely and have to put the first character in this line.
214  n = 1;
215  }
216 
217  lines.push_back(word.substr(0, n));
218 
219  // Check if the remainder of the string fits in one line.
220  //
221  // Unfortunately we can't use the existing partial text extents as the
222  // extent of the remainder may be different when it's rendered in a
223  // separate line instead of as part of the same one, so we have to
224  // recompute it.
225  const wxString rest = word.substr(n);
226  const wxCoord restWidth = dc.GetTextExtent(rest).x;
227  if ( restWidth <= maxWidth )
228  {
229  line = rest;
230  return restWidth;
231  }
232 
233  // Break the rest of the word into lines.
234  //
235  // TODO: Perhaps avoid recursion? The code is simpler like this but using a
236  // loop in this function would probably be more efficient.
237  return BreakWord(dc, rest, maxWidth, lines, line);
238 }
239 
240 
241 #define GRID_CELL_MARGIN 4
242 
243 int GRIDCELL_AUTOWRAP_STRINGRENDERER::GetHeight( wxDC& aDC, wxGrid* aGrid, int aRow, int aCol )
244 {
245  wxGridCellAttr* attr = aGrid->GetOrCreateCellAttr( aRow, aCol );
246  wxRect rect;
247 
248  aDC.SetFont( attr->GetFont() );
249  rect.SetWidth( aGrid->GetColSize( aCol ) - ( 2 * GRID_CELL_MARGIN ) );
250 
251  const size_t numLines = GetTextLines( *aGrid, aDC, *attr, rect, aRow, aCol ).size();
252  const int textHeight = numLines * aDC.GetCharHeight();
253 
254  attr->DecRef();
255 
256  return textHeight + ( 2 * GRID_CELL_MARGIN );
257 }
258 
259 
260 // a helper class to handle components to edit
262 {
263 public:
264  SCH_COMPONENT* m_Component; // the schematic component
265  int m_Row; // the row index in m_grid
266  SCH_SCREEN* m_Screen; // the screen where m_Component lives
267  wxString m_Reference; // the schematic reference, only to display it in list
268  wxString m_InitialLibId; // the Lib Id of the component before any change
269  bool m_IsOrphan; // true if a component has no corresponding symbol found in libs
270 
272  {
273  m_Component = aComponent;
275  m_Row = -1;
276  m_IsOrphan = false;
277  m_Screen = nullptr;
278  }
279 
280  // Returns a string like mylib:symbol_name from the LIB_ID of the component
281  wxString GetStringLibId()
282  {
284  }
285 
286  // Returns a string containing the reference of the component
288  {
289  return m_Reference;
290  }
291 };
292 
293 
303 {
304 public:
307 
309 
311 
312 private:
313  bool m_isModified; // set to true if the schematic is modified
314  std::vector<int> m_OrphansRowIndexes; // list of rows containing orphan lib_id
315 
316  std::vector<CMP_CANDIDATE> m_components;
317 
319 
320  void initDlg();
321 
328  void AddRowToGrid( bool aMarkRow, const wxString& aReferences, const wxString& aStrLibId );
329 
331  bool validateLibIds();
332 
334  void revertChanges();
335 
340  bool setLibIdByBrowser( int aRow );
341 
342  // Events handlers
343 
344  // called on a right click or a left double click:
345  void onCellBrowseLib( wxGridEvent& event ) override;
346 
347  // Apply changes, but do not close the dialog
348  void onApplyButton( wxCommandEvent& event ) override;
349 
350  // Cancel all changes, and close the dialog
351  void onCancel( wxCommandEvent& event ) override
352  {
353  if( m_isModified )
354  revertChanges();
355 
356  // Just skipping the event doesn't work after the library browser was run
357  if( IsQuasiModal() )
358  EndQuasiModal( wxID_CANCEL );
359  else
360  event.Skip();
361  }
362 
363  // Undo all changes, and clear the list of new lib_ids
364  void onUndoChangesButton( wxCommandEvent& event ) override;
365 
366  // Try to find a candidate for non existing symbols
367  void onClickOrphansButton( wxCommandEvent& event ) override;
368 
369  // UI event, to enable/disable buttons
370  void updateUIChangesButton( wxUpdateUIEvent& event ) override
371  {
372  m_buttonUndo->Enable( m_isModified );
373  }
374 
375  // Automatically called when click on OK button
376  bool TransferDataFromWindow() override;
377 
378  void AdjustGridColumns( int aWidth );
379 
380  void OnSizeGrid( wxSizeEvent& event ) override;
381 };
382 
383 
386 {
388 
389  m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
390 
391  initDlg();
392 
394 }
395 
396 
398 {
399  // Delete the GRID_TRICKS.
400  m_grid->PopEventHandler( true );
401 
402  m_autoWrapRenderer->DecRef();
403 }
404 
405 
406 // A sort compare function to sort components list by LIB_ID and then reference
407 static bool sort_by_libid( const CMP_CANDIDATE& cmp1, const CMP_CANDIDATE& cmp2 )
408 {
409  if( cmp1.m_Component->GetLibId() == cmp2.m_Component->GetLibId() )
410  return cmp1.m_Reference.Cmp( cmp2.m_Reference ) < 0;
411 
412  return cmp1.m_Component->GetLibId() < cmp2.m_Component->GetLibId();
413 }
414 
415 
417 {
418  // Clear the FormBuilder rows
419  m_grid->DeleteRows( 0, m_grid->GetNumberRows() );
420 
421  m_isModified = false;
422 
423  // This option build the full component list
424  // In complex hierarchies, the same component is in fact duplicated, but
425  // it is listed with different references (one by sheet instance)
426  // the list is larger and looks like it contains all components
427  const SCH_SHEET_LIST& sheets = GetParent()->Schematic().GetSheets();
428  SCH_REFERENCE_LIST references;
429  // build the full list of components including component having no symbol in loaded libs
430  // (orphan components)
431  sheets.GetComponents( references, /* include power symbols */true,
432  /* include orphan components */true );
433 
434  for( unsigned ii = 0; ii < references.GetCount(); ii++ )
435  {
436  SCH_REFERENCE& item = references[ii];
437  CMP_CANDIDATE candidate( item.GetComp() );
438  candidate.m_Screen = item.GetSheetPath().LastScreen();
439  SCH_SHEET_PATH sheetpath = item.GetSheetPath();
440  candidate.m_Reference = candidate.m_Component->GetRef( &sheetpath );
441  int unitcount = candidate.m_Component->GetUnitCount();
442  candidate.m_IsOrphan = ( unitcount == 0 );
443  m_components.push_back( candidate );
444  }
445 
446  if( m_components.size() == 0 )
447  return;
448 
449  // now sort by lib id to create groups of items having the same lib id
450  std::sort( m_components.begin(), m_components.end(), sort_by_libid );
451 
452  // Now, fill m_grid
453  wxString last_str_libid = m_components.front().GetStringLibId();
454  int row = 0;
455  wxString refs;
456  wxString last_ref;
457  bool mark_cell = m_components.front().m_IsOrphan;
458 
459  for( auto& cmp : m_components )
460  {
461  wxString str_libid = cmp.GetStringLibId();
462 
463  if( last_str_libid != str_libid )
464  {
465  // Add last group to grid
466  AddRowToGrid( mark_cell, refs, last_str_libid );
467 
468  // prepare next entry
469  mark_cell = cmp.m_IsOrphan;
470  last_str_libid = str_libid;
471  refs.Empty();
472  row++;
473  }
474  else if( cmp.GetSchematicReference() == last_ref )
475  {
476  cmp.m_Row = row;
477  continue;
478  }
479 
480  last_ref = cmp.GetSchematicReference();
481 
482  if( !refs.IsEmpty() )
483  refs += wxT( ", " );
484 
485  refs += cmp.GetSchematicReference();
486  cmp.m_Row = row;
487  }
488 
489  // Add last component group:
490  AddRowToGrid( mark_cell, refs, last_str_libid );
491 
492  // Allows only the selection by row
493  m_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
494 
495  m_buttonOrphanItems->Enable( m_OrphansRowIndexes.size() > 0 );
496  Layout();
497 }
498 
499 
501 {
502  return dynamic_cast<SCH_EDIT_FRAME*>( wxDialog::GetParent() );
503 }
504 
505 
506 void DIALOG_EDIT_COMPONENTS_LIBID::AddRowToGrid( bool aMarkRow, const wxString& aReferences,
507  const wxString& aStrLibId )
508 {
509  int row = m_grid->GetNumberRows();
510 
511  if( aMarkRow ) // a orphan component exists, set m_AsOrphanCmp as true
512  m_OrphansRowIndexes.push_back( row );
513 
514  m_grid->AppendRows( 1 );
515 
516  m_grid->SetCellValue( row, COL_REFS, aReferences );
517  m_grid->SetReadOnly( row, COL_REFS );
518 
519  m_grid->SetCellValue( row, COL_CURR_LIBID, aStrLibId );
520  m_grid->SetReadOnly( row, COL_CURR_LIBID );
521 
522  if( aMarkRow ) // A symbol is not existing in libraries: mark the cell
523  {
524  wxFont font = m_grid->GetDefaultCellFont();
525  font.MakeBold();
526  font.MakeItalic();
527  m_grid->SetCellFont( row, COL_CURR_LIBID, font );
528  }
529 
530  m_grid->SetCellRenderer( row, COL_REFS, m_autoWrapRenderer->Clone() );
531 
532  // wxWidgets' AutoRowHeight fails when used with wxGridCellAutoWrapStringRenderer
533  // (fixed in 2014, but didn't get in to wxWidgets 3.0.2)
534  wxClientDC dc( this );
535  m_grid->SetRowSize( row, m_autoWrapRenderer->GetHeight( dc, m_grid, row, COL_REFS ) );
536 
537  // set new libid column browse button
538  wxGridCellAttr* attr = new wxGridCellAttr;
539  attr->SetEditor( new GRID_CELL_SYMBOL_ID_EDITOR( this, aStrLibId ) );
540  m_grid->SetAttr( row, COL_NEW_LIBID, attr );
541 }
542 
543 
545 {
546  if( !m_grid->CommitPendingChanges() )
547  return false;
548 
549  int row_max = m_grid->GetNumberRows() - 1;
550 
551  for( int row = 0; row <= row_max; row++ )
552  {
553  wxString new_libid = m_grid->GetCellValue( row, COL_NEW_LIBID );
554 
555  if( new_libid.IsEmpty() )
556  continue;
557 
558  // a new lib id is found. validate this new value
559  LIB_ID id;
560  id.Parse( new_libid, LIB_ID::ID_SCH );
561 
562  if( !id.IsValid() )
563  {
564  wxString msg;
565  msg.Printf( _( "Symbol library identifier \"%s\" is not valid." ), new_libid );
566  wxMessageBox( msg );
567 
568  m_grid->SetFocus();
569  m_grid->MakeCellVisible( row, COL_NEW_LIBID );
570  m_grid->SetGridCursor( row, COL_NEW_LIBID );
571 
572  m_grid->EnableCellEditControl( true );
573  m_grid->ShowCellEditControl();
574 
575  return false;
576  }
577  }
578 
579  return true;
580 }
581 
582 
583 void DIALOG_EDIT_COMPONENTS_LIBID::onApplyButton( wxCommandEvent& event )
584 {
585  if( TransferDataFromWindow() )
586  GetParent()->GetCanvas()->Refresh();
587 }
588 
589 
591 {
592  revertChanges();
593 
594  int row_max = m_grid->GetNumberRows() - 1;
595 
596  for( int row = 0; row <= row_max; row++ )
597  {
598  m_grid->SetCellValue( row, COL_NEW_LIBID, wxEmptyString );
599  }
600 
601  m_isModified = false;
602 }
603 
604 
606 {
607  int row = event.GetRow();
608  m_grid->SelectRow( row ); // only for user, to show the selected line
609 
610  setLibIdByBrowser( row );
611 
612 }
613 
614 
616 {
617  std::vector< wxString > libs = Prj().SchSymbolLibTable()->GetLogicalLibs();
618  wxArrayString aliasNames;
619  wxArrayString candidateSymbNames;
620 
621  unsigned fixesCount = 0;
622 
623  // Try to find a candidate for non existing symbols in any loaded library
624  for( unsigned ii = 0; ii < m_OrphansRowIndexes.size(); ii++ )
625  {
626  wxString orphanLibid = m_grid->GetCellValue( m_OrphansRowIndexes[ii], COL_CURR_LIBID );
627  int grid_row_idx = m_OrphansRowIndexes[ii]; //row index in m_grid for the current item
628 
629  LIB_ID curr_libid;
630  curr_libid.Parse( orphanLibid, LIB_ID::ID_SCH, true );
631  wxString symbName = curr_libid.GetLibItemName();
632  // number of full LIB_ID candidates (because we search for a symbol name
633  // inside all avaiable libraries, perhaps the same symbol name can be found
634  // in more than one library, giving ambiguity
635  int libIdCandidateCount = 0;
636  candidateSymbNames.Clear();
637 
638  // now try to find a candidate
639  for( auto &lib : libs )
640  {
641  aliasNames.Clear();
642 
643  try
644  {
645  Prj().SchSymbolLibTable()->EnumerateSymbolLib( lib, aliasNames );
646  }
647  catch( const IO_ERROR& ) {} // ignore, it is handled below
648 
649  if( aliasNames.IsEmpty() )
650  continue;
651 
652  // Find a symbol name in symbols inside this library:
653  int index = aliasNames.Index( symbName );
654 
655  if( index != wxNOT_FOUND )
656  {
657  // a candidate is found!
658  libIdCandidateCount++;
659  wxString newLibid = lib + ':' + symbName;
660 
661  // Uses the first found. Most of time, it is alone.
662  // Others will be stored in a candidate list
663  if( libIdCandidateCount <= 1 )
664  {
665  m_grid->SetCellValue( grid_row_idx, COL_NEW_LIBID, newLibid );
666  candidateSymbNames.Add( m_grid->GetCellValue( grid_row_idx, COL_NEW_LIBID ) );
667  fixesCount++;
668  }
669  else // Store other candidates for later selection
670  {
671  candidateSymbNames.Add( newLibid );
672  }
673  }
674  }
675 
676  // If more than one LIB_ID candidate, ask for selection between candidates:
677  if( libIdCandidateCount > 1 )
678  {
679  // Mainly for user: select the row being edited
680  m_grid->SelectRow( grid_row_idx );
681 
682  wxString msg;
683  msg.Printf( _( "Available Candidates for %s " ),
684  m_grid->GetCellValue( grid_row_idx, COL_CURR_LIBID ) );
685 
686  wxSingleChoiceDialog dlg ( this, msg,
687  wxString::Format( _( "Candidates count %d " ), libIdCandidateCount ),
688  candidateSymbNames );
689 
690  if( dlg.ShowModal() == wxID_OK )
691  m_grid->SetCellValue( grid_row_idx, COL_NEW_LIBID, dlg.GetStringSelection() );
692  }
693  }
694 
695  if( fixesCount < m_OrphansRowIndexes.size() ) // Not all orphan components are fixed
696  {
697  wxMessageBox( wxString::Format( _( "%u link(s) mapped, %u not found" ),
698  fixesCount,
699  (unsigned) m_OrphansRowIndexes.size() - fixesCount ) );
700  }
701  else
702  wxMessageBox( wxString::Format( _( "All %u link(s) resolved" ), fixesCount ) );
703 }
704 
705 
707 {
708 #if 0
709  // Use dialog symbol selector to choose a symbol
710  SCH_BASE_FRAME::HISTORY_LIST dummy;
711  SCH_BASE_FRAME::COMPONENT_SELECTION sel =
712  m_frame->SelectComponentFromLibrary( NULL, dummy, true, 0, 0, false );
713 #else
714  // Use library viewer to choose a symbol
715  LIB_ID aPreselectedLibid;
716  wxString current = m_grid->GetCellValue( aRow, COL_NEW_LIBID );
717 
718  if( current.IsEmpty() )
719  current = m_grid->GetCellValue( aRow, COL_CURR_LIBID );
720 
721  if( !current.IsEmpty() )
722  aPreselectedLibid.Parse( current, LIB_ID::ID_SCH, true );
723 
724  COMPONENT_SELECTION sel =
725  GetParent()->SelectComponentFromLibBrowser( this, NULL, aPreselectedLibid, 0, 0 );
726 #endif
727 
728  if( sel.LibId.empty() ) // command aborted
729  return false;
730 
731  if( !sel.LibId.IsValid() ) // Should not occur
732  {
733  wxMessageBox( _( "Invalid symbol library identifier" ) );
734  return false;
735  }
736 
737  wxString new_libid;
738  new_libid = sel.LibId.Format();
739 
740  m_grid->SetCellValue( aRow, COL_NEW_LIBID, new_libid );
741 
742  return true;
743 }
744 
745 
747 {
748  if( !validateLibIds() )
749  return false;
750 
751  bool change = false;
752  int row_max = m_grid->GetNumberRows() - 1;
753 
754  for( int row = 0; row <= row_max; row++ )
755  {
756  wxString new_libid = m_grid->GetCellValue( row, COL_NEW_LIBID );
757 
758  if( new_libid.IsEmpty() || new_libid == m_grid->GetCellValue( row, COL_CURR_LIBID ) )
759  continue;
760 
761  // A new lib id is found and was already validated.
762  LIB_ID id;
763  id.Parse( new_libid, LIB_ID::ID_SCH, true );
764 
765  for( CMP_CANDIDATE& cmp : m_components )
766  {
767  if( cmp.m_Row != row )
768  continue;
769 
770  LIB_PART* symbol = nullptr;
771 
772  try
773  {
774  symbol = Prj().SchSymbolLibTable()->LoadSymbol( id );
775  }
776  catch( const IO_ERROR& ioe )
777  {
778  wxString msg;
779 
780  msg.Printf( _( "Error occurred loading symbol %s from library %s."
781  "\n\n%s" ),
782  id.GetLibItemName().wx_str(),
783  id.GetLibNickname().wx_str(),
784  ioe.What() );
785 
786  DisplayError( this, msg );
787  }
788 
789  if( symbol == nullptr )
790  continue;
791 
792  cmp.m_Screen->Remove( cmp.m_Component );
793  SCH_FIELD* value = cmp.m_Component->GetField( VALUE );
794 
795  // If value is a proxy for the itemName then make sure it gets updated
796  if( cmp.m_Component->GetLibId().GetLibItemName().wx_str() == value->GetText() )
797  value->SetText( id.GetLibItemName().wx_str() );
798 
799  cmp.m_Component->SetLibId( id );
800  cmp.m_Component->SetLibSymbol( symbol->Flatten().release() );
801  change = true;
802  cmp.m_Screen->Append( cmp.m_Component );
803  cmp.m_Screen->SetModify();
804  }
805  }
806 
807  if( change )
808  {
809  m_isModified = true;
810  }
811 
812  return true;
813 }
814 
815 
817 {
818  bool change = false;
819  int row_max = m_grid->GetNumberRows() - 1;
820 
821  for( int row = 0; row <= row_max; row++ )
822  {
823  for( CMP_CANDIDATE& cmp : m_components )
824  {
825  if( cmp.m_Row != row )
826  continue;
827 
828  LIB_ID id;
829  id.Parse( cmp.m_InitialLibId, LIB_ID::ID_SCH, true );
830 
831  if( cmp.m_Component->GetLibId() != id )
832  {
833  LIB_PART* symbol = nullptr;
834 
835  try
836  {
837  symbol = Prj().SchSymbolLibTable()->LoadSymbol( id );
838  }
839  catch( const IO_ERROR& ioe )
840  {
841  // It's probably a bad idea to show a user error dialog here because the
842  // the reason to use this dialog is when there are a bunch of broken library
843  // symbol links so just show a debug trace message for development purposes.
844  wxLogTrace( traceSymbolResolver,
845  "Error occurred loading symbol %s from library %s."
846  "\n\n%s",
847  id.GetLibItemName().wx_str(),
848  id.GetLibNickname().wx_str(),
849  ioe.What() );
850  }
851 
852  cmp.m_Screen->Remove( cmp.m_Component );
853 
854  if( symbol )
855  cmp.m_Component->SetLibSymbol( symbol->Flatten().release() );
856  else
857  cmp.m_Component->SetLibSymbol( symbol );
858 
859  cmp.m_Component->SetLibId( id );
860  cmp.m_Screen->Append( cmp.m_Component );
861  change = true;
862  }
863  }
864  }
865 
866  if( change )
867  GetParent()->GetCanvas()->Refresh();
868 }
869 
870 
872 {
873  // Account for scroll bars
874  aWidth -= ( m_grid->GetSize().x - m_grid->GetClientSize().x );
875 
876  int colWidth = aWidth / 3;
877 
878  m_grid->SetColSize( COL_REFS, colWidth );
879  aWidth -= colWidth;
880 
881  colWidth = 0;
882  for( int row = 0; row < m_grid->GetNumberRows(); ++row )
883  {
884  wxString cellValue = m_grid->GetCellValue( row, COL_CURR_LIBID );
885  colWidth = std::max( colWidth, GetTextSize( cellValue, m_grid ).x );
886  }
887 
888  colWidth += 20;
889  m_grid->SetColSize( COL_CURR_LIBID, colWidth );
890  aWidth -= colWidth;
891 
892  colWidth = 0;
893  for( int row = 0; row < m_grid->GetNumberRows(); ++row )
894  {
895  wxString cellValue = m_grid->GetCellValue( row, COL_NEW_LIBID );
896  colWidth = std::max( colWidth, GetTextSize( cellValue, m_grid ).x );
897  }
898 
899  colWidth += 20;
900  m_grid->SetColSize( COL_NEW_LIBID, std::max( colWidth, aWidth ) );
901 }
902 
903 
905 {
906  AdjustGridColumns( event.GetSize().GetX() );
907 
908  wxClientDC dc( this );
909 
910  // wxWidgets' AutoRowHeight fails when used with wxGridCellAutoWrapStringRenderer
911  for( int row = 0; row < m_grid->GetNumberRows(); ++row )
912  m_grid->SetRowSize( row, m_autoWrapRenderer->GetHeight( dc, m_grid, row, COL_REFS ) );
913 
914  event.Skip();
915 }
916 
917 
919 {
920  // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
921  // frame. Therefore this dialog as a modal frame parent, MUST be run under
922  // quasimodal mode for the quasimodal frame support to work. So don't use
923  // the QUASIMODAL macros here.
924  DIALOG_EDIT_COMPONENTS_LIBID dlg( aCaller );
925  // DO NOT use ShowModal() here, otherwise the library browser will not work
926  // properly.
927  dlg.ShowQuasiModal();
928 
929  return dlg.IsSchematicModified();
930 }
DIALOG_EDIT_COMPONENTS_LIBID(SCH_EDIT_FRAME *aParent)
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:239
SCH_SHEET_LIST.
void revertChanges()
Reverts all changes already made.
SCH_FIELD instances are attached to a component and provide a place for the component's value,...
Definition: sch_field.h:52
const UTF8 & GetLibItemName() const
Definition: lib_id.h:114
const wxChar *const traceSymbolResolver
Flag to enable debug output of symbol library resolver results.
SCH_SHEET_LIST GetSheets() const
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:92
std::vector< CMP_CANDIDATE > m_components
This file is part of the common library.
const SCH_SHEET_PATH & GetSheetPath() const
wxCoord BreakWord(wxDC &dc, const wxString &word, wxCoord maxWidth, wxArrayString &lines, wxString &line)
bool setLibIdByBrowser(int aRow)
run the lib browser and set the selected LIB_ID for row aRow
SCH_COMPONENT * GetComp() const
void OnSizeGrid(wxSizeEvent &event) override
GRIDCELL_AUTOWRAP_STRINGRENDERER * m_autoWrapRenderer
void FinishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
void BreakLine(wxDC &dc, const wxString &logicalLine, wxCoord maxWidth, wxArrayString &lines)
GRID_TRICKS is used to add mouse and command handling (such as cut, copy, and paste) to a WX_GRID ins...
Definition: grid_tricks.h:51
Schematic editor (Eeschema) main window.
wxArrayString GetTextLines(wxGrid &grid, wxDC &dc, const wxGridCellAttr &attr, const wxRect &rect, int row, int col)
#define COL_CURR_LIBID
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
bool IsValid() const
Definition: lib_id.h:171
bool empty() const
Definition: lib_id.h:186
SCH_REFERENCE_LIST is used to create a flattened list of components because in a complex hierarchy,...
wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition: common.cpp:268
CMP_CANDIDATE(SCH_COMPONENT *aComponent)
#define GRID_CELL_MARGIN
Dialog to globally edit the LIB_ID of groups if components having the same initial LIB_ID.
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:116
#define NULL
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
void GetComponents(SCH_REFERENCE_LIST &aReferences, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanComponents=false) const
Function GetComponents adds a SCH_REFERENCE() object to aReferences for each component in the list of...
int ShowQuasiModal()
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
int GetHeight(wxDC &aDC, wxGrid *aGrid, int aRow, int aCol)
static bool sort_by_libid(const CMP_CANDIDATE &cmp1, const CMP_CANDIDATE &cmp2)
void onCancel(wxCommandEvent &event) override
SCHEMATIC & Schematic() const
Define a library symbol object.
bool IsQuasiModal()
Definition: dialog_shim.h:123
bool InvokeDialogEditComponentsLibId(SCH_EDIT_FRAME *aCaller)
Run a dialog to modify the LIB_ID of components for instance when a symbol has moved from a symbol li...
wxLogTrace helper definitions.
SCH_SHEET_PATH.
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:174
std::unique_ptr< LIB_PART > Flatten() const
Return a flattened symbol inheritance to the caller.
UTF8 Format() const
Definition: lib_id.cpp:237
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=NULL) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
COMPONENT_SELECTION SelectComponentFromLibBrowser(wxTopLevelWindow *aParent, const SCHLIB_FILTER *aFilter, const LIB_ID &aPreselectedLibid, int aUnit, int aConvert)
Function SelectComponentFromLibBrowser Calls the library viewer to select component to import into sc...
Definition: getpart.cpp:45
unsigned GetCount() const
Function GetCount.
Field Value of part, i.e. "3.3K".
wxGridCellRenderer * Clone() const override
void EndQuasiModal(int retCode)
void onCellBrowseLib(wxGridEvent &event) override
void onApplyButton(wxCommandEvent &event) override
see class PGM_BASE
SCH_SCREEN * LastScreen()
Function LastScreen.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
#define _(s)
Definition: 3d_actions.cpp:33
#define COL_NEW_LIBID
void AddRowToGrid(bool aMarkRow, const wxString &aReferences, const wxString &aStrLibId)
Add a new row (new entry) in m_grid.
Schematic symbol object.
Definition: sch_component.h:88
Class DIALOG_EDIT_COMPONENTS_LIBID_BASE.
wxString GetUniStringLibId() const
Definition: lib_id.h:148
int Parse(const UTF8 &aId, LIB_ID_TYPE aType, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:122
const LIB_ID & GetLibId() const
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
void onUndoChangesButton(wxCommandEvent &event) override
void onClickOrphansButton(wxCommandEvent &event) override
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:126
SCH_REFERENCE is used as a helper to define a component's reference designator in a schematic.
void updateUIChangesButton(wxUpdateUIEvent &event) override
bool validateLibIds()
returns true if all new lib id are valid