mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 02:03:12 +02:00
Recommendation is to avoid using the year nomenclature as this information is already encoded in the git repo. Avoids needing to repeatly update. Also updates AUTHORS.txt from current repo with contributor names
280 lines
7.7 KiB
C++
280 lines
7.7 KiB
C++
/*
|
|
* 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 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 "increment.h"
|
|
|
|
#include <wx/wxcrt.h>
|
|
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <regex>
|
|
|
|
|
|
KICOMMON_API bool IncrementString( wxString& name, int aIncrement )
|
|
{
|
|
if( name.IsEmpty() )
|
|
return true;
|
|
|
|
wxString suffix;
|
|
wxString digits;
|
|
wxString outputFormat;
|
|
wxString outputNumber;
|
|
int ii = name.Len() - 1;
|
|
int dCount = 0;
|
|
|
|
while( ii >= 0 && !wxIsdigit( name.GetChar( ii ) ) )
|
|
{
|
|
suffix = name.GetChar( ii ) + suffix;
|
|
ii--;
|
|
}
|
|
|
|
while( ii >= 0 && wxIsdigit( name.GetChar( ii ) ) )
|
|
{
|
|
digits = name.GetChar( ii ) + digits;
|
|
ii--;
|
|
dCount++;
|
|
}
|
|
|
|
if( digits.IsEmpty() )
|
|
return true;
|
|
|
|
long number = 0;
|
|
|
|
if( digits.ToLong( &number ) )
|
|
{
|
|
number += aIncrement;
|
|
|
|
// Don't let result go below zero
|
|
|
|
if( number > -1 )
|
|
{
|
|
name.Remove( ii + 1 );
|
|
//write out a format string with correct number of leading zeroes
|
|
outputFormat.Printf( wxS( "%%0%dld" ), dCount );
|
|
//write out the number using the format string
|
|
outputNumber.Printf( outputFormat, number );
|
|
name << outputNumber << suffix;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
std::optional<wxString> STRING_INCREMENTER::Increment( const wxString& aStr, int aDelta,
|
|
size_t aRightIndex ) const
|
|
{
|
|
if( aStr.IsEmpty() )
|
|
return std::nullopt;
|
|
|
|
wxString remaining = aStr;
|
|
std::vector<std::pair<wxString, STRING_PART_TYPE>> parts;
|
|
size_t goodParts = 0;
|
|
|
|
// Keep popping chunks off the string until we have what we need
|
|
while( goodParts < ( aRightIndex + 1 ) && !remaining.IsEmpty() )
|
|
{
|
|
static const std::regex integerRegex( R"(\d+$)" );
|
|
// ABC or abc but not Abc
|
|
static const std::regex sameCaseAlphabetRegex( R"(([a-z]+|[A-Z]+)$)" );
|
|
// Skippables - for now anything that isn't a letter or number
|
|
static const std::regex skipRegex( R"([^a-zA-Z0-9]+$)" );
|
|
|
|
std::string remainingStr = remaining.ToStdString();
|
|
std::smatch match;
|
|
if( std::regex_search( remainingStr, match, integerRegex ) )
|
|
{
|
|
parts.push_back( { match.str(), STRING_PART_TYPE::INTEGER } );
|
|
remaining = remaining.Left( remaining.Len() - match.str().size() );
|
|
goodParts++;
|
|
}
|
|
else if( std::regex_search( remainingStr, match, sameCaseAlphabetRegex ) )
|
|
{
|
|
parts.push_back( { match.str(), STRING_PART_TYPE::ALPHABETIC } );
|
|
remaining = remaining.Left( remaining.Len() - match.str().size() );
|
|
goodParts++;
|
|
}
|
|
else if( std::regex_search( remainingStr, match, skipRegex ) )
|
|
{
|
|
parts.push_back( { match.str(), STRING_PART_TYPE::SKIP } );
|
|
remaining = remaining.Left( remaining.Len() - match.str().size() );
|
|
}
|
|
else
|
|
{
|
|
// Out of ideas
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Couldn't find the part we wanted
|
|
if( goodParts < aRightIndex + 1 )
|
|
return std::nullopt;
|
|
|
|
// Increment the part we wanted
|
|
bool didIncrement = incrementPart( parts.back().first, parts.back().second, aDelta );
|
|
|
|
if( !didIncrement )
|
|
return std::nullopt;
|
|
|
|
// Reassemble the string - the left-over part, then parts in reverse
|
|
wxString result = remaining;
|
|
for( auto it = parts.rbegin(); it != parts.rend(); ++it )
|
|
{
|
|
result << it->first;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
static bool containsIOSQXZ( const wxString& aStr )
|
|
{
|
|
static const wxString iosqxz = "IOSQXZ";
|
|
for( const wxUniChar& c : aStr )
|
|
{
|
|
if( iosqxz.Contains( c ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool STRING_INCREMENTER::incrementPart( wxString& aPart, STRING_PART_TYPE aType, int aDelta ) const
|
|
{
|
|
switch( aType )
|
|
{
|
|
case STRING_PART_TYPE::INTEGER:
|
|
{
|
|
long number = 0;
|
|
bool zeroPadded = aPart.StartsWith( '0' );
|
|
size_t oldLen = aPart.Len();
|
|
|
|
if( aPart.ToLong( &number ) )
|
|
{
|
|
number += aDelta;
|
|
|
|
// Going below zero makes things awkward
|
|
// and is not usually that useful.
|
|
if( number < 0 )
|
|
return false;
|
|
|
|
aPart.Printf( "%ld", number );
|
|
|
|
// If the number was zero-padded, we need to re-pad it
|
|
if( zeroPadded )
|
|
{
|
|
aPart = wxString( "0", oldLen - aPart.Len() ) + aPart;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case STRING_PART_TYPE::ALPHABETIC:
|
|
{
|
|
// Covert to uppercase
|
|
wxString upper = aPart.Upper();
|
|
bool wasUpper = aPart == upper;
|
|
|
|
static const wxString alphabetFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY";
|
|
|
|
const wxString& alpha =
|
|
( m_SkipIOSQXZ & !containsIOSQXZ( aPart ) ) ? alphaNoIOSQXZ : alphabetFull;
|
|
|
|
int index = IndexFromAlphabetic( upper, alpha );
|
|
|
|
// Something was not in the alphabet
|
|
if( index == -1 )
|
|
return false;
|
|
|
|
// It's such a big number that we don't want to increment it
|
|
if( index > m_AlphabeticMaxIndex && m_AlphabeticMaxIndex >= 0 )
|
|
return false;
|
|
|
|
index += aDelta;
|
|
|
|
if( index < 0 )
|
|
return false;
|
|
|
|
wxString newStr = AlphabeticFromIndex( index, alpha, true );
|
|
|
|
if( !wasUpper )
|
|
newStr = newStr.Lower();
|
|
|
|
aPart = newStr;
|
|
|
|
return true;
|
|
}
|
|
case STRING_PART_TYPE::SKIP: break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
KICOMMON_API int IndexFromAlphabetic( const wxString& aStr, const wxString& aAlphabet )
|
|
{
|
|
int index = 0;
|
|
const int radix = aAlphabet.Length();
|
|
|
|
for( size_t i = 0; i < aStr.Len(); i++ )
|
|
{
|
|
int alphaIndex = aAlphabet.Find( aStr[i] );
|
|
|
|
if( alphaIndex == wxNOT_FOUND )
|
|
return -1;
|
|
|
|
if( i != aStr.Len() - 1 )
|
|
alphaIndex++;
|
|
|
|
index += alphaIndex * pow( radix, aStr.Len() - 1 - i );
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
|
|
wxString KICOMMON_API AlphabeticFromIndex( size_t aN, const wxString& aAlphabet,
|
|
bool aZeroBasedNonUnitCols )
|
|
{
|
|
wxString itemNum;
|
|
bool firstRound = true;
|
|
const int radix = aAlphabet.Length();
|
|
|
|
do
|
|
{
|
|
int modN = aN % radix;
|
|
|
|
if( aZeroBasedNonUnitCols && !firstRound )
|
|
modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx"
|
|
|
|
itemNum.insert( 0, 1, aAlphabet[modN] );
|
|
|
|
aN /= radix;
|
|
firstRound = false;
|
|
} while( aN );
|
|
|
|
return itemNum;
|
|
}
|