leocad/common/lc_category.cpp

288 lines
6.8 KiB
C++
Raw Normal View History

2013-08-09 06:57:18 +02:00
#include "lc_global.h"
#include "lc_category.h"
#include "lc_file.h"
#include "lc_profile.h"
2020-01-11 02:40:14 +01:00
std::vector<lcLibraryCategory> gCategories;
2013-08-09 06:57:18 +02:00
void lcResetDefaultCategories()
{
lcResetCategories(gCategories);
lcRemoveProfileKey(LC_PROFILE_CATEGORIES);
}
void lcLoadDefaultCategories(bool BuiltInLibrary)
{
QByteArray Buffer = lcGetProfileBuffer(LC_PROFILE_CATEGORIES);
2013-08-09 06:57:18 +02:00
if (Buffer.isEmpty() || !lcLoadCategories(Buffer, gCategories))
2013-08-09 06:57:18 +02:00
lcResetCategories(gCategories, BuiltInLibrary);
}
void lcSaveDefaultCategories()
{
2016-02-17 00:11:52 +01:00
QByteArray ByteArray;
QTextStream Stream(&ByteArray, QIODevice::WriteOnly);
2013-08-09 06:57:18 +02:00
2016-02-17 00:11:52 +01:00
lcSaveCategories(Stream, gCategories);
2013-08-09 06:57:18 +02:00
2016-02-17 00:11:52 +01:00
lcSetProfileBuffer(LC_PROFILE_CATEGORIES, ByteArray);
2013-08-09 06:57:18 +02:00
}
2020-01-11 02:40:14 +01:00
void lcResetCategories(std::vector<lcLibraryCategory>& Categories, bool BuiltInLibrary)
2013-08-09 06:57:18 +02:00
{
const char DefaultCategories[] =
{
"Animal=^%Animal | ^%Bone\n"
"Antenna=^%Antenna\n"
"Arch=^%Arch\n"
"Bar=^%Bar\n"
"Baseplate=^%Baseplate | ^%Platform\n"
2017-02-05 04:59:02 +01:00
"Boat=^%Boat | ^%Sail\n"
2013-08-09 06:57:18 +02:00
"Brick=^%Brick\n"
"Container=^%Container | ^%Box | ^Chest | ^%Storage | ^Mailbox\n"
"Door and Window=^%Door | ^%Window | ^%Glass | ^%Freestyle | ^%Gate | ^%Garage | ^%Roller\n"
2017-02-05 04:59:02 +01:00
"Electric=^%Battery | ^%Electric\n"
2013-08-09 06:57:18 +02:00
"Hinge and Bracket=^%Hinge | ^%Bracket | ^%Turntable\n"
2017-02-05 04:59:02 +01:00
"Hose=^%Hose | ^%Rubber | ^%String\n"
2013-08-09 06:57:18 +02:00
"Minifig=^%Minifig\n"
2017-02-05 04:59:02 +01:00
"Miscellaneous=^%Arm | ^%Barrel | ^%Brush | ^%Bucket | ^%Cardboard | ^%Claw | ^%Cockpit | ^%Cocoon | ^%Conveyor | ^%Crane | ^%Cupboard | ^%Fence | ^%Gold | ^%Handle | ^%Hook | ^%Jack | ^%Key | ^%Ladder | ^%Medical | ^%Motor | ^%Rock | ^%Rope | ^%Slide | ^%Sheet | ^%Snow | ^%Sports | ^%Spring | ^%Staircase | ^%Stretcher | ^%Tap | ^%Tipper | ^%Trailer | ^%Umbrella | ^%Winch\n"
"Other=^%Ball | ^%Belville | ^%BigFig | ^%Die | ^%Duplo | ^%Fabuland | ^%Figure | ^%Homemaker | ^%Maxifig | ^%Microfig | ^%Mursten | ^%Quatro | ^%Scala | ^%Znap\n"
2013-08-09 06:57:18 +02:00
"Panel=^%Panel | ^%Castle Wall | ^%Castle Turret\n"
"Plant=^%Plant\n"
"Plate=^%Plate\n"
"Round=^%Cylinder | ^%Cone | ^%Dish | ^%Dome | ^%Hemisphere | ^%Round\n"
"Sign and Flag=^%Flag | ^%Roadsign | ^%Streetlight | ^%Flagpost | ^%Lamppost | ^%Signpost\n"
"Slope=^%Slope | ^%Roof\n"
"Sticker=^%Sticker\n"
"Support=^%Support\n"
"Technic=^%Technic | ^%Rack\n"
"Tile=^%Tile\n"
"Train=^%Train | ^%Monorail | ^%Magnet\n"
2017-02-05 04:59:02 +01:00
"Tyre and Wheel=^%Tyre | %^Wheel | %^Wheels | ^%Castle Wagon | ^%Axle\n"
"Vehicle=^%Bike | ^%Canvas | ^%Car | ^%Excavator | ^%Exhaust | ^%Forklift | ^%Grab Jaw | ^%Jet | ^%Landing | ^%Motorcycle | ^%Plane | ^%Propellor | ^%Tail | ^%Tractor | ^%Vehicle | ^%Wheelbarrow\n"
2013-08-09 06:57:18 +02:00
"Windscreen=^%Windscreen\n"
"Wedge=^%Wedge\n"
"Wing=^%Wing\n"
};
const char BuiltInCategories[] =
{
2014-09-10 03:41:37 +02:00
"Baseplate=^%Baseplate\n"
2013-08-09 06:57:18 +02:00
"Brick=^%Brick\n"
"Plate=^%Plate\n"
2014-09-11 21:55:34 +02:00
"Slope=^%Slope\n"
"Tile=^%Tile\n"
2013-08-09 06:57:18 +02:00
};
QByteArray Buffer;
2013-08-09 06:57:18 +02:00
if (BuiltInLibrary)
Buffer.append(BuiltInCategories, sizeof(BuiltInCategories));
2013-08-09 06:57:18 +02:00
else
Buffer.append(DefaultCategories, sizeof(DefaultCategories));
2013-08-09 06:57:18 +02:00
lcLoadCategories(Buffer, Categories);
2013-08-09 06:57:18 +02:00
}
2020-01-11 02:40:14 +01:00
bool lcLoadCategories(const QString& FileName, std::vector<lcLibraryCategory>& Categories)
2013-08-09 06:57:18 +02:00
{
QFile File(FileName);
2013-08-09 06:57:18 +02:00
if (!File.open(QIODevice::ReadOnly))
2013-08-09 06:57:18 +02:00
return false;
QByteArray FileData = File.readAll();
return lcLoadCategories(FileData, Categories);
2013-08-09 06:57:18 +02:00
}
2020-01-11 02:40:14 +01:00
bool lcLoadCategories(const QByteArray& Buffer, std::vector<lcLibraryCategory>& Categories)
2013-08-09 06:57:18 +02:00
{
2020-01-11 02:40:14 +01:00
Categories.clear();
2013-08-09 06:57:18 +02:00
QTextStream Stream(Buffer);
2013-08-09 06:57:18 +02:00
for (QString Line = Stream.readLine(); !Line.isNull(); Line = Stream.readLine())
2013-08-09 06:57:18 +02:00
{
int Equals = Line.indexOf('=');
2013-08-09 06:57:18 +02:00
if (Equals == -1)
2013-08-09 06:57:18 +02:00
continue;
QString Name = Line.left(Equals);
QString Keywords = Line.mid(Equals + 1);
2013-08-09 06:57:18 +02:00
2020-01-11 02:40:14 +01:00
lcLibraryCategory Category;
2013-08-09 06:57:18 +02:00
2020-01-11 02:40:14 +01:00
Category.Name = Name;
Category.Keywords = Keywords.toLatin1();
Categories.emplace_back(std::move(Category));
2013-08-09 06:57:18 +02:00
}
return true;
}
2020-01-11 02:40:14 +01:00
bool lcSaveCategories(const QString& FileName, const std::vector<lcLibraryCategory>& Categories)
2013-08-09 06:57:18 +02:00
{
2016-02-17 00:11:52 +01:00
QFile File(FileName);
2013-08-09 06:57:18 +02:00
2016-02-17 00:11:52 +01:00
if (!File.open(QIODevice::WriteOnly))
2013-08-09 06:57:18 +02:00
return false;
2016-02-17 00:11:52 +01:00
QTextStream Stream(&File);
return lcSaveCategories(Stream, Categories);
2013-08-09 06:57:18 +02:00
}
2020-01-11 02:40:14 +01:00
bool lcSaveCategories(QTextStream& Stream, const std::vector<lcLibraryCategory>& Categories)
2013-08-09 06:57:18 +02:00
{
2016-02-17 00:11:52 +01:00
QString Format("%1=%2\r\n");
2013-08-09 06:57:18 +02:00
2019-03-29 01:59:58 +01:00
for (const lcLibraryCategory& Category : Categories)
2017-02-08 18:41:48 +01:00
Stream << Format.arg(Category.Name, QString::fromLatin1(Category.Keywords));
2013-08-09 06:57:18 +02:00
2016-02-17 00:11:52 +01:00
Stream.flush();
2013-08-09 06:57:18 +02:00
return true;
}
2017-02-08 03:55:54 +01:00
bool lcMatchCategory(const char* PieceName, const char* Expression)
{
// Check if we need to split the test expression.
const char* p = Expression;
while (*p)
{
if (*p == '!')
{
return !lcMatchCategory(PieceName, p + 1);
}
else if (*p == '(')
{
// const char* Start = p;
int c = 0;
// Skip what's inside the parenthesis.
do
{
if (*p == '(')
c++;
else if (*p == ')')
c--;
else if (*p == 0)
return false; // Mismatched parenthesis.
p++;
}
while (c);
if (*p == 0)
break;
}
else if ((*p == '|') || (*p == '&'))
{
std::string LeftStr(Expression, (p - Expression) - 1);
std::string RightStr(p + 1);
if (*p == '|')
return lcMatchCategory(PieceName, LeftStr.c_str()) || lcMatchCategory(PieceName, RightStr.c_str());
else
return lcMatchCategory(PieceName, LeftStr.c_str()) && lcMatchCategory(PieceName, RightStr.c_str());
}
p++;
}
if (strchr(Expression, '('))
{
p = Expression;
while (*p)
{
if (*p == '(')
{
const char* Start = p;
int c = 0;
// Extract what's inside the parenthesis.
do
{
if (*p == '(')
c++;
else if (*p == ')')
c--;
else if (*p == 0)
return false; // Mismatched parenthesis.
p++;
}
while (c);
std::string SubExpression(Start + 1, p - Start - 2);
return lcMatchCategory(PieceName, SubExpression.c_str());
}
p++;
}
}
const char* SearchStart = Expression;
while (isspace(*SearchStart))
SearchStart++;
const char* SearchEnd = SearchStart + strlen(SearchStart) - 1;
while (SearchEnd >= SearchStart && isspace(*SearchEnd))
SearchEnd--;
// Testing a simple case.
std::string Search;
if (SearchStart != SearchEnd)
Search = std::string(SearchStart, SearchEnd - SearchStart + 1);
const char* Word = Search.c_str();
// Check for modifiers.
bool WholeWord = 0;
bool Begin = 0;
for (;;)
{
if (Word[0] == '^')
WholeWord = true;
else if (Word[0] == '%')
Begin = true;
else
break;
Word++;
}
const char* Result = strcasestr(PieceName, Word);
if (!Result)
return false;
if (Begin && (Result != PieceName))
{
if ((Result != PieceName + 1) || ((Result[-1] != '_') && (Result[-1] != '~')))
return false;
}
if (WholeWord)
{
char End = Result[strlen(Word)];
if ((End != 0) && (End != ' '))
return false;
if ((Result != PieceName) && ((Result[-1] == '_') || (Result[-1] == '~')))
Result--;
if ((Result != PieceName) && (Result[-1] != ' '))
return false;
}
return true;
}