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