mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-13 17:53:11 +02:00
Eeschema: Symbol editor: pin table CSV interchange
This adds the ability to export the pin table content to a CSV file or the clipboard as CSV, then re-import it from CSV or TSV. This allows: * to round-trip pin table data via a spreadsheet program, so that the pin data can be manipulated in a richer/more familiar editing environment * an import method to bring in tabular pin data from other formats "semi-automatically", without having to write a full-blown symbol generator. Relates-To: https://gitlab.com/kicad/code/kicad/-/issues/19207
This commit is contained in:
parent
ceab28bc21
commit
4c8b971021
@ -244,6 +244,7 @@ target_link_libraries( kicommon
|
||||
fmt::fmt
|
||||
CURL::libcurl
|
||||
picosha2
|
||||
rapidcsv
|
||||
${ZSTD_LIBRARY}
|
||||
${wxWidgets_LIBRARIES}
|
||||
${LIBGIT2_LIBRARIES}
|
||||
@ -556,6 +557,8 @@ set( COMMON_IO_SRCS
|
||||
# EasyEDA pro
|
||||
io/easyedapro/easyedapro_parser.cpp
|
||||
io/easyedapro/easyedapro_import_utils.cpp
|
||||
|
||||
io/csv.cpp
|
||||
)
|
||||
|
||||
set ( COMMON_TRANSLINE_CALCULATION_SRCS
|
||||
|
@ -26,6 +26,12 @@
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/sstream.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <io/csv.h>
|
||||
|
||||
|
||||
bool SaveClipboard( const std::string& aTextUTF8 )
|
||||
@ -106,4 +112,66 @@ std::unique_ptr<wxImage> GetImageFromClipboard()
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SaveTabularDataToClipboard( const std::vector<std::vector<wxString>>& aData )
|
||||
{
|
||||
wxLogNull doNotLog; // disable logging of failed clipboard actions
|
||||
|
||||
if( wxTheClipboard->Open() )
|
||||
{
|
||||
wxDataObjectComposite* data = new wxDataObjectComposite();
|
||||
|
||||
// Set plain text CSV
|
||||
{
|
||||
wxStringOutputStream os;
|
||||
CSV_WRITER writer( os );
|
||||
writer.WriteLines( aData );
|
||||
|
||||
data->Add( new wxTextDataObject( os.GetString() ), true );
|
||||
}
|
||||
|
||||
// At this point, it would be great if we could add some format that spreadsheet
|
||||
// programs can understand without asking the user for options: perhaps SYLK or DIF.
|
||||
// But it doesn't seem like WX allows to put arbitrary MIME types on the clipboard,
|
||||
// even with wxCustomDataObject( wxDataFormat( "mime/type" ) ), which just ends up as
|
||||
// wxDF_PRIVATE, and wxDF_SYLK/DIF aren't mapped on GTK.
|
||||
|
||||
wxTheClipboard->SetData( data );
|
||||
wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
|
||||
wxTheClipboard->Close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GetTabularDataFromClipboard( std::vector<std::vector<wxString>>& aData )
|
||||
{
|
||||
// Again, it would be ideal if we could detect a spreadsheet mimetype here,
|
||||
// but WX doesn't seem to do that on Linux, at least.
|
||||
|
||||
bool ok = false;
|
||||
|
||||
// First try for text data
|
||||
if( wxTheClipboard->Open() )
|
||||
{
|
||||
if( wxTheClipboard->IsSupported( wxDF_TEXT ) )
|
||||
{
|
||||
wxTextDataObject data;
|
||||
if( wxTheClipboard->GetData( data ) )
|
||||
{
|
||||
ok = AutoDecodeCSV( data.GetText(), aData );
|
||||
}
|
||||
}
|
||||
|
||||
// We could also handle .csv wxDF_FILENAMEs here
|
||||
|
||||
wxTheClipboard->Close();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
98
common/io/csv.cpp
Normal file
98
common/io/csv.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
|
||||
|
||||
#include "csv.h"
|
||||
|
||||
#include <wx/txtstrm.h>
|
||||
|
||||
|
||||
#include <rapidcsv/rapidcsv.h>
|
||||
|
||||
|
||||
CSV_WRITER::CSV_WRITER( wxOutputStream& aStream ) :
|
||||
m_stream( aStream ), m_delimiter( wxT( "," ) ), m_quote( wxT( "\"" ) ), m_lineTerminator( wxT( "\n" ) ),
|
||||
m_escape( wxEmptyString )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CSV_WRITER::WriteLines( const std::vector<std::vector<wxString>>& aRows )
|
||||
{
|
||||
for( const auto& row : aRows )
|
||||
{
|
||||
WriteLine( row );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSV_WRITER::WriteLine( const std::vector<wxString>& cols )
|
||||
{
|
||||
wxString line;
|
||||
for( size_t i = 0; i < cols.size(); ++i )
|
||||
{
|
||||
wxString colVal = cols[i];
|
||||
|
||||
if( i > 0 )
|
||||
line += m_delimiter;
|
||||
|
||||
double test;
|
||||
if( !colVal.ToDouble( &test ) )
|
||||
{
|
||||
bool useEscape = m_escape.size();
|
||||
|
||||
if( useEscape )
|
||||
{
|
||||
colVal.Replace( m_quote, m_escape + m_quote, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
colVal.Replace( m_quote, m_quote + m_quote, true );
|
||||
}
|
||||
|
||||
colVal = m_quote + colVal + m_quote;
|
||||
}
|
||||
|
||||
line += colVal;
|
||||
}
|
||||
line += m_lineTerminator;
|
||||
|
||||
m_stream.Write( line.c_str(), line.length() );
|
||||
}
|
||||
|
||||
|
||||
bool AutoDecodeCSV( const wxString& aInput, std::vector<std::vector<wxString>>& aData )
|
||||
{
|
||||
// Read the first line to determine the delimiter
|
||||
bool trimCells = true;
|
||||
bool skipCommentLines = true;
|
||||
bool skipEmptyLines = true;
|
||||
char commentPrefix = '#';
|
||||
char delimiter = ',';
|
||||
|
||||
// Assume if we find a tab, we are dealing with a TSV file
|
||||
if( aInput.find( '\t' ) != std::string::npos )
|
||||
{
|
||||
delimiter = '\t';
|
||||
}
|
||||
|
||||
std::istringstream inputStream( aInput.ToStdString() );
|
||||
|
||||
rapidcsv::Document doc( inputStream, rapidcsv::LabelParams( -1, -1 ),
|
||||
rapidcsv::SeparatorParams( delimiter, trimCells ), rapidcsv::ConverterParams(),
|
||||
rapidcsv::LineReaderParams( skipCommentLines, commentPrefix, skipEmptyLines ) );
|
||||
|
||||
// Read the data into aData
|
||||
aData.clear();
|
||||
|
||||
for( size_t i = 0; i < doc.GetRowCount(); ++i )
|
||||
{
|
||||
std::vector<wxString>& row = aData.emplace_back();
|
||||
for( size_t j = 0; j < doc.GetColumnCount(); ++j )
|
||||
{
|
||||
std::string cell = doc.GetCell<std::string>( j, i );
|
||||
row.emplace_back( cell );
|
||||
}
|
||||
}
|
||||
|
||||
// Anything in the first row?
|
||||
return aData[0].size() > 0;
|
||||
}
|
78
common/io/csv.h
Normal file
78
common/io/csv.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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 3
|
||||
* 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:
|
||||
* https://www.gnu.org/licenses/gpl-3.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 3 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/stream.h>
|
||||
|
||||
#include <istream>
|
||||
|
||||
|
||||
class CSV_WRITER
|
||||
{
|
||||
public:
|
||||
CSV_WRITER( wxOutputStream& aStream );
|
||||
|
||||
/**
|
||||
* Write a single row to the stream.
|
||||
*/
|
||||
void WriteLine( const std::vector<wxString>& aCols );
|
||||
/**
|
||||
* Write a vector of rows to the stream.
|
||||
*
|
||||
* @param cols The rows to write.
|
||||
*/
|
||||
void WriteLines( const std::vector<std::vector<wxString>>& aRows );
|
||||
|
||||
void SetDelimiter( const wxString& aDelimiter )
|
||||
{
|
||||
m_delimiter = aDelimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the delimiter escape char. If set to wxEmptyString (default), the
|
||||
* delimiter is doubled for escaping.
|
||||
*/
|
||||
void SetEscape( const wxString& aEscape )
|
||||
{
|
||||
m_escape = aEscape;
|
||||
}
|
||||
|
||||
private:
|
||||
wxOutputStream& m_stream;
|
||||
wxString m_delimiter;
|
||||
wxString m_quote;
|
||||
wxString m_lineTerminator;
|
||||
wxString m_escape;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Try to guess the format of a T/CSV file and decode it into aData.
|
||||
*
|
||||
* This can handle the most common cases of CSV files and TSV files
|
||||
* (double/single quoted strings, commas or tabs as delimiters, but it's
|
||||
* not completely foolproof).
|
||||
*/
|
||||
bool AutoDecodeCSV( const wxString& aInput, std::vector<std::vector<wxString>>& aData );
|
@ -198,6 +198,7 @@ const std::string FILEEXT::JpegFileExtension( "jpg" );
|
||||
const std::string FILEEXT::TextFileExtension( "txt" );
|
||||
const std::string FILEEXT::MarkdownFileExtension( "md" );
|
||||
const std::string FILEEXT::CsvFileExtension( "csv" );
|
||||
const std::string FILEEXT::TsvFileExtension( "tsv" );
|
||||
const std::string FILEEXT::XmlFileExtension( "xml" );
|
||||
const std::string FILEEXT::JsonFileExtension( "json" );
|
||||
const std::string FILEEXT::PythonFileExtension( "py" );
|
||||
@ -415,6 +416,12 @@ wxString FILEEXT::CsvFileWildcard()
|
||||
}
|
||||
|
||||
|
||||
wxString FILEEXT::CsvTsvFileWildcard()
|
||||
{
|
||||
return _( "CSV/TSV Files" ) + AddFileExtListToFilter( { CsvFileExtension, TsvFileExtension } );
|
||||
}
|
||||
|
||||
|
||||
wxString FILEEXT::PdfFileWildcard()
|
||||
{
|
||||
return _( "Portable document format files" ) + AddFileExtListToFilter( { "pdf" } );
|
||||
|
@ -22,23 +22,33 @@
|
||||
*/
|
||||
|
||||
#include "dialog_lib_edit_pin_table.h"
|
||||
|
||||
#include <wx/filedlg.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/tokenzr.h>
|
||||
|
||||
#include "grid_tricks.h"
|
||||
#include <richio.h>
|
||||
#include <sch_pin.h>
|
||||
#include <pin_numbers.h>
|
||||
#include "pgm_base.h"
|
||||
#include <base_units.h>
|
||||
#include <bitmaps.h>
|
||||
#include <clipboard.h>
|
||||
#include <confirm.h>
|
||||
#include <symbol_edit_frame.h>
|
||||
#include <symbol_editor_settings.h>
|
||||
#include <io/csv.h>
|
||||
#include <kiplatform/ui.h>
|
||||
#include <widgets/grid_icon_text_helpers.h>
|
||||
#include <widgets/grid_combobox.h>
|
||||
#include <widgets/wx_grid.h>
|
||||
#include <widgets/bitmap_button.h>
|
||||
#include <widgets/std_bitmap_button.h>
|
||||
#include <wildcards_and_files_ext.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <wx/tokenzr.h>
|
||||
#include <tool/action_menu.h>
|
||||
#include <string_utils.h>
|
||||
|
||||
#define UNITS_ALL _( "ALL" )
|
||||
@ -47,6 +57,266 @@
|
||||
#define DEMORGAN_ALT _( "Alternate" )
|
||||
|
||||
|
||||
static wxString GetPinTableColLabel( int aCol )
|
||||
{
|
||||
switch( aCol )
|
||||
{
|
||||
case COL_PIN_COUNT: return _( "Count" );
|
||||
case COL_NUMBER: return _( "Number" );
|
||||
case COL_NAME: return _( "Name" );
|
||||
case COL_TYPE: return _( "Electrical Type" );
|
||||
case COL_SHAPE: return _( "Graphic Style" );
|
||||
case COL_ORIENTATION: return _( "Orientation" );
|
||||
case COL_NUMBER_SIZE: return _( "Number Text Size" );
|
||||
case COL_NAME_SIZE: return _( "Name Text Size" );
|
||||
case COL_LENGTH: return _( "Length" );
|
||||
case COL_POSX: return _( "X Position" );
|
||||
case COL_POSY: return _( "Y Position" );
|
||||
case COL_VISIBLE: return _( "Visible" );
|
||||
case COL_UNIT: return _( "Unit" );
|
||||
case COL_DEMORGAN: return _( "De Morgan" );
|
||||
default: wxFAIL; return wxEmptyString;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static COL_ORDER GetColTypeForString( const wxString& aStr )
|
||||
{
|
||||
for( int i = 0; i < COL_COUNT; i++ )
|
||||
{
|
||||
if( GetPinTableColLabel( i ).IsSameAs( aStr, false ) )
|
||||
return (COL_ORDER) i;
|
||||
}
|
||||
return COL_COUNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that handles conversion of various pin data fields into strings for display in the
|
||||
* UI or serialisation to formats like CSV.
|
||||
*/
|
||||
class PIN_INFO_FORMATTER
|
||||
{
|
||||
public:
|
||||
enum class BOOL_FORMAT
|
||||
{
|
||||
ZERO_ONE,
|
||||
TRUE_FALSE,
|
||||
};
|
||||
|
||||
PIN_INFO_FORMATTER( UNITS_PROVIDER& aUnitsProvider, bool aIncludeUnits, BOOL_FORMAT aBoolFormat ) :
|
||||
m_unitsProvider( aUnitsProvider ),
|
||||
m_includeUnits( aIncludeUnits ),
|
||||
m_boolFormat( aBoolFormat )
|
||||
{
|
||||
}
|
||||
|
||||
wxString Format( const SCH_PIN& aPin, int aFieldId ) const
|
||||
{
|
||||
wxString val;
|
||||
switch( aFieldId )
|
||||
{
|
||||
case COL_NAME:
|
||||
val << aPin.GetName();
|
||||
break;
|
||||
case COL_NUMBER:
|
||||
val << aPin.GetNumber();
|
||||
break;
|
||||
case COL_TYPE:
|
||||
val << PinTypeNames()[static_cast<int>( aPin.GetType() )];
|
||||
break;
|
||||
case COL_SHAPE:
|
||||
val << PinShapeNames()[static_cast<int>( aPin.GetShape() )];
|
||||
break;
|
||||
case COL_ORIENTATION:
|
||||
{
|
||||
const int index = PinOrientationIndex( aPin.GetOrientation() );
|
||||
if( index >= 0)
|
||||
val << PinOrientationNames()[ index ];
|
||||
break;
|
||||
}
|
||||
case COL_NUMBER_SIZE:
|
||||
val << m_unitsProvider.StringFromValue( aPin.GetNumberTextSize(), m_includeUnits );
|
||||
break;
|
||||
case COL_NAME_SIZE:
|
||||
val << m_unitsProvider.StringFromValue( aPin.GetNameTextSize(), m_includeUnits );
|
||||
break;
|
||||
case COL_LENGTH:
|
||||
val << m_unitsProvider.StringFromValue( aPin.GetLength(), m_includeUnits );
|
||||
break;
|
||||
case COL_POSX:
|
||||
val << m_unitsProvider.StringFromValue( aPin.GetPosition().x, m_includeUnits );
|
||||
break;
|
||||
case COL_POSY:
|
||||
val << m_unitsProvider.StringFromValue( aPin.GetPosition().y, m_includeUnits );
|
||||
break;
|
||||
case COL_VISIBLE:
|
||||
val << stringFromBool( aPin.IsVisible() );
|
||||
break;
|
||||
case COL_UNIT:
|
||||
if( aPin.GetUnit() )
|
||||
val << LIB_SYMBOL::LetterSubReference( aPin.GetUnit(), 'A' );
|
||||
else
|
||||
val << UNITS_ALL;
|
||||
break;
|
||||
case COL_DEMORGAN:
|
||||
switch( aPin.GetBodyStyle() )
|
||||
{
|
||||
case BODY_STYLE::BASE:
|
||||
val << DEMORGAN_STD;
|
||||
break;
|
||||
case BODY_STYLE::DEMORGAN:
|
||||
val << DEMORGAN_ALT;
|
||||
break;
|
||||
default:
|
||||
val << DEMORGAN_ALL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
wxFAIL_MSG( wxString::Format( "Invalid field id %d", aFieldId ) );
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the pin from the given col/string.
|
||||
*
|
||||
* How much this should follow the format is debatable, but for now it's fairly permissive
|
||||
* (e.g. bools import as 0/1 and no/yes).
|
||||
*/
|
||||
void UpdatePin( SCH_PIN& aPin, const wxString& aValue, int aFieldId, const LIB_SYMBOL& aSymbol ) const
|
||||
{
|
||||
switch( aFieldId )
|
||||
{
|
||||
case COL_NUMBER:
|
||||
aPin.SetNumber( aValue );
|
||||
break;
|
||||
|
||||
case COL_NAME:
|
||||
aPin.SetName( aValue );
|
||||
break;
|
||||
|
||||
case COL_TYPE:
|
||||
if( PinTypeNames().Index( aValue, false ) != wxNOT_FOUND )
|
||||
aPin.SetType( (ELECTRICAL_PINTYPE) PinTypeNames().Index( aValue ) );
|
||||
break;
|
||||
|
||||
case COL_SHAPE:
|
||||
if( PinShapeNames().Index( aValue, false ) != wxNOT_FOUND )
|
||||
aPin.SetShape( (GRAPHIC_PINSHAPE) PinShapeNames().Index( aValue ) );
|
||||
break;
|
||||
|
||||
case COL_ORIENTATION:
|
||||
if( PinOrientationNames().Index( aValue, false ) != wxNOT_FOUND )
|
||||
aPin.SetOrientation( (PIN_ORIENTATION) PinOrientationNames().Index( aValue ) );
|
||||
break;
|
||||
|
||||
case COL_NUMBER_SIZE:
|
||||
aPin.SetNumberTextSize( m_unitsProvider.ValueFromString( aValue ) );
|
||||
break;
|
||||
|
||||
case COL_NAME_SIZE:
|
||||
aPin.SetNameTextSize( m_unitsProvider.ValueFromString( aValue ) );
|
||||
break;
|
||||
|
||||
case COL_LENGTH:
|
||||
aPin.ChangeLength( m_unitsProvider.ValueFromString( aValue ) );
|
||||
break;
|
||||
|
||||
case COL_POSX:
|
||||
aPin.SetPosition( VECTOR2I( m_unitsProvider.ValueFromString( aValue ),
|
||||
aPin.GetPosition().y ) );
|
||||
break;
|
||||
|
||||
case COL_POSY:
|
||||
aPin.SetPosition( VECTOR2I( aPin.GetPosition().x, m_unitsProvider.ValueFromString( aValue ) ) );
|
||||
break;
|
||||
|
||||
case COL_VISIBLE:
|
||||
aPin.SetVisible(boolFromString( aValue ));
|
||||
break;
|
||||
|
||||
case COL_UNIT:
|
||||
if( aValue == UNITS_ALL )
|
||||
{
|
||||
aPin.SetUnit( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i = 1; i <= aSymbol.GetUnitCount(); i++ )
|
||||
{
|
||||
if( aValue == LIB_SYMBOL::LetterSubReference( i, 'A' ) )
|
||||
{
|
||||
aPin.SetUnit( i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case COL_DEMORGAN:
|
||||
if( aValue == DEMORGAN_STD )
|
||||
aPin.SetBodyStyle( 1 );
|
||||
else if( aValue == DEMORGAN_ALT )
|
||||
aPin.SetBodyStyle( 2 );
|
||||
else
|
||||
aPin.SetBodyStyle( 0 );
|
||||
break;
|
||||
|
||||
default:
|
||||
wxFAIL_MSG( wxString::Format( "Invalid field id %d", aFieldId ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
wxString stringFromBool( bool aValue ) const
|
||||
{
|
||||
switch( m_boolFormat )
|
||||
{
|
||||
case BOOL_FORMAT::ZERO_ONE: return aValue ? wxT( "1" ) : wxT( "0" );
|
||||
case BOOL_FORMAT::TRUE_FALSE:
|
||||
return aValue ? _( "True" ) : _( "False" );
|
||||
// no default
|
||||
}
|
||||
wxCHECK_MSG( false, wxEmptyString, "Invalid BOOL_FORMAT" );
|
||||
}
|
||||
|
||||
bool boolFromString( const wxString& aValue ) const
|
||||
{
|
||||
if( aValue == wxS( "1" ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if( aValue == wxS( "0" ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if( aValue.IsSameAs( _( "True" ), false ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if( aValue.IsSameAs( _( "False" ), false ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( wxString::Format( "string '%s' can't be converted to boolean correctly, "
|
||||
"it will have been perceived as FALSE",
|
||||
aValue ) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
UNITS_PROVIDER& m_unitsProvider;
|
||||
bool m_includeUnits;
|
||||
BOOL_FORMAT m_boolFormat;
|
||||
};
|
||||
|
||||
|
||||
void getSelectedArea( WX_GRID* aGrid, int* aRowStart, int* aRowCount )
|
||||
{
|
||||
wxGridCellCoordsArray topLeft = aGrid->GetSelectionBlockTopLeft();
|
||||
@ -114,24 +384,7 @@ public:
|
||||
|
||||
wxString GetColLabelValue( int aCol ) override
|
||||
{
|
||||
switch( aCol )
|
||||
{
|
||||
case COL_PIN_COUNT: return _( "Count" );
|
||||
case COL_NUMBER: return _( "Number" );
|
||||
case COL_NAME: return _( "Name" );
|
||||
case COL_TYPE: return _( "Electrical Type" );
|
||||
case COL_SHAPE: return _( "Graphic Style" );
|
||||
case COL_ORIENTATION: return _( "Orientation" );
|
||||
case COL_NUMBER_SIZE: return _( "Number Text Size" );
|
||||
case COL_NAME_SIZE: return _( "Name Text Size" );
|
||||
case COL_LENGTH: return _( "Length" );
|
||||
case COL_POSX: return _( "X Position" );
|
||||
case COL_POSY: return _( "Y Position" );
|
||||
case COL_VISIBLE: return _( "Visible" );
|
||||
case COL_UNIT: return _( "Unit" );
|
||||
case COL_DEMORGAN: return _( "De Morgan" );
|
||||
default: wxFAIL; return wxEmptyString;
|
||||
}
|
||||
return GetPinTableColLabel( aCol );
|
||||
}
|
||||
|
||||
bool IsEmptyCell( int row, int col ) override
|
||||
@ -163,87 +416,18 @@ public:
|
||||
if( pins.empty() )
|
||||
return fieldValue;
|
||||
|
||||
for( SCH_PIN* pin : pins )
|
||||
PIN_INFO_FORMATTER formatter( *aParentFrame, true, PIN_INFO_FORMATTER::BOOL_FORMAT::ZERO_ONE );
|
||||
|
||||
for( const SCH_PIN* pin : pins )
|
||||
{
|
||||
wxString val;
|
||||
|
||||
switch( aCol )
|
||||
{
|
||||
case COL_PIN_COUNT:
|
||||
val << pins.size();
|
||||
break;
|
||||
|
||||
case COL_NUMBER:
|
||||
val = pin->GetNumber();
|
||||
break;
|
||||
|
||||
case COL_NAME:
|
||||
val = pin->GetName();
|
||||
break;
|
||||
|
||||
case COL_TYPE:
|
||||
val = PinTypeNames()[static_cast<int>( pin->GetType() )];
|
||||
break;
|
||||
|
||||
case COL_SHAPE:
|
||||
val = PinShapeNames()[static_cast<int>( pin->GetShape() )];
|
||||
break;
|
||||
|
||||
case COL_ORIENTATION:
|
||||
if( PinOrientationIndex( pin->GetOrientation() ) >= 0 )
|
||||
val = PinOrientationNames()[ PinOrientationIndex( pin->GetOrientation() ) ];
|
||||
|
||||
break;
|
||||
|
||||
case COL_NUMBER_SIZE:
|
||||
val = aParentFrame->StringFromValue( pin->GetNumberTextSize(), true );
|
||||
break;
|
||||
|
||||
case COL_NAME_SIZE:
|
||||
val = aParentFrame->StringFromValue( pin->GetNameTextSize(), true );
|
||||
break;
|
||||
|
||||
case COL_LENGTH:
|
||||
val = aParentFrame->StringFromValue( pin->GetLength(), true );
|
||||
break;
|
||||
|
||||
case COL_POSX:
|
||||
val = aParentFrame->StringFromValue( pin->GetPosition().x, true );
|
||||
break;
|
||||
|
||||
case COL_POSY:
|
||||
val = aParentFrame->StringFromValue( pin->GetPosition().y, true );
|
||||
break;
|
||||
|
||||
case COL_VISIBLE:
|
||||
val = StringFromBool( pin->IsVisible() );
|
||||
break;
|
||||
|
||||
case COL_UNIT:
|
||||
if( pin->GetUnit() )
|
||||
val = LIB_SYMBOL::LetterSubReference( pin->GetUnit(), 'A' );
|
||||
else
|
||||
val = UNITS_ALL;
|
||||
|
||||
break;
|
||||
|
||||
case COL_DEMORGAN:
|
||||
switch( pin->GetBodyStyle() )
|
||||
{
|
||||
case BODY_STYLE::BASE:
|
||||
val = DEMORGAN_STD;
|
||||
break;
|
||||
case BODY_STYLE::DEMORGAN:
|
||||
val = DEMORGAN_ALT;
|
||||
break;
|
||||
default:
|
||||
val = DEMORGAN_ALL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
wxFAIL;
|
||||
val << formatter.Format( *pin, aCol );
|
||||
break;
|
||||
}
|
||||
|
||||
@ -365,96 +549,11 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
PIN_INFO_FORMATTER formatter( *m_frame, true, PIN_INFO_FORMATTER::BOOL_FORMAT::ZERO_ONE );
|
||||
|
||||
for( SCH_PIN* pin : pins )
|
||||
{
|
||||
switch( aCol )
|
||||
{
|
||||
case COL_NUMBER:
|
||||
if( !m_pinTable->IsDisplayGrouped() )
|
||||
pin->SetNumber( value );
|
||||
|
||||
break;
|
||||
|
||||
case COL_NAME:
|
||||
pin->SetName( value );
|
||||
break;
|
||||
|
||||
case COL_TYPE:
|
||||
if( PinTypeNames().Index( value ) != wxNOT_FOUND )
|
||||
pin->SetType( (ELECTRICAL_PINTYPE) PinTypeNames().Index( value ) );
|
||||
|
||||
break;
|
||||
|
||||
case COL_SHAPE:
|
||||
if( PinShapeNames().Index( value ) != wxNOT_FOUND )
|
||||
pin->SetShape( (GRAPHIC_PINSHAPE) PinShapeNames().Index( value ) );
|
||||
|
||||
break;
|
||||
|
||||
case COL_ORIENTATION:
|
||||
if( PinOrientationNames().Index( value ) != wxNOT_FOUND )
|
||||
pin->SetOrientation(
|
||||
PinOrientationCode( PinOrientationNames().Index( value ) ) );
|
||||
break;
|
||||
|
||||
case COL_NUMBER_SIZE:
|
||||
pin->SetNumberTextSize( m_frame->ValueFromString( value ) );
|
||||
break;
|
||||
|
||||
case COL_NAME_SIZE:
|
||||
pin->SetNameTextSize( m_frame->ValueFromString( value ) );
|
||||
break;
|
||||
|
||||
case COL_LENGTH:
|
||||
pin->ChangeLength( m_frame->ValueFromString( value ) );
|
||||
break;
|
||||
|
||||
case COL_POSX:
|
||||
pin->SetPosition( VECTOR2I( m_frame->ValueFromString( value ),
|
||||
pin->GetPosition().y ) );
|
||||
break;
|
||||
|
||||
case COL_POSY:
|
||||
pin->SetPosition( VECTOR2I( pin->GetPosition().x,
|
||||
m_frame->ValueFromString( value ) ) );
|
||||
break;
|
||||
|
||||
case COL_VISIBLE:
|
||||
pin->SetVisible(BoolFromString( value ));
|
||||
break;
|
||||
|
||||
case COL_UNIT:
|
||||
if( value == UNITS_ALL )
|
||||
{
|
||||
pin->SetUnit( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int i = 1; i <= m_symbol->GetUnitCount(); i++ )
|
||||
{
|
||||
if( value == LIB_SYMBOL::LetterSubReference( i, 'A' ) )
|
||||
{
|
||||
pin->SetUnit( i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case COL_DEMORGAN:
|
||||
if( value == DEMORGAN_STD )
|
||||
pin->SetBodyStyle( 1 );
|
||||
else if( value == DEMORGAN_ALT )
|
||||
pin->SetBodyStyle( 2 );
|
||||
else
|
||||
pin->SetBodyStyle( 0 );
|
||||
break;
|
||||
|
||||
default:
|
||||
wxFAIL;
|
||||
break;
|
||||
}
|
||||
formatter.UpdatePin( *pin, value, aCol, *m_symbol );
|
||||
}
|
||||
|
||||
m_edited = true;
|
||||
@ -672,34 +771,6 @@ public:
|
||||
return m_edited;
|
||||
}
|
||||
|
||||
private:
|
||||
static wxString StringFromBool( bool aValue )
|
||||
{
|
||||
if( aValue )
|
||||
return wxT( "1" );
|
||||
else
|
||||
return wxT( "0" );
|
||||
}
|
||||
|
||||
static bool BoolFromString( wxString aValue )
|
||||
{
|
||||
if( aValue == wxS( "1" ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if( aValue == wxS( "0" ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( wxString::Format( "string '%s' can't be converted to boolean correctly, "
|
||||
"it will have been perceived as FALSE",
|
||||
aValue ) );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SYMBOL_EDIT_FRAME* m_frame;
|
||||
|
||||
@ -719,6 +790,185 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class PIN_TABLE_EXPORT
|
||||
{
|
||||
public:
|
||||
PIN_TABLE_EXPORT( UNITS_PROVIDER& aUnitsProvider ) :
|
||||
m_unitsProvider( aUnitsProvider )
|
||||
{
|
||||
}
|
||||
|
||||
void ExportData( std::vector<SCH_PIN*>& aPins, const wxString& aToFile ) const
|
||||
{
|
||||
std::vector<int> exportCols {
|
||||
COL_NUMBER,
|
||||
COL_NAME,
|
||||
COL_TYPE,
|
||||
COL_SHAPE,
|
||||
COL_ORIENTATION,
|
||||
COL_NUMBER_SIZE,
|
||||
COL_NAME_SIZE,
|
||||
COL_LENGTH,
|
||||
COL_POSX,
|
||||
COL_POSY,
|
||||
COL_VISIBLE,
|
||||
COL_UNIT,
|
||||
COL_DEMORGAN,
|
||||
};
|
||||
|
||||
std::vector<std::vector<wxString>> exportTable;
|
||||
exportTable.reserve( aPins.size() + 1 );
|
||||
|
||||
std::vector<wxString> headers;
|
||||
for( int col : exportCols )
|
||||
{
|
||||
headers.push_back( GetPinTableColLabel( col ) );
|
||||
}
|
||||
exportTable.emplace_back( std::move( headers ) );
|
||||
|
||||
PIN_INFO_FORMATTER formatter( m_unitsProvider, false, PIN_INFO_FORMATTER::BOOL_FORMAT::TRUE_FALSE );
|
||||
|
||||
for( const SCH_PIN* pin : aPins )
|
||||
{
|
||||
std::vector<wxString>& cols = exportTable.emplace_back( 0 );
|
||||
cols.reserve( exportCols.size() );
|
||||
for( int col : exportCols )
|
||||
{
|
||||
cols.emplace_back( formatter.Format( *pin, col ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( !aToFile.IsEmpty() )
|
||||
{
|
||||
wxFileOutputStream os( aToFile );
|
||||
CSV_WRITER writer( os );
|
||||
writer.WriteLines( exportTable );
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveTabularDataToClipboard( exportTable );
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
UNITS_PROVIDER& m_unitsProvider;
|
||||
};
|
||||
|
||||
|
||||
class PIN_TABLE_IMPORT
|
||||
{
|
||||
public:
|
||||
PIN_TABLE_IMPORT( EDA_BASE_FRAME& aFrame ) :
|
||||
m_frame( aFrame )
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<SCH_PIN>> ImportData( bool aFromFile, LIB_SYMBOL& aSym ) const
|
||||
{
|
||||
wxString path;
|
||||
|
||||
if( aFromFile )
|
||||
{
|
||||
path = promptForFile();
|
||||
if( path.IsEmpty() )
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::vector<wxString>> csvData;
|
||||
bool ok = false;
|
||||
|
||||
if( !path.IsEmpty() )
|
||||
{
|
||||
// Read file content
|
||||
wxString csvFileContent = SafeReadFile( path, "r" );
|
||||
ok = AutoDecodeCSV( csvFileContent, csvData );
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = GetTabularDataFromClipboard( csvData );
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<SCH_PIN>> pins;
|
||||
|
||||
PIN_INFO_FORMATTER formatter( m_frame, false, PIN_INFO_FORMATTER::BOOL_FORMAT::TRUE_FALSE );
|
||||
|
||||
if( ok )
|
||||
{
|
||||
// The first thing we need to do is map the CSV columns to the pin table columns
|
||||
// (in case the user reorders them)
|
||||
std::vector<COL_ORDER> headerCols = getColOrderFromCSV( csvData[0] );
|
||||
|
||||
for( size_t i = 1; i < csvData.size(); ++i )
|
||||
{
|
||||
std::vector<wxString>& cols = csvData[i];
|
||||
|
||||
auto pin = std::make_unique<SCH_PIN>( &aSym );
|
||||
|
||||
// Ignore cells that stick out to the right of the headers
|
||||
size_t maxCol = std::min( headerCols.size(), cols.size() );
|
||||
|
||||
for( size_t j = 0; j < maxCol; ++j )
|
||||
{
|
||||
// Skip unrecognised columns
|
||||
if( headerCols[j] == COL_COUNT )
|
||||
continue;
|
||||
|
||||
formatter.UpdatePin( *pin, cols[j], headerCols[j], aSym );
|
||||
}
|
||||
|
||||
pins.emplace_back( std::move( pin ) );
|
||||
}
|
||||
}
|
||||
return pins;
|
||||
}
|
||||
|
||||
private:
|
||||
wxString promptForFile() const
|
||||
{
|
||||
wxFileDialog dlg( &m_frame, _( "Select pin data file" ), "", "", FILEEXT::CsvTsvFileWildcard(),
|
||||
wxFD_OPEN | wxFD_FILE_MUST_EXIST );
|
||||
|
||||
if( dlg.ShowModal() == wxID_CANCEL )
|
||||
return wxEmptyString;
|
||||
|
||||
return dlg.GetPath();
|
||||
}
|
||||
|
||||
std::vector<COL_ORDER> getColOrderFromCSV( const std::vector<wxString>& aHeaderRow ) const
|
||||
{
|
||||
std::vector<COL_ORDER> colOrder;
|
||||
wxArrayString unknownHeaders;
|
||||
|
||||
for( size_t i = 0; i < aHeaderRow.size(); ++i )
|
||||
{
|
||||
COL_ORDER col = GetColTypeForString( aHeaderRow[i] );
|
||||
|
||||
if( col >= COL_COUNT )
|
||||
unknownHeaders.push_back( aHeaderRow[i] );
|
||||
|
||||
colOrder.push_back( col );
|
||||
}
|
||||
|
||||
if( unknownHeaders.size() )
|
||||
{
|
||||
wxString msg = wxString::Format( _( "Unknown columns in data: %s. These columns will be ignored." ),
|
||||
AccumulateDescriptions( unknownHeaders ) );
|
||||
|
||||
showWarning( msg );
|
||||
}
|
||||
|
||||
return colOrder;
|
||||
}
|
||||
|
||||
void showWarning( const wxString& aMsg ) const
|
||||
{
|
||||
wxMessageBox( aMsg, _( "CSV import warning" ), wxOK | wxICON_WARNING );
|
||||
}
|
||||
|
||||
EDA_BASE_FRAME& m_frame;
|
||||
};
|
||||
|
||||
|
||||
DIALOG_LIB_EDIT_PIN_TABLE::DIALOG_LIB_EDIT_PIN_TABLE( SYMBOL_EDIT_FRAME* parent,
|
||||
LIB_SYMBOL* aSymbol ) :
|
||||
DIALOG_LIB_EDIT_PIN_TABLE_BASE( parent ),
|
||||
@ -1134,6 +1384,82 @@ void DIALOG_LIB_EDIT_PIN_TABLE::OnFilterChoice( wxCommandEvent& event )
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_LIB_EDIT_PIN_TABLE::OnImportButtonClick( wxCommandEvent& event )
|
||||
{
|
||||
bool fromFile = event.GetEventObject() == m_btnImportFromFile;
|
||||
bool replaceAll = m_rbReplaceAll->GetValue();
|
||||
|
||||
PIN_TABLE_IMPORT importer( *m_editFrame );
|
||||
std::vector<std::unique_ptr<SCH_PIN>> newPins = importer.ImportData( fromFile, *m_symbol );
|
||||
|
||||
if( !newPins.size() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( replaceAll )
|
||||
{
|
||||
// This is quite a dance with a segfault without smart pointers
|
||||
for( SCH_PIN* pin : m_pins )
|
||||
delete pin;
|
||||
m_pins.clear();
|
||||
}
|
||||
|
||||
for( auto& newPin : newPins )
|
||||
{
|
||||
m_pins.push_back( newPin.release() );
|
||||
}
|
||||
|
||||
m_cbGroup->SetValue( false );
|
||||
m_dataModel->RebuildRows( m_pins, false, false );
|
||||
|
||||
updateSummary();
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_LIB_EDIT_PIN_TABLE::OnExportButtonClick( wxCommandEvent& event )
|
||||
{
|
||||
bool toFile = event.GetEventObject() == m_btnExportToFile;
|
||||
bool onlyShown = m_rbExportOnlyShownPins->GetValue();
|
||||
|
||||
wxString filePath = wxEmptyString;
|
||||
|
||||
if( toFile )
|
||||
{
|
||||
wxFileName fn( m_symbol->GetName() );
|
||||
fn.SetExt( FILEEXT::CsvFileExtension );
|
||||
wxFileDialog dlg( this, _( "Select pin data file" ), "", fn.GetFullName(), FILEEXT::CsvFileWildcard(),
|
||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
|
||||
|
||||
if( dlg.ShowModal() == wxID_CANCEL )
|
||||
return;
|
||||
|
||||
filePath = dlg.GetPath();
|
||||
}
|
||||
|
||||
std::vector<SCH_PIN*> pinsToExport;
|
||||
|
||||
if( onlyShown )
|
||||
{
|
||||
for( int i = 0; i < m_dataModel->GetNumberRows(); ++i )
|
||||
{
|
||||
for( SCH_PIN* pin : m_dataModel->GetRowPins( i ) )
|
||||
{
|
||||
pinsToExport.push_back( pin );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pinsToExport = m_pins;
|
||||
}
|
||||
|
||||
|
||||
PIN_TABLE_EXPORT exporter( *m_editFrame );
|
||||
exporter.ExportData( pinsToExport, filePath );
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_LIB_EDIT_PIN_TABLE::adjustGridColumns()
|
||||
{
|
||||
// Account for scroll bars
|
||||
|
@ -46,6 +46,7 @@ enum COL_ORDER
|
||||
};
|
||||
|
||||
|
||||
class ACTION_MENU;
|
||||
class PIN_TABLE_DATA_MODEL;
|
||||
class SYMBOL_EDIT_FRAME;
|
||||
|
||||
@ -69,6 +70,8 @@ public:
|
||||
void OnGroupSelected( wxCommandEvent& event ) override;
|
||||
void OnFilterCheckBox( wxCommandEvent& event ) override;
|
||||
void OnFilterChoice( wxCommandEvent& event ) override;
|
||||
void OnImportButtonClick( wxCommandEvent& event ) override;
|
||||
void OnExportButtonClick( wxCommandEvent& event ) override;
|
||||
void OnUpdateUI( wxUpdateUIEvent& event ) override;
|
||||
void OnCancel( wxCommandEvent& event ) override;
|
||||
void OnClose( wxCloseEvent& event ) override;
|
||||
|
@ -20,6 +20,12 @@ DIALOG_LIB_EDIT_PIN_TABLE_BASE::DIALOG_LIB_EDIT_PIN_TABLE_BASE( wxWindow* parent
|
||||
wxBoxSizer* top_sizer;
|
||||
top_sizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
wxBoxSizer* bSizer6;
|
||||
bSizer6 = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
wxBoxSizer* bLeftGridSizer;
|
||||
bLeftGridSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
wxBoxSizer* bSummarySizer;
|
||||
bSummarySizer = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
@ -54,7 +60,7 @@ DIALOG_LIB_EDIT_PIN_TABLE_BASE::DIALOG_LIB_EDIT_PIN_TABLE_BASE( wxWindow* parent
|
||||
bSummarySizer->Add( m_duplicate_pins, 0, wxRIGHT|wxLEFT|wxALIGN_CENTER_VERTICAL, 5 );
|
||||
|
||||
|
||||
top_sizer->Add( bSummarySizer, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 10 );
|
||||
bLeftGridSizer->Add( bSummarySizer, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 10 );
|
||||
|
||||
wxBoxSizer* bGridMarginsSizer;
|
||||
bGridMarginsSizer = new wxBoxSizer( wxVERTICAL );
|
||||
@ -115,7 +121,79 @@ DIALOG_LIB_EDIT_PIN_TABLE_BASE::DIALOG_LIB_EDIT_PIN_TABLE_BASE( wxWindow* parent
|
||||
bGridMarginsSizer->Add( m_grid, 1, wxEXPAND|wxLEFT|wxRIGHT|wxTOP, 10 );
|
||||
|
||||
|
||||
top_sizer->Add( bGridMarginsSizer, 1, wxEXPAND|wxRIGHT|wxLEFT, 5 );
|
||||
bLeftGridSizer->Add( bGridMarginsSizer, 1, wxEXPAND|wxRIGHT|wxLEFT, 5 );
|
||||
|
||||
|
||||
bSizer6->Add( bLeftGridSizer, 1, wxEXPAND, 5 );
|
||||
|
||||
wxBoxSizer* bRightPaneSizer;
|
||||
bRightPaneSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_exportPane = new wxCollapsiblePane( this, wxID_ANY, _("Export"), wxDefaultPosition, wxDefaultSize, wxCP_DEFAULT_STYLE );
|
||||
m_exportPane->Collapse( false );
|
||||
|
||||
wxBoxSizer* bExportSizer;
|
||||
bExportSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_rbExportAllPins = new wxRadioButton( m_exportPane->GetPane(), wxID_ANY, _("Export all pins"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bExportSizer->Add( m_rbExportAllPins, 0, wxALL, 5 );
|
||||
|
||||
m_rbExportOnlyShownPins = new wxRadioButton( m_exportPane->GetPane(), wxID_ANY, _("Export only shown pins"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bExportSizer->Add( m_rbExportOnlyShownPins, 0, wxALL, 5 );
|
||||
|
||||
wxBoxSizer* bExportBtnSizer;
|
||||
bExportBtnSizer = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
m_btnExportToFile = new wxButton( m_exportPane->GetPane(), wxID_ANY, _("To File..."), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bExportBtnSizer->Add( m_btnExportToFile, 0, wxALL, 5 );
|
||||
|
||||
m_btnExportToClipboard = new wxButton( m_exportPane->GetPane(), wxID_ANY, _("To Clipboard"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bExportBtnSizer->Add( m_btnExportToClipboard, 0, wxALL, 5 );
|
||||
|
||||
|
||||
bExportSizer->Add( bExportBtnSizer, 1, wxEXPAND, 5 );
|
||||
|
||||
|
||||
m_exportPane->GetPane()->SetSizer( bExportSizer );
|
||||
m_exportPane->GetPane()->Layout();
|
||||
bExportSizer->Fit( m_exportPane->GetPane() );
|
||||
bRightPaneSizer->Add( m_exportPane, 1, wxEXPAND | wxALL, 5 );
|
||||
|
||||
m_importPane = new wxCollapsiblePane( this, wxID_ANY, _("Import"), wxDefaultPosition, wxDefaultSize, wxCP_DEFAULT_STYLE );
|
||||
m_importPane->Collapse( false );
|
||||
|
||||
wxBoxSizer* bImportSizer;
|
||||
bImportSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_rbReplaceAll = new wxRadioButton( m_importPane->GetPane(), wxID_ANY, _("Replace all existing pins"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bImportSizer->Add( m_rbReplaceAll, 0, wxALL, 5 );
|
||||
|
||||
m_radioBtn1 = new wxRadioButton( m_importPane->GetPane(), wxID_ANY, _("Append to existing pins"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bImportSizer->Add( m_radioBtn1, 0, wxALL, 5 );
|
||||
|
||||
wxBoxSizer* bImportBtnSizer;
|
||||
bImportBtnSizer = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
m_btnImportFromFile = new wxButton( m_importPane->GetPane(), wxID_ANY, _("From File..."), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bImportBtnSizer->Add( m_btnImportFromFile, 0, wxALL, 5 );
|
||||
|
||||
m_btnImportFromClipboard = new wxButton( m_importPane->GetPane(), wxID_ANY, _("From Clipboard"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
bImportBtnSizer->Add( m_btnImportFromClipboard, 0, wxALL, 5 );
|
||||
|
||||
|
||||
bImportSizer->Add( bImportBtnSizer, 1, wxEXPAND, 5 );
|
||||
|
||||
|
||||
m_importPane->GetPane()->SetSizer( bImportSizer );
|
||||
m_importPane->GetPane()->Layout();
|
||||
bImportSizer->Fit( m_importPane->GetPane() );
|
||||
bRightPaneSizer->Add( m_importPane, 1, wxEXPAND | wxALL, 5 );
|
||||
|
||||
|
||||
bSizer6->Add( bRightPaneSizer, 0, wxEXPAND, 5 );
|
||||
|
||||
|
||||
top_sizer->Add( bSizer6, 1, wxEXPAND, 5 );
|
||||
|
||||
wxBoxSizer* bBottomSizer;
|
||||
bBottomSizer = new wxBoxSizer( wxHORIZONTAL );
|
||||
@ -188,6 +266,10 @@ DIALOG_LIB_EDIT_PIN_TABLE_BASE::DIALOG_LIB_EDIT_PIN_TABLE_BASE( wxWindow* parent
|
||||
m_grid->Connect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnCellSelected ), NULL, this );
|
||||
m_grid->Connect( wxEVT_GRID_EDITOR_SHOWN, wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnCellSelected ), NULL, this );
|
||||
m_grid->Connect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnSize ), NULL, this );
|
||||
m_btnExportToFile->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnExportButtonClick ), NULL, this );
|
||||
m_btnExportToClipboard->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnExportButtonClick ), NULL, this );
|
||||
m_btnImportFromFile->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnImportButtonClick ), NULL, this );
|
||||
m_btnImportFromClipboard->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnImportButtonClick ), NULL, this );
|
||||
m_addButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnAddRow ), NULL, this );
|
||||
m_deleteButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnDeleteRow ), NULL, this );
|
||||
m_cbGroup->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnRebuildRows ), NULL, this );
|
||||
@ -207,6 +289,10 @@ DIALOG_LIB_EDIT_PIN_TABLE_BASE::~DIALOG_LIB_EDIT_PIN_TABLE_BASE()
|
||||
m_grid->Disconnect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnCellSelected ), NULL, this );
|
||||
m_grid->Disconnect( wxEVT_GRID_EDITOR_SHOWN, wxGridEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnCellSelected ), NULL, this );
|
||||
m_grid->Disconnect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnSize ), NULL, this );
|
||||
m_btnExportToFile->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnExportButtonClick ), NULL, this );
|
||||
m_btnExportToClipboard->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnExportButtonClick ), NULL, this );
|
||||
m_btnImportFromFile->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnImportButtonClick ), NULL, this );
|
||||
m_btnImportFromClipboard->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnImportButtonClick ), NULL, this );
|
||||
m_addButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnAddRow ), NULL, this );
|
||||
m_deleteButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnDeleteRow ), NULL, this );
|
||||
m_cbGroup->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_LIB_EDIT_PIN_TABLE_BASE::OnRebuildRows ), NULL, this );
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -23,11 +23,13 @@ class WX_GRID;
|
||||
#include <wx/settings.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/grid.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/radiobut.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/collpane.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/dialog.h>
|
||||
@ -49,6 +51,16 @@ class DIALOG_LIB_EDIT_PIN_TABLE_BASE : public DIALOG_SHIM
|
||||
wxStaticText* m_staticTextDuplicatePins;
|
||||
wxStaticText* m_duplicate_pins;
|
||||
WX_GRID* m_grid;
|
||||
wxCollapsiblePane* m_exportPane;
|
||||
wxRadioButton* m_rbExportAllPins;
|
||||
wxRadioButton* m_rbExportOnlyShownPins;
|
||||
wxButton* m_btnExportToFile;
|
||||
wxButton* m_btnExportToClipboard;
|
||||
wxCollapsiblePane* m_importPane;
|
||||
wxRadioButton* m_rbReplaceAll;
|
||||
wxRadioButton* m_radioBtn1;
|
||||
wxButton* m_btnImportFromFile;
|
||||
wxButton* m_btnImportFromClipboard;
|
||||
STD_BITMAP_BUTTON* m_addButton;
|
||||
STD_BITMAP_BUTTON* m_deleteButton;
|
||||
BITMAP_BUTTON* m_divider1;
|
||||
@ -68,6 +80,8 @@ class DIALOG_LIB_EDIT_PIN_TABLE_BASE : public DIALOG_SHIM
|
||||
virtual void OnCellEdited( wxGridEvent& event ) = 0;
|
||||
virtual void OnCellSelected( wxGridEvent& event ) = 0;
|
||||
virtual void OnSize( wxSizeEvent& event ) = 0;
|
||||
virtual void OnExportButtonClick( wxCommandEvent& event ) = 0;
|
||||
virtual void OnImportButtonClick( wxCommandEvent& event ) = 0;
|
||||
virtual void OnAddRow( wxCommandEvent& event ) = 0;
|
||||
virtual void OnDeleteRow( wxCommandEvent& event ) = 0;
|
||||
virtual void OnRebuildRows( wxCommandEvent& event ) = 0;
|
||||
|
1
eeschema/dialogs/pin_table_data_model.h
Normal file
1
eeschema/dialogs/pin_table_data_model.h
Normal file
@ -0,0 +1 @@
|
||||
#pragma once
|
@ -25,8 +25,10 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class wxImage;
|
||||
class wxString;
|
||||
|
||||
/**
|
||||
* Store information to the system clipboard.
|
||||
@ -37,6 +39,16 @@ class wxImage;
|
||||
*/
|
||||
bool SaveClipboard( const std::string& aTextUTF8 );
|
||||
|
||||
/**
|
||||
* Store tabular data to the system clipboard.
|
||||
*/
|
||||
bool SaveTabularDataToClipboard( const std::vector<std::vector<wxString>>& aData );
|
||||
|
||||
/**
|
||||
* Attempt to get tabular data from the clipboard.
|
||||
*/
|
||||
bool GetTabularDataFromClipboard( std::vector<std::vector<wxString>>& aData );
|
||||
|
||||
/**
|
||||
* Return the information currently stored in the system clipboard.
|
||||
*
|
||||
@ -52,4 +64,4 @@ std::string GetClipboardUTF8();
|
||||
*
|
||||
* If there's a filename there, and it can be loaded as an image, do that.
|
||||
*/
|
||||
std::unique_ptr<wxImage> GetImageFromClipboard();
|
||||
std::unique_ptr<wxImage> GetImageFromClipboard();
|
||||
|
@ -358,6 +358,27 @@ inline void AccumulateDescription( wxString& aDesc, const wxString& aItem )
|
||||
aDesc << aItem;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build a comma-separated list from a collection of wxStrings.
|
||||
* (e.g. std::vector, wxArrayString, etc).
|
||||
*/
|
||||
template <typename T>
|
||||
inline void AccumulateDescriptions( wxString& aDesc, const T& aItemCollection )
|
||||
{
|
||||
for( const auto& item : aItemCollection )
|
||||
AccumulateDescription( aDesc, item );
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
inline wxString AccumulateDescriptions( const T& aItemCollection )
|
||||
{
|
||||
wxString desc;
|
||||
AccumulateDescriptions( desc, aItemCollection );
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split \a aString to a string list separated at \a aSplitter.
|
||||
*
|
||||
|
@ -187,6 +187,7 @@ public:
|
||||
static const std::string TextFileExtension;
|
||||
static const std::string MarkdownFileExtension;
|
||||
static const std::string CsvFileExtension;
|
||||
static const std::string TsvFileExtension;
|
||||
static const std::string XmlFileExtension;
|
||||
static const std::string JsonFileExtension;
|
||||
static const std::string PythonFileExtension;
|
||||
@ -245,6 +246,7 @@ public:
|
||||
static wxString PADSNetlistFileWildcard();
|
||||
static wxString HtmlFileWildcard();
|
||||
static wxString CsvFileWildcard();
|
||||
static wxString CsvTsvFileWildcard();
|
||||
static wxString PcbFileWildcard();
|
||||
static wxString CadstarArchiveFilesWildcard();
|
||||
static wxString AltiumProjectFilesWildcard();
|
||||
|
@ -93,6 +93,7 @@ static ARC_EDIT_MODE arcEditModeToEnum( int aIndex )
|
||||
wxFAIL_MSG( wxString::Format( "Invalid index for ARC_EDIT_MODE: %d", aIndex ) );
|
||||
break;
|
||||
}
|
||||
|
||||
return ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS;
|
||||
};
|
||||
|
||||
|
@ -120,9 +120,9 @@ std::optional<TOOLBAR_CONFIGURATION> FOOTPRINT_EDIT_TOOLBAR_SETTINGS::DefaultToo
|
||||
{
|
||||
std::unique_ptr<ACTION_MENU> arcMenu = std::make_unique<ACTION_MENU>( false, selTool );
|
||||
|
||||
arcMenu->Add( PCB_ACTIONS::pointEditorArcKeepCenter, ACTION_MENU::CHECK );
|
||||
arcMenu->Add( PCB_ACTIONS::pointEditorArcKeepEndpoint, ACTION_MENU::CHECK );
|
||||
arcMenu->Add( PCB_ACTIONS::pointEditorArcKeepRadius, ACTION_MENU::CHECK );
|
||||
arcMenu->Add( ACTIONS::pointEditorArcKeepCenter, ACTION_MENU::CHECK );
|
||||
arcMenu->Add( ACTIONS::pointEditorArcKeepEndpoint, ACTION_MENU::CHECK );
|
||||
arcMenu->Add( ACTIONS::pointEditorArcKeepRadius, ACTION_MENU::CHECK );
|
||||
|
||||
return arcMenu;
|
||||
};
|
||||
|
@ -60,6 +60,7 @@ set( QA_COMMON_SRCS
|
||||
|
||||
libeval/test_numeric_evaluator.cpp
|
||||
|
||||
io/test_csv.cpp
|
||||
io/altium/test_altium_parser.cpp
|
||||
io/altium/test_altium_parser_utils.cpp
|
||||
io/cadstar/test_cadstar_parts_parser.cpp
|
||||
@ -117,4 +118,4 @@ endif()
|
||||
|
||||
kicad_add_boost_test( qa_common qa_common )
|
||||
|
||||
setup_qa_env( qa_common )
|
||||
setup_qa_env( qa_common )
|
||||
|
160
qa/tests/common/io/test_csv.cpp
Normal file
160
qa/tests/common/io/test_csv.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <io/csv.h>
|
||||
|
||||
#include <wx/sstream.h>
|
||||
|
||||
|
||||
/**
|
||||
* Define a stream function for logging this type.
|
||||
*/
|
||||
template <typename T>
|
||||
std::ostream& boost_test_print_type( std::ostream& os, const std::vector<std::vector<T>>& aTable )
|
||||
{
|
||||
os << "TABLE[ " << std::endl;
|
||||
for( size_t i = 0; i < aTable.size(); ++i )
|
||||
{
|
||||
const auto& row = aTable[i];
|
||||
os << " Row " << i << " [ ";
|
||||
for( size_t j = 0; j < row.size(); ++j )
|
||||
{
|
||||
os << row[j];
|
||||
if( j < row.size() - 1 )
|
||||
os << ", ";
|
||||
}
|
||||
os << "] " << std::endl;
|
||||
}
|
||||
os << " ]" << std::endl;
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
static bool TableDataEqual( const std::vector<std::vector<wxString>>& aExpected,
|
||||
const std::vector<std::vector<wxString>>& aActual )
|
||||
{
|
||||
if( aExpected.size() != aActual.size() )
|
||||
{
|
||||
BOOST_TEST_MESSAGE( "Row count mismatch: " << aExpected.size() << " != " << aActual.size() );
|
||||
return false;
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < aExpected.size(); ++i )
|
||||
{
|
||||
BOOST_TEST_INFO_SCOPE( "Row " << i );
|
||||
if( aExpected[i].size() != aActual[i].size() )
|
||||
return false;
|
||||
|
||||
for( size_t j = 0; j < aExpected[i].size(); ++j )
|
||||
{
|
||||
if( aExpected[i][j] != aActual[i][j] )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( CsvTests )
|
||||
|
||||
|
||||
struct CsvRoundTripCase
|
||||
{
|
||||
wxString m_name;
|
||||
std::vector<std::vector<wxString>> m_rows;
|
||||
wxString m_expected;
|
||||
};
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( BasicRoundTrips )
|
||||
{
|
||||
// clang-format off
|
||||
static const std::vector<CsvRoundTripCase> testCases = {
|
||||
{
|
||||
"Basic CSV, Double Quoted, Backslash escaped",
|
||||
{
|
||||
{ "Head 1", "Head 2", "Head, \"3\"" },
|
||||
{ "Row 1 Col 1", "Row 1 Col 2", "Row 1 Col 3" }
|
||||
},
|
||||
"\"Head 1\",\"Head 2\",\"Head, \"\"3\"\"\"\n"
|
||||
"\"Row 1 Col 1\",\"Row 1 Col 2\",\"Row 1 Col 3\"\n",
|
||||
},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
for( const auto& testCase : testCases )
|
||||
{
|
||||
BOOST_TEST_INFO_SCOPE( testCase.m_name );
|
||||
|
||||
wxStringOutputStream os;
|
||||
CSV_WRITER writer( os );
|
||||
writer.WriteLines( testCase.m_rows );
|
||||
|
||||
BOOST_CHECK_EQUAL( os.GetString(), testCase.m_expected );
|
||||
|
||||
std::vector<std::vector<wxString>> readRows;
|
||||
|
||||
bool result = AutoDecodeCSV( os.GetString(), readRows );
|
||||
BOOST_CHECK( result );
|
||||
BOOST_CHECK_PREDICATE( TableDataEqual, ( testCase.m_rows )( readRows ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct CsvDecodeCase
|
||||
{
|
||||
wxString m_name;
|
||||
wxString m_input;
|
||||
std::vector<std::vector<wxString>> m_expectedRows;
|
||||
};
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( BasicDecode )
|
||||
{
|
||||
// clang-format off
|
||||
static const std::vector<CsvDecodeCase> testCases = {
|
||||
{
|
||||
"Basic TSV, Double Quoted",
|
||||
"\"Head 1\"\t\"Head 2\"\t\"Head, 3\"\n"
|
||||
"\"Row 1 Col 1\"\t\"Row 1 Col 2\"\t\"Row 1 Col 3\"\n",
|
||||
{
|
||||
{ "Head 1", "Head 2", "Head, 3" },
|
||||
{ "Row 1 Col 1", "Row 1 Col 2", "Row 1 Col 3" }
|
||||
},
|
||||
}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
for( const auto& testCase : testCases )
|
||||
{
|
||||
BOOST_TEST_INFO_SCOPE( testCase.m_name );
|
||||
|
||||
std::vector<std::vector<wxString>> readRows;
|
||||
|
||||
bool result = AutoDecodeCSV( testCase.m_input, readRows );
|
||||
BOOST_CHECK( result );
|
||||
BOOST_CHECK_PREDICATE( TableDataEqual, ( testCase.m_expectedRows )( readRows ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
1
thirdparty/CMakeLists.txt
vendored
1
thirdparty/CMakeLists.txt
vendored
@ -68,5 +68,6 @@ add_subdirectory( picosha2 )
|
||||
add_subdirectory( json_schema_validator )
|
||||
add_subdirectory( pegtl )
|
||||
add_subdirectory( 3dxware_sdk )
|
||||
add_subdirectory( rapidcsv )
|
||||
add_subdirectory( turtle )
|
||||
add_subdirectory( thread-pool )
|
||||
|
7
thirdparty/rapidcsv/CMakeLists.txt
vendored
Normal file
7
thirdparty/rapidcsv/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
add_library( rapidcsv INTERFACE )
|
||||
|
||||
target_include_directories( rapidcsv INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} )
|
||||
|
||||
target_sources( rapidcsv INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/rapidcsv/rapidcsv.h
|
||||
)
|
29
thirdparty/rapidcsv/LICENSE.BSD3
vendored
Normal file
29
thirdparty/rapidcsv/LICENSE.BSD3
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2017, Kristofer Berggren
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
6
thirdparty/rapidcsv/README.md
vendored
Normal file
6
thirdparty/rapidcsv/README.md
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
This directory contains the rapidcsv library, which is a C++ CSV parser and writer.
|
||||
It is a header-only library, and released under the BSD 3-Clause License, a
|
||||
copy of which is included in the LICENSE file.
|
||||
|
||||
The files come from:
|
||||
* https://github.com/d99kris/rapidcsv
|
1943
thirdparty/rapidcsv/rapidcsv/rapidcsv.h
vendored
Normal file
1943
thirdparty/rapidcsv/rapidcsv/rapidcsv.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user