7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2024-11-24 00:34:47 +00:00
kicad/eeschema/dialogs/dialog_edit_symbols_libid.cpp
Wayne Stambaugh a6923b3de3 Cache full schematic sheet list sorted by sheet page number.
The sheet list is returned as a copy of the cached list rather than a
reference to prevent external code from changing the list.  While not as
performant, it eliminates the risk of the sheet list being altered in
ways that could break the schematic.  The sheet list should only be
updated by calling SCHEMATIC::RefreshHierarchy() when any appropriate
sheet changes are made.

Note to developers: there is something inherently different about how the
QA tests are loading and handling schematics versus the schematic editor.
Using the cached sheet list for the SCHEMATIC object will cause some QA
test to fail.  This is why SCHEMATIC::Hierarchy() has not replaced
SCHEMATIC::BuildSheetListSortedByPageNumbers() everywhere.
2024-10-09 09:58:50 -04:00

848 lines
27 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2017 Jean-Pierre Charras, jp.charras@wanadoo.fr
* Copyright 1992-2022, 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file eeschema/dialogs/dialog_edit_symbols_libid.cpp
* @brief Dialog to remap library id of symbols to another library id
*/
#include <confirm.h>
#include <sch_edit_frame.h>
#include <sch_symbol.h>
#include <sch_reference_list.h>
#include <schematic.h>
#include <symbol_lib_table.h>
#include <trace_helpers.h>
#include <widgets/wx_grid.h>
#include <dialog_edit_symbols_libid_base.h>
#include <wx/tokenzr.h>
#include <wx/choicdlg.h>
#include <wx/dcclient.h>
#include <wx/msgdlg.h>
#include <grid_tricks.h>
#include <widgets/grid_text_button_helpers.h>
#include <kiplatform/ui.h>
#include <string_utils.h>
#include <project_sch.h>
#define COL_REFS 0
#define COL_CURR_LIBID 1
#define COL_NEW_LIBID 2
// a re-implementation of wxGridCellAutoWrapStringRenderer to allow workaround to autorowsize bug
class GRIDCELL_AUTOWRAP_STRINGRENDERER : public wxGridCellAutoWrapStringRenderer
{
public:
int GetHeight( wxDC& aDC, wxGrid* aGrid, int aRow, int aCol );
wxGridCellRenderer *Clone() const override
{ return new GRIDCELL_AUTOWRAP_STRINGRENDERER; }
private:
// HELPER ROUTINES UNCHANGED FROM wxWidgets IMPLEMENTATION
wxArrayString GetTextLines( wxGrid& grid, wxDC& dc, const wxGridCellAttr& attr,
const wxRect& rect, int row, int col );
// Helper methods of GetTextLines()
// Break a single logical line of text into several physical lines, all of
// which are added to the lines array. The lines are broken at maxWidth and
// the dc is used for measuring text extent only.
void BreakLine( wxDC& dc, const wxString& logicalLine, wxCoord maxWidth, wxArrayString& lines );
// Break a word, which is supposed to be wider than maxWidth, into several
// lines, which are added to lines array and the last, incomplete, of which
// is returned in line output parameter.
//
// Returns the width of the last line.
wxCoord BreakWord( wxDC& dc, const wxString& word, wxCoord maxWidth, wxArrayString& lines,
wxString& line );
};
// PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
wxArrayString GRIDCELL_AUTOWRAP_STRINGRENDERER::GetTextLines( wxGrid& grid, wxDC& dc,
const wxGridCellAttr& attr,
const wxRect& rect, int row, int col )
{
dc.SetFont( attr.GetFont() );
const wxCoord maxWidth = rect.GetWidth();
// Transform logical lines into physical ones, wrapping the longer ones.
const wxArrayString logicalLines = wxSplit( grid.GetCellValue( row, col ), '\n', '\0' );
// Trying to do anything if the column is hidden anyhow doesn't make sense
// and we run into problems in BreakLine() in this case.
if( maxWidth <= 0 )
return logicalLines;
wxArrayString physicalLines;
for( const wxString& line : logicalLines )
{
if( dc.GetTextExtent( line ).x > maxWidth )
{
// Line does not fit, break it up.
BreakLine( dc, line, maxWidth, physicalLines );
}
else // The entire line fits as is
{
physicalLines.push_back( line );
}
}
return physicalLines;
}
// PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
void GRIDCELL_AUTOWRAP_STRINGRENDERER::BreakLine( wxDC& dc, const wxString& logicalLine,
wxCoord maxWidth, wxArrayString& lines )
{
wxCoord lineWidth = 0;
wxString line;
// For each word
wxStringTokenizer wordTokenizer( logicalLine, wxS( " \t" ), wxTOKEN_RET_DELIMS );
while( wordTokenizer.HasMoreTokens() )
{
const wxString word = wordTokenizer.GetNextToken();
const wxCoord wordWidth = dc.GetTextExtent( word ).x;
if( lineWidth + wordWidth < maxWidth )
{
// Word fits, just add it to this line.
line += word;
lineWidth += wordWidth;
}
else
{
// Word does not fit, check whether the word is itself wider that
// available width
if( wordWidth < maxWidth )
{
// Word can fit in a new line, put it at the beginning
// of the new line.
lines.push_back( line );
line = word;
lineWidth = wordWidth;
}
else // Word cannot fit in available width at all.
{
if( !line.empty() )
{
lines.push_back( line );
line.clear();
lineWidth = 0;
}
// Break it up in several lines.
lineWidth = BreakWord( dc, word, maxWidth, lines, line );
}
}
}
if( !line.empty() )
lines.push_back( line );
}
// PRIVATE METHOD UNCHANGED FROM wxWidgets IMPLEMENTATION
wxCoord GRIDCELL_AUTOWRAP_STRINGRENDERER::BreakWord( wxDC& dc, const wxString& word,
wxCoord maxWidth, wxArrayString& lines,
wxString& line )
{
wxArrayInt widths;
dc.GetPartialTextExtents( word, widths );
// TODO: Use binary search to find the first element > maxWidth.
const unsigned count = widths.size();
unsigned n;
for( n = 0; n < count; n++ )
{
if( widths[n] > maxWidth )
break;
}
if( n == 0 )
{
// This is a degenerate case: the first character of the word is
// already wider than the available space, so we just can't show it
// completely and have to put the first character in this line.
n = 1;
}
lines.push_back( word.substr( 0, n ) );
// Check if the remainder of the string fits in one line.
//
// Unfortunately we can't use the existing partial text extents as the
// extent of the remainder may be different when it's rendered in a
// separate line instead of as part of the same one, so we have to
// recompute it.
const wxString rest = word.substr( n );
const wxCoord restWidth = dc.GetTextExtent( rest ).x;
if( restWidth <= maxWidth )
{
line = rest;
return restWidth;
}
// Break the rest of the word into lines.
//
// TODO: Perhaps avoid recursion? The code is simpler like this but using a
// loop in this function would probably be more efficient.
return BreakWord( dc, rest, maxWidth, lines, line );
}
#define GRID_CELL_MARGIN 4
int GRIDCELL_AUTOWRAP_STRINGRENDERER::GetHeight( wxDC& aDC, wxGrid* aGrid, int aRow, int aCol )
{
wxGridCellAttr* attr = aGrid->GetOrCreateCellAttr( aRow, aCol );
wxRect rect;
aDC.SetFont( attr->GetFont() );
rect.SetWidth( aGrid->GetColSize( aCol ) - ( 2 * GRID_CELL_MARGIN ) );
const size_t numLines = GetTextLines( *aGrid, aDC, *attr, rect, aRow, aCol ).size();
const int textHeight = numLines * aDC.GetCharHeight();
attr->DecRef();
return textHeight + ( 2 * GRID_CELL_MARGIN );
}
/**
* A helper to handle symbols to edit.
*/
class SYMBOL_CANDIDATE
{
public:
SYMBOL_CANDIDATE( SCH_SYMBOL* aSymbol )
{
m_Symbol = aSymbol;
m_InitialLibId = m_Symbol->GetLibId().Format().wx_str();
m_Row = -1;
m_IsOrphan = false;
m_Screen = nullptr;
}
// Return a string like mylib:symbol_name from the #LIB_ID of the symbol.
wxString GetStringLibId()
{
return m_Symbol->GetLibId().GetUniStringLibId();
}
SCH_SYMBOL* m_Symbol; // the schematic symbol
int m_Row; // the row index in m_grid
SCH_SCREEN* m_Screen; // the screen where m_Symbol lives
wxString m_Reference; // the schematic reference, only to display it in list
wxString m_InitialLibId; // the Lib Id of the symbol before any change.
bool m_IsOrphan; // true if a symbol has no corresponding symbol found in libs.
};
/**
* Dialog to globally edit the #LIB_ID of groups if symbols having the same initial LIB_ID.
*
* This is useful when you want to:
* * move a symbol from a symbol library to another symbol library.
* * change the nickname of a library.
* * globally replace the symbol used by a group of symbols by another symbol.
*/
class DIALOG_EDIT_SYMBOLS_LIBID : public DIALOG_EDIT_SYMBOLS_LIBID_BASE
{
public:
DIALOG_EDIT_SYMBOLS_LIBID( SCH_EDIT_FRAME* aParent );
~DIALOG_EDIT_SYMBOLS_LIBID() override;
SCH_EDIT_FRAME* GetParent();
bool IsSchematicModified() { return m_isModified; }
private:
void initDlg();
/**
* Add a new row (new entry) in m_grid.
*
* @param aMarkRow set to true to use bold/italic font in column COL_CURR_LIBID.
* @param aReferences is the value of cell( aRowId, COL_REFS).
* @param aStrLibId is the value of cell( aRowId, COL_CURR_LIBID).
*/
void AddRowToGrid( bool aMarkRow, const wxString& aReferences, const wxString& aStrLibId );
/// returns true if all new lib id are valid
bool validateLibIds();
/**
* Run the lib browser and set the selected #LIB_ID for \a aRow.
*
* @param aRow is the row to edit.
* @return false if the command was aborted.
*/
bool setLibIdByBrowser( int aRow );
// Event handlers
// called on a right click or a left double click:
void onCellBrowseLib( wxGridEvent& event ) override;
// Cancel all changes, and close the dialog
void onCancel( wxCommandEvent& event ) override
{
// Just skipping the event doesn't work after the library browser was run
if( IsQuasiModal() )
EndQuasiModal( wxID_CANCEL );
else
event.Skip();
}
// Try to find a candidate for non existing symbols
void onClickOrphansButton( wxCommandEvent& event ) override;
// Automatically called when click on OK button
bool TransferDataFromWindow() override;
void AdjustGridColumns();
void OnSizeGrid( wxSizeEvent& event ) override;
bool m_isModified; // set to true if the schematic is modified
std::vector<int> m_OrphansRowIndexes; // list of rows containing orphan lib_id
std::vector<SYMBOL_CANDIDATE> m_symbols;
GRIDCELL_AUTOWRAP_STRINGRENDERER* m_autoWrapRenderer;
};
DIALOG_EDIT_SYMBOLS_LIBID::DIALOG_EDIT_SYMBOLS_LIBID( SCH_EDIT_FRAME* aParent ) :
DIALOG_EDIT_SYMBOLS_LIBID_BASE( aParent )
{
m_autoWrapRenderer = new GRIDCELL_AUTOWRAP_STRINGRENDERER;
m_grid->PushEventHandler( new GRID_TRICKS( m_grid ) );
initDlg();
finishDialogSettings();
}
DIALOG_EDIT_SYMBOLS_LIBID::~DIALOG_EDIT_SYMBOLS_LIBID()
{
// Delete the GRID_TRICKS.
m_grid->PopEventHandler( true );
m_autoWrapRenderer->DecRef();
}
// A sort compare function to sort symbols list by LIB_ID and then reference.
static bool sort_by_libid( const SYMBOL_CANDIDATE& candidate1, const SYMBOL_CANDIDATE& candidate2 )
{
if( candidate1.m_Symbol->GetLibId() == candidate2.m_Symbol->GetLibId() )
return candidate1.m_Reference.Cmp( candidate2.m_Reference ) < 0;
return candidate1.m_Symbol->GetLibId() < candidate2.m_Symbol->GetLibId();
}
void DIALOG_EDIT_SYMBOLS_LIBID::initDlg()
{
// Clear the FormBuilder rows
m_grid->ClearRows();
m_isModified = false;
// This option build the full symbol list.
// In complex hierarchies, the same symbol is in fact duplicated, but
// it is listed with different references (one by sheet instance)
// the list is larger and looks like it contains all symbols.
SCH_REFERENCE_LIST references;
// build the full list of symbols including symbol having no symbol in loaded libs
// (orphan symbols)
GetParent()->Schematic().Hierarchy().GetSymbols( references,
true /* include power symbols */,
true /* include orphan symbols */ );
for( unsigned ii = 0; ii < references.GetCount(); ii++ )
{
SCH_REFERENCE& item = references[ii];
SYMBOL_CANDIDATE candidate( item.GetSymbol() );
candidate.m_Screen = item.GetSheetPath().LastScreen();
SCH_SHEET_PATH sheetpath = item.GetSheetPath();
candidate.m_Reference = candidate.m_Symbol->GetRef( &sheetpath );
int unitcount = candidate.m_Symbol->GetUnitCount();
candidate.m_IsOrphan = ( unitcount == 0 );
m_symbols.push_back( candidate );
}
if( m_symbols.size() == 0 )
return;
// now sort by lib id to create groups of items having the same lib id
std::sort( m_symbols.begin(), m_symbols.end(), sort_by_libid );
// Now, fill m_grid
wxString last_str_libid = m_symbols.front().GetStringLibId();
int row = 0;
wxString refs;
wxString last_ref;
bool mark_cell = m_symbols.front().m_IsOrphan;
for( SYMBOL_CANDIDATE& symbol : m_symbols )
{
wxString str_libid = symbol.GetStringLibId();
if( last_str_libid != str_libid )
{
// Add last group to grid
AddRowToGrid( mark_cell, refs, last_str_libid );
// prepare next entry
mark_cell = symbol.m_IsOrphan;
last_str_libid = str_libid;
refs.Empty();
row++;
}
else if( symbol.m_Reference == last_ref )
{
symbol.m_Row = row;
continue;
}
last_ref = symbol.m_Reference;
if( !refs.IsEmpty() )
refs += wxT( ", " );
refs += symbol.m_Reference;
symbol.m_Row = row;
}
// Add last symbol group:
AddRowToGrid( mark_cell, refs, last_str_libid );
// Allows only the selection by row
m_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
m_buttonOrphanItems->Enable( m_OrphansRowIndexes.size() > 0 );
Layout();
}
SCH_EDIT_FRAME* DIALOG_EDIT_SYMBOLS_LIBID::GetParent()
{
return dynamic_cast<SCH_EDIT_FRAME*>( wxDialog::GetParent() );
}
void DIALOG_EDIT_SYMBOLS_LIBID::AddRowToGrid( bool aMarkRow, const wxString& aReferences,
const wxString& aStrLibId )
{
int row = m_grid->GetNumberRows();
if( aMarkRow ) // An orphaned symbol exists, set m_AsOrphanCmp as true.
m_OrphansRowIndexes.push_back( row );
m_grid->AppendRows( 1 );
m_grid->SetCellValue( row, COL_REFS, UnescapeString( aReferences ) );
m_grid->SetReadOnly( row, COL_REFS );
m_grid->SetCellValue( row, COL_CURR_LIBID, UnescapeString( aStrLibId ) );
m_grid->SetReadOnly( row, COL_CURR_LIBID );
if( aMarkRow ) // A symbol is not existing in libraries: mark the cell
{
wxFont font = m_grid->GetDefaultCellFont();
font.MakeBold();
font.MakeItalic();
m_grid->SetCellFont( row, COL_CURR_LIBID, font );
}
m_grid->SetCellRenderer( row, COL_REFS, m_autoWrapRenderer->Clone() );
// wxWidgets' AutoRowHeight fails when used with wxGridCellAutoWrapStringRenderer
// (fixed in 2014, but didn't get in to wxWidgets 3.0.2)
wxClientDC dc( this );
m_grid->SetRowSize( row, m_autoWrapRenderer->GetHeight( dc, m_grid, row, COL_REFS ) );
// set new libid column browse button
wxGridCellAttr* attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_SYMBOL_ID_EDITOR( this, UnescapeString( aStrLibId ) ) );
m_grid->SetAttr( row, COL_NEW_LIBID, attr );
}
wxString getLibIdValue( const WX_GRID* aGrid, int aRow, int aCol )
{
wxString rawValue = aGrid->GetCellValue( aRow, aCol );
if( rawValue.IsEmpty() )
return rawValue;
wxString itemName;
wxString libName = rawValue.BeforeFirst( ':', &itemName );
return EscapeString( libName, CTX_LIBID ) + ':' + EscapeString( itemName, CTX_LIBID );
}
bool DIALOG_EDIT_SYMBOLS_LIBID::validateLibIds()
{
if( !m_grid->CommitPendingChanges() )
return false;
int row_max = m_grid->GetNumberRows() - 1;
for( int row = 0; row <= row_max; row++ )
{
wxString new_libid = getLibIdValue( m_grid, row, COL_NEW_LIBID );
if( new_libid.IsEmpty() )
continue;
// a new lib id is found. validate this new value
LIB_ID id;
id.Parse( new_libid );
if( !id.IsValid() )
{
wxString msg;
msg.Printf( _( "Symbol library identifier %s is not valid." ), new_libid );
wxMessageBox( msg );
m_grid->SetFocus();
m_grid->MakeCellVisible( row, COL_NEW_LIBID );
m_grid->SetGridCursor( row, COL_NEW_LIBID );
m_grid->EnableCellEditControl( true );
m_grid->ShowCellEditControl();
return false;
}
}
return true;
}
void DIALOG_EDIT_SYMBOLS_LIBID::onCellBrowseLib( wxGridEvent& event )
{
int row = event.GetRow();
m_grid->SelectRow( row ); // only for user, to show the selected line
setLibIdByBrowser( row );
}
void DIALOG_EDIT_SYMBOLS_LIBID::onClickOrphansButton( wxCommandEvent& event )
{
std::vector<wxString> libs = PROJECT_SCH::SchSymbolLibTable( &Prj() )->GetLogicalLibs();
wxArrayString aliasNames;
wxArrayString candidateSymbNames;
unsigned fixesCount = 0;
// Try to find a candidate for non existing symbols in any loaded library
for( int orphanRow : m_OrphansRowIndexes )
{
wxString orphanLibid = getLibIdValue( m_grid, orphanRow, COL_CURR_LIBID );
int grid_row_idx = orphanRow; //row index in m_grid for the current item
LIB_ID curr_libid;
curr_libid.Parse( orphanLibid, true );
wxString symbolName = curr_libid.GetLibItemName();
// number of full LIB_ID candidates (because we search for a symbol name
// inside all available libraries, perhaps the same symbol name can be found
// in more than one library, giving ambiguity
int libIdCandidateCount = 0;
candidateSymbNames.Clear();
// now try to find a candidate
for( const wxString &lib : libs )
{
aliasNames.Clear();
try
{
PROJECT_SCH::SchSymbolLibTable( &Prj() )->EnumerateSymbolLib( lib, aliasNames );
}
catch( const IO_ERROR& ) {} // ignore, it is handled below
if( aliasNames.IsEmpty() )
continue;
// Find a symbol name in symbols inside this library:
int index = aliasNames.Index( symbolName );
if( index != wxNOT_FOUND )
{
// a candidate is found!
libIdCandidateCount++;
wxString newLibid = lib + ':' + symbolName;
// Uses the first found. Most of time, it is alone.
// Others will be stored in a candidate list
if( libIdCandidateCount <= 1 )
{
m_grid->SetCellValue( grid_row_idx, COL_NEW_LIBID, UnescapeString( newLibid ) );
candidateSymbNames.Add( m_grid->GetCellValue( grid_row_idx, COL_NEW_LIBID ) );
fixesCount++;
}
else // Store other candidates for later selection
{
candidateSymbNames.Add( UnescapeString( newLibid ) );
}
}
}
// If more than one LIB_ID candidate, ask for selection between candidates:
if( libIdCandidateCount > 1 )
{
// Mainly for user: select the row being edited
m_grid->SelectRow( grid_row_idx );
wxString msg;
msg.Printf( _( "Available Candidates for %s " ),
m_grid->GetCellValue( grid_row_idx, COL_CURR_LIBID ) );
wxSingleChoiceDialog dlg ( this, msg,
wxString::Format( _( "Candidates count %d " ),
libIdCandidateCount ),
candidateSymbNames );
if( dlg.ShowModal() == wxID_OK )
m_grid->SetCellValue( grid_row_idx, COL_NEW_LIBID, dlg.GetStringSelection() );
}
}
if( fixesCount < m_OrphansRowIndexes.size() ) // Not all orphan symbols are fixed.
{
wxMessageBox( wxString::Format( _( "%u link(s) mapped, %u not found" ),
fixesCount,
(unsigned) m_OrphansRowIndexes.size() - fixesCount ) );
}
else
{
wxMessageBox( wxString::Format( _( "All %u link(s) resolved" ), fixesCount ) );
}
}
bool DIALOG_EDIT_SYMBOLS_LIBID::setLibIdByBrowser( int aRow )
{
// Use library viewer to choose a symbol
std::vector<PICKED_SYMBOL> dummyHistory;
std::vector<PICKED_SYMBOL> dummyAlreadyPlaced;
LIB_ID preselected;
wxString current = getLibIdValue( m_grid, aRow, COL_NEW_LIBID );
if( current.IsEmpty() )
current = getLibIdValue( m_grid, aRow, COL_CURR_LIBID );
if( !current.IsEmpty() )
preselected.Parse( current, true );
PICKED_SYMBOL sel = GetParent()->PickSymbolFromLibrary(
nullptr, dummyHistory, dummyAlreadyPlaced, false, &preselected, false );
if( sel.LibId.empty() ) // command aborted
return false;
if( !sel.LibId.IsValid() ) // Should not occur
{
wxMessageBox( _( "Invalid symbol library identifier" ) );
return false;
}
wxString new_libid;
new_libid = sel.LibId.Format().wx_str();
m_grid->SetCellValue( aRow, COL_NEW_LIBID, UnescapeString( new_libid ) );
return true;
}
bool DIALOG_EDIT_SYMBOLS_LIBID::TransferDataFromWindow()
{
if( !validateLibIds() )
return false;
auto getName = []( const LIB_ID& aLibId )
{
return UnescapeString( aLibId.GetLibItemName().wx_str() );
};
int row_max = m_grid->GetNumberRows() - 1;
for( int row = 0; row <= row_max; row++ )
{
wxString new_libid = getLibIdValue( m_grid, row, COL_NEW_LIBID );
if( new_libid.IsEmpty() || new_libid == getLibIdValue( m_grid, row, COL_CURR_LIBID ) )
continue;
// A new lib id is found and was already validated.
LIB_ID id;
id.Parse( new_libid, true );
for( SYMBOL_CANDIDATE& candidate : m_symbols )
{
if( candidate.m_Row != row )
continue;
LIB_SYMBOL* symbol = nullptr;
try
{
symbol = PROJECT_SCH::SchSymbolLibTable( &Prj() )->LoadSymbol( id );
}
catch( const IO_ERROR& ioe )
{
wxString msg;
msg.Printf( _( "Error loading symbol %s from library %s.\n\n%s" ),
id.GetLibItemName().wx_str(),
id.GetLibNickname().wx_str(),
ioe.What() );
DisplayError( this, msg );
}
if( symbol == nullptr )
continue;
GetParent()->SaveCopyInUndoList( candidate.m_Screen, candidate.m_Symbol,
UNDO_REDO::CHANGED, m_isModified );
m_isModified = true;
candidate.m_Screen->Remove( candidate.m_Symbol );
SCH_FIELD* value = candidate.m_Symbol->GetField( VALUE_FIELD );
// If value is a proxy for the itemName then make sure it gets updated
if( getName( candidate.m_Symbol->GetLibId() ) == value->GetText() )
candidate.m_Symbol->SetValueFieldText( getName( id ) );
candidate.m_Symbol->SetLibId( id );
candidate.m_Symbol->SetLibSymbol( symbol->Flatten().release() );
candidate.m_Screen->Append( candidate.m_Symbol );
candidate.m_Screen->SetContentModified();
if ( m_checkBoxUpdateFields->IsChecked() )
{
candidate.m_Symbol->UpdateFields( nullptr,
false, /* update style */
false, /* update ref */
false, /* update other fields */
false, /* reset ref */
true /* reset other fields */ );
}
}
}
return true;
}
void DIALOG_EDIT_SYMBOLS_LIBID::AdjustGridColumns()
{
// Account for scroll bars
int width = KIPLATFORM::UI::GetUnobscuredSize( m_grid ).x;
int colWidth = width / 3;
m_grid->SetColSize( COL_REFS, colWidth );
width -= colWidth;
colWidth = 0;
for( int row = 0; row < m_grid->GetNumberRows(); ++row )
{
wxString cellValue = m_grid->GetCellValue( row, COL_CURR_LIBID );
colWidth = std::max( colWidth, KIUI::GetTextSize( cellValue, m_grid ).x );
}
colWidth += 20;
m_grid->SetColSize( COL_CURR_LIBID, colWidth );
width -= colWidth;
colWidth = 0;
for( int row = 0; row < m_grid->GetNumberRows(); ++row )
{
wxString cellValue = m_grid->GetCellValue( row, COL_NEW_LIBID );
colWidth = std::max( colWidth, KIUI::GetTextSize( cellValue, m_grid ).x );
}
colWidth += 20;
m_grid->SetColSize( COL_NEW_LIBID, std::max( colWidth, width ) );
}
void DIALOG_EDIT_SYMBOLS_LIBID::OnSizeGrid( wxSizeEvent& event )
{
AdjustGridColumns();
wxClientDC dc( this );
// wxWidgets' AutoRowHeight fails when used with wxGridCellAutoWrapStringRenderer
for( int row = 0; row < m_grid->GetNumberRows(); ++row )
m_grid->SetRowSize( row, m_autoWrapRenderer->GetHeight( dc, m_grid, row, COL_REFS ) );
event.Skip();
}
bool InvokeDialogEditSymbolsLibId( SCH_EDIT_FRAME* aCaller )
{
// This dialog itself subsequently can invoke a KIWAY_PLAYER as a quasimodal
// frame. Therefore this dialog as a modal frame parent, MUST be run under
// quasimodal mode for the quasimodal frame support to work. So don't use
// the QUASIMODAL macros here.
DIALOG_EDIT_SYMBOLS_LIBID dlg( aCaller );
// DO NOT use ShowModal() here, otherwise the library browser will not work properly.
dlg.ShowQuasiModal();
return dlg.IsSchematicModified();
}