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-2018 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 <sch_edit_frame.h>
33 #include <class_drawpanel.h>
34 #include <sch_component.h>
35 #include <sch_reference_list.h>
36 #include <pgm_base.h>
37 #include <symbol_lib_table.h>
38 #include <widgets/wx_grid.h>
39 
41 #include <wx/tokenzr.h>
42 
43 #define COL_REFS 0
44 #define COL_CURR_LIBID 1
45 #define COL_NEW_LIBID 2
46 
47 // a re-implementation of wxGridCellAutoWrapStringRenderer to allow workaround to autorowsize bug
48 class GRIDCELL_AUTOWRAP_STRINGRENDERER : public wxGridCellAutoWrapStringRenderer
49 {
50 public:
51  int GetHeight( wxDC& aDC, wxGrid* aGrid, int aRow, int aCol );
52 
53  wxGridCellRenderer *Clone() const override
54  { return new GRIDCELL_AUTOWRAP_STRINGRENDERER; }
55 
56 private:
57  // HELPER ROUTINES UNCHANGED FROM wxWidgets IMPLEMENTATION
58 
59  wxArrayString GetTextLines( wxGrid& grid,
60  wxDC& dc,
61  const wxGridCellAttr& attr,
62  const wxRect& rect,
63  int row, int col);
64 
65  // Helper methods of GetTextLines()
66 
67  // Break a single logical line of text into several physical lines, all of
68  // which are added to the lines array. The lines are broken at maxWidth and
69  // the dc is used for measuring text extent only.
70  void BreakLine(wxDC& dc,
71  const wxString& logicalLine,
72  wxCoord maxWidth,
73  wxArrayString& lines);
74 
75  // Break a word, which is supposed to be wider than maxWidth, into several
76  // lines, which are added to lines array and the last, incomplete, of which
77  // is returned in line output parameter.
78  //
79  // Returns the width of the last line.
80  wxCoord BreakWord(wxDC& dc,
81  const wxString& word,
82  wxCoord maxWidth,
83  wxArrayString& lines,
84  wxString& line);
85 };
86 
87 
88 // PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
89 wxArrayString
91  wxDC& dc,
92  const wxGridCellAttr& attr,
93  const wxRect& rect,
94  int row, int col)
95 {
96  dc.SetFont(attr.GetFont());
97  const wxCoord maxWidth = rect.GetWidth();
98 
99  // Transform logical lines into physical ones, wrapping the longer ones.
100  const wxArrayString
101  logicalLines = wxSplit(grid.GetCellValue(row, col), '\n', '\0');
102 
103  // Trying to do anything if the column is hidden anyhow doesn't make sense
104  // and we run into problems in BreakLine() in this case.
105  if ( maxWidth <= 0 )
106  return logicalLines;
107 
108  wxArrayString physicalLines;
109  for ( wxArrayString::const_iterator it = logicalLines.begin();
110  it != logicalLines.end();
111  ++it )
112  {
113  const wxString& line = *it;
114 
115  if ( dc.GetTextExtent(line).x > maxWidth )
116  {
117  // Line does not fit, break it up.
118  BreakLine(dc, line, maxWidth, physicalLines);
119  }
120  else // The entire line fits as is
121  {
122  physicalLines.push_back(line);
123  }
124  }
125 
126  return physicalLines;
127 }
128 
129 
130 // PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
131 void
133  const wxString& logicalLine,
134  wxCoord maxWidth,
135  wxArrayString& lines)
136 {
137  wxCoord lineWidth = 0;
138  wxString line;
139 
140  // For each word
141  wxStringTokenizer wordTokenizer(logicalLine, wxS(" \t"), wxTOKEN_RET_DELIMS);
142  while ( wordTokenizer.HasMoreTokens() )
143  {
144  const wxString word = wordTokenizer.GetNextToken();
145  const wxCoord wordWidth = dc.GetTextExtent(word).x;
146  if ( lineWidth + wordWidth < maxWidth )
147  {
148  // Word fits, just add it to this line.
149  line += word;
150  lineWidth += wordWidth;
151  }
152  else
153  {
154  // Word does not fit, check whether the word is itself wider that
155  // available width
156  if ( wordWidth < maxWidth )
157  {
158  // Word can fit in a new line, put it at the beginning
159  // of the new line.
160  lines.push_back(line);
161  line = word;
162  lineWidth = wordWidth;
163  }
164  else // Word cannot fit in available width at all.
165  {
166  if ( !line.empty() )
167  {
168  lines.push_back(line);
169  line.clear();
170  lineWidth = 0;
171  }
172 
173  // Break it up in several lines.
174  lineWidth = BreakWord(dc, word, maxWidth, lines, line);
175  }
176  }
177  }
178 
179  if ( !line.empty() )
180  lines.push_back(line);
181 }
182 
183 
184 // PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
185 wxCoord
187  const wxString& word,
188  wxCoord maxWidth,
189  wxArrayString& lines,
190  wxString& line)
191 {
192  wxArrayInt widths;
193  dc.GetPartialTextExtents(word, widths);
194 
195  // TODO: Use binary search to find the first element > maxWidth.
196  const unsigned count = widths.size();
197  unsigned n;
198  for ( n = 0; n < count; n++ )
199  {
200  if ( widths[n] > maxWidth )
201  break;
202  }
203 
204  if ( n == 0 )
205  {
206  // This is a degenerate case: the first character of the word is
207  // already wider than the available space, so we just can't show it
208  // completely and have to put the first character in this line.
209  n = 1;
210  }
211 
212  lines.push_back(word.substr(0, n));
213 
214  // Check if the remainder of the string fits in one line.
215  //
216  // Unfortunately we can't use the existing partial text extents as the
217  // extent of the remainder may be different when it's rendered in a
218  // separate line instead of as part of the same one, so we have to
219  // recompute it.
220  const wxString rest = word.substr(n);
221  const wxCoord restWidth = dc.GetTextExtent(rest).x;
222  if ( restWidth <= maxWidth )
223  {
224  line = rest;
225  return restWidth;
226  }
227 
228  // Break the rest of the word into lines.
229  //
230  // TODO: Perhaps avoid recursion? The code is simpler like this but using a
231  // loop in this function would probably be more efficient.
232  return BreakWord(dc, rest, maxWidth, lines, line);
233 }
234 
235 
236 #define GRID_CELL_MARGIN 3
237 
238 int GRIDCELL_AUTOWRAP_STRINGRENDERER::GetHeight( wxDC& aDC, wxGrid* aGrid, int aRow, int aCol )
239 {
240  wxGridCellAttr* attr = aGrid->GetOrCreateCellAttr( aRow, aCol );
241  wxRect rect;
242 
243  aDC.SetFont( attr->GetFont() );
244  rect.SetWidth( aGrid->GetColSize( aCol ) - ( 2 * GRID_CELL_MARGIN ) );
245 
246  const size_t numLines = GetTextLines( *aGrid, aDC, *attr, rect, aRow, aCol ).size();
247  const int textHeight = numLines *aDC.GetCharHeight();
248 
249  attr->DecRef();
250 
251  return textHeight + ( 2 * GRID_CELL_MARGIN );
252 }
253 
254 
255 // a helper class to handle components to edit
257 {
258 public:
259  SCH_COMPONENT* m_Component; // the schematic component
260  int m_Row; // the row index in m_grid
261  SCH_SCREEN* m_Screen; // the screen where m_Component lives
262  wxString m_Reference; // the schematic reference, only to display it in list
263  wxString m_InitialLibId; // the Lib Id of the component before any change
264  bool m_IsOrphan; // true if a component has no corresponding symbol found in libs
265 
267  {
268  m_Component = aComponent;
269  m_InitialLibId = m_Component->GetLibId().Format();
270  m_Row = -1;
271  m_IsOrphan = false;
272  m_Screen = nullptr;
273  }
274 
275  // Returns a string like mylib:symbol_name from the LIB_ID of the component
276  wxString GetStringLibId()
277  {
278  return m_Component->GetLibId().GetUniStringLibId();
279  }
280 
281  // Returns a string containing the reference of the component
283  {
284  return m_Reference;
285  }
286 };
287 
288 
298 {
299 public:
301  ~DIALOG_EDIT_COMPONENTS_LIBID() override;
302 
303  bool IsSchematicModified() { return m_isModified; }
304 
305 private:
307  bool m_isModified; // set to true if the schematic is modified
308  std::vector<int> m_OrphansRowIndexes; // list of rows containing orphan lib_id
309 
310  std::vector<CMP_CANDIDATE> m_components;
311 
313 
314  void initDlg();
315 
322  void AddRowToGrid( bool aMarkRow, const wxString& aReferences, const wxString& aStrLibId );
323 
325  bool validateLibIds();
326 
328  void revertChanges();
329 
334  bool setLibIdByBrowser( int aRow );
335 
336  // Events handlers
337 
338  // called on a right click or a left double click:
339  void onCellBrowseLib( wxGridEvent& event ) override;
340 
341  // Apply changes, but do not close the dialog
342  void onApplyButton( wxCommandEvent& event ) override;
343 
344  // Cancel all changes, and close the dialog
345  void onCancel( wxCommandEvent& event ) override
346  {
347  if( m_isModified )
348  revertChanges();
349  event.Skip();
350  }
351 
352  // Undo all changes, and clear the list of new lib_ids
353  void onUndoChangesButton( wxCommandEvent& event ) override;
354 
355  // Try to find a candidate for non existing symbols
356  void onClickOrphansButton( wxCommandEvent& event ) override;
357 
358  // UI event, to enable/disable buttons
359  void updateUIChangesButton( wxUpdateUIEvent& event ) override
360  {
361  m_buttonUndo->Enable( m_isModified );
362  }
363 
364  // Automatically called when click on OK button
365  bool TransferDataFromWindow() override;
366 
367  void AdjustGridColumns( int aWidth );
368 
369  void OnSizeGrid( wxSizeEvent& event ) override;
370 };
371 
372 
375 {
376  m_parent = aParent;
378 
379  initDlg();
380 
382 }
383 
384 
386 {
387  m_autoWrapRenderer->DecRef();
388 }
389 
390 
391 // A sort compare function to sort components list by LIB_ID and then reference
392 static bool sort_by_libid( const CMP_CANDIDATE& cmp1, const CMP_CANDIDATE& cmp2 )
393 {
394  if( cmp1.m_Component->GetLibId() == cmp2.m_Component->GetLibId() )
395  return cmp1.m_Reference.Cmp( cmp2.m_Reference ) < 0;
396 
397  return cmp1.m_Component->GetLibId() < cmp2.m_Component->GetLibId();
398 }
399 
400 
402 {
403  // Clear the FormBuilder rows
404  m_grid->DeleteRows( 0, m_grid->GetNumberRows() );
405 
406  m_isModified = false;
407 
408  // Build the component list:
409 #if 0
410  // This option build a component list that works fine to edit LIB_ID fields, but does not display
411  // all components in a complex hierarchy.
412  // the list is shorter, but can be look like there are missing components in list
413  SCH_SCREENS screens;
414 
415  for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
416  {
417  for( SCH_ITEM* item = screen->GetDrawItems(); item; item = item->Next() )
418  {
419  if( item->Type() == SCH_COMPONENT_T )
420  {
421  CMP_CANDIDATE candidate( static_cast< SCH_COMPONENT* >( item ) );
422  candidate.m_Screen = screen;
423  candidate.m_Reference = candidate.m_Component->GetField( REFERENCE )->GetFullyQualifiedText();
424  m_components.push_back( candidate );
425  }
426  }
427  }
428 #else
429  // This option build the full component list
430  // In complex hierarchies, the same component is in fact duplicated, but
431  // it is listed with different references (one by sheet instance)
432  // the list is larger and looks like it contains all components
433  SCH_SHEET_LIST sheets( g_RootSheet );
434  SCH_REFERENCE_LIST references;
435  // build the full list of components including component having no symbol in loaded libs
436  // (orphan components)
437  sheets.GetComponents( references, /* include power symbols */true,
438  /* include orphan components */true );
439 
440  for( unsigned ii = 0; ii < references.GetCount(); ii++ )
441  {
442  SCH_REFERENCE& item = references[ii];
443  CMP_CANDIDATE candidate( item.GetComp() );
444  candidate.m_Screen = item.GetSheetPath().LastScreen();
445  SCH_SHEET_PATH sheetpath = item.GetSheetPath();
446  candidate.m_Reference = candidate.m_Component->GetRef( &sheetpath );
447  // For multi units per package , add unit id.
448  // however, there is a problem: the unit id stored is always >= 1
449  // and 1 for no multi units.
450  // so add unit id only if unit > 1 if the unit count is > 1
451  // (can be 0 if the symbol is not found)
452  int unit = candidate.m_Component->GetUnitSelection( &sheetpath );
453  int unitcount = candidate.m_Component->GetUnitCount();
454  candidate.m_IsOrphan = ( unitcount == 0 );
455 
456  if( unitcount > 1 || unit > 1 )
457  {
458  candidate.m_Reference << wxChar( ('A' + unit -1) );
459  }
460 
461  m_components.push_back( candidate );
462  }
463 #endif
464 
465  if( m_components.size() == 0 )
466  return;
467 
468  // now sort by lib id to create groups of items having the same lib id
469  std::sort( m_components.begin(), m_components.end(), sort_by_libid );
470 
471  // Now, fill m_grid
472  wxString last_str_libid = m_components.front().GetStringLibId();
473  int row = 0;
474  wxString refs;
475  bool mark_cell = m_components.front().m_IsOrphan;
476  CMP_CANDIDATE* cmp = nullptr;
477 
478  for( unsigned ii = 0; ii < m_components.size(); ii++ )
479  {
480  cmp = &m_components[ii];
481 
482  wxString str_libid = cmp->GetStringLibId();
483 
484  if( last_str_libid != str_libid )
485  {
486  // Add last group to grid
487  AddRowToGrid( mark_cell, refs, last_str_libid );
488 
489  // prepare next entry
490  mark_cell = cmp->m_IsOrphan;
491  last_str_libid = str_libid;
492  refs.Empty();
493  row++;
494  }
495 
496  if( !refs.IsEmpty() )
497  refs += wxT( ", " );
498 
499  refs += cmp->GetSchematicReference();
500  cmp->m_Row = row;
501  }
502 
503  // Add last component group:
504  AddRowToGrid( mark_cell, refs, last_str_libid );
505 
506  // Allows only the selection by row
507  m_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
508 
509  m_buttonOrphanItems->Enable( m_OrphansRowIndexes.size() > 0 );
510  Layout();
511 }
512 
513 
514 void DIALOG_EDIT_COMPONENTS_LIBID::AddRowToGrid( bool aMarkRow, const wxString& aReferences,
515  const wxString& aStrLibId )
516 {
517  int row = m_grid->GetNumberRows();
518 
519  if( aMarkRow ) // a orphan component exists, set m_AsOrphanCmp as true
520  m_OrphansRowIndexes.push_back( row );
521 
522  m_grid->AppendRows( 1 );
523 
524  m_grid->SetCellValue( row, COL_REFS, aReferences );
525  m_grid->SetReadOnly( row, COL_REFS );
526 
527  m_grid->SetCellValue( row, COL_CURR_LIBID, aStrLibId );
528  m_grid->SetReadOnly( row, COL_CURR_LIBID );
529 
530  if( aMarkRow ) // A symbol is not existing in libraries: mark the cell
531  {
532  wxFont font = m_grid->GetDefaultCellFont();
533  font.MakeBold();
534  font.MakeItalic();
535  m_grid->SetCellFont( row, COL_CURR_LIBID, font );
536  }
537 
538  m_grid->SetCellRenderer( row, COL_REFS, m_autoWrapRenderer->Clone() );
539 
540  // wxWidgets' AutoRowHeight fails when used with wxGridCellAutoWrapStringRenderer
541  // (fixed in 2014, but didn't get in to wxWidgets 3.0.2)
542  wxClientDC dc( this );
543  m_grid->SetRowSize( row, m_autoWrapRenderer->GetHeight( dc, m_grid, row, COL_REFS ) );
544 }
545 
546 
548 {
549  if( !m_grid->CommitPendingChanges() )
550  return false;
551 
552  int row_max = m_grid->GetNumberRows() - 1;
553 
554  for( int row = 0; row <= row_max; row++ )
555  {
556  wxString new_libid = m_grid->GetCellValue( row, COL_NEW_LIBID );
557 
558  if( new_libid.IsEmpty() )
559  continue;
560 
561  // a new lib id is found. validate this new value
562  LIB_ID id;
563  id.Parse( new_libid, LIB_ID::ID_SCH );
564 
565  if( !id.IsValid() )
566  {
567  wxString msg;
568  msg.Printf( _( "Symbol library identifier \"%s\" is not valid." ), new_libid );
569  wxMessageBox( msg );
570 
571  m_grid->SetFocus();
572  m_grid->MakeCellVisible( row, COL_NEW_LIBID );
573  m_grid->SetGridCursor( row, COL_NEW_LIBID );
574 
575  m_grid->EnableCellEditControl( true );
576  m_grid->ShowCellEditControl();
577 
578  return false;
579  }
580  }
581 
582  return true;
583 }
584 
585 
586 void DIALOG_EDIT_COMPONENTS_LIBID::onApplyButton( wxCommandEvent& event )
587 {
588  if( TransferDataFromWindow() )
589  m_parent->GetCanvas()->Refresh();
590 }
591 
592 
594 {
595  revertChanges();
596 
597  int row_max = m_grid->GetNumberRows() - 1;
598 
599  for( int row = 0; row <= row_max; row++ )
600  {
601  m_grid->SetCellValue( row, COL_NEW_LIBID, wxEmptyString );
602  }
603 
604  m_isModified = false;
605 }
606 
607 
609 {
610  int row = event.GetRow();
611  m_grid->SelectRow( row ); // only for user, to show the selected line
612 
613  setLibIdByBrowser( row );
614 
615 }
616 
617 
619 {
620  std::vector< wxString > libs = Prj().SchSymbolLibTable()->GetLogicalLibs();
621  wxArrayString aliasNames;
622  wxArrayString candidateSymbNames;
623 
624  unsigned fixesCount = 0;
625 
626  // Try to find a candidate for non existing symbols in any loaded library
627  for( unsigned ii = 0; ii < m_OrphansRowIndexes.size(); ii++ )
628  {
629  wxString orphanLibid = m_grid->GetCellValue( m_OrphansRowIndexes[ii], COL_CURR_LIBID );
630  int grid_row_idx = m_OrphansRowIndexes[ii]; //row index in m_grid for the current item
631 
632  LIB_ID curr_libid;
633  curr_libid.Parse( orphanLibid, LIB_ID::ID_SCH, true );
634  wxString symbName = curr_libid.GetLibItemName();
635  // number of full LIB_ID candidates (because we search for a symbol name
636  // inside all avaiable libraries, perhaps the same symbol name can be found
637  // in more than one library, giving ambiguity
638  int libIdCandidateCount = 0;
639  candidateSymbNames.Clear();
640 
641  // now try to find a candidate
642  for( auto &lib : libs )
643  {
644  aliasNames.Clear();
645 
646  try
647  {
648  Prj().SchSymbolLibTable()->EnumerateSymbolLib( lib, aliasNames );
649  }
650  catch( const IO_ERROR& ) {} // ignore, it is handled below
651 
652  if( aliasNames.IsEmpty() )
653  continue;
654 
655  // Find a symbol name in symbols inside this library:
656  int index = aliasNames.Index( symbName );
657 
658  if( index != wxNOT_FOUND )
659  {
660  // a candidate is found!
661  libIdCandidateCount++;
662  wxString newLibid = lib + ':' + symbName;
663 
664  // Uses the first found. Most of time, it is alone.
665  // Others will be stored in a candidate list
666  if( libIdCandidateCount <= 1 )
667  {
668  m_grid->SetCellValue( grid_row_idx, COL_NEW_LIBID, newLibid );
669  candidateSymbNames.Add( m_grid->GetCellValue( grid_row_idx, COL_NEW_LIBID ) );
670  fixesCount++;
671  }
672  else // Store other candidates for later selection
673  {
674  candidateSymbNames.Add( newLibid );
675  }
676  }
677  }
678 
679  // If more than one LIB_ID candidate, ask for selection between candidates:
680  if( libIdCandidateCount > 1 )
681  {
682  // Mainly for user: select the row being edited
683  m_grid->SelectRow( grid_row_idx );
684 
685  wxString msg;
686  msg.Printf( _( "Available Candidates for %s " ),
687  m_grid->GetCellValue( grid_row_idx, COL_CURR_LIBID ) );
688 
689  wxSingleChoiceDialog dlg ( this, msg,
690  wxString::Format( _( "Candidates count %d " ), libIdCandidateCount ),
691  candidateSymbNames );
692 
693  if( dlg.ShowModal() == wxID_OK )
694  m_grid->SetCellValue( grid_row_idx, COL_NEW_LIBID, dlg.GetStringSelection() );
695  }
696  }
697 
698  if( fixesCount < m_OrphansRowIndexes.size() ) // Not all orphan components are fixed
699  {
700  wxMessageBox( wxString::Format( _( "%u link(s) mapped, %d not found" ),
701  fixesCount, m_OrphansRowIndexes.size() - fixesCount ) );
702  }
703  else
704  wxMessageBox( wxString::Format( _( "All %u link(s) resolved" ), fixesCount ) );
705 }
706 
707 
709 {
710 #if 0
711  // Use dialog symbol selector to choose a symbol
714  m_frame->SelectComponentFromLibrary( NULL, dummy, true, 0, 0, false );
715 #else
716  // Use library viewer to choose a symbol
717  LIB_ID aPreselectedLibid;
719  m_parent->SelectComponentFromLibBrowser( this, NULL, aPreselectedLibid, 0, 0 );
720 #endif
721 
722  if( sel.LibId.empty() ) // command aborted
723  return false;
724 
725  if( !sel.LibId.IsValid() ) // Should not occur
726  {
727  wxMessageBox( _( "Invalid symbol library identifier" ) );
728  return false;
729  }
730 
731  wxString new_libid;
732  new_libid = sel.LibId.Format();
733 
734  m_grid->SetCellValue( aRow, COL_NEW_LIBID, new_libid );
735 
736  return true;
737 }
738 
739 
741 {
742  if( !validateLibIds() )
743  return false;
744 
745  bool change = false;
746  int row_max = m_grid->GetNumberRows() - 1;
747 
748  for( int row = 0; row <= row_max; row++ )
749  {
750  wxString new_libid = m_grid->GetCellValue( row, COL_NEW_LIBID );
751 
752  if( new_libid.IsEmpty() || new_libid == m_grid->GetCellValue( row, COL_CURR_LIBID ) )
753  continue;
754 
755  // a new lib id is found and was already validated.
756  // set this new value
757  LIB_ID id;
758  id.Parse( new_libid, LIB_ID::ID_SCH, true );
759 
760  for( CMP_CANDIDATE& cmp : m_components )
761  {
762  if( cmp.m_Row != row )
763  continue;
764 
765  cmp.m_Component->SetLibId( id );
766  change = true;
767  cmp.m_Screen->SetModify();
768  }
769  }
770 
771  if( change )
772  {
773  m_isModified = true;
774  SCH_SCREENS schematic;
775  schematic.UpdateSymbolLinks( true );
776  }
777 
778  return true;
779 }
780 
781 
783 {
784  bool change = false;
785  int row_max = m_grid->GetNumberRows() - 1;
786 
787  for( int row = 0; row <= row_max; row++ )
788  {
789  for( CMP_CANDIDATE& cmp : m_components )
790  {
791  if( cmp.m_Row != row )
792  continue;
793 
794  LIB_ID id;
795  id.Parse( cmp.m_InitialLibId, LIB_ID::ID_SCH, true );
796 
797  if( cmp.m_Component->GetLibId() != id )
798  {
799  cmp.m_Component->SetLibId( id );
800  change = true;
801  }
802  }
803  }
804 
805  if( change )
806  {
807  SCH_SCREENS schematic;
808  schematic.UpdateSymbolLinks( true );
809  m_parent->GetCanvas()->Refresh();
810  }
811 }
812 
813 
815 {
816  // Account for scroll bars
817  aWidth -= ( m_grid->GetSize().x - m_grid->GetClientSize().x );
818 
819  m_grid->SetColSize( 0, aWidth / 3 );
820  m_grid->SetColSize( 1, aWidth / 3 );
821  m_grid->SetColSize( 2, aWidth - m_grid->GetColSize( 0 ) - m_grid->GetColSize( 1 ) );
822 }
823 
824 
826 {
827  AdjustGridColumns( event.GetSize().GetX() );
828 
829  wxClientDC dc( this );
830 
831  // wxWidgets' AutoRowHeight fails when used with wxGridCellAutoWrapStringRenderer
832  for( int row = 0; row < m_grid->GetNumberRows(); ++row )
833  m_grid->SetRowSize( row, m_autoWrapRenderer->GetHeight( dc, m_grid, row, COL_REFS ) );
834 
835  event.Skip();
836 }
837 
838 
840 {
841  // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
842  // frame. Therefore this dialog as a modal frame parent, MUST be run under
843  // quasimodal mode for the quasimodal frame support to work. So don't use
844  // the QUASIMODAL macros here.
845  DIALOG_EDIT_COMPONENTS_LIBID dlg( aCaller );
846  // DO NOT use ShowModal() here, otherwise the library browser will not work
847  // properly.
848  dlg.ShowQuasiModal();
849 
850  return dlg.IsSchematicModified();
851 }
DIALOG_EDIT_COMPONENTS_LIBID(SCH_EDIT_FRAME *aParent)
bool IsValid() const
Definition: lib_id.h:171
Class SCH_SHEET_LIST.
void revertChanges()
Reverts all changes already made.
bool empty() const
Definition: lib_id.h:186
SCH_SCREEN * GetNext()
virtual void Refresh(bool eraseBackground=true, const wxRect *rect=NULL) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
Definition: draw_panel.cpp:321
std::vector< CMP_CANDIDATE > m_components
EDA_DRAW_PANEL * GetCanvas()
Definition: draw_frame.h:374
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:54
wxCoord BreakWord(wxDC &dc, const wxString &word, wxCoord maxWidth, wxArrayString &lines, wxString &line)
const wxString GetFullyQualifiedText() const
Function GetFullyQualifiedText returns the fully qualified field text by allowing for the part suffix...
Definition: sch_field.cpp:80
bool setLibIdByBrowser(int aRow)
run the lib browser and set the selected LIB_ID for row aRow
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)
std::vector< COMPONENT_SELECTION > HISTORY_LIST
Schematic editor (Eeschema) main window.
wxArrayString GetTextLines(wxGrid &grid, wxDC &dc, const wxGridCellAttr &attr, const wxRect &rect, int row, int col)
SCH_SCREEN * LastScreen() const
Function LastScreen.
#define COL_CURR_LIBID
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
SCH_ITEM * Next() const
Field Reference of part, i.e. "IC21".
wxString GetUniStringLibId() const
Definition: lib_id.h:148
Class SCH_REFERENCE_LIST is used to create a flattened list of components because in a complex hierar...
CMP_CANDIDATE(SCH_COMPONENT *aComponent)
#define GRID_CELL_MARGIN
DIALOG_EDIT_COMPONENTS_LIBID is a dialog to globally edit the LIB_ID of groups if components having t...
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
SCH_SHEET * g_RootSheet
Definition: eeschema.cpp:56
SCH_FIELD * GetField(int aFieldNdx) const
Returns a field in this symbol.
const UTF8 & GetLibItemName() const
Definition: lib_id.h:114
int ShowQuasiModal()
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
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...
Class SCH_SHEET_PATH.
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:152
wxGridCellRenderer * Clone() const override
void onCellBrowseLib(wxGridEvent &event) override
Definition the SCH_COMPONENT class for Eeschema.
void onApplyButton(wxCommandEvent &event) override
see class PGM_BASE
void UpdateSymbolLinks(bool aForce=false)
Initialize or reinitialize the weak reference to the LIB_PART for each SCH_COMPONENT found in the ful...
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 when a LIB_PART is not found in library to draw a dummy shape This component is a 400 mils squar...
SCH_SHEET_PATH GetSheetPath() const
#define COL_NEW_LIBID
void AddRowToGrid(bool aMarkRow, const wxString &aReferences, const wxString &aStrLibId)
Add a new row (new entry) in m_grid.
Class SCH_COMPONENT describes a real schematic component.
Definition: sch_component.h:69
const LIB_ID & GetLibId() const
UTF8 Format() const
Definition: lib_id.cpp:237
Class DIALOG_EDIT_COMPONENTS_LIBID_BASE.
SCH_SCREEN * GetFirst()
void GetComponents(SCH_REFERENCE_LIST &aReferences, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanComponents=false)
Function GetComponents adds a SCH_REFERENCE() object to aReferences for each component in the list of...
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
unsigned GetCount()
Function GetCount.
SCH_COMPONENT * GetComp() 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
Class SCH_ITEM is a base class for any item which can be embedded within the SCHEMATIC container clas...
void onUndoChangesButton(wxCommandEvent &event) override
void onClickOrphansButton(wxCommandEvent &event) override
Class SCH_REFERENCE is used as a helper to define a component&#39;s reference designator in a schematic...
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:534
void updateUIChangesButton(wxUpdateUIEvent &event) override
bool validateLibIds()
returns true if all new lib id are valid