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