void mapExistingAnnotation( std::map<timestamp_t, wxString>& aMap ) { SCH_SHEET_LIST sheets( g_RootSheet ); SCH_REFERENCE_LIST references; sheets.GetComponents( references ); for( size_t i = 0; i < references.GetCount(); i++ ) { SCH_COMPONENT* comp = references[ i ].GetComp(); wxString ref = comp->GetField( REFERENCE )->GetFullyQualifiedText(); if( !ref.Contains( wxT( "?" ) ) ) aMap[ comp->GetTimeStamp() ] = ref; } }
bool SCH_EDIT_FRAME::ProcessCmpToFootprintLinkFile( wxString& aFullFilename, bool aForceFieldsVisibleAttribute, bool aFieldsVisibleAttributeState ) { // Build a flat list of components in schematic: SCH_REFERENCE_LIST referencesList; SCH_SHEET_LIST SheetList; SheetList.GetComponents( referencesList, false ); FILE* cmpFile = wxFopen( aFullFilename, wxT( "rt" ) ); if( cmpFile == NULL ) return false; // cmpFileReader dtor will close cmpFile FILE_LINE_READER cmpFileReader( cmpFile, aFullFilename ); // Now, for each component found in file, // replace footprint field value by the new value: wxString reference; wxString footprint; wxString buffer; wxString value; while( cmpFileReader.ReadLine() ) { buffer = FROM_UTF8( cmpFileReader.Line() ); if( ! buffer.StartsWith( wxT("BeginCmp") ) ) continue; // Begin component description. reference.Empty(); footprint.Empty(); while( cmpFileReader.ReadLine() ) { buffer = FROM_UTF8( cmpFileReader.Line() ); if( buffer.StartsWith( wxT("EndCmp") ) ) break; // store string value, stored between '=' and ';' delimiters. value = buffer.AfterFirst( '=' ); value = value.BeforeLast( ';'); value.Trim(true); value.Trim(false); if( buffer.StartsWith( wxT("Reference") ) ) { reference = value; continue; } if( buffer.StartsWith( wxT("IdModule =" ) ) ) { footprint = value; continue; } } // A block is read: initialize the footprint field of the correponding component // if the footprint name is not empty if( reference.IsEmpty() ) continue; // Search the component in the flat list for( unsigned ii = 0; ii < referencesList.GetCount(); ii++ ) { if( reference.CmpNoCase( referencesList[ii].GetRef() ) == 0 ) { // We have found a candidate. // Note: it can be not unique (multiple parts per package) // So we *do not* stop the search here SCH_COMPONENT* component = referencesList[ii].GetComponent(); SCH_FIELD * fpfield = component->GetField( FOOTPRINT ); fpfield->SetText( footprint ); if( aForceFieldsVisibleAttribute ) { component->GetField( FOOTPRINT ) ->SetVisible( aFieldsVisibleAttributeState ); } } } } return true; }
void SCH_EDIT_FRAME::backAnnotateFootprints( const std::string& aChangedSetOfReferences ) throw( IO_ERROR, boost::bad_pointer ) { // Build a flat list of components in schematic: SCH_REFERENCE_LIST refs; SCH_SHEET_LIST sheets( g_RootSheet ); bool isChanged = false; sheets.GetComponents( Prj().SchLibs(), refs, false ); DSNLEXER lexer( aChangedSetOfReferences, FROM_UTF8( __func__ ) ); PTREE doc; try { Scan( &doc, &lexer ); #if defined(DEBUG) && 0 STRING_FORMATTER sf; Format( &sf, 0, 0, doc ); printf( "%s: '%s'\n", __func__, sf.GetString().c_str() ); #endif CPTREE& back_anno = doc.get_child( "back_annotation" ); wxString footprint; for( PTREE::const_iterator ref = back_anno.begin(); ref != back_anno.end(); ++ref ) { wxASSERT( ref->first == "ref" ); wxString reference = (UTF8&) ref->second.front().first; // Ensure the "fpid" node contains a footprint name, // and get it if exists if( ref->second.get_child( "fpid" ).size() ) { wxString tmp = (UTF8&) ref->second.get_child( "fpid" ).front().first; footprint = tmp; } else footprint.Empty(); // DBG( printf( "%s: ref:%s fpid:%s\n", __func__, TO_UTF8( reference ), TO_UTF8( footprint ) ); ) // Search the component in the flat list for( unsigned ii = 0; ii < refs.GetCount(); ++ii ) { if( reference == refs[ii].GetRef() ) { // We have found a candidate. // Note: it can be not unique (multiple parts per package) // So we *do not* stop the search here SCH_COMPONENT* component = refs[ii].GetComp(); SCH_FIELD* fpfield = component->GetField( FOOTPRINT ); const wxString& oldfp = fpfield->GetText(); if( !oldfp && fpfield->IsVisible() ) { fpfield->SetVisible( false ); } // DBG( printf("%s: ref:%s fpid:%s\n", __func__, TO_UTF8( refs[ii].GetRef() ), TO_UTF8( footprint ) );) if( oldfp != footprint ) isChanged = true; fpfield->SetText( footprint ); } } } } catch( const PTREE_ERROR& ex ) { // remap the exception to something the caller is likely to understand. THROW_IO_ERROR( ex.what() ); } if( isChanged ) OnModify(); }
void SCH_REFERENCE_LIST::Annotate( bool aUseSheetNum, int aSheetIntervalId, int aStartNumber, SCH_MULTI_UNIT_REFERENCE_MAP aLockedUnitMap ) { if ( componentFlatList.size() == 0 ) return; int LastReferenceNumber = 0; int NumberOfUnits, Unit; /* calculate index of the first component with the same reference prefix * than the current component. All components having the same reference * prefix will receive a reference number with consecutive values: * IC .. will be set to IC4, IC4, IC5 ... */ unsigned first = 0; // calculate the last used number for this reference prefix: #ifdef USE_OLD_ALGO int minRefId = 0; // when using sheet number, ensure ref number >= sheet number* aSheetIntervalId if( aUseSheetNum ) minRefId = componentFlatList[first].m_SheetNum * aSheetIntervalId; LastReferenceNumber = GetLastReference( first, minRefId ); #else int minRefId; // when using sheet number, ensure ref number >= sheet number* aSheetIntervalId if( aUseSheetNum ) minRefId = componentFlatList[first].m_SheetNum * aSheetIntervalId + 1; else minRefId = aStartNumber + 1; // For multi units components, when "keep order of multi unit" option is selected, // store the list of already used full references. // The algorithm try to allocate the new reference to components having the same // old reference. // This algo works fine as long as the previous annotation has no duplicates. // But when a hierarchy is reannotated with this option, the previous anotation can // have duplicate references, and obviously we must fix these duplicate. // therefore do not try to allocate a full reference more than once when trying // to keep this order of multi units. // inUseRefs keep trace of previously allocated references std::unordered_set<wxString> inUseRefs; // This is the list of all Id already in use for a given reference prefix. // Will be refilled for each new reference prefix. std::vector<int>idList; GetRefsInUse( first, idList, minRefId ); #endif for( unsigned ii = 0; ii < componentFlatList.size(); ii++ ) { if( componentFlatList[ii].m_Flag ) continue; // Check whether this component is in aLockedUnitMap. SCH_REFERENCE_LIST* lockedList = NULL; for( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair : aLockedUnitMap ) { unsigned n_refs = pair.second.GetCount(); for( unsigned thisRefI = 0; thisRefI < n_refs; ++thisRefI ) { SCH_REFERENCE &thisRef = pair.second[thisRefI]; if( thisRef.IsSameInstance( componentFlatList[ii] ) ) { lockedList = &pair.second; break; } } if( lockedList != NULL ) break; } if( ( componentFlatList[first].CompareRef( componentFlatList[ii] ) != 0 ) || ( aUseSheetNum && ( componentFlatList[first].m_SheetNum != componentFlatList[ii].m_SheetNum ) ) ) { // New reference found: we need a new ref number for this reference first = ii; #ifdef USE_OLD_ALGO minRefId = 0; // when using sheet number, ensure ref number >= sheet number* aSheetIntervalId if( aUseSheetNum ) minRefId = componentFlatList[ii].m_SheetNum * aSheetIntervalId; LastReferenceNumber = GetLastReference( ii, minRefId ); #else // when using sheet number, ensure ref number >= sheet number* aSheetIntervalId if( aUseSheetNum ) minRefId = componentFlatList[ii].m_SheetNum * aSheetIntervalId + 1; else minRefId = aStartNumber + 1; GetRefsInUse( first, idList, minRefId ); #endif } // Annotation of one part per package components (trivial case). if( componentFlatList[ii].GetLibPart()->GetUnitCount() <= 1 ) { if( componentFlatList[ii].m_IsNew ) { #ifdef USE_OLD_ALGO LastReferenceNumber++; #else LastReferenceNumber = CreateFirstFreeRefId( idList, minRefId ); #endif componentFlatList[ii].m_NumRef = LastReferenceNumber; } componentFlatList[ii].m_Unit = 1; componentFlatList[ii].m_Flag = 1; componentFlatList[ii].m_IsNew = false; continue; } // Annotation of multi-unit parts ( n units per part ) (complex case) NumberOfUnits = componentFlatList[ii].GetLibPart()->GetUnitCount(); if( componentFlatList[ii].m_IsNew ) { #ifdef USE_OLD_ALGO LastReferenceNumber++; #else LastReferenceNumber = CreateFirstFreeRefId( idList, minRefId ); #endif componentFlatList[ii].m_NumRef = LastReferenceNumber; if( !componentFlatList[ii].IsUnitsLocked() ) componentFlatList[ii].m_Unit = 1; componentFlatList[ii].m_Flag = 1; } // If this component is in aLockedUnitMap, copy the annotation to all // components that are not it if( lockedList != NULL ) { unsigned n_refs = lockedList->GetCount(); for( unsigned thisRefI = 0; thisRefI < n_refs; ++thisRefI ) { SCH_REFERENCE &thisRef = (*lockedList)[thisRefI]; if( thisRef.IsSameInstance( componentFlatList[ii] ) ) { // This is the component we're currently annotating. Hold the unit! componentFlatList[ii].m_Unit = thisRef.m_Unit; } if( thisRef.CompareValue( componentFlatList[ii] ) != 0 ) continue; if( thisRef.CompareLibName( componentFlatList[ii] ) != 0 ) continue; // Find the matching component for( unsigned jj = ii + 1; jj < componentFlatList.size(); jj++ ) { if( ! thisRef.IsSameInstance( componentFlatList[jj] ) ) continue; wxString ref_candidate = buildFullReference( componentFlatList[ii] ); // propagate the new reference and unit selection to the "old" component, // if this new full reference is not already used (can happens when initial // multiunits components have duplicate references) if( inUseRefs.find( ref_candidate ) == inUseRefs.end() ) { componentFlatList[jj].m_NumRef = componentFlatList[ii].m_NumRef; componentFlatList[jj].m_Unit = thisRef.m_Unit; componentFlatList[jj].m_IsNew = false; componentFlatList[jj].m_Flag = 1; // lock this new full reference inUseRefs.insert( ref_candidate ); break; } } } } else { /* search for others units of this component. * we search for others parts that have the same value and the same * reference prefix (ref without ref number) */ for( Unit = 1; Unit <= NumberOfUnits; Unit++ ) { if( componentFlatList[ii].m_Unit == Unit ) continue; int found = FindUnit( ii, Unit ); if( found >= 0 ) continue; // this unit exists for this reference (unit already annotated) // Search a component to annotate ( same prefix, same value, not annotated) for( unsigned jj = ii + 1; jj < componentFlatList.size(); jj++ ) { if( componentFlatList[jj].m_Flag ) // already tested continue; if( componentFlatList[ii].CompareRef( componentFlatList[jj] ) != 0 ) continue; if( componentFlatList[jj].CompareValue( componentFlatList[ii] ) != 0 ) continue; if( componentFlatList[jj].CompareLibName( componentFlatList[ii] ) != 0 ) continue; if( !componentFlatList[jj].m_IsNew ) continue; // Component without reference number found, annotate it if possible if( !componentFlatList[jj].IsUnitsLocked() || ( componentFlatList[jj].m_Unit == Unit ) ) { componentFlatList[jj].m_NumRef = componentFlatList[ii].m_NumRef; componentFlatList[jj].m_Unit = Unit; componentFlatList[jj].m_Flag = 1; componentFlatList[jj].m_IsNew = false; break; } } } } } }
void SCH_REFERENCE_LIST::Annotate( bool aUseSheetNum, int aSheetIntervalId, SCH_MULTI_UNIT_REFERENCE_MAP aLockedUnitMap ) { if ( componentFlatList.size() == 0 ) return; int LastReferenceNumber = 0; int NumberOfUnits, Unit; /* calculate index of the first component with the same reference prefix * than the current component. All components having the same reference * prefix will receive a reference number with consecutive values: * IC .. will be set to IC4, IC4, IC5 ... */ unsigned first = 0; // calculate the last used number for this reference prefix: #ifdef USE_OLD_ALGO int minRefId = 0; // when using sheet number, ensure ref number >= sheet number* aSheetIntervalId if( aUseSheetNum ) minRefId = componentFlatList[first].m_SheetNum * aSheetIntervalId; LastReferenceNumber = GetLastReference( first, minRefId ); #else int minRefId = 1; // when using sheet number, ensure ref number >= sheet number* aSheetIntervalId if( aUseSheetNum ) minRefId = componentFlatList[first].m_SheetNum * aSheetIntervalId + 1; // This is the list of all Id already in use for a given reference prefix. // Will be refilled for each new reference prefix. std::vector<int>idList; GetRefsInUse( first, idList, minRefId ); #endif for( unsigned ii = 0; ii < componentFlatList.size(); ii++ ) { if( componentFlatList[ii].m_Flag ) continue; // Check whether this component is in aLockedUnitMap. SCH_REFERENCE_LIST* lockedList = NULL; for( SCH_MULTI_UNIT_REFERENCE_MAP::value_type& pair : aLockedUnitMap ) { unsigned n_refs = pair.second.GetCount(); for( unsigned thisRefI = 0; thisRefI < n_refs; ++thisRefI ) { SCH_REFERENCE &thisRef = pair.second[thisRefI]; if( thisRef.IsSameInstance( componentFlatList[ii] ) ) { lockedList = &pair.second; break; } } if( lockedList != NULL ) break; } if( ( componentFlatList[first].CompareRef( componentFlatList[ii] ) != 0 ) || ( aUseSheetNum && ( componentFlatList[first].m_SheetNum != componentFlatList[ii].m_SheetNum ) ) ) { // New reference found: we need a new ref number for this reference first = ii; #ifdef USE_OLD_ALGO minRefId = 0; // when using sheet number, ensure ref number >= sheet number* aSheetIntervalId if( aUseSheetNum ) minRefId = componentFlatList[ii].m_SheetNum * aSheetIntervalId; LastReferenceNumber = componentFlatList.GetLastReference( ii, minRefId ); #else minRefId = 1; // when using sheet number, ensure ref number >= sheet number* aSheetIntervalId if( aUseSheetNum ) minRefId = componentFlatList[ii].m_SheetNum * aSheetIntervalId + 1; GetRefsInUse( first, idList, minRefId ); #endif } // Annotation of one part per package components (trivial case). if( componentFlatList[ii].GetLibPart()->GetUnitCount() <= 1 ) { if( componentFlatList[ii].m_IsNew ) { #ifdef USE_OLD_ALGO LastReferenceNumber++; #else LastReferenceNumber = CreateFirstFreeRefId( idList, minRefId ); #endif componentFlatList[ii].m_NumRef = LastReferenceNumber; } componentFlatList[ii].m_Unit = 1; componentFlatList[ii].m_Flag = 1; componentFlatList[ii].m_IsNew = false; continue; } // Annotation of multi-unit parts ( n units per part ) (complex case) NumberOfUnits = componentFlatList[ii].GetLibPart()->GetUnitCount(); if( componentFlatList[ii].m_IsNew ) { #ifdef USE_OLD_ALGO LastReferenceNumber++; #else LastReferenceNumber = CreateFirstFreeRefId( idList, minRefId ); #endif componentFlatList[ii].m_NumRef = LastReferenceNumber; if( !componentFlatList[ii].IsUnitsLocked() ) componentFlatList[ii].m_Unit = 1; componentFlatList[ii].m_Flag = 1; } // If this component is in aLockedUnitMap, copy the annotation to all // components that are not it if( lockedList != NULL ) { unsigned n_refs = lockedList->GetCount(); for( unsigned thisRefI = 0; thisRefI < n_refs; ++thisRefI ) { SCH_REFERENCE &thisRef = (*lockedList)[thisRefI]; if( thisRef.IsSameInstance( componentFlatList[ii] ) ) { // This is the component we're currently annotating. Hold the unit! componentFlatList[ii].m_Unit = thisRef.m_Unit; } if( thisRef.CompareValue( componentFlatList[ii] ) != 0 ) continue; if( thisRef.CompareLibName( componentFlatList[ii] ) != 0 ) continue; // Find the matching component for( unsigned jj = ii + 1; jj < componentFlatList.size(); jj++ ) { if( ! thisRef.IsSameInstance( componentFlatList[jj] ) ) continue; componentFlatList[jj].m_NumRef = componentFlatList[ii].m_NumRef; componentFlatList[jj].m_Unit = thisRef.m_Unit; componentFlatList[jj].m_IsNew = false; componentFlatList[jj].m_Flag = 1; break; } } } else { /* search for others units of this component. * we search for others parts that have the same value and the same * reference prefix (ref without ref number) */ for( Unit = 1; Unit <= NumberOfUnits; Unit++ ) { if( componentFlatList[ii].m_Unit == Unit ) continue; int found = FindUnit( ii, Unit ); if( found >= 0 ) continue; // this unit exists for this reference (unit already annotated) // Search a component to annotate ( same prefix, same value, not annotated) for( unsigned jj = ii + 1; jj < componentFlatList.size(); jj++ ) { if( componentFlatList[jj].m_Flag ) // already tested continue; if( componentFlatList[ii].CompareRef( componentFlatList[jj] ) != 0 ) continue; if( componentFlatList[jj].CompareValue( componentFlatList[ii] ) != 0 ) continue; if( componentFlatList[jj].CompareLibName( componentFlatList[ii] ) != 0 ) continue; if( !componentFlatList[jj].m_IsNew ) continue; // Component without reference number found, annotate it if possible if( !componentFlatList[jj].IsUnitsLocked() || ( componentFlatList[jj].m_Unit == Unit ) ) { componentFlatList[jj].m_NumRef = componentFlatList[ii].m_NumRef; componentFlatList[jj].m_Unit = Unit; componentFlatList[jj].m_Flag = 1; componentFlatList[jj].m_IsNew = false; break; } } } } } }
void SCH_EDIT_FRAME::AnnotateComponents( bool aAnnotateSchematic, ANNOTATE_ORDER_T aSortOption, ANNOTATE_OPTION_T aAlgoOption, int aStartNumber, bool aResetAnnotation, bool aRepairTimestamps, bool aLockUnits, REPORTER& aReporter ) { SCH_REFERENCE_LIST references; SCH_SCREENS screens; // Build the sheet list. SCH_SHEET_LIST sheets( g_RootSheet ); // Map of locked components SCH_MULTI_UNIT_REFERENCE_MAP lockedComponents; // Map of previous annotation for building info messages std::map<timestamp_t, wxString> previousAnnotation; // Test for and replace duplicate time stamps in components and sheets. Duplicate // time stamps can happen with old schematics, schematic conversions, or manual // editing of files. if( aRepairTimestamps ) { int count = screens.ReplaceDuplicateTimeStamps(); if( count ) { wxString msg; msg.Printf( _( "%d duplicate time stamps were found and replaced." ), count ); aReporter.ReportTail( msg, REPORTER::RPT_WARNING ); } } // If units must be locked, collect all the sets that must be annotated together. if( aLockUnits ) { if( aAnnotateSchematic ) { sheets.GetMultiUnitComponents( lockedComponents ); } else { m_CurrentSheet->GetMultiUnitComponents( lockedComponents ); } } // Store previous annotations for building info messages mapExistingAnnotation( previousAnnotation ); // If it is an annotation for all the components, reset previous annotation. if( aResetAnnotation ) DeleteAnnotation( !aAnnotateSchematic ); // Set sheet number and number of sheets. SetSheetNumberAndCount(); // Build component list if( aAnnotateSchematic ) { sheets.GetComponents( references ); } else { m_CurrentSheet->GetComponents( references ); } // Break full components reference in name (prefix) and number: // example: IC1 become IC, and 1 references.SplitReferences(); switch( aSortOption ) { default: case SORT_BY_X_POSITION: references.SortByXCoordinate(); break; case SORT_BY_Y_POSITION: references.SortByYCoordinate(); break; } bool useSheetNum = false; int idStep = 100; switch( aAlgoOption ) { default: case INCREMENTAL_BY_REF: break; case SHEET_NUMBER_X_100: useSheetNum = true; break; case SHEET_NUMBER_X_1000: useSheetNum = true; idStep = 1000; break; } // Recalculate and update reference numbers in schematic references.Annotate( useSheetNum, idStep, aStartNumber, lockedComponents ); references.UpdateAnnotation(); for( size_t i = 0; i < references.GetCount(); i++ ) { SCH_COMPONENT* comp = references[ i ].GetComp(); wxString prevRef = previousAnnotation[ comp->GetTimeStamp() ]; wxString newRef = comp->GetField( REFERENCE )->GetFullyQualifiedText(); wxString msg; if( prevRef.Length() ) { if( newRef == prevRef ) continue; if( comp->GetUnitCount() > 1 ) msg.Printf( _( "Updated %s (unit %s) from %s to %s" ), GetChars( comp->GetField( VALUE )->GetShownText() ), LIB_PART::SubReference( comp->GetUnit(), false ), GetChars( prevRef ), GetChars( newRef ) ); else msg.Printf( _( "Updated %s from %s to %s" ), GetChars( comp->GetField( VALUE )->GetShownText() ), GetChars( prevRef ), GetChars( newRef ) ); } else { if( comp->GetUnitCount() > 1 ) msg.Printf( _( "Annotated %s (unit %s) as %s" ), GetChars( comp->GetField( VALUE )->GetShownText() ), LIB_PART::SubReference( comp->GetUnit(), false ), GetChars( newRef ) ); else msg.Printf( _( "Annotated %s as %s" ), GetChars( comp->GetField( VALUE )->GetShownText() ), GetChars( newRef ) ); } aReporter.Report( msg, REPORTER::RPT_ACTION ); } // Final control (just in case ... ). if( !CheckAnnotate( aReporter, !aAnnotateSchematic ) ) aReporter.ReportTail( _( "Annotation complete." ), REPORTER::RPT_ACTION ); // Update on screen references, that can be modified by previous calculations: m_CurrentSheet->UpdateAllScreenReferences(); SetSheetNumberAndCount(); SyncView(); GetCanvas()->Refresh(); OnModify(); }
/** * Add a list of component items to the BOM manager * Creates consolidated groups of components as required */ void BOM_TABLE_MODEL::SetComponents( SCH_REFERENCE_LIST aRefs, const TEMPLATE_FIELDNAMES& aTemplateFields ) { // Add default columns AddDefaultColumns(); // Extract all component fields for( unsigned int ii=0; ii<aRefs.GetCount(); ii++ ) { auto ref = aRefs.GetItem( ii ); auto cmp = ref.GetComp(); if( cmp ) { AddComponentFields( cmp ); } } // Add template fields if they are not already added for( auto field : aTemplateFields ) { BOM_COLUMN* col; col = ColumnList.GetColumnByTitle( field.m_Name ); if( !col ) { col = new BOM_COLUMN( ColumnList.NextFieldId(), BOM_COL_TYPE_USER, field.m_Name, true, false ); ColumnList.AddColumn( col ); } // Add template value for that field m_fieldTemplates[col->Id()] = field.m_Value; } // Group multi-unit components together m_components.clear(); m_fieldValues.clear(); // Iterate through each unique component for( unsigned int ii=0; ii<aRefs.GetCount(); ii++ ) { auto ref = aRefs.GetItem( ii ); bool found = false; for( auto& cmp : m_components ) { if( cmp->AddUnit( ref ) ) { found = true; break; } } if( !found ) { // Find the field:value map associated with this component wxString refDes = ref.GetComp()->GetField( REFERENCE )->GetText(); bool dataFound = false; BOM_FIELD_VALUES* values; for( auto& data : m_fieldValues ) { // Look for a match based on RefDes if( data->GetReference().Cmp( refDes ) == 0 ) { dataFound = true; values = &*data; } } if( !dataFound ) { values = new BOM_FIELD_VALUES( refDes, &m_fieldTemplates ); m_fieldValues.push_back( std::unique_ptr<BOM_FIELD_VALUES>( values ) ); } auto* newComponent = new BOM_TABLE_COMPONENT( nullptr, &ColumnList, values ); newComponent->AddUnit( ref ); m_components.push_back( std::unique_ptr<BOM_TABLE_COMPONENT>( newComponent ) ); } } SetBackupPoint(); }