927 lines
29 KiB
C++
927 lines
29 KiB
C++
|
|
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2011-2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
|
* Copyright (C) 2007-2017 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
|
|
*/
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <fctsys.h>
|
|
#include <pgm_base.h>
|
|
#include <kiway.h>
|
|
#include <confirm.h>
|
|
#include <class_drawpanel.h>
|
|
#include <sch_edit_frame.h>
|
|
#include <id.h>
|
|
#include <base_units.h>
|
|
|
|
#include <general.h>
|
|
#include <lib_edit_frame.h>
|
|
#include <class_library.h>
|
|
#include <sch_component.h>
|
|
#include <sch_field.h>
|
|
#include <template_fieldnames.h>
|
|
#include <dialog_helpers.h>
|
|
#include <sch_validators.h>
|
|
|
|
#include <bitmaps.h>
|
|
#include "eda_doc.h"
|
|
|
|
#include <dialog_edit_libentry_fields_in_lib_base.h>
|
|
#ifdef KICAD_SPICE
|
|
#include <dialog_spice_model.h>
|
|
#include <netlist_exporter_pspice.h>
|
|
#endif /* KICAD_SPICE */
|
|
|
|
// Local variables:
|
|
static int s_SelectedRow;
|
|
|
|
#define COLUMN_FIELD_NAME 0
|
|
#define COLUMN_TEXT 1
|
|
|
|
class DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB : public DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB_BASE
|
|
{
|
|
public:
|
|
DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB( LIB_EDIT_FRAME* aParent, LIB_PART* aLibEntry );
|
|
//~DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB() {}
|
|
|
|
private:
|
|
// Events handlers:
|
|
void OnInitDialog( wxInitDialogEvent& event ) override;
|
|
void OnCloseDialog( wxCloseEvent& event ) override;
|
|
void OnSizeFieldsList( wxSizeEvent& event ) override;
|
|
|
|
void OnListItemDeselected( wxListEvent& event ) override;
|
|
void OnListItemSelected( wxListEvent& event ) override;
|
|
void addFieldButtonHandler( wxCommandEvent& event ) override;
|
|
void EditSpiceModel( wxCommandEvent& event ) override;
|
|
|
|
/**
|
|
* Function deleteFieldButtonHandler
|
|
* deletes a field.
|
|
* MANDATORY_FIELDS cannot be deleted.
|
|
* If a field is empty, it is removed.
|
|
* if not empty, the text is removed.
|
|
*/
|
|
void deleteFieldButtonHandler( wxCommandEvent& event ) override;
|
|
|
|
void moveUpButtonHandler( wxCommandEvent& event ) override;
|
|
void moveDownButtonHandler( wxCommandEvent& event ) override;
|
|
void OnCancelButtonClick( wxCommandEvent& event ) override;
|
|
void OnOKButtonClick( wxCommandEvent& event ) override;
|
|
void showButtonHandler( wxCommandEvent& event ) override;
|
|
|
|
// internal functions:
|
|
void setSelectedFieldNdx( int aFieldNdx );
|
|
|
|
int getSelectedFieldNdx();
|
|
|
|
/**
|
|
* Function initBuffers
|
|
* sets up to edit the given component.
|
|
*/
|
|
void initBuffers();
|
|
|
|
/**
|
|
* Function findField
|
|
* searches m_FieldsBuf and returns a LIB_FIELD with \a aFieldName or NULL if
|
|
* not found.
|
|
*/
|
|
LIB_FIELD* findField( const wxString& aFieldName );
|
|
|
|
/**
|
|
* Function copySelectedFieldToPanel
|
|
* sets the values displayed on the panel according to
|
|
* the currently selected field row
|
|
*/
|
|
void copySelectedFieldToPanel();
|
|
|
|
/**
|
|
* Function copyPanelToSelectedField
|
|
* copies the values displayed on the panel fields to the currently selected field
|
|
* @return bool - true if all fields are OK, else false if the user has put
|
|
* bad data into a field, and this value can be used to deny a row change.
|
|
*/
|
|
bool copyPanelToSelectedField();
|
|
|
|
void setRowItem( int aFieldNdx, const wxString& aName, const wxString& aValue );
|
|
void setRowItem( int aFieldNdx, const LIB_FIELD& aField )
|
|
{
|
|
setRowItem( aFieldNdx, aField.GetName(), aField.GetText() );
|
|
}
|
|
|
|
/**
|
|
* Function updateDisplay
|
|
* update the listbox showing fields, according to the fields texts
|
|
* must be called after a text change in fields, if this change is not an edition
|
|
*/
|
|
void updateDisplay( )
|
|
{
|
|
for( unsigned ii = MANDATORY_FIELDS; ii<m_FieldsBuf.size(); ii++ )
|
|
setRowItem( ii, m_FieldsBuf[ii] );
|
|
}
|
|
|
|
LIB_EDIT_FRAME* m_parent;
|
|
LIB_PART* m_libEntry;
|
|
bool m_skipCopyFromPanel;
|
|
|
|
/// a copy of the edited component's LIB_FIELDs
|
|
std::vector <LIB_FIELD> m_FieldsBuf;
|
|
};
|
|
|
|
|
|
void LIB_EDIT_FRAME::InstallFieldsEditorDialog( wxCommandEvent& event )
|
|
{
|
|
if( !GetCurPart() )
|
|
return;
|
|
|
|
m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor() );
|
|
|
|
DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB dlg( this, GetCurPart() );
|
|
|
|
if( GetDrawItem() && GetDrawItem()->Type() == LIB_FIELD_T )
|
|
SetDrawItem( nullptr ); // selected LIB_FIELD might be deleted
|
|
|
|
// 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.
|
|
if( dlg.ShowQuasiModal() != wxID_OK )
|
|
return;
|
|
|
|
UpdateAliasSelectList();
|
|
UpdatePartSelectList();
|
|
DisplayLibInfos();
|
|
Refresh();
|
|
}
|
|
|
|
|
|
DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB(
|
|
LIB_EDIT_FRAME* aParent,
|
|
LIB_PART* aLibEntry ) :
|
|
DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB_BASE( aParent )
|
|
{
|
|
m_parent = aParent;
|
|
m_libEntry = aLibEntry;
|
|
m_skipCopyFromPanel = false;
|
|
|
|
#ifndef KICAD_SPICE
|
|
m_spiceFieldsButton->Show(false);
|
|
#endif
|
|
|
|
// Configure button logos
|
|
addFieldButton->SetBitmap( KiBitmap( plus_xpm ) );
|
|
deleteFieldButton->SetBitmap( KiBitmap( minus_xpm ) );
|
|
moveUpButton->SetBitmap( KiBitmap( go_up_xpm ) );
|
|
moveDownButton->SetBitmap( KiBitmap( go_down_xpm ) );
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::OnInitDialog( wxInitDialogEvent& event )
|
|
{
|
|
m_skipCopyFromPanel = false;
|
|
wxListItem columnLabel;
|
|
|
|
columnLabel.SetImage( -1 );
|
|
|
|
columnLabel.SetText( _( "Name" ) );
|
|
fieldListCtrl->InsertColumn( COLUMN_FIELD_NAME, columnLabel );
|
|
|
|
columnLabel.SetText( _( "Value" ) );
|
|
fieldListCtrl->InsertColumn( COLUMN_TEXT, columnLabel );
|
|
|
|
m_staticTextUnitSize->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
|
|
m_staticTextUnitPosX->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
|
|
m_staticTextUnitPosY->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
|
|
|
|
initBuffers();
|
|
copySelectedFieldToPanel();
|
|
|
|
stdDialogButtonSizerOK->SetDefault();
|
|
|
|
// Now all widgets have the size fixed, call FinishDialogSettings
|
|
FinishDialogSettings();
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::OnListItemDeselected( wxListEvent& event )
|
|
{
|
|
if( !m_skipCopyFromPanel )
|
|
{
|
|
if( !copyPanelToSelectedField() )
|
|
event.Skip(); // do not go to the next row
|
|
}
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::OnListItemSelected( wxListEvent& event )
|
|
{
|
|
// remember the selected row, statically
|
|
s_SelectedRow = event.GetIndex();
|
|
|
|
copySelectedFieldToPanel();
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::OnCancelButtonClick( wxCommandEvent& event )
|
|
{
|
|
EndQuasiModal( wxID_CANCEL );
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::OnCloseDialog( wxCloseEvent& event )
|
|
{
|
|
// On wxWidgets 2.8, and on Linux, call EndQuasiModal here is mandatory
|
|
// Otherwise, the main event loop is never restored, and Eeschema does not
|
|
// respond to any event, because the DIALOG_SHIM destructor is never called.
|
|
// on wxWidgets 3.0, or on Windows, the DIALOG_SHIM destructor is called,
|
|
// and calls EndQuasiModal.
|
|
// Therefore calling EndQuasiModal here is not mandatory but it creates no issues.
|
|
EndQuasiModal( wxID_CANCEL );
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::OnSizeFieldsList( wxSizeEvent& event )
|
|
{
|
|
int newWidth = event.GetSize().GetX();
|
|
fieldListCtrl->SetColumnWidth( COLUMN_TEXT, newWidth - fieldListCtrl->GetColumnWidth( 0 ) );
|
|
event.Skip();
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::OnOKButtonClick( wxCommandEvent& event )
|
|
{
|
|
if( !copyPanelToSelectedField() )
|
|
return;
|
|
|
|
// test if reference prefix is acceptable
|
|
if( !SCH_COMPONENT::IsReferenceStringValid( m_FieldsBuf[REFERENCE].GetText() ) )
|
|
{
|
|
DisplayError( NULL, _( "Illegal reference. References must start with a letter." ) );
|
|
return;
|
|
}
|
|
|
|
/* Note: this code is now (2010-dec-04) not used, because the value field is no more editable
|
|
* because changing the value is equivalent to create a new component or alias.
|
|
* This is now handled in libedit main frame, and no more in this dialog
|
|
* but this code is not removed, just in case
|
|
*/
|
|
/* If a new name entered in the VALUE field, that it not an existing alias name
|
|
* or root alias of the component */
|
|
wxString newvalue = m_FieldsBuf[VALUE].GetText();
|
|
|
|
if( m_libEntry->HasAlias( newvalue ) && !m_libEntry->GetAlias( newvalue )->IsRoot() )
|
|
{
|
|
wxString msg = wxString::Format(
|
|
_( "A new name is entered for this component\n"
|
|
"An alias %s already exists!\n"
|
|
"Cannot update this component" ),
|
|
GetChars( newvalue )
|
|
);
|
|
DisplayError( this, msg );
|
|
return;
|
|
}
|
|
/* End unused code */
|
|
|
|
// save old cmp in undo list
|
|
m_parent->SaveCopyInUndoList( m_libEntry );
|
|
|
|
// delete any fields with no name or no value before we copy all of m_FieldsBuf
|
|
// back into the component
|
|
for( unsigned i = MANDATORY_FIELDS; i < m_FieldsBuf.size(); )
|
|
{
|
|
if( m_FieldsBuf[i].GetName().IsEmpty() || m_FieldsBuf[i].GetText().IsEmpty() )
|
|
{
|
|
m_FieldsBuf.erase( m_FieldsBuf.begin() + i );
|
|
continue;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
// copy all the fields back, fully replacing any previous fields
|
|
m_libEntry->SetFields( m_FieldsBuf );
|
|
|
|
// We need to keep the name and the value the same at the moment!
|
|
SetName( m_libEntry->GetValueField().GetText() );
|
|
|
|
m_parent->OnModify();
|
|
|
|
EndQuasiModal( wxID_OK );
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::EditSpiceModel( wxCommandEvent& event )
|
|
{
|
|
#ifdef KICAD_SPICE
|
|
// DIALOG_SPICE_MODEL expects a SCH_COMPONENT,
|
|
// and a list of SCH_FIELDS to create/edit/delete Spice fields.
|
|
SCH_COMPONENT component; // This dummy component
|
|
|
|
// Build fields list from the m_FieldsBuf fields buffer dialog
|
|
// to be sure to use the current fields.
|
|
SCH_FIELDS schfields;
|
|
|
|
for( unsigned ii = 0; ii < m_FieldsBuf.size(); ++ii )
|
|
{
|
|
LIB_FIELD& libfield = m_FieldsBuf[ii];
|
|
SCH_FIELD schfield( libfield.GetTextPos(), libfield.GetId(),
|
|
&component, libfield.GetName() );
|
|
schfield.ImportValues( m_FieldsBuf[ii] );
|
|
schfield.SetText( m_FieldsBuf[ii].GetText() );
|
|
|
|
schfields.push_back( schfield );
|
|
}
|
|
|
|
component.SetFields( schfields );
|
|
|
|
DIALOG_SPICE_MODEL dialog( this, component, schfields );
|
|
|
|
if( dialog.ShowModal() != wxID_OK )
|
|
return;
|
|
|
|
// Transfer sch fields to the m_FieldsBuf fields buffer dialog:
|
|
m_FieldsBuf.clear();
|
|
|
|
for( unsigned ii = 0; ii < schfields.size(); ii++ )
|
|
{
|
|
LIB_FIELD libfield;
|
|
schfields[ii].ExportValues( libfield );
|
|
m_FieldsBuf.push_back( libfield );
|
|
}
|
|
|
|
// ... and to the panel:
|
|
copySelectedFieldToPanel();
|
|
|
|
updateDisplay();
|
|
#endif /* KICAD_SPICE */
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::addFieldButtonHandler( wxCommandEvent& event )
|
|
{
|
|
// in case m_FieldsBuf[REFERENCE].m_Orient has changed on screen only, grab
|
|
// screen contents.
|
|
if( !copyPanelToSelectedField() )
|
|
return;
|
|
|
|
unsigned fieldNdx = m_FieldsBuf.size();
|
|
|
|
LIB_FIELD blank( fieldNdx );
|
|
|
|
m_FieldsBuf.push_back( blank );
|
|
m_FieldsBuf[fieldNdx].SetName( TEMPLATE_FIELDNAME::GetDefaultFieldName( fieldNdx ) );
|
|
|
|
setRowItem( fieldNdx, m_FieldsBuf[fieldNdx] );
|
|
|
|
m_skipCopyFromPanel = true;
|
|
setSelectedFieldNdx( fieldNdx );
|
|
m_skipCopyFromPanel = false;
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::deleteFieldButtonHandler( wxCommandEvent& event )
|
|
{
|
|
unsigned fieldNdx = getSelectedFieldNdx();
|
|
|
|
if( fieldNdx >= m_FieldsBuf.size() ) // traps the -1 case too
|
|
return;
|
|
|
|
if( fieldNdx < MANDATORY_FIELDS )
|
|
{
|
|
wxBell();
|
|
return;
|
|
}
|
|
|
|
m_skipCopyFromPanel = true;
|
|
|
|
if( m_FieldsBuf[fieldNdx].GetText().IsEmpty() )
|
|
{
|
|
m_FieldsBuf.erase( m_FieldsBuf.begin() + fieldNdx );
|
|
fieldListCtrl->DeleteItem( fieldNdx );
|
|
|
|
if( fieldNdx >= m_FieldsBuf.size() )
|
|
--fieldNdx;
|
|
}
|
|
else
|
|
{
|
|
m_FieldsBuf[fieldNdx].Empty();
|
|
copySelectedFieldToPanel();
|
|
}
|
|
|
|
updateDisplay( );
|
|
|
|
setRowItem( fieldNdx, m_FieldsBuf[fieldNdx] );
|
|
setSelectedFieldNdx( fieldNdx );
|
|
m_skipCopyFromPanel = false;
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::moveDownButtonHandler( wxCommandEvent& event )
|
|
{
|
|
unsigned int fieldNdx = getSelectedFieldNdx();
|
|
|
|
// Ensure there is at least one field after this one
|
|
if( fieldNdx >= ( m_FieldsBuf.size() - 1 ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// The first field which can be moved up is the second user field
|
|
// so any field which id < MANDATORY_FIELDS cannot be moved down
|
|
if( fieldNdx < MANDATORY_FIELDS )
|
|
return;
|
|
|
|
if( !copyPanelToSelectedField() )
|
|
return;
|
|
|
|
// swap the fieldNdx field with the one before it, in both the vector
|
|
// and in the fieldListCtrl
|
|
LIB_FIELD tmp = m_FieldsBuf[fieldNdx + 1];
|
|
|
|
m_FieldsBuf[fieldNdx + 1] = m_FieldsBuf[fieldNdx];
|
|
setRowItem( fieldNdx + 1, m_FieldsBuf[fieldNdx] );
|
|
m_FieldsBuf[fieldNdx + 1].SetId( fieldNdx + 1 );
|
|
|
|
m_FieldsBuf[fieldNdx] = tmp;
|
|
setRowItem( fieldNdx, tmp );
|
|
m_FieldsBuf[fieldNdx].SetId( fieldNdx );
|
|
|
|
updateDisplay( );
|
|
|
|
m_skipCopyFromPanel = true;
|
|
setSelectedFieldNdx( fieldNdx + 1 );
|
|
m_skipCopyFromPanel = false;
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB:: moveUpButtonHandler( wxCommandEvent& event )
|
|
{
|
|
unsigned fieldNdx = getSelectedFieldNdx();
|
|
|
|
if( fieldNdx >= m_FieldsBuf.size() ) // traps the -1 case too
|
|
return;
|
|
|
|
// The first field which can be moved up is the second user field
|
|
// so any field which id <= MANDATORY_FIELDS cannot be moved up
|
|
if( fieldNdx <= MANDATORY_FIELDS )
|
|
return;
|
|
|
|
if( !copyPanelToSelectedField() )
|
|
return;
|
|
|
|
// swap the fieldNdx field with the one before it, in both the vector
|
|
// and in the fieldListCtrl
|
|
LIB_FIELD tmp = m_FieldsBuf[fieldNdx - 1];
|
|
|
|
m_FieldsBuf[fieldNdx - 1] = m_FieldsBuf[fieldNdx];
|
|
setRowItem( fieldNdx - 1, m_FieldsBuf[fieldNdx] );
|
|
m_FieldsBuf[fieldNdx - 1].SetId(fieldNdx - 1);
|
|
|
|
m_FieldsBuf[fieldNdx] = tmp;
|
|
setRowItem( fieldNdx, tmp );
|
|
m_FieldsBuf[fieldNdx].SetId(fieldNdx);
|
|
|
|
updateDisplay( );
|
|
|
|
m_skipCopyFromPanel = true;
|
|
setSelectedFieldNdx( fieldNdx - 1 );
|
|
m_skipCopyFromPanel = false;
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::showButtonHandler( wxCommandEvent& event )
|
|
{
|
|
unsigned fieldNdx = getSelectedFieldNdx();
|
|
|
|
if( fieldNdx == DATASHEET )
|
|
{
|
|
wxString datasheet_uri = fieldValueTextCtrl->GetValue();
|
|
datasheet_uri = ResolveUriByEnvVars( datasheet_uri );
|
|
GetAssociatedDocument( this, datasheet_uri );
|
|
}
|
|
else if( fieldNdx == FOOTPRINT )
|
|
{
|
|
// pick a footprint using the footprint picker.
|
|
wxString fpid;
|
|
|
|
KIWAY_PLAYER* frame = Kiway().Player( FRAME_PCB_MODULE_VIEWER_MODAL, true, this );
|
|
|
|
if( frame->ShowModal( &fpid, this ) )
|
|
{
|
|
fieldValueTextCtrl->SetValue( fpid );
|
|
setRowItem( fieldNdx, m_FieldsBuf[fieldNdx].GetName( false ), fpid );
|
|
}
|
|
|
|
frame->Destroy();
|
|
}
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::setSelectedFieldNdx( int aFieldNdx )
|
|
{
|
|
// deselect old selection, but I think this is done by single selection
|
|
// flag within fieldListCtrl
|
|
// fieldListCtrl->SetItemState( s_SelectedRow, 0, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
|
|
|
|
if( aFieldNdx >= (int) m_FieldsBuf.size() )
|
|
aFieldNdx = m_FieldsBuf.size() - 1;
|
|
|
|
if( aFieldNdx < 0 )
|
|
aFieldNdx = 0;
|
|
|
|
fieldListCtrl->SetItemState( aFieldNdx, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
|
|
fieldListCtrl->EnsureVisible( aFieldNdx );
|
|
|
|
s_SelectedRow = aFieldNdx;
|
|
}
|
|
|
|
|
|
int DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::getSelectedFieldNdx()
|
|
{
|
|
return s_SelectedRow;
|
|
}
|
|
|
|
|
|
/**
|
|
* Function findfield
|
|
* searches a LIB_FIELD_LIST for aFieldName.
|
|
*/
|
|
static LIB_FIELD* findfield( const LIB_FIELDS& aList, const wxString& aFieldName )
|
|
{
|
|
const LIB_FIELD* field = NULL;
|
|
|
|
for( unsigned i=0; i<aList.size(); ++i )
|
|
{
|
|
if( aFieldName == aList[i].GetName() )
|
|
{
|
|
field = &aList[i]; // best to avoid casting here.
|
|
break;
|
|
}
|
|
}
|
|
return (LIB_FIELD*) field; // remove const-ness last
|
|
}
|
|
|
|
|
|
LIB_FIELD* DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::findField( const wxString& aFieldName )
|
|
{
|
|
for( unsigned i=0; i<m_FieldsBuf.size(); ++i )
|
|
{
|
|
if( aFieldName == m_FieldsBuf[i].GetName() )
|
|
return &m_FieldsBuf[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::initBuffers()
|
|
{
|
|
LIB_FIELDS cmpFields;
|
|
|
|
m_libEntry->GetFields( cmpFields );
|
|
|
|
/* We have 3 component related field lists to be aware of: 1) UI
|
|
presentation (m_FieldsBuf), 2) fields in component ram copy, and 3)
|
|
fields recorded with component on disk. m_FieldsBuf is the list of UI
|
|
fields, and this list is not the same as the list which is in the
|
|
component, which is also not the same as the list on disk. All 3 lists
|
|
are potentially different. In the UI we choose to preserve the order of
|
|
the first MANDATORY_FIELDS which are sometimes called fixed fields. Then
|
|
we append the template fieldnames in the exact same order as the
|
|
template fieldname editor shows them. Then we append any user defined
|
|
fieldnames which came from the component, and user can modify it during
|
|
editing, but cannot delete or move a fixed field.
|
|
*/
|
|
|
|
m_FieldsBuf.clear();
|
|
|
|
/* When this code was written, all field constructors ensured that the
|
|
MANDATORY_FIELDS are all present within a component (in ram only). So we can
|
|
knowingly copy them over in the normal order. Copy only the fixed fields
|
|
at first. Please do not break the field constructors.
|
|
*/
|
|
|
|
// fixed fields:
|
|
for( int i=0; i<MANDATORY_FIELDS; ++i )
|
|
{
|
|
m_FieldsBuf.push_back( cmpFields[i] );
|
|
}
|
|
|
|
// Add template fieldnames:
|
|
// Now copy in the template fields, in the order that they are present in the
|
|
// template field editor UI.
|
|
SCH_EDIT_FRAME* editor = (SCH_EDIT_FRAME*) Kiway().Player( FRAME_SCH, true );
|
|
|
|
const TEMPLATE_FIELDNAMES& tfnames = editor->GetTemplateFieldNames();
|
|
|
|
for( TEMPLATE_FIELDNAMES::const_iterator it = tfnames.begin(); it!=tfnames.end(); ++it )
|
|
{
|
|
// add a new field unconditionally to the UI only for this template fieldname
|
|
|
|
// field id must not be in range 0 - MANDATORY_FIELDS, set before saving to disk
|
|
LIB_FIELD fld( m_libEntry, -1 );
|
|
|
|
// See if field by same name already exists in component.
|
|
LIB_FIELD* libField = findfield( cmpFields, it->m_Name );
|
|
|
|
// If the field does not already exist in the component, then we
|
|
// use defaults from the template fieldname, otherwise the original
|
|
// values from the component will be set.
|
|
if( !libField )
|
|
{
|
|
fld.SetName( it->m_Name );
|
|
fld.SetText( it->m_Value ); // empty? ok too.
|
|
|
|
if( !it->m_Visible )
|
|
fld.SetVisible( false );
|
|
else
|
|
fld.SetVisible( true );
|
|
}
|
|
else
|
|
{
|
|
fld = *libField; // copy values from component, m_Name too
|
|
}
|
|
|
|
m_FieldsBuf.push_back( fld );
|
|
}
|
|
|
|
// Lastly, append any original fields from the component which were not added
|
|
// from the set of fixed fields nor from the set of template fields.
|
|
for( unsigned i=MANDATORY_FIELDS; i<cmpFields.size(); ++i )
|
|
{
|
|
LIB_FIELD* cmp = &cmpFields[i];
|
|
LIB_FIELD* buf = findField( cmp->GetName() );
|
|
|
|
if( !buf )
|
|
{
|
|
m_FieldsBuf.push_back( *cmp );
|
|
}
|
|
}
|
|
|
|
/* field names have become more important than field ids, so we cannot
|
|
mangle the names in the buffer, but can do so in the panel, see elsewhere.
|
|
m_FieldsBuf[VALUE].m_Name << wxT( "/" ) << _( "Chip Name" );
|
|
*/
|
|
|
|
for( unsigned ii = 0; ii < m_FieldsBuf.size(); ++ii )
|
|
{
|
|
setRowItem( ii, m_FieldsBuf[ii] );
|
|
}
|
|
|
|
// put focus on the list ctrl
|
|
fieldListCtrl->SetFocus();
|
|
|
|
// resume editing at the last row edited, last time dialog was up.
|
|
if ( s_SelectedRow < (int) m_FieldsBuf.size() )
|
|
s_SelectedRow = 0;
|
|
|
|
setSelectedFieldNdx( s_SelectedRow );
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::setRowItem( int aFieldNdx, const wxString& aName, const wxString& aValue )
|
|
{
|
|
wxASSERT( aFieldNdx >= 0 );
|
|
|
|
// insert blanks if aFieldNdx is referencing a "yet to be defined" row
|
|
while( aFieldNdx >= fieldListCtrl->GetItemCount() )
|
|
{
|
|
long ndx = fieldListCtrl->InsertItem( fieldListCtrl->GetItemCount(), wxEmptyString );
|
|
|
|
wxASSERT( ndx >= 0 );
|
|
|
|
fieldListCtrl->SetItem( ndx, COLUMN_TEXT, wxEmptyString );
|
|
}
|
|
|
|
fieldListCtrl->SetItem( aFieldNdx, COLUMN_FIELD_NAME, aName );
|
|
fieldListCtrl->SetItem( aFieldNdx, COLUMN_TEXT, aValue );
|
|
|
|
// recompute the column widths here, after setting texts
|
|
int totalWidth = fieldListCtrl->GetSize().GetWidth();
|
|
fieldListCtrl->SetColumnWidth( COLUMN_FIELD_NAME, wxLIST_AUTOSIZE );
|
|
fieldListCtrl->SetColumnWidth( COLUMN_TEXT, totalWidth - fieldListCtrl->GetColumnWidth( 0 ) );
|
|
}
|
|
|
|
|
|
void DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::copySelectedFieldToPanel()
|
|
{
|
|
unsigned fieldNdx = getSelectedFieldNdx();
|
|
|
|
if( fieldNdx >= m_FieldsBuf.size() ) // traps the -1 case too
|
|
return;
|
|
|
|
LIB_FIELD& field = m_FieldsBuf[fieldNdx];
|
|
|
|
showCheckBox->SetValue( field.IsVisible() );
|
|
|
|
rotateCheckBox->SetValue( field.GetTextAngle() == TEXT_ANGLE_VERT );
|
|
|
|
int style = 0;
|
|
|
|
if( field.IsItalic() )
|
|
style = 1;
|
|
|
|
if( field.IsBold() )
|
|
style |= 2;
|
|
|
|
m_StyleRadioBox->SetSelection( style );
|
|
|
|
// Select the right text justification
|
|
if( field.GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
|
|
m_FieldHJustifyCtrl->SetSelection(0);
|
|
else if( field.GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
|
|
m_FieldHJustifyCtrl->SetSelection(2);
|
|
else
|
|
m_FieldHJustifyCtrl->SetSelection(1);
|
|
|
|
if( field.GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
|
|
m_FieldVJustifyCtrl->SetSelection(0);
|
|
else if( field.GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
|
|
m_FieldVJustifyCtrl->SetSelection(2);
|
|
else
|
|
m_FieldVJustifyCtrl->SetSelection(1);
|
|
|
|
|
|
// Field names have become more important than field ids, so we cannot
|
|
// mangle the names in the buffer but we can do so in the panel.
|
|
if( field.GetId() == VALUE )
|
|
{
|
|
// This field is the lib name and the default value when loading this component in
|
|
// schematic. The value is now not editable here (in this dialog) because changing
|
|
// it is equivalent to create a new component or alias. This is handles in libedir,
|
|
// not in this dialog.
|
|
fieldNameTextCtrl->SetValue( field.GetName() + wxT( " / " ) + _( "Chip Name" ) );
|
|
fieldValueTextCtrl->Enable( false );
|
|
}
|
|
else
|
|
{
|
|
fieldValueTextCtrl->Enable( true );
|
|
fieldNameTextCtrl->SetValue( field.GetName() );
|
|
}
|
|
|
|
// if fieldNdx == REFERENCE, VALUE, FOOTPRINT, or DATASHEET, then disable field name editing
|
|
fieldNameTextCtrl->Enable( fieldNdx >= MANDATORY_FIELDS );
|
|
fieldNameTextCtrl->SetEditable( fieldNdx >= MANDATORY_FIELDS );
|
|
|
|
// only user defined fields may be moved, and not the top most user defined
|
|
// field since it would be moving up into the fixed fields, > not >=
|
|
moveUpButton->Enable( fieldNdx > MANDATORY_FIELDS );
|
|
moveDownButton->Enable( ( fieldNdx >= MANDATORY_FIELDS ) && ( fieldNdx < ( m_FieldsBuf.size() - 1 ) ) );
|
|
|
|
// if fieldNdx == REFERENCE, VALUE, then disable delete button
|
|
deleteFieldButton->Enable( fieldNdx >= MANDATORY_FIELDS );
|
|
|
|
fieldValueTextCtrl->SetValidator( SCH_FIELD_VALIDATOR( true, field.GetId() ) );
|
|
fieldValueTextCtrl->SetValue( field.GetText() );
|
|
|
|
textSizeTextCtrl->SetValue( EDA_GRAPHIC_TEXT_CTRL::FormatSize( g_UserUnit, field.GetTextSize().x ) );
|
|
|
|
m_show_datasheet_button->Enable( fieldNdx == DATASHEET || fieldNdx == FOOTPRINT );
|
|
|
|
if( fieldNdx == DATASHEET )
|
|
{
|
|
m_show_datasheet_button->SetLabel( _( "Show Datasheet" ) );
|
|
m_show_datasheet_button->SetToolTip(
|
|
_("If your datasheet is given as an http:// link,"
|
|
" then pressing this button should bring it up in your webbrowser.") );
|
|
}
|
|
else if( fieldNdx == FOOTPRINT )
|
|
{
|
|
m_show_datasheet_button->SetLabel( _( "Browse Footprints" ) );
|
|
m_show_datasheet_button->SetToolTip(
|
|
_("Open the footprint browser to choose a footprint and assign it.") );
|
|
}
|
|
else
|
|
{
|
|
m_show_datasheet_button->SetLabel( wxEmptyString );
|
|
m_show_datasheet_button->SetToolTip(
|
|
_("Used only for fields Footprint and Datasheet.") );
|
|
}
|
|
|
|
wxPoint coord = field.GetTextPos();
|
|
wxPoint zero;
|
|
|
|
// If the field value is empty and the position is at relative zero, we set the
|
|
// initial position as a small offset from the ref field, and orient
|
|
// it the same as the ref field. That is likely to put it at least
|
|
// close to the desired position.
|
|
if( coord == zero && field.GetText().IsEmpty() )
|
|
{
|
|
rotateCheckBox->SetValue( m_FieldsBuf[REFERENCE].GetTextAngle() == TEXT_ANGLE_VERT );
|
|
|
|
coord.x = m_FieldsBuf[REFERENCE].GetTextPos().x +
|
|
(fieldNdx - MANDATORY_FIELDS + 1) * 100;
|
|
coord.y = m_FieldsBuf[REFERENCE].GetTextPos().y +
|
|
(fieldNdx - MANDATORY_FIELDS + 1) * 100;
|
|
|
|
// coord can compute negative if field is < MANDATORY_FIELDS, e.g. FOOTPRINT.
|
|
// That is ok, we basically don't want all the new empty fields on
|
|
// top of each other.
|
|
}
|
|
|
|
wxString coordText = StringFromValue( g_UserUnit, coord.x );
|
|
posXTextCtrl->SetValue( coordText );
|
|
|
|
// Note: the Y axis for components in lib is from bottom to top
|
|
// and the screen axis is top to bottom: we must change the y coord sign for editing
|
|
coord.y = -coord.y;
|
|
coordText = StringFromValue( g_UserUnit, coord.y );
|
|
posYTextCtrl->SetValue( coordText );
|
|
}
|
|
|
|
|
|
bool DIALOG_EDIT_LIBENTRY_FIELDS_IN_LIB::copyPanelToSelectedField()
|
|
{
|
|
unsigned fieldNdx = getSelectedFieldNdx();
|
|
|
|
if( fieldNdx >= m_FieldsBuf.size() ) // traps the -1 case too
|
|
return true;
|
|
|
|
// Check for illegal field text.
|
|
if( fieldValueTextCtrl->GetValidator()
|
|
&& !fieldValueTextCtrl->GetValidator()->Validate( this ) )
|
|
return false;
|
|
|
|
LIB_FIELD& field = m_FieldsBuf[fieldNdx];
|
|
|
|
if( showCheckBox->GetValue() )
|
|
field.SetVisible( true );
|
|
else
|
|
field.SetVisible( false );
|
|
|
|
if( rotateCheckBox->GetValue() )
|
|
field.SetTextAngle( TEXT_ANGLE_VERT );
|
|
else
|
|
field.SetTextAngle( TEXT_ANGLE_HORIZ );
|
|
|
|
// Copy the text justification
|
|
static const EDA_TEXT_HJUSTIFY_T hjustify[3] = {
|
|
GR_TEXT_HJUSTIFY_LEFT, GR_TEXT_HJUSTIFY_CENTER,
|
|
GR_TEXT_HJUSTIFY_RIGHT
|
|
};
|
|
|
|
static const EDA_TEXT_VJUSTIFY_T vjustify[3] = {
|
|
GR_TEXT_VJUSTIFY_BOTTOM, GR_TEXT_VJUSTIFY_CENTER,
|
|
GR_TEXT_VJUSTIFY_TOP
|
|
};
|
|
|
|
field.SetHorizJustify( hjustify[m_FieldHJustifyCtrl->GetSelection()] );
|
|
field.SetVertJustify( vjustify[m_FieldVJustifyCtrl->GetSelection()] );
|
|
|
|
// Blank/empty field texts for REFERENCE and VALUE are not allowed.
|
|
// (Value is the name of the component in lib!)
|
|
// Change them only if user provided a non blank value
|
|
if( !fieldValueTextCtrl->GetValue().IsEmpty() || fieldNdx > VALUE )
|
|
field.SetText( fieldValueTextCtrl->GetValue() );
|
|
|
|
// FieldNameTextCtrl has a tricked value in it for VALUE index, do not copy it back.
|
|
// It has the "Chip Name" appended.
|
|
if( field.GetId() >= MANDATORY_FIELDS )
|
|
{
|
|
wxString name = fieldNameTextCtrl->GetValue();
|
|
field.SetName( name );
|
|
}
|
|
|
|
setRowItem( fieldNdx, field ); // update fieldListCtrl
|
|
|
|
int tmp = EDA_GRAPHIC_TEXT_CTRL::ParseSize( textSizeTextCtrl->GetValue(), g_UserUnit );
|
|
|
|
field.SetTextSize( wxSize( tmp, tmp ) );
|
|
|
|
int style = m_StyleRadioBox->GetSelection();
|
|
|
|
field.SetItalic( (style & 1 ) != 0 );
|
|
field.SetBold( (style & 2 ) != 0 );
|
|
|
|
wxPoint pos( ValueFromString( g_UserUnit, posXTextCtrl->GetValue() ),
|
|
ValueFromString( g_UserUnit, posYTextCtrl->GetValue() ) );
|
|
|
|
// Note: the Y axis for components in lib is from bottom to top
|
|
// and the screen axis is top to bottom: we must change the y coord sign for editing
|
|
pos.y = -pos.y;
|
|
|
|
field.SetTextPos( pos );
|
|
|
|
return true;
|
|
}
|