mirror of
https://github.com/notepad-plus-plus/notepad-plus-plus.git
synced 2025-07-30 01:04:57 +02:00
Merge pull request #86 from andreas-jonsson/decimal_sort
[NEW_FEATURE] Refine sort lines feature: Add Sort Lines Lexicographically/Integer/Decimal (comma) /Decimal (dot) with Ascending and Descending commands.
This commit is contained in:
commit
805ab2b9ff
@ -749,131 +749,32 @@ generic_string stringJoin(const std::vector<generic_string>& strings, const gene
|
|||||||
return joined;
|
return joined;
|
||||||
}
|
}
|
||||||
|
|
||||||
long long stollStrict(const generic_string& input)
|
generic_string stringTakeWhileAdmissable(const generic_string& input, const generic_string& admissable)
|
||||||
{
|
{
|
||||||
if (input.empty())
|
// Find first non-admissable character in "input", and remove everything after it.
|
||||||
|
size_t idx = input.find_first_not_of(admissable);
|
||||||
|
if (idx == std::string::npos)
|
||||||
{
|
{
|
||||||
throw std::invalid_argument("Empty input.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Check minus characters.
|
|
||||||
const int minuses = std::count(input.begin(), input.end(), TEXT('-'));
|
|
||||||
if (minuses > 1)
|
|
||||||
{
|
|
||||||
throw std::invalid_argument("More than one minus sign.");
|
|
||||||
}
|
|
||||||
else if (minuses == 1 && input[0] != TEXT('-'))
|
|
||||||
{
|
|
||||||
throw std::invalid_argument("Minus sign must be first.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for other characters which are not allowed.
|
|
||||||
if (input.find_first_not_of(TEXT("-0123456789")) != std::string::npos)
|
|
||||||
{
|
|
||||||
throw std::invalid_argument("Invalid character found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::stoll(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool allLinesAreNumericOrEmpty(const std::vector<generic_string>& lines)
|
|
||||||
{
|
|
||||||
for (const generic_string& line : lines)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!line.empty())
|
|
||||||
{
|
|
||||||
stollStrict(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::invalid_argument&)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (std::out_of_range&)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<generic_string> repeatString(const generic_string& text, const size_t count)
|
|
||||||
{
|
|
||||||
std::vector<generic_string> output;
|
|
||||||
output.reserve(count);
|
|
||||||
for (size_t i = 0; i < count; ++i)
|
|
||||||
{
|
|
||||||
output.push_back(text);
|
|
||||||
}
|
|
||||||
assert(output.size() == count);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<generic_string> lexicographicSort(std::vector<generic_string> input, bool isDescending)
|
|
||||||
{
|
|
||||||
std::sort(input.begin(), input.end(), [isDescending](generic_string a, generic_string b)
|
|
||||||
{
|
|
||||||
if (isDescending)
|
|
||||||
{
|
|
||||||
return a.compare(b) > 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return a.compare(b) < 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return input.substr(0, idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<generic_string> numericSort(std::vector<generic_string> input, bool isDescending)
|
double stodLocale(const generic_string& str, _locale_t loc, size_t* idx)
|
||||||
{
|
{
|
||||||
// Pre-condition: all strings in "input" are either empty or convertible to int with stoiStrict.
|
// Copied from the std::stod implementation but uses _wcstod_l instead of wcstod.
|
||||||
// Note that empty lines are filtered out and added back manually to the output at the end.
|
const wchar_t* ptr = str.c_str();
|
||||||
std::vector<long long> nonEmptyInputAsNumbers;
|
errno = 0;
|
||||||
size_t nofEmptyLines = 0;
|
wchar_t* eptr;
|
||||||
nonEmptyInputAsNumbers.reserve(input.size());
|
double ans = ::_wcstod_l(ptr, &eptr, loc);
|
||||||
for (const generic_string& line : input)
|
if (ptr == eptr)
|
||||||
{
|
throw new std::invalid_argument("invalid stod argument");
|
||||||
if (line.empty())
|
if (errno == ERANGE)
|
||||||
{
|
throw new std::out_of_range("stod argument out of range");
|
||||||
++nofEmptyLines;
|
if (idx != NULL)
|
||||||
}
|
*idx = (size_t)(eptr - ptr);
|
||||||
else
|
return ans;
|
||||||
{
|
|
||||||
nonEmptyInputAsNumbers.push_back(stollStrict(line));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(nonEmptyInputAsNumbers.size() + nofEmptyLines == input.size());
|
|
||||||
std::sort(nonEmptyInputAsNumbers.begin(), nonEmptyInputAsNumbers.end(), [isDescending](long long a, long long b)
|
|
||||||
{
|
|
||||||
if (isDescending)
|
|
||||||
{
|
|
||||||
return a > b;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return a < b;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
std::vector<generic_string> output;
|
|
||||||
output.reserve(input.size());
|
|
||||||
const std::vector<generic_string> empties = repeatString(TEXT(""), nofEmptyLines);
|
|
||||||
if (!isDescending)
|
|
||||||
{
|
|
||||||
output.insert(output.end(), empties.begin(), empties.end());
|
|
||||||
}
|
|
||||||
for (const long long& sortedNumber : nonEmptyInputAsNumbers)
|
|
||||||
{
|
|
||||||
output.push_back(std::to_wstring(sortedNumber));
|
|
||||||
}
|
|
||||||
if (isDescending)
|
|
||||||
{
|
|
||||||
output.insert(output.end(), empties.begin(), empties.end());
|
|
||||||
}
|
|
||||||
assert(output.size() == input.size());
|
|
||||||
return output;
|
|
||||||
}
|
}
|
@ -190,11 +190,7 @@ generic_string stringToUpper(generic_string strToConvert);
|
|||||||
generic_string stringReplace(generic_string subject, const generic_string& search, const generic_string& replace);
|
generic_string stringReplace(generic_string subject, const generic_string& search, const generic_string& replace);
|
||||||
std::vector<generic_string> stringSplit(const generic_string& input, const generic_string& delimiter);
|
std::vector<generic_string> stringSplit(const generic_string& input, const generic_string& delimiter);
|
||||||
generic_string stringJoin(const std::vector<generic_string>& strings, const generic_string& separator);
|
generic_string stringJoin(const std::vector<generic_string>& strings, const generic_string& separator);
|
||||||
long long stollStrict(const generic_string& input);
|
generic_string stringTakeWhileAdmissable(const generic_string& input, const generic_string& admissable);
|
||||||
bool allLinesAreNumericOrEmpty(const std::vector<generic_string>& lines);
|
double stodLocale(const generic_string& str, _locale_t loc, size_t* idx = NULL);
|
||||||
std::vector<generic_string> repeatString(const generic_string& text, const size_t count);
|
|
||||||
|
|
||||||
std::vector<generic_string> numericSort(std::vector<generic_string> input, bool isDescending);
|
|
||||||
std::vector<generic_string> lexicographicSort(std::vector<generic_string> input, bool isDescending);
|
|
||||||
|
|
||||||
#endif //M30_IDE_COMMUN_H
|
#endif //M30_IDE_COMMUN_H
|
||||||
|
262
PowerEditor/src/MISC/Common/Sorters.h
Normal file
262
PowerEditor/src/MISC/Common/Sorters.h
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
// This file is part of Notepad++ project
|
||||||
|
// Copyright (C)2003 Don HO <don.h@free.fr>
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Note that the GPL places important restrictions on "derived works", yet
|
||||||
|
// it does not provide a detailed definition of that term. To avoid
|
||||||
|
// misunderstandings, we consider an application to constitute a
|
||||||
|
// "derivative work" for the purpose of this license if it does any of the
|
||||||
|
// following:
|
||||||
|
// 1. Integrates source code from Notepad++.
|
||||||
|
// 2. Integrates/includes/aggregates Notepad++ into a proprietary executable
|
||||||
|
// installer, such as those produced by InstallShield.
|
||||||
|
// 3. Links to a library or executes a program that does any of the above.
|
||||||
|
//
|
||||||
|
// 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, write to the Free Software
|
||||||
|
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef M30_IDE_SORTERS_H
|
||||||
|
#define M30_IDE_SORTERS_H
|
||||||
|
|
||||||
|
// Base interface for line sorting.
|
||||||
|
class ISorter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool _isDescending;
|
||||||
|
size_t _fromColumn, _toColumn;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool isDescending() const
|
||||||
|
{
|
||||||
|
return _isDescending;
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_string getSortKey(const generic_string& input)
|
||||||
|
{
|
||||||
|
if (isSortingSpecificColumns())
|
||||||
|
{
|
||||||
|
return input.substr(_fromColumn, 1 + _toColumn - _fromColumn);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSortingSpecificColumns()
|
||||||
|
{
|
||||||
|
return _fromColumn != 0 && _toColumn != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
ISorter(bool isDescending, size_t fromColumn, size_t toColumn) : _isDescending(isDescending), _fromColumn(fromColumn), _toColumn(toColumn)
|
||||||
|
{
|
||||||
|
assert(_fromColumn <= _toColumn);
|
||||||
|
};
|
||||||
|
virtual ~ISorter() { };
|
||||||
|
virtual std::vector<generic_string> sort(std::vector<generic_string> lines) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Implementation of lexicographic sorting of lines.
|
||||||
|
class LexicographicSorter : public ISorter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LexicographicSorter(bool isDescending, size_t fromColumn, size_t toColumn) : ISorter(isDescending, fromColumn, toColumn) { };
|
||||||
|
|
||||||
|
std::vector<generic_string> sort(std::vector<generic_string> lines) override
|
||||||
|
{
|
||||||
|
// Note that both branches here are equivalent in the sense that they give always give the same answer.
|
||||||
|
// However, if we are *not* sorting specific columns, then we get a 40% speed improvement by not calling
|
||||||
|
// getSortKey() so many times.
|
||||||
|
if (isSortingSpecificColumns())
|
||||||
|
{
|
||||||
|
std::sort(lines.begin(), lines.end(), [this](generic_string a, generic_string b)
|
||||||
|
{
|
||||||
|
if (isDescending())
|
||||||
|
{
|
||||||
|
return getSortKey(a).compare(getSortKey(b)) > 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return getSortKey(a).compare(getSortKey(b)) < 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::sort(lines.begin(), lines.end(), [this](generic_string a, generic_string b)
|
||||||
|
{
|
||||||
|
if (isDescending())
|
||||||
|
{
|
||||||
|
return a.compare(b) > 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return a.compare(b) < 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert each line to a number and then sort.
|
||||||
|
// The conversion must be implemented in classes which inherit from this, see prepareStringForConversion and convertStringToNumber.
|
||||||
|
template<typename T_Num>
|
||||||
|
class NumericSorter : public ISorter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NumericSorter(bool isDescending, size_t fromColumn, size_t toColumn) : ISorter(isDescending, fromColumn, toColumn)
|
||||||
|
{
|
||||||
|
_usLocale = ::_wcreate_locale(LC_NUMERIC, TEXT("en-US"));
|
||||||
|
};
|
||||||
|
|
||||||
|
~NumericSorter()
|
||||||
|
{
|
||||||
|
::_free_locale(_usLocale);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<generic_string> sort(std::vector<generic_string> lines) override
|
||||||
|
{
|
||||||
|
// Note that empty lines are filtered out and added back manually to the output at the end.
|
||||||
|
std::vector<std::pair<size_t, T_Num>> nonEmptyInputAsNumbers;
|
||||||
|
std::vector<generic_string> empties;
|
||||||
|
nonEmptyInputAsNumbers.reserve(lines.size());
|
||||||
|
for (size_t lineIndex = 0; lineIndex < lines.size(); ++lineIndex)
|
||||||
|
{
|
||||||
|
const generic_string originalLine = lines[lineIndex];
|
||||||
|
const generic_string preparedLine = prepareStringForConversion(originalLine);
|
||||||
|
if (considerStringEmpty(preparedLine))
|
||||||
|
{
|
||||||
|
empties.push_back(originalLine);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nonEmptyInputAsNumbers.push_back(make_pair(lineIndex, convertStringToNumber(preparedLine)));
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
throw lineIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(nonEmptyInputAsNumbers.size() + empties.size() == lines.size());
|
||||||
|
const bool descending = isDescending();
|
||||||
|
std::sort(nonEmptyInputAsNumbers.begin(), nonEmptyInputAsNumbers.end(), [descending](std::pair<size_t, T_Num> a, std::pair<size_t, T_Num> b)
|
||||||
|
{
|
||||||
|
if (descending)
|
||||||
|
{
|
||||||
|
return a.second > b.second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return a.second < b.second;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
std::vector<generic_string> output;
|
||||||
|
output.reserve(lines.size());
|
||||||
|
if (!isDescending())
|
||||||
|
{
|
||||||
|
output.insert(output.end(), empties.begin(), empties.end());
|
||||||
|
}
|
||||||
|
for (auto it = nonEmptyInputAsNumbers.begin(); it != nonEmptyInputAsNumbers.end(); ++it)
|
||||||
|
{
|
||||||
|
output.push_back(lines[it->first]);
|
||||||
|
}
|
||||||
|
if (isDescending())
|
||||||
|
{
|
||||||
|
output.insert(output.end(), empties.begin(), empties.end());
|
||||||
|
}
|
||||||
|
assert(output.size() == lines.size());
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool considerStringEmpty(const generic_string& input)
|
||||||
|
{
|
||||||
|
// String has something else than just whitespace.
|
||||||
|
return input.find_first_not_of(TEXT(" \t\r\n")) == std::string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the string for conversion to number.
|
||||||
|
virtual generic_string prepareStringForConversion(const generic_string& input) = 0;
|
||||||
|
|
||||||
|
// Should convert the input string to a number of the correct type.
|
||||||
|
// If unable to convert, throw either std::invalid_argument or std::out_of_range.
|
||||||
|
virtual T_Num convertStringToNumber(const generic_string& input) = 0;
|
||||||
|
|
||||||
|
// We need a fixed locale so we get the same string-to-double behavior across all computers.
|
||||||
|
// This is the "enUS" locale.
|
||||||
|
_locale_t _usLocale;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts lines to long long before sorting.
|
||||||
|
class IntegerSorter : public NumericSorter<long long>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IntegerSorter(bool isDescending, size_t fromColumn, size_t toColumn) : NumericSorter<long long>(isDescending, fromColumn, toColumn) { };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual generic_string prepareStringForConversion(const generic_string& input)
|
||||||
|
{
|
||||||
|
return stringTakeWhileAdmissable(getSortKey(input), TEXT(" \t\r\n0123456789-"));
|
||||||
|
}
|
||||||
|
|
||||||
|
long long convertStringToNumber(const generic_string& input) override
|
||||||
|
{
|
||||||
|
return std::stoll(input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts lines to double before sorting (assumes decimal comma).
|
||||||
|
class DecimalCommaSorter : public NumericSorter<double>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DecimalCommaSorter(bool isDescending, size_t fromColumn, size_t toColumn) : NumericSorter<double>(isDescending, fromColumn, toColumn) { };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
generic_string prepareStringForConversion(const generic_string& input) override
|
||||||
|
{
|
||||||
|
generic_string admissablePart = stringTakeWhileAdmissable(getSortKey(input), TEXT(" \t\r\n0123456789,-"));
|
||||||
|
return stringReplace(admissablePart, TEXT(","), TEXT("."));
|
||||||
|
}
|
||||||
|
|
||||||
|
double convertStringToNumber(const generic_string& input) override
|
||||||
|
{
|
||||||
|
return stodLocale(input, _usLocale);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts lines to double before sorting (assumes decimal dot).
|
||||||
|
class DecimalDotSorter : public NumericSorter<double>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DecimalDotSorter(bool isDescending, size_t fromColumn, size_t toColumn) : NumericSorter<double>(isDescending, fromColumn, toColumn) { };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
generic_string prepareStringForConversion(const generic_string& input) override
|
||||||
|
{
|
||||||
|
return stringTakeWhileAdmissable(getSortKey(input), TEXT(" \t\r\n0123456789.-"));
|
||||||
|
}
|
||||||
|
|
||||||
|
double convertStringToNumber(const generic_string& input) override
|
||||||
|
{
|
||||||
|
return stodLocale(input, _usLocale);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //M30_IDE_SORTERS_H
|
@ -274,15 +274,24 @@ BEGIN
|
|||||||
MENUITEM "Duplicate Current Line", IDM_EDIT_DUP_LINE
|
MENUITEM "Duplicate Current Line", IDM_EDIT_DUP_LINE
|
||||||
MENUITEM "Split Lines", IDM_EDIT_SPLIT_LINES
|
MENUITEM "Split Lines", IDM_EDIT_SPLIT_LINES
|
||||||
MENUITEM "Join Lines", IDM_EDIT_JOIN_LINES
|
MENUITEM "Join Lines", IDM_EDIT_JOIN_LINES
|
||||||
MENUITEM "Sort Lines in Ascending Order", IDM_EDIT_SORTLINES_ASCENDING
|
|
||||||
MENUITEM "Sort Lines in Descending Order", IDM_EDIT_SORTLINES_DESCENDING
|
|
||||||
MENUITEM "Move Up Current Line", IDM_EDIT_LINE_UP
|
MENUITEM "Move Up Current Line", IDM_EDIT_LINE_UP
|
||||||
MENUITEM "Move Down Current Line", IDM_EDIT_LINE_DOWN
|
MENUITEM "Move Down Current Line", IDM_EDIT_LINE_DOWN
|
||||||
MENUITEM "Remove Empty Lines", IDM_EDIT_REMOVEEMPTYLINES
|
MENUITEM "Remove Empty Lines", IDM_EDIT_REMOVEEMPTYLINES
|
||||||
MENUITEM "Remove Empty Lines (Containing Blank characters)", IDM_EDIT_REMOVEEMPTYLINESWITHBLANK
|
MENUITEM "Remove Empty Lines (Containing Blank characters)", IDM_EDIT_REMOVEEMPTYLINESWITHBLANK
|
||||||
MENUITEM "Insert Blank Line Above Current", IDM_EDIT_BLANKLINEABOVECURRENT
|
MENUITEM "Insert Blank Line Above Current", IDM_EDIT_BLANKLINEABOVECURRENT
|
||||||
MENUITEM "Insert Blank Line Below Current", IDM_EDIT_BLANKLINEBELOWCURRENT
|
MENUITEM "Insert Blank Line Below Current", IDM_EDIT_BLANKLINEBELOWCURRENT
|
||||||
|
MENUITEM SEPARATOR
|
||||||
|
MENUITEM "Sort Lines Lexicographically Ascending", IDM_EDIT_SORTLINES_LEXICOGRAPHIC_ASCENDING
|
||||||
|
MENUITEM "Sort Lines As Integers Ascending", IDM_EDIT_SORTLINES_INTEGER_ASCENDING
|
||||||
|
MENUITEM "Sort Lines As Decimals (Comma) Ascending", IDM_EDIT_SORTLINES_DECIMALCOMMA_ASCENDING
|
||||||
|
MENUITEM "Sort Lines As Decimals (Dot) Ascending", IDM_EDIT_SORTLINES_DECIMALDOT_ASCENDING
|
||||||
|
MENUITEM SEPARATOR
|
||||||
|
MENUITEM "Sort Lines Lexicographically Descending", IDM_EDIT_SORTLINES_LEXICOGRAPHIC_DESCENDING
|
||||||
|
MENUITEM "Sort Lines As Integers Descending", IDM_EDIT_SORTLINES_INTEGER_DESCENDING
|
||||||
|
MENUITEM "Sort Lines As Decimals (Comma) Descending", IDM_EDIT_SORTLINES_DECIMALCOMMA_DESCENDING
|
||||||
|
MENUITEM "Sort Lines As Decimals (Dot) Descending", IDM_EDIT_SORTLINES_DECIMALDOT_DESCENDING
|
||||||
END
|
END
|
||||||
|
MENUITEM SEPARATOR
|
||||||
POPUP "Comment/Uncomment"
|
POPUP "Comment/Uncomment"
|
||||||
BEGIN
|
BEGIN
|
||||||
MENUITEM "Toggle Single Line Comment", IDM_EDIT_BLOCK_COMMENT
|
MENUITEM "Toggle Single Line Comment", IDM_EDIT_BLOCK_COMMENT
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "VerticalFileSwitcher.h"
|
#include "VerticalFileSwitcher.h"
|
||||||
#include "documentMap.h"
|
#include "documentMap.h"
|
||||||
#include "functionListPanel.h"
|
#include "functionListPanel.h"
|
||||||
|
#include "Sorters.h"
|
||||||
|
|
||||||
|
|
||||||
void Notepad_plus::macroPlayback(Macro macro)
|
void Notepad_plus::macroPlayback(Macro macro)
|
||||||
@ -344,44 +345,106 @@ void Notepad_plus::command(int id)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IDM_EDIT_SORTLINES_ASCENDING:
|
case IDM_EDIT_SORTLINES_LEXICOGRAPHIC_ASCENDING:
|
||||||
case IDM_EDIT_SORTLINES_DESCENDING:
|
case IDM_EDIT_SORTLINES_LEXICOGRAPHIC_DESCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_INTEGER_ASCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_INTEGER_DESCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_DECIMALCOMMA_ASCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_DECIMALCOMMA_DESCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_DECIMALDOT_ASCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_DECIMALDOT_DESCENDING:
|
||||||
{
|
{
|
||||||
// default: no selection
|
size_t fromLine = 0, toLine = 0;
|
||||||
size_t fromLine = 0;
|
size_t fromColumn = 0, toColumn = 0;
|
||||||
size_t toLine = _pEditView->execute(SCI_GETLINECOUNT) - 1;
|
|
||||||
|
|
||||||
// multi-selection is not allowed
|
bool hasLineSelection = false;
|
||||||
if (_pEditView->execute(SCI_GETSELECTIONS) > 1)
|
if (_pEditView->execute(SCI_GETSELECTIONS) > 1)
|
||||||
return;
|
{
|
||||||
|
|
||||||
// retangle-selection is not allowed
|
|
||||||
if (_pEditView->execute(SCI_SELECTIONISRECTANGLE))
|
if (_pEditView->execute(SCI_SELECTIONISRECTANGLE))
|
||||||
|
{
|
||||||
|
ColumnModeInfos colInfos = _pEditView->getColumnModeSelectInfo();
|
||||||
|
int leftPos = colInfos.begin()->_selLpos;
|
||||||
|
int rightPos = colInfos.rbegin()->_selRpos;
|
||||||
|
int startPos = min(leftPos, rightPos);
|
||||||
|
int endPos = max(leftPos, rightPos);
|
||||||
|
fromLine = _pEditView->execute(SCI_LINEFROMPOSITION, startPos);
|
||||||
|
toLine = _pEditView->execute(SCI_LINEFROMPOSITION, endPos);
|
||||||
|
fromColumn = _pEditView->execute(SCI_GETCOLUMN, leftPos);
|
||||||
|
toColumn = _pEditView->execute(SCI_GETCOLUMN, rightPos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
int selStart = _pEditView->execute(SCI_GETSELECTIONSTART);
|
int selStart = _pEditView->execute(SCI_GETSELECTIONSTART);
|
||||||
int selEnd = _pEditView->execute(SCI_GETSELECTIONEND);
|
int selEnd = _pEditView->execute(SCI_GETSELECTIONEND);
|
||||||
bool hasSelection = selStart != selEnd;
|
hasLineSelection = selStart != selEnd;
|
||||||
|
if (hasLineSelection)
|
||||||
pair<int, int> lineRange = _pEditView->getSelectionLinesRange();
|
{
|
||||||
|
pair<int, int> lineRange = _pEditView->getSelectionLinesRange();
|
||||||
if (hasSelection)
|
// One single line selection is not allowed.
|
||||||
|
if (lineRange.first == lineRange.second)
|
||||||
{
|
{
|
||||||
// one single line selection is not allowed
|
|
||||||
if ((lineRange.second - lineRange.first) == 0)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
fromLine = lineRange.first;
|
fromLine = lineRange.first;
|
||||||
toLine = lineRange.second;
|
toLine = lineRange.second;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No selection.
|
||||||
|
fromLine = 0;
|
||||||
|
toLine = _pEditView->execute(SCI_GETLINECOUNT) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDescending = id == IDM_EDIT_SORTLINES_LEXICOGRAPHIC_DESCENDING ||
|
||||||
|
id == IDM_EDIT_SORTLINES_INTEGER_DESCENDING ||
|
||||||
|
id == IDM_EDIT_SORTLINES_DECIMALCOMMA_DESCENDING ||
|
||||||
|
id == IDM_EDIT_SORTLINES_DECIMALDOT_DESCENDING;
|
||||||
|
|
||||||
_pEditView->execute(SCI_BEGINUNDOACTION);
|
_pEditView->execute(SCI_BEGINUNDOACTION);
|
||||||
_pEditView->sortLines(fromLine, toLine, id == IDM_EDIT_SORTLINES_DESCENDING);
|
std::unique_ptr<ISorter> pSorter;
|
||||||
|
if (id == IDM_EDIT_SORTLINES_LEXICOGRAPHIC_DESCENDING || id == IDM_EDIT_SORTLINES_LEXICOGRAPHIC_ASCENDING)
|
||||||
|
{
|
||||||
|
pSorter = std::unique_ptr<ISorter>(new LexicographicSorter(isDescending, fromColumn, toColumn));
|
||||||
|
}
|
||||||
|
else if (id == IDM_EDIT_SORTLINES_INTEGER_DESCENDING || id == IDM_EDIT_SORTLINES_INTEGER_ASCENDING)
|
||||||
|
{
|
||||||
|
pSorter = std::unique_ptr<ISorter>(new IntegerSorter(isDescending, fromColumn, toColumn));
|
||||||
|
}
|
||||||
|
else if (id == IDM_EDIT_SORTLINES_DECIMALCOMMA_DESCENDING || id == IDM_EDIT_SORTLINES_DECIMALCOMMA_ASCENDING)
|
||||||
|
{
|
||||||
|
pSorter = std::unique_ptr<ISorter>(new DecimalCommaSorter(isDescending, fromColumn, toColumn));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pSorter = std::unique_ptr<ISorter>(new DecimalDotSorter(isDescending, fromColumn, toColumn));
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_pEditView->sortLines(fromLine, toLine, pSorter.get());
|
||||||
|
}
|
||||||
|
catch (size_t& failedLineIndex)
|
||||||
|
{
|
||||||
|
generic_string lineNo = std::to_wstring(1 + fromLine + failedLineIndex);
|
||||||
|
_nativeLangSpeaker.messageBox("SortingError",
|
||||||
|
_pPublicInterface->getHSelf(),
|
||||||
|
TEXT("Unable to perform numeric sort due to line $STR_REPLACE$."),
|
||||||
|
TEXT("Sorting Error"),
|
||||||
|
MB_OK | MB_ICONINFORMATION | MB_APPLMODAL,
|
||||||
|
0,
|
||||||
|
lineNo.c_str()); // We don't use intInfo since it would require casting size_t -> int.
|
||||||
|
}
|
||||||
_pEditView->execute(SCI_ENDUNDOACTION);
|
_pEditView->execute(SCI_ENDUNDOACTION);
|
||||||
|
|
||||||
if (hasSelection) // there was 1 selection, so we restore it
|
if (hasLineSelection) // there was 1 selection, so we restore it
|
||||||
{
|
{
|
||||||
int posStart = _pEditView->execute(SCI_POSITIONFROMLINE, lineRange.first);
|
int posStart = _pEditView->execute(SCI_POSITIONFROMLINE, fromLine);
|
||||||
int posEnd = _pEditView->execute(SCI_GETLINEENDPOSITION, lineRange.second);
|
int posEnd = _pEditView->execute(SCI_GETLINEENDPOSITION, toLine);
|
||||||
_pEditView->execute(SCI_SETSELECTIONSTART, posStart);
|
_pEditView->execute(SCI_SETSELECTIONSTART, posStart);
|
||||||
_pEditView->execute(SCI_SETSELECTIONEND, posEnd);
|
_pEditView->execute(SCI_SETSELECTIONEND, posEnd);
|
||||||
}
|
}
|
||||||
@ -2614,8 +2677,14 @@ void Notepad_plus::command(int id)
|
|||||||
case IDM_EDIT_RTL :
|
case IDM_EDIT_RTL :
|
||||||
case IDM_EDIT_LTR :
|
case IDM_EDIT_LTR :
|
||||||
case IDM_EDIT_BEGINENDSELECT:
|
case IDM_EDIT_BEGINENDSELECT:
|
||||||
case IDM_EDIT_SORTLINES_ASCENDING:
|
case IDM_EDIT_SORTLINES_LEXICOGRAPHIC_ASCENDING:
|
||||||
case IDM_EDIT_SORTLINES_DESCENDING:
|
case IDM_EDIT_SORTLINES_LEXICOGRAPHIC_DESCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_INTEGER_ASCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_INTEGER_DESCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_DECIMALCOMMA_ASCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_DECIMALCOMMA_DESCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_DECIMALDOT_ASCENDING:
|
||||||
|
case IDM_EDIT_SORTLINES_DECIMALDOT_DESCENDING:
|
||||||
case IDM_EDIT_BLANKLINEABOVECURRENT:
|
case IDM_EDIT_BLANKLINEABOVECURRENT:
|
||||||
case IDM_EDIT_BLANKLINEBELOWCURRENT:
|
case IDM_EDIT_BLANKLINEBELOWCURRENT:
|
||||||
case IDM_VIEW_FULLSCREENTOGGLE :
|
case IDM_VIEW_FULLSCREENTOGGLE :
|
||||||
|
@ -127,8 +127,14 @@ WinMenuKeyDefinition winKeyDefs[] = {
|
|||||||
{VK_SPACE, IDM_EDIT_FUNCCALLTIP, true, false, true, NULL},
|
{VK_SPACE, IDM_EDIT_FUNCCALLTIP, true, false, true, NULL},
|
||||||
{VK_R, IDM_EDIT_RTL, true, true, false, NULL},
|
{VK_R, IDM_EDIT_RTL, true, true, false, NULL},
|
||||||
{VK_L, IDM_EDIT_LTR, true, true, false, NULL},
|
{VK_L, IDM_EDIT_LTR, true, true, false, NULL},
|
||||||
{VK_NULL, IDM_EDIT_SORTLINES_ASCENDING, false, false, false, NULL},
|
{VK_NULL, IDM_EDIT_SORTLINES_LEXICOGRAPHIC_ASCENDING, false, false, false, NULL },
|
||||||
{VK_NULL, IDM_EDIT_SORTLINES_DESCENDING, false, false, false, NULL},
|
{VK_NULL, IDM_EDIT_SORTLINES_LEXICOGRAPHIC_DESCENDING, false, false, false, NULL },
|
||||||
|
{VK_NULL, IDM_EDIT_SORTLINES_INTEGER_ASCENDING, false, false, false, NULL },
|
||||||
|
{VK_NULL, IDM_EDIT_SORTLINES_INTEGER_DESCENDING, false, false, false, NULL },
|
||||||
|
{VK_NULL, IDM_EDIT_SORTLINES_DECIMALCOMMA_ASCENDING, false, false, false, NULL },
|
||||||
|
{VK_NULL, IDM_EDIT_SORTLINES_DECIMALCOMMA_DESCENDING, false, false, false, NULL },
|
||||||
|
{VK_NULL, IDM_EDIT_SORTLINES_DECIMALDOT_ASCENDING, false, false, false, NULL },
|
||||||
|
{VK_NULL, IDM_EDIT_SORTLINES_DECIMALDOT_DESCENDING, false, false, false, NULL },
|
||||||
{VK_RETURN, IDM_EDIT_BLANKLINEABOVECURRENT, true, true, false, NULL},
|
{VK_RETURN, IDM_EDIT_BLANKLINEABOVECURRENT, true, true, false, NULL},
|
||||||
{VK_RETURN, IDM_EDIT_BLANKLINEBELOWCURRENT, true, true, true, NULL},
|
{VK_RETURN, IDM_EDIT_BLANKLINEBELOWCURRENT, true, true, true, NULL},
|
||||||
{VK_F, IDM_SEARCH_FIND, true, false, false, NULL},
|
{VK_F, IDM_SEARCH_FIND, true, false, false, NULL},
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "precompiledHeaders.h"
|
#include "precompiledHeaders.h"
|
||||||
#include "ScintillaEditView.h"
|
#include "ScintillaEditView.h"
|
||||||
#include "Parameters.h"
|
#include "Parameters.h"
|
||||||
|
#include "Sorters.h"
|
||||||
#include "TCHAR.h"
|
#include "TCHAR.h"
|
||||||
|
|
||||||
|
|
||||||
@ -2969,7 +2970,7 @@ void ScintillaEditView::insertNewLineBelowCurrentLine()
|
|||||||
execute(SCI_SETEMPTYSELECTION, execute(SCI_POSITIONFROMLINE, current_line + 1));
|
execute(SCI_SETEMPTYSELECTION, execute(SCI_POSITIONFROMLINE, current_line + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScintillaEditView::sortLines(size_t fromLine, size_t toLine, bool isDescending)
|
void ScintillaEditView::sortLines(size_t fromLine, size_t toLine, ISorter *pSort)
|
||||||
{
|
{
|
||||||
if (fromLine >= toLine)
|
if (fromLine >= toLine)
|
||||||
{
|
{
|
||||||
@ -2990,23 +2991,16 @@ void ScintillaEditView::sortLines(size_t fromLine, size_t toLine, bool isDescend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(toLine - fromLine + 1 == splitText.size());
|
assert(toLine - fromLine + 1 == splitText.size());
|
||||||
const bool isNumericSort = allLinesAreNumericOrEmpty(splitText);
|
const std::vector<generic_string> sortedText = pSort->sort(splitText);
|
||||||
std::vector<generic_string> sortedText;
|
|
||||||
if (isNumericSort)
|
|
||||||
{
|
|
||||||
sortedText = numericSort(splitText, isDescending);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sortedText = lexicographicSort(splitText, isDescending);
|
|
||||||
}
|
|
||||||
const generic_string joined = stringJoin(sortedText, getEOLString());
|
const generic_string joined = stringJoin(sortedText, getEOLString());
|
||||||
if (sortEntireDocument)
|
if (sortEntireDocument)
|
||||||
{
|
{
|
||||||
|
assert(joined.length() == text.length());
|
||||||
replaceTarget(joined.c_str(), startPos, endPos);
|
replaceTarget(joined.c_str(), startPos, endPos);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
assert(joined.length() + getEOLString().length() == text.length());
|
||||||
replaceTarget((joined + getEOLString()).c_str(), startPos, endPos);
|
replaceTarget((joined + getEOLString()).c_str(), startPos, endPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,8 @@ struct LanguageName {
|
|||||||
int lexerID;
|
int lexerID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ISorter;
|
||||||
|
|
||||||
class ScintillaEditView : public Window
|
class ScintillaEditView : public Window
|
||||||
{
|
{
|
||||||
friend class Finder;
|
friend class Finder;
|
||||||
@ -636,7 +638,7 @@ public:
|
|||||||
};
|
};
|
||||||
void scrollPosToCenter(int pos);
|
void scrollPosToCenter(int pos);
|
||||||
generic_string getEOLString();
|
generic_string getEOLString();
|
||||||
void sortLines(size_t fromLine, size_t toLine, bool isDescending);
|
void sortLines(size_t fromLine, size_t toLine, ISorter *pSort);
|
||||||
void changeTextDirection(bool isRTL);
|
void changeTextDirection(bool isRTL);
|
||||||
bool isTextDirectionRTL() const;
|
bool isTextDirectionRTL() const;
|
||||||
|
|
||||||
|
@ -1071,41 +1071,20 @@ generic_string NativeLangSpeaker::getAttrNameStr(const TCHAR *defaultStr, const
|
|||||||
return defaultStr;
|
return defaultStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NativeLangSpeaker::messageBox(const char *msgBoxTagName, HWND hWnd, TCHAR *defaultMessage, TCHAR *defaultTitle, int msgBoxType, int intInfo, TCHAR *strInfo)
|
int NativeLangSpeaker::messageBox(const char *msgBoxTagName, HWND hWnd, const TCHAR *defaultMessage, const TCHAR *defaultTitle, int msgBoxType, int intInfo, const TCHAR *strInfo)
|
||||||
{
|
{
|
||||||
generic_string msg, title;
|
generic_string msg, title;
|
||||||
size_t index;
|
|
||||||
TCHAR int2Write[256];
|
|
||||||
TCHAR intPlaceHolderSymbol[] = TEXT("$INT_REPLACE$");
|
|
||||||
TCHAR strPlaceHolderSymbol[] = TEXT("$STR_REPLACE$");
|
|
||||||
|
|
||||||
size_t intPlaceHolderLen = lstrlen(intPlaceHolderSymbol);
|
|
||||||
size_t strPlaceHolderLen = lstrlen(strPlaceHolderSymbol);
|
|
||||||
|
|
||||||
generic_sprintf(int2Write, TEXT("%d"), intInfo);
|
|
||||||
|
|
||||||
if (!getMsgBoxLang(msgBoxTagName, title, msg))
|
if (!getMsgBoxLang(msgBoxTagName, title, msg))
|
||||||
{
|
{
|
||||||
title = defaultTitle;
|
title = defaultTitle;
|
||||||
msg = defaultMessage;
|
msg = defaultMessage;
|
||||||
}
|
}
|
||||||
index = title.find(intPlaceHolderSymbol);
|
title = stringReplace(title, TEXT("$INT_REPLACE$"), std::to_wstring(intInfo));
|
||||||
if (index != string::npos)
|
msg = stringReplace(msg, TEXT("$INT_REPLACE$"), std::to_wstring(intInfo));
|
||||||
title.replace(index, intPlaceHolderLen, int2Write);
|
|
||||||
|
|
||||||
index = msg.find(intPlaceHolderSymbol);
|
|
||||||
if (index != string::npos)
|
|
||||||
msg.replace(index, intPlaceHolderLen, int2Write);
|
|
||||||
|
|
||||||
if (strInfo)
|
if (strInfo)
|
||||||
{
|
{
|
||||||
index = title.find(strPlaceHolderSymbol);
|
title = stringReplace(title, TEXT("$STR_REPLACE$"), strInfo);
|
||||||
if (index != string::npos)
|
msg = stringReplace(msg, TEXT("$STR_REPLACE$"), strInfo);
|
||||||
title.replace(index, strPlaceHolderLen, strInfo);
|
|
||||||
|
|
||||||
index = msg.find(strPlaceHolderSymbol);
|
|
||||||
if (index != string::npos)
|
|
||||||
msg.replace(index, strPlaceHolderLen, strInfo);
|
|
||||||
}
|
}
|
||||||
return ::MessageBox(hWnd, msg.c_str(), title.c_str(), msgBoxType);
|
return ::MessageBox(hWnd, msg.c_str(), title.c_str(), msgBoxType);
|
||||||
}
|
}
|
@ -76,7 +76,7 @@ public:
|
|||||||
generic_string getProjectPanelLangMenuStr(const char * nodeName, int cmdID, const TCHAR *defaultStr) const;
|
generic_string getProjectPanelLangMenuStr(const char * nodeName, int cmdID, const TCHAR *defaultStr) const;
|
||||||
//generic_string getFunctionListPanelLangStr(const char *nodeName, const TCHAR *defaultStr) const;
|
//generic_string getFunctionListPanelLangStr(const char *nodeName, const TCHAR *defaultStr) const;
|
||||||
generic_string getAttrNameStr(const TCHAR *defaultStr, const char *nodeL1Name, const char *nodeL2Name = NULL) const;
|
generic_string getAttrNameStr(const TCHAR *defaultStr, const char *nodeL1Name, const char *nodeL2Name = NULL) const;
|
||||||
int messageBox(const char *msgBoxTagName, HWND hWnd, TCHAR *message, TCHAR *title, int msgBoxType, int intInfo = 0, TCHAR *strInfo = NULL);
|
int messageBox(const char *msgBoxTagName, HWND hWnd, const TCHAR *message, const TCHAR *title, int msgBoxType, int intInfo = 0, const TCHAR *strInfo = NULL);
|
||||||
private:
|
private:
|
||||||
TiXmlNodeA *_nativeLangA;
|
TiXmlNodeA *_nativeLangA;
|
||||||
int _nativeLangEncoding;
|
int _nativeLangEncoding;
|
||||||
|
@ -112,8 +112,14 @@
|
|||||||
#define IDM_EDIT_REMOVEEMPTYLINESWITHBLANK (IDM_EDIT + 56)
|
#define IDM_EDIT_REMOVEEMPTYLINESWITHBLANK (IDM_EDIT + 56)
|
||||||
#define IDM_EDIT_BLANKLINEABOVECURRENT (IDM_EDIT + 57)
|
#define IDM_EDIT_BLANKLINEABOVECURRENT (IDM_EDIT + 57)
|
||||||
#define IDM_EDIT_BLANKLINEBELOWCURRENT (IDM_EDIT + 58)
|
#define IDM_EDIT_BLANKLINEBELOWCURRENT (IDM_EDIT + 58)
|
||||||
#define IDM_EDIT_SORTLINES_ASCENDING (IDM_EDIT + 59)
|
#define IDM_EDIT_SORTLINES_LEXICOGRAPHIC_ASCENDING (IDM_EDIT + 59)
|
||||||
#define IDM_EDIT_SORTLINES_DESCENDING (IDM_EDIT + 60)
|
#define IDM_EDIT_SORTLINES_LEXICOGRAPHIC_DESCENDING (IDM_EDIT + 60)
|
||||||
|
#define IDM_EDIT_SORTLINES_INTEGER_ASCENDING (IDM_EDIT + 61)
|
||||||
|
#define IDM_EDIT_SORTLINES_INTEGER_DESCENDING (IDM_EDIT + 62)
|
||||||
|
#define IDM_EDIT_SORTLINES_DECIMALCOMMA_ASCENDING (IDM_EDIT + 63)
|
||||||
|
#define IDM_EDIT_SORTLINES_DECIMALCOMMA_DESCENDING (IDM_EDIT + 64)
|
||||||
|
#define IDM_EDIT_SORTLINES_DECIMALDOT_ASCENDING (IDM_EDIT + 65)
|
||||||
|
#define IDM_EDIT_SORTLINES_DECIMALDOT_DESCENDING (IDM_EDIT + 66)
|
||||||
|
|
||||||
// Menu macro
|
// Menu macro
|
||||||
#define IDM_MACRO_STARTRECORDINGMACRO (IDM_EDIT + 18)
|
#define IDM_MACRO_STARTRECORDINGMACRO (IDM_EDIT + 18)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user