KiCad PCB EDA Suite
dialog_edit_symbols_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-2021 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 <confirm.h>
32 #include <sch_edit_frame.h>
33 #include <sch_symbol.h>
34 #include <sch_reference_list.h>
35 #include <schematic.h>
36 #include <symbol_lib_table.h>
37 #include <trace_helpers.h>
38 #include <widgets/wx_grid.h>
39 
41 #include <wx/tokenzr.h>
42 #include <wx/choicdlg.h>
43 #include <wx/dcclient.h>
44 #include <grid_tricks.h>
46 #include <string_utils.h>
47 
48 
49 #define COL_REFS 0
50 #define COL_CURR_LIBID 1
51 #define COL_NEW_LIBID 2
52 
53 // a re-implementation of wxGridCellAutoWrapStringRenderer to allow workaround to autorowsize bug
54 class GRIDCELL_AUTOWRAP_STRINGRENDERER : public wxGridCellAutoWrapStringRenderer
55 {
56 public:
57  int GetHeight( wxDC& aDC, wxGrid* aGrid, int aRow, int aCol );
58 
59  wxGridCellRenderer *Clone() const override
60  { return new GRIDCELL_AUTOWRAP_STRINGRENDERER; }
61 
62 private:
63  // HELPER ROUTINES UNCHANGED FROM wxWidgets IMPLEMENTATION
64 
65  wxArrayString GetTextLines( wxGrid& grid, wxDC& dc, const wxGridCellAttr& attr,
66  const wxRect& rect, int row, int col );
67 
68  // Helper methods of GetTextLines()
69 
70  // Break a single logical line of text into several physical lines, all of
71  // which are added to the lines array. The lines are broken at maxWidth and
72  // the dc is used for measuring text extent only.
73  void BreakLine( wxDC& dc, const wxString& logicalLine, wxCoord maxWidth, 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, const wxString& word, wxCoord maxWidth, wxArrayString& lines,
81  wxString& line );
82 };
83 
84 
85 // PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
86 wxArrayString GRIDCELL_AUTOWRAP_STRINGRENDERER::GetTextLines( wxGrid& grid, wxDC& dc,
87  const wxGridCellAttr& attr,
88  const wxRect& rect, int row, int col )
89 {
90  dc.SetFont( attr.GetFont() );
91  const wxCoord maxWidth = rect.GetWidth();
92 
93  // Transform logical lines into physical ones, wrapping the longer ones.
94  const wxArrayString logicalLines = wxSplit( grid.GetCellValue( row, col ), '\n', '\0' );
95 
96  // Trying to do anything if the column is hidden anyhow doesn't make sense
97  // and we run into problems in BreakLine() in this case.
98  if( maxWidth <= 0 )
99  return logicalLines;
100 
101  wxArrayString physicalLines;
102 
103  for( const wxString& line : logicalLines )
104  {
105  if( dc.GetTextExtent( line ).x > maxWidth )
106  {
107  // Line does not fit, break it up.
108  BreakLine( dc, line, maxWidth, physicalLines );
109  }
110  else // The entire line fits as is
111  {
112  physicalLines.push_back( line );
113  }
114  }
115 
116  return physicalLines;
117 }
118 
119 
120 // PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
121 void GRIDCELL_AUTOWRAP_STRINGRENDERER::BreakLine( wxDC& dc, const wxString& logicalLine,
122  wxCoord maxWidth, wxArrayString& lines )
123 {
124  wxCoord lineWidth = 0;
125  wxString line;
126 
127  // For each word
128  wxStringTokenizer wordTokenizer( logicalLine, wxS( " \t" ), wxTOKEN_RET_DELIMS );
129 
130  while( wordTokenizer.HasMoreTokens() )
131  {
132  const wxString word = wordTokenizer.GetNextToken();
133  const wxCoord wordWidth = dc.GetTextExtent( word ).x;
134 
135  if( lineWidth + wordWidth < maxWidth )
136  {
137  // Word fits, just add it to this line.
138  line += word;
139  lineWidth += wordWidth;
140  }
141  else
142  {
143  // Word does not fit, check whether the word is itself wider that
144  // available width
145  if( wordWidth < maxWidth )
146  {
147  // Word can fit in a new line, put it at the beginning
148  // of the new line.
149  lines.push_back( line );
150  line = word;
151  lineWidth = wordWidth;
152  }
153  else // Word cannot fit in available width at all.
154  {
155  if( !line.empty() )
156  {
157  lines.push_back( line );
158  line.clear();
159  lineWidth = 0;
160  }
161 
162  // Break it up in several lines.
163  lineWidth = BreakWord( dc, word, maxWidth, lines, line );
164  }
165  }
166  }
167 
168  if( !line.empty() )
169  lines.push_back( line );
170 }
171 
172 
173 // PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
174 wxCoord GRIDCELL_AUTOWRAP_STRINGRENDERER::BreakWord( wxDC& dc, const wxString& word,
175  wxCoord maxWidth, wxArrayString& lines,
176  wxString& line )
177 {
178  wxArrayInt widths;
179  dc.GetPartialTextExtents( word, widths );
180 
181  // TODO: Use binary search to find the first element > maxWidth.
182  const unsigned count = widths.size();
183  unsigned n;
184 
185  for( n = 0; n < count; n++ )
186  {
187  if( widths[n] > maxWidth )
188  break;
189  }
190 
191  if( n == 0 )
192  {
193  // This is a degenerate case: the first character of the word is
194  // already wider than the available space, so we just can't show it
195  // completely and have to put the first character in this line.
196  n = 1;
197  }
198 
199  lines.push_back( word.substr( 0, n ) );
200 
201  // Check if the remainder of the string fits in one line.
202  //
203  // Unfortunately we can't use the existing partial text extents as the
204  // extent of the remainder may be different when it's rendered in a
205  // separate line instead of as part of the same one, so we have to
206  // recompute it.
207  const wxString rest = word.substr( n );
208  const wxCoord restWidth = dc.GetTextExtent( rest ).x;
209 
210  if( restWidth <= maxWidth )
211  {
212  line = rest;
213  return restWidth;
214  }
215 
216  // Break the rest of the word into lines.
217  //
218  // TODO: Perhaps avoid recursion? The code is simpler like this but using a
219  // loop in this function would probably be more efficient.
220  return BreakWord( dc, rest, maxWidth, lines, line );
221 }
222 
223 
224 #define GRID_CELL_MARGIN 4
225 
226 int GRIDCELL_AUTOWRAP_STRINGRENDERER::GetHeight( wxDC& aDC, wxGrid* aGrid, int aRow, int aCol )
227 {
228  wxGridCellAttr* attr = aGrid->GetOrCreateCellAttr( aRow, aCol );
229  wxRect rect;
230 
231  aDC.SetFont( attr->GetFont() );
232  rect.SetWidth( aGrid->GetColSize( aCol ) - ( 2 * GRID_CELL_MARGIN ) );
233 
234  const size_t numLines = GetTextLines( *aGrid, aDC, *attr, rect, aRow, aCol ).size();
235  const int textHeight = numLines * aDC.GetCharHeight();
236 
237  attr->DecRef();
238 
239  return textHeight + ( 2 * GRID_CELL_MARGIN );
240 }
241 
242 
247 {
248 public:
250  {
251  m_Symbol = aSymbol;
253  m_Row = -1;
254  m_IsOrphan = false;
255  m_Screen = nullptr;
256  }
257 
258  // Return a string like mylib:symbol_name from the #LIB_ID of the symbol.
259  wxString GetStringLibId()
260  {
261  return m_Symbol->GetLibId().GetUniStringLibId();
262  }
263 
264  SCH_SYMBOL* m_Symbol; // the schematic symbol
265  int m_Row; // the row index in m_grid
266  SCH_SCREEN* m_Screen; // the screen where m_Symbol lives
267  wxString m_Reference; // the schematic reference, only to display it in list
268  wxString m_InitialLibId; // the Lib Id of the symbol before any change.
269  bool m_IsOrphan; // true if a symbol has no corresponding symbol found in libs.
270 };
271 
272 
282 {
283 public:
285  ~DIALOG_EDIT_SYMBOLS_LIBID() override;
286 
288 
290 
291 private:
292  void initDlg();
293 
301  void AddRowToGrid( bool aMarkRow, const wxString& aReferences, const wxString& aStrLibId );
302 
304  bool validateLibIds();
305 
312  bool setLibIdByBrowser( int aRow );
313 
314  // Event handlers
315 
316  // called on a right click or a left double click:
317  void onCellBrowseLib( wxGridEvent& event ) override;
318 
319  // Cancel all changes, and close the dialog
320  void onCancel( wxCommandEvent& event ) override
321  {
322  // Just skipping the event doesn't work after the library browser was run
323  if( IsQuasiModal() )
324  EndQuasiModal( wxID_CANCEL );
325  else
326  event.Skip();
327  }
328 
329  // Try to find a candidate for non existing symbols
330  void onClickOrphansButton( wxCommandEvent& event ) override;
331 
332  // Automatically called when click on OK button
333  bool TransferDataFromWindow() override;
334 
335  void AdjustGridColumns( int aWidth );
336 
337  void OnSizeGrid( wxSizeEvent& event ) override;
338 
339  bool m_isModified; // set to true if the schematic is modified
340  std::vector<int> m_OrphansRowIndexes; // list of rows containing orphan lib_id
341 
342  std::vector<SYMBOL_CANDIDATE> m_symbols;
343 
345 };
346 
347 
350 {
352 
353  m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
354 
355  initDlg();
356 
358 }
359 
360 
362 {
363  // Delete the GRID_TRICKS.
364  m_grid->PopEventHandler( true );
365 
366  m_autoWrapRenderer->DecRef();
367 }
368 
369 
370 // A sort compare function to sort symbols list by LIB_ID and then reference.
371 static bool sort_by_libid( const SYMBOL_CANDIDATE& candidate1, const SYMBOL_CANDIDATE& candidate2 )
372 {
373  if( candidate1.m_Symbol->GetLibId() == candidate2.m_Symbol->GetLibId() )
374  return candidate1.m_Reference.Cmp( candidate2.m_Reference ) < 0;
375 
376  return candidate1.m_Symbol->GetLibId() < candidate2.m_Symbol->GetLibId();
377 }
378 
379 
381 {
382  // Clear the FormBuilder rows
383  m_grid->ClearRows();
384 
385  m_isModified = false;
386 
387  // This option build the full symbol list.
388  // In complex hierarchies, the same symbol is in fact duplicated, but
389  // it is listed with different references (one by sheet instance)
390  // the list is larger and looks like it contains all symbols.
391  const SCH_SHEET_LIST& sheets = GetParent()->Schematic().GetSheets();
392  SCH_REFERENCE_LIST references;
393 
394  // build the full list of symbols including symbol having no symbol in loaded libs
395  // (orphan symbols)
396  sheets.GetSymbols( references, /* include power symbols */ true,
397  /* include orphan symbols */ true );
398 
399  for( unsigned ii = 0; ii < references.GetCount(); ii++ )
400  {
401  SCH_REFERENCE& item = references[ii];
402  SYMBOL_CANDIDATE candidate( item.GetSymbol() );
403  candidate.m_Screen = item.GetSheetPath().LastScreen();
404  SCH_SHEET_PATH sheetpath = item.GetSheetPath();
405  candidate.m_Reference = candidate.m_Symbol->GetRef( &sheetpath );
406  int unitcount = candidate.m_Symbol->GetUnitCount();
407  candidate.m_IsOrphan = ( unitcount == 0 );
408  m_symbols.push_back( candidate );
409  }
410 
411  if( m_symbols.size() == 0 )
412  return;
413 
414  // now sort by lib id to create groups of items having the same lib id
415  std::sort( m_symbols.begin(), m_symbols.end(), sort_by_libid );
416 
417  // Now, fill m_grid
418  wxString last_str_libid = m_symbols.front().GetStringLibId();
419  int row = 0;
420  wxString refs;
421  wxString last_ref;
422  bool mark_cell = m_symbols.front().m_IsOrphan;
423 
424  for( SYMBOL_CANDIDATE& symbol : m_symbols )
425  {
426  wxString str_libid = symbol.GetStringLibId();
427 
428  if( last_str_libid != str_libid )
429  {
430  // Add last group to grid
431  AddRowToGrid( mark_cell, refs, last_str_libid );
432 
433  // prepare next entry
434  mark_cell = symbol.m_IsOrphan;
435  last_str_libid = str_libid;
436  refs.Empty();
437  row++;
438  }
439  else if( symbol.m_Reference == last_ref )
440  {
441  symbol.m_Row = row;
442  continue;
443  }
444 
445  last_ref = symbol.m_Reference;
446 
447  if( !refs.IsEmpty() )
448  refs += wxT( ", " );
449 
450  refs += symbol.m_Reference;
451  symbol.m_Row = row;
452  }
453 
454  // Add last symbol group:
455  AddRowToGrid( mark_cell, refs, last_str_libid );
456 
457  // Allows only the selection by row
458  m_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
459 
460  m_buttonOrphanItems->Enable( m_OrphansRowIndexes.size() > 0 );
461  Layout();
462 }
463 
464 
466 {
467  return dynamic_cast<SCH_EDIT_FRAME*>( wxDialog::GetParent() );
468 }
469 
470 
471 void DIALOG_EDIT_SYMBOLS_LIBID::AddRowToGrid( bool aMarkRow, const wxString& aReferences,
472  const wxString& aStrLibId )
473 {
474  int row = m_grid->GetNumberRows();
475 
476  if( aMarkRow ) // An orphaned symbol exists, set m_AsOrphanCmp as true.
477  m_OrphansRowIndexes.push_back( row );
478 
479  m_grid->AppendRows( 1 );
480 
481  m_grid->SetCellValue( row, COL_REFS, UnescapeString( aReferences ) );
482  m_grid->SetReadOnly( row, COL_REFS );
483 
484  m_grid->SetCellValue( row, COL_CURR_LIBID, UnescapeString( aStrLibId ) );
485  m_grid->SetReadOnly( row, COL_CURR_LIBID );
486 
487  if( aMarkRow ) // A symbol is not existing in libraries: mark the cell
488  {
489  wxFont font = m_grid->GetDefaultCellFont();
490  font.MakeBold();
491  font.MakeItalic();
492  m_grid->SetCellFont( row, COL_CURR_LIBID, font );
493  }
494 
495  m_grid->SetCellRenderer( row, COL_REFS, m_autoWrapRenderer->Clone() );
496 
497  // wxWidgets' AutoRowHeight fails when used with wxGridCellAutoWrapStringRenderer
498  // (fixed in 2014, but didn't get in to wxWidgets 3.0.2)
499  wxClientDC dc( this );
500  m_grid->SetRowSize( row, m_autoWrapRenderer->GetHeight( dc, m_grid, row, COL_REFS ) );
501 
502  // set new libid column browse button
503  wxGridCellAttr* attr = new wxGridCellAttr;
504  attr->SetEditor( new GRID_CELL_SYMBOL_ID_EDITOR( this, UnescapeString( aStrLibId ) ) );
505  m_grid->SetAttr( row, COL_NEW_LIBID, attr );
506 }
507 
508 
509 wxString getLibIdValue( const WX_GRID* aGrid, int aRow, int aCol )
510 {
511  wxString rawValue = aGrid->GetCellValue( aRow, aCol );
512 
513  if( rawValue.IsEmpty() )
514  return rawValue;
515 
516  wxString itemName;
517  wxString libName = rawValue.BeforeFirst( ':', &itemName );
518 
519  return EscapeString( libName, CTX_LIBID ) + ':' + EscapeString( itemName, CTX_LIBID );
520 }
521 
522 
524 {
525  if( !m_grid->CommitPendingChanges() )
526  return false;
527 
528  int row_max = m_grid->GetNumberRows() - 1;
529 
530  for( int row = 0; row <= row_max; row++ )
531  {
532  wxString new_libid = getLibIdValue( m_grid, row, COL_NEW_LIBID );
533 
534  if( new_libid.IsEmpty() )
535  continue;
536 
537  // a new lib id is found. validate this new value
538  LIB_ID id;
539  id.Parse( new_libid );
540 
541  if( !id.IsValid() )
542  {
543  wxString msg;
544  msg.Printf( _( "Symbol library identifier %s is not valid." ), new_libid );
545  wxMessageBox( msg );
546 
547  m_grid->SetFocus();
548  m_grid->MakeCellVisible( row, COL_NEW_LIBID );
549  m_grid->SetGridCursor( row, COL_NEW_LIBID );
550 
551  m_grid->EnableCellEditControl( true );
552  m_grid->ShowCellEditControl();
553 
554  return false;
555  }
556  }
557 
558  return true;
559 }
560 
561 
563 {
564  int row = event.GetRow();
565  m_grid->SelectRow( row ); // only for user, to show the selected line
566 
567  setLibIdByBrowser( row );
568 
569 }
570 
571 
573 {
574  std::vector<wxString> libs = Prj().SchSymbolLibTable()->GetLogicalLibs();
575  wxArrayString aliasNames;
576  wxArrayString candidateSymbNames;
577 
578  unsigned fixesCount = 0;
579 
580  // Try to find a candidate for non existing symbols in any loaded library
581  for( int orphanRow : m_OrphansRowIndexes )
582  {
583  wxString orphanLibid = getLibIdValue( m_grid, orphanRow, COL_CURR_LIBID );
584  int grid_row_idx = orphanRow; //row index in m_grid for the current item
585 
586  LIB_ID curr_libid;
587  curr_libid.Parse( orphanLibid, true );
588  wxString symbolName = curr_libid.GetLibItemName();
589 
590  // number of full LIB_ID candidates (because we search for a symbol name
591  // inside all available libraries, perhaps the same symbol name can be found
592  // in more than one library, giving ambiguity
593  int libIdCandidateCount = 0;
594  candidateSymbNames.Clear();
595 
596  // now try to find a candidate
597  for( const wxString &lib : libs )
598  {
599  aliasNames.Clear();
600 
601  try
602  {
603  Prj().SchSymbolLibTable()->EnumerateSymbolLib( lib, aliasNames );
604  }
605  catch( const IO_ERROR& ) {} // ignore, it is handled below
606 
607  if( aliasNames.IsEmpty() )
608  continue;
609 
610  // Find a symbol name in symbols inside this library:
611  int index = aliasNames.Index( symbolName );
612 
613  if( index != wxNOT_FOUND )
614  {
615  // a candidate is found!
616  libIdCandidateCount++;
617  wxString newLibid = lib + ':' + symbolName;
618 
619  // Uses the first found. Most of time, it is alone.
620  // Others will be stored in a candidate list
621  if( libIdCandidateCount <= 1 )
622  {
623  m_grid->SetCellValue( grid_row_idx, COL_NEW_LIBID, UnescapeString( newLibid ) );
624  candidateSymbNames.Add( m_grid->GetCellValue( grid_row_idx, COL_NEW_LIBID ) );
625  fixesCount++;
626  }
627  else // Store other candidates for later selection
628  {
629  candidateSymbNames.Add( UnescapeString( newLibid ) );
630  }
631  }
632  }
633 
634  // If more than one LIB_ID candidate, ask for selection between candidates:
635  if( libIdCandidateCount > 1 )
636  {
637  // Mainly for user: select the row being edited
638  m_grid->SelectRow( grid_row_idx );
639 
640  wxString msg;
641  msg.Printf( _( "Available Candidates for %s " ),
642  m_grid->GetCellValue( grid_row_idx, COL_CURR_LIBID ) );
643 
644  wxSingleChoiceDialog dlg ( this, msg,
645  wxString::Format( _( "Candidates count %d " ),
646  libIdCandidateCount ),
647  candidateSymbNames );
648 
649  if( dlg.ShowModal() == wxID_OK )
650  m_grid->SetCellValue( grid_row_idx, COL_NEW_LIBID, dlg.GetStringSelection() );
651  }
652  }
653 
654  if( fixesCount < m_OrphansRowIndexes.size() ) // Not all orphan symbols are fixed.
655  {
656  wxMessageBox( wxString::Format( _( "%u link(s) mapped, %u not found" ),
657  fixesCount,
658  (unsigned) m_OrphansRowIndexes.size() - fixesCount ) );
659  }
660  else
661  {
662  wxMessageBox( wxString::Format( _( "All %u link(s) resolved" ), fixesCount ) );
663  }
664 }
665 
666 
668 {
669 #if 0
670  // Use dialog symbol selector to choose a symbol
671  SCH_BASE_FRAME::HISTORY_LIST dummy;
672  SCH_BASE_FRAME::PICKED_SYMBOL sel = m_frame->SelectComponentFromLibrary( nullptr, dummy, true,
673  0, 0, false );
674 #else
675  // Use library viewer to choose a symbol
676  LIB_ID preselected;
677  wxString current = getLibIdValue( m_grid, aRow, COL_NEW_LIBID );
678 
679  if( current.IsEmpty() )
680  current = getLibIdValue( m_grid, aRow, COL_CURR_LIBID );
681 
682  if( !current.IsEmpty() )
683  preselected.Parse( current, true );
684 
685  PICKED_SYMBOL sel = GetParent()->PickSymbolFromLibBrowser( this, nullptr, preselected, 0, 0 );
686 #endif
687 
688  if( sel.LibId.empty() ) // command aborted
689  return false;
690 
691  if( !sel.LibId.IsValid() ) // Should not occur
692  {
693  wxMessageBox( _( "Invalid symbol library identifier" ) );
694  return false;
695  }
696 
697  wxString new_libid;
698  new_libid = sel.LibId.Format();
699 
700  m_grid->SetCellValue( aRow, COL_NEW_LIBID, UnescapeString( new_libid ) );
701 
702  return true;
703 }
704 
705 
707 {
708  if( !validateLibIds() )
709  return false;
710 
711  auto getName = []( const LIB_ID& aLibId )
712  {
713  return UnescapeString( aLibId.GetLibItemName().wx_str() );
714  };
715 
716  int row_max = m_grid->GetNumberRows() - 1;
717 
718  for( int row = 0; row <= row_max; row++ )
719  {
720  wxString new_libid = getLibIdValue( m_grid, row, COL_NEW_LIBID );
721 
722  if( new_libid.IsEmpty() || new_libid == getLibIdValue( m_grid, row, COL_CURR_LIBID ) )
723  continue;
724 
725  // A new lib id is found and was already validated.
726  LIB_ID id;
727  id.Parse( new_libid, true );
728 
729  for( SYMBOL_CANDIDATE& candidate : m_symbols )
730  {
731  if( candidate.m_Row != row )
732  continue;
733 
734  LIB_SYMBOL* symbol = nullptr;
735 
736  try
737  {
738  symbol = Prj().SchSymbolLibTable()->LoadSymbol( id );
739  }
740  catch( const IO_ERROR& ioe )
741  {
742  wxString msg;
743 
744  msg.Printf( _( "Error loading symbol %s from library %s.\n\n%s" ),
745  id.GetLibItemName().wx_str(),
746  id.GetLibNickname().wx_str(),
747  ioe.What() );
748 
749  DisplayError( this, msg );
750  }
751 
752  if( symbol == nullptr )
753  continue;
754 
755  GetParent()->SaveCopyInUndoList( candidate.m_Screen, candidate.m_Symbol,
757  m_isModified = true;
758 
759  candidate.m_Screen->Remove( candidate.m_Symbol );
760  SCH_FIELD* value = candidate.m_Symbol->GetField( VALUE_FIELD );
761 
762  // If value is a proxy for the itemName then make sure it gets updated
763  if( getName( candidate.m_Symbol->GetLibId() ) == value->GetText() )
764  candidate.m_Symbol->SetValue( getName( id ) );
765 
766  candidate.m_Symbol->SetLibId( id );
767  candidate.m_Symbol->SetLibSymbol( symbol->Flatten().release() );
768  candidate.m_Screen->Append( candidate.m_Symbol );
769  candidate.m_Screen->SetContentModified();
770 
771  if ( m_checkBoxUpdateFields->IsChecked() )
772  {
773  candidate.m_Symbol->UpdateFields( nullptr,
774  false, /* update style */
775  false, /* update ref */
776  false, /* update other fields */
777  false, /* reset ref */
778  true /* reset other fields */ );
779  }
780  }
781  }
782 
783  return true;
784 }
785 
786 
788 {
789  // Account for scroll bars
790  aWidth -= ( m_grid->GetSize().x - m_grid->GetClientSize().x );
791 
792  int colWidth = aWidth / 3;
793 
794  m_grid->SetColSize( COL_REFS, colWidth );
795  aWidth -= colWidth;
796 
797  colWidth = 0;
798 
799  for( int row = 0; row < m_grid->GetNumberRows(); ++row )
800  {
801  wxString cellValue = m_grid->GetCellValue( row, COL_CURR_LIBID );
802  colWidth = std::max( colWidth, KIUI::GetTextSize( cellValue, m_grid ).x );
803  }
804 
805  colWidth += 20;
806  m_grid->SetColSize( COL_CURR_LIBID, colWidth );
807  aWidth -= colWidth;
808 
809  colWidth = 0;
810 
811  for( int row = 0; row < m_grid->GetNumberRows(); ++row )
812  {
813  wxString cellValue = m_grid->GetCellValue( row, COL_NEW_LIBID );
814  colWidth = std::max( colWidth, KIUI::GetTextSize( cellValue, m_grid ).x );
815  }
816 
817  colWidth += 20;
818  m_grid->SetColSize( COL_NEW_LIBID, std::max( colWidth, aWidth ) );
819 }
820 
821 
822 void DIALOG_EDIT_SYMBOLS_LIBID::OnSizeGrid( wxSizeEvent& event )
823 {
824  AdjustGridColumns( event.GetSize().GetX() );
825 
826  wxClientDC dc( this );
827 
828  // wxWidgets' AutoRowHeight fails when used with wxGridCellAutoWrapStringRenderer
829  for( int row = 0; row < m_grid->GetNumberRows(); ++row )
830  m_grid->SetRowSize( row, m_autoWrapRenderer->GetHeight( dc, m_grid, row, COL_REFS ) );
831 
832  event.Skip();
833 }
834 
835 
837 {
838  // This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
839  // frame. Therefore this dialog as a modal frame parent, MUST be run under
840  // quasimodal mode for the quasimodal frame support to work. So don't use
841  // the QUASIMODAL macros here.
842  DIALOG_EDIT_SYMBOLS_LIBID dlg( aCaller );
843 
844  // DO NOT use ShowModal() here, otherwise the library browser will not work properly.
845  dlg.ShowQuasiModal();
846 
847  return dlg.IsSchematicModified();
848 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:279
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:49
GRIDCELL_AUTOWRAP_STRINGRENDERER * m_autoWrapRenderer
const UTF8 & GetLibItemName() const
Definition: lib_id.h:104
void GetSymbols(SCH_REFERENCE_LIST &aReferences, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets.
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)
void BreakLine(wxDC &dc, const wxString &logicalLine, wxCoord maxWidth, wxArrayString &lines)
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:55
bool IsQuasiModal() const
Definition: dialog_shim.h:106
#define COL_REFS
void onCancel(wxCommandEvent &event) override
Schematic editor (Eeschema) main window.
wxArrayString GetTextLines(wxGrid &grid, wxDC &dc, const wxGridCellAttr &attr, const wxRect &rect, int row, int col)
DIALOG_EDIT_SYMBOLS_LIBID(SCH_EDIT_FRAME *aParent)
SCH_SYMBOL * GetSymbol() const
SYMBOL_CANDIDATE(SCH_SYMBOL *aSymbol)
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
bool IsValid() const
Check if this LID_ID is valid.
Definition: lib_id.h:159
Define a library symbol object.
Definition: lib_symbol.h:96
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
Definition: lib_symbol.cpp:331
bool empty() const
Definition: lib_id.h:180
Class DIALOG_EDIT_SYMBOLS_LIBID_BASE.
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
Dialog to globally edit the LIB_ID of groups if symbols having the same initial LIB_ID.
void onCellBrowseLib(wxGridEvent &event) override
Field Value of part, i.e. "3.3K".
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: ui_common.cpp:70
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
int ShowQuasiModal()
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
int GetHeight(wxDC &aDC, wxGrid *aGrid, int aRow, int aCol)
#define GRID_CELL_MARGIN
SCHEMATIC & Schematic() const
static bool sort_by_libid(const SYMBOL_CANDIDATE &candidate1, const SYMBOL_CANDIDATE &candidate2)
wxString getLibIdValue(const WX_GRID *aGrid, int aRow, int aCol)
size_t GetCount() const
#define _(s)
wxLogTrace helper definitions.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:190
void onClickOrphansButton(wxCommandEvent &event) override
UTF8 Format() const
Definition: lib_id.cpp:116
wxString UnescapeString(const wxString &aSource)
bool InvokeDialogEditSymbolsLibId(SCH_EDIT_FRAME *aCaller)
Run a dialog to modify the LIB_ID of symbols for instance when a symbol has moved from a symbol libra...
PICKED_SYMBOL PickSymbolFromLibBrowser(wxTopLevelWindow *aParent, const SCHLIB_FILTER *aFilter, const LIB_ID &aPreselectedLibId, int aUnit, int aConvert)
Call the library viewer to select symbol to import into schematic.
Definition: getpart.cpp:42
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
SCH_SHEET_LIST GetSheets() const override
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:87
wxGridCellRenderer * Clone() const override
void EndQuasiModal(int retCode)
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:49
std::vector< SYMBOL_CANDIDATE > m_symbols
#define COL_NEW_LIBID
Schematic symbol object.
Definition: sch_symbol.h:78
SCH_SCREEN * LastScreen()
void SaveCopyInUndoList(SCH_SCREEN *aScreen, SCH_ITEM *aItemToCopy, UNDO_REDO aTypeCommand, bool aAppend)
Create a copy of the current schematic item, and put it in the undo list.
A helper to handle symbols to edit.
#define COL_CURR_LIBID
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:106
wxString GetUniStringLibId() const
Definition: lib_id.h:134
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
LIB_ID LibId
Definition: sch_screen.h:82
bool setLibIdByBrowser(int aRow)
Run the lib browser and set the selected LIB_ID for aRow.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
void OnSizeGrid(wxSizeEvent &event) override
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
bool validateLibIds()
returns true if all new lib id are valid
A helper to define a symbol's reference designator in a schematic.
void AddRowToGrid(bool aMarkRow, const wxString &aReferences, const wxString &aStrLibId)
Add a new row (new entry) in m_grid.
const LIB_ID & GetLibId() const
Definition: sch_symbol.h:147