diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5291a38 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# C++ objects and libs +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.dll +*.dylib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* diff --git a/cmxf.pro b/cmxf.pro new file mode 100644 index 0000000..3fb29a8 --- /dev/null +++ b/cmxf.pro @@ -0,0 +1,146 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2016-03-08T21:04:56 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = Transfer_Tool +TEMPLATE = app + +RC_FILE = cmxf.rc +RESOURCES = cmxf.qrc + +# The application version +VERSION = 1.0.3 +DEFINES += APP_VERSION=\\\"$$VERSION\\\" + +win32 { + DEFINES += BUILDTIME=\\\"$$system('echo %time%')\\\" + DEFINES += BUILDDATE=\\\"$$system('echo %date%')\\\" +} else { + DEFINES += BUILDTIME=\\\"$$system(date '+%H:%M')\\\" + DEFINES += BUILDDATE=\\\"$$system(date '+%d/%m/%y')\\\" +} + +SOURCES += main.cpp\ + mainwindow.cpp \ + database/database.cpp \ + database/index.cpp \ + settings.cpp \ + models/index_model.cpp \ + database/city.cpp \ + data_types/city_weather.cpp \ + data_types/string.cpp \ + database/club.cpp \ + database/club_comp.cpp \ + database/club_comp_history.cpp \ + database/colour.cpp \ + database/continent.cpp \ + database/name.cpp \ + database/nation.cpp \ + database/stadium.cpp \ + database/staff_comp.cpp \ + database/staff_comp_history.cpp \ + database/staff_history.cpp \ + database/official.cpp \ + data_types/date.cpp \ + database/non_player.cpp \ + database/player.cpp \ + database/staff.cpp \ + database/staff_preferences.cpp \ + importer/transfer_importer.cpp \ + data_types/data_pointer.cpp \ + data_types/ptr_club.cpp \ + data_types/ptr_club_comp.cpp \ + data_types/ptr_name.cpp \ + data_types/ptr_staff.cpp \ + data_types/staff_classification.cpp \ + data_types/cm_char.cpp \ + spreadsheet/spreadsheet.cpp \ + progress_window.cpp \ + data_types/year.cpp \ + importer/vlookup_hash.cpp \ + ui/summary_item.cpp \ + ui/style_sheet.cpp \ + data_types/ptr_nation.cpp \ + data_types/job.cpp \ + data_types/attribute.cpp \ + data_types/ptr_player.cpp \ + data_types/ptr_non_player.cpp \ + data_types/cm_int.cpp \ + data_types/cm_uchar.cpp \ + exporter/exporter.cpp \ + exporter/exporter_item.cpp \ + exporter/exporter_model.cpp \ + data_types/cm_short.cpp \ + data_types/reputation.cpp \ + data_types/wage.cpp \ + data_types/ptr_preferences.cpp \ + data_types/ability.cpp \ + data_types/squad.cpp \ + data_types/player_squad_number.cpp + +HEADERS += mainwindow.h \ + database/database.h \ + database/index.h \ + data_types/data_types.h \ + settings.h \ + models/index_model.h \ + database/city.h \ + data_types/city_weather.h \ + data_types/weather.h \ + data_types/string.h \ + database/club.h \ + database/club_comp.h \ + database/club_comp_history.h \ + database/colour.h \ + database/continent.h \ + database/name.h \ + database/nation.h \ + database/stadium.h \ + database/staff_comp.h \ + database/staff_comp_history.h \ + database/staff_history.h \ + database/official.h \ + data_types/date.h \ + database/non_player.h \ + database/player.h \ + database/staff.h \ + database/staff_preferences.h \ + importer/transfer_importer.h \ + data_types/data_pointer.h \ + data_types/ptr_club.h \ + data_types/ptr_club_comp.h \ + data_types/ptr_name.h \ + data_types/ptr_staff.h \ + data_types/staff_classification.h \ + data_types/cm_char.h \ + spreadsheet/spreadsheet.h \ + progress_window.h \ + data_types/year.h \ + importer/vlookup_hash.h \ + ui/summary_item.h \ + ui/style_sheet.h \ + data_types/ptr_nation.h \ + data_types/job.h \ + data_types/attribute.h \ + data_types/ptr_player.h \ + data_types/ptr_non_player.h \ + data_types/cm_int.h \ + data_types/cm_uchar.h \ + exporter/exporter.h \ + exporter/exporter_item.h \ + exporter/exporter_model.h \ + data_types/cm_short.h \ + data_types/reputation.h \ + data_types/wage.h \ + data_types/ptr_preferences.h \ + data_types/ability.h \ + data_types/squad.h \ + data_types/player_squad_number.h + +FORMS += mainwindow.ui diff --git a/cmxf.qrc b/cmxf.qrc new file mode 100644 index 0000000..9bef3cb --- /dev/null +++ b/cmxf.qrc @@ -0,0 +1,8 @@ + + + icons/document-open.png + icons/document-save.png + icons/document-save-as.png + icons/row-buffer.png + + diff --git a/cmxf.rc b/cmxf.rc new file mode 100644 index 0000000..2ea8798 --- /dev/null +++ b/cmxf.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "icons/app.ico" \ No newline at end of file diff --git a/data_types/ability.cpp b/data_types/ability.cpp new file mode 100644 index 0000000..22f3127 --- /dev/null +++ b/data_types/ability.cpp @@ -0,0 +1,58 @@ +#include "ability.h" + +// --- Default constructor: Base class -- // +Ability::Ability() : + CM_Short(0) +{ + +} + +// --- Default constructor: Current ability -- // +AbilityCurrent::AbilityCurrent() : + Ability() +{ + +} + +// --- Default constructor: Potential ability -- // +AbilityPotential::AbilityPotential() : + Ability() +{ + +} + +// --- Set from QVariant --- // +void Ability::set(const QVariant &value) +{ + QString tmp = value.toString(); + + if(tmp.isEmpty()) + return; + + short i = static_cast(value.toInt()); + + if(i < 0) + i = 0; + else if(i > 200) + i = 200; + + m_Value = i; +} + +// --- Set from QVariant --- // +void AbilityPotential::set(const QVariant &value) +{ + QString tmp = value.toString(); + + if(tmp.isEmpty()) + return; + + short i = static_cast(value.toInt()); + + if(i < -2) + i = -2; + else if(i > 200) + i = 200; + + m_Value = i; +} diff --git a/data_types/ability.h b/data_types/ability.h new file mode 100644 index 0000000..662e38d --- /dev/null +++ b/data_types/ability.h @@ -0,0 +1,39 @@ +#ifndef ABILITY_H +#define ABILITY_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "cm_short.h" + +// --- Current/Potential Ability Base Class --- // +class Ability : public CM_Short +{ +public: + // Constructor + Ability(); + + // Set data + void set(const QVariant &value); +}; + +// --- Current Ability Wrapper --- // +class AbilityCurrent : public Ability +{ +public: + // Constructor + AbilityCurrent(); +}; + +// --- Potential Ability Wrapper --- // +class AbilityPotential : public Ability +{ +public: + // Constructor + AbilityPotential(); + + // Set data + void set(const QVariant &value); +}; + +#endif // ABILITY_H diff --git a/data_types/attribute.cpp b/data_types/attribute.cpp new file mode 100644 index 0000000..812f9a3 --- /dev/null +++ b/data_types/attribute.cpp @@ -0,0 +1,25 @@ +#include "attribute.h" +#include + +Attribute::Attribute() +{ + m_Value = 0; +} + +// --- Set from QVariant --- // +void Attribute::set(const QVariant &value) +{ + QString tmp = value.toString(); + + if(tmp.isEmpty() || tmp.contains("-")) + return; + + int i = value.toInt(); + + if(i < 0) + i = 0; + else if(i > 20) + i = 20; + + m_Value = i; +} diff --git a/data_types/attribute.h b/data_types/attribute.h new file mode 100644 index 0000000..46a636b --- /dev/null +++ b/data_types/attribute.h @@ -0,0 +1,20 @@ +#ifndef ATTRIBUTE_H +#define ATTRIBUTE_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "cm_char.h" + +// ---Player/non-player attribute (0-20 scale) --- // +class Attribute : public CM_Char +{ +public: + Attribute(); + + // Set data + void set(const QVariant &value); +}; + +#endif // ATTRIBUTE_H diff --git a/data_types/city_weather.cpp b/data_types/city_weather.cpp new file mode 100644 index 0000000..d407d63 --- /dev/null +++ b/data_types/city_weather.cpp @@ -0,0 +1,7 @@ +#include "city_weather.h" + +CityWeather::CityWeather() +{ + +} + diff --git a/data_types/city_weather.h b/data_types/city_weather.h new file mode 100644 index 0000000..9d2c239 --- /dev/null +++ b/data_types/city_weather.h @@ -0,0 +1,22 @@ +#ifndef CITY_WEATHER_H +#define CITY_WEATHER_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "data_types.h" +#include "weather.h" + +class CityWeather +{ +private: + int CityWeatherID; // ID of the city (auto generated by editor) + char CityWeatherName[ SHORT_TEXT_LENGTH ]; // Name of the city which is modelled (editable) + WEATHER_SEASON CityWeatherSeason[ 4 ]; // Seasonal model for this city (editable) + WEATHER_CONDITION CityWeatherCondition; // Current Weather in this city (not editable) + +public: + CityWeather(); +}; + +#endif // CITY_WEATHER_H diff --git a/data_types/cm_char.cpp b/data_types/cm_char.cpp new file mode 100644 index 0000000..5a7e631 --- /dev/null +++ b/data_types/cm_char.cpp @@ -0,0 +1,26 @@ +#include "cm_char.h" + +// --- Default constructor --- // +CM_Char::CM_Char() : + m_Value(-1) +{ + +} + +// --- Get data (short) --- // +short CM_Char::get() const +{ + return static_cast(m_Value); +} + +// --- Get data (QString) --- // +QString CM_Char::getText() const +{ + return QString::number(m_Value); +} + +// --- Set data (int) --- // +void CM_Char::set(int i) +{ + m_Value = static_cast(i); +} diff --git a/data_types/cm_char.h b/data_types/cm_char.h new file mode 100644 index 0000000..668c5af --- /dev/null +++ b/data_types/cm_char.h @@ -0,0 +1,27 @@ +#ifndef CM_CHAR_H +#define CM_CHAR_H + +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +// --- Char wrapper --- // +class CM_Char +{ +protected: + char m_Value; + +public: + CM_Char(); + + // Get data + short get() const; + QString getText() const; + + // Set data + void set(int i); +}; + +#endif // CM_CHAR_H diff --git a/data_types/cm_int.cpp b/data_types/cm_int.cpp new file mode 100644 index 0000000..fb04fc6 --- /dev/null +++ b/data_types/cm_int.cpp @@ -0,0 +1,36 @@ +#include "cm_int.h" + +CM_Int::CM_Int(const int &i) : + m_Value(i) +{ + +} + +// --- Get data --- // +int CM_Int::get() const +{ + return m_Value; +} + +// --- Get data as text --- // +QString CM_Int::getText() const +{ + return QString::number(m_Value); +} + +// --- Set --- // +void CM_Int::set(const int &i) +{ + m_Value = i; +} + +// --- Set --- // +void CM_Int::set(const QVariant &value, double exchangeRate) +{ + const QString tmp = value.toString(); + + if(tmp.isEmpty() || tmp.contains("-")) + return; + else + m_Value = static_cast(value.toFloat() * exchangeRate); +} diff --git a/data_types/cm_int.h b/data_types/cm_int.h new file mode 100644 index 0000000..5467e72 --- /dev/null +++ b/data_types/cm_int.h @@ -0,0 +1,28 @@ +#ifndef CM_INT_H +#define CM_INT_H + +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +// --- Integer wrapper --- // +class CM_Int +{ +protected: + int m_Value; + +public: + CM_Int(const int &i = 0); + + // Get data + int get() const; + QString getText() const; + + // Set data + void set(const int &i); + void set(const QVariant &value, double exchangeRate = 1.0); +}; + +#endif // CM_INT_H diff --git a/data_types/cm_short.cpp b/data_types/cm_short.cpp new file mode 100644 index 0000000..358ed95 --- /dev/null +++ b/data_types/cm_short.cpp @@ -0,0 +1,31 @@ +#include "cm_short.h" + +CM_Short::CM_Short(const short &i) : + m_Value(i) +{ + +} + +// --- Get data --- // +short CM_Short::get() const +{ + return m_Value; +} + +// --- Get data (QString) --- // +QString CM_Short::getText() const +{ + return QString::number(m_Value); +} + +// --- Set data (int) --- // +void CM_Short::set(int &i) +{ + m_Value = static_cast(i); +} + +// --- Set data (short) --- // +void CM_Short::set(short &i) +{ + m_Value = i; +} diff --git a/data_types/cm_short.h b/data_types/cm_short.h new file mode 100644 index 0000000..1498f5b --- /dev/null +++ b/data_types/cm_short.h @@ -0,0 +1,27 @@ +#ifndef CM_SHORT_H +#define CM_SHORT_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +// --- Short wrapper --- // +class CM_Short +{ +protected: + short m_Value; + +public: + CM_Short(const short &i = 0); + + // Get data + short get() const; + QString getText() const; + + // Set data + void set(int &i); + void set(short &i); +}; + +#endif // CM_SHORT_H diff --git a/data_types/cm_uchar.cpp b/data_types/cm_uchar.cpp new file mode 100644 index 0000000..a72662b --- /dev/null +++ b/data_types/cm_uchar.cpp @@ -0,0 +1,39 @@ +#include "cm_uchar.h" +#include + +// --- Default constructor --- // +CM_UChar::CM_UChar() : + m_Value(0) +{ + +} + +// --- Get data (short) --- // +unsigned short CM_UChar::get() const +{ + return static_cast(m_Value); +} + +// --- Get data (QString) --- // +QString CM_UChar::getText() const +{ + return QString::number(m_Value); +} + + +// --- Set --- // +void CM_UChar::set(const int &i) +{ + m_Value = static_cast(i); +} + +// --- Set --- // +void CM_UChar::set(const QVariant &value) +{ + QString tmp = value.toString(); + + if(tmp.isEmpty() || tmp.contains("-")) + return; + else + m_Value = static_cast(value.toInt()); +} diff --git a/data_types/cm_uchar.h b/data_types/cm_uchar.h new file mode 100644 index 0000000..d63fbd1 --- /dev/null +++ b/data_types/cm_uchar.h @@ -0,0 +1,29 @@ +#ifndef CM_UCHAR_H +#define CM_UCHAR_H + +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +// --- Unsigned char wrapper --- // +class CM_UChar +{ +protected: + unsigned char m_Value; + +public: + CM_UChar(); + + // Get data + unsigned short get() const; + QString getText() const; + + // Set data + void set(const int &i); + void set(const QVariant &value); +}; + + +#endif // CM_UCHAR_H diff --git a/data_types/data_pointer.cpp b/data_types/data_pointer.cpp new file mode 100644 index 0000000..34f5764 --- /dev/null +++ b/data_types/data_pointer.cpp @@ -0,0 +1,83 @@ +#include "data_pointer.h" + +// --- Static data --- // +QString DataPointer::s_IdentifierNoneText = "_none"; +QString DataPointer::s_IdentifierOutOfRangeText = "_outofrange"; +QString DataPointer::s_NoneText = "[None]"; +QString DataPointer::s_OutOfRangeText = "[Error: Out of range]"; + +// --- Default Constructor --- // +DataPointer::DataPointer() : + id(-1) +{ + +} + +// --- Constructor --- // +DataPointer::DataPointer(const int i) : + id(i) +{ + +} + +// --- Get id --- // +int DataPointer::get() const +{ + return id; +} + +// --- Get text (generic virtual function) --- // +QString DataPointer::getText() const +{ + return "[Invalid Sub-Class]"; +} + +// --- Return whether id is none --- // +bool DataPointer::isNone() const +{ + return(id < 0); +} + +// --- Return whether id is pointing to an int (i.e. not none) --- // +bool DataPointer::isPointer() const +{ + return(id >= 0); +} + +// --- Clear the pointer --- // +void DataPointer::clear() +{ + id = -1; +} + +// --- Set id --- // +void DataPointer::set(const int &i) +{ + id = i; +} + +// --- Set id --- // +void DataPointer::set(const QVariant &value) +{ + id = value.toInt(); +} + +// --- Set id without any data validation checks --- // +void DataPointer::setNoCheck(const QVariant &value) +{ + const QString tmp = value.toString(); + const int i = value.toInt(); + + if(tmp.isEmpty() || tmp.contains("-")) + return; + else if(i < -2) + id = -1; + else + id = i; +} + +// --- Comparison operator overload --- // +bool DataPointer::operator ==(const int &b) const +{ + return id == b; +} diff --git a/data_types/data_pointer.h b/data_types/data_pointer.h new file mode 100644 index 0000000..37b6f01 --- /dev/null +++ b/data_types/data_pointer.h @@ -0,0 +1,42 @@ +#ifndef DATA_POINTER_H +#define DATA_POINTER_H + +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +// --- Integer id/pointer base class --- // +class DataPointer +{ +protected: + int id; + + // Default text + static QString s_IdentifierNoneText; + static QString s_IdentifierOutOfRangeText; + static QString s_NoneText; + static QString s_OutOfRangeText; + +public: + DataPointer(); + DataPointer(const int i); + + // Get data + int get() const; + QString getText() const; + bool isNone() const; + bool isPointer() const; + + // Set data + void clear(); + void set(const int &i); + void set(const QVariant &value); + void setNoCheck(const QVariant &value); + + // Operator overloading + bool operator ==(const int &b) const; +}; + +#endif // DATA_POINTER_H diff --git a/data_types/data_types.h b/data_types/data_types.h new file mode 100644 index 0000000..6391f32 --- /dev/null +++ b/data_types/data_types.h @@ -0,0 +1,110 @@ +#ifndef DATABASE_TYPES +#define DATABASE_TYPES + +// Staff classification +enum ENUM_CLASSIFICATION { + INVALID_CLASSIFICATION, + NON_PLAYER, + PLAYER, + NON_PLAYER_AND_PLAYER, + YOUTH_PLAYER, + NEW_HUMAN_MANAGER, + SPARE_NON_PLAYER +}; + +// Selected leagues defs +enum ENUM_CLUB_COMP_SELECTED_STATUS { + NOT_SELECTED = 0x0000, + SELECTED_BACKGROUND = 0x0001, + SELECTED_FOREGROUND = 0x0002, + ALL_DIVISIONS_ACTIVE = 0x0004 +}; + +// Club sizes +enum ENUM_CLUB_SIZES { + DIRECTOR_SIZE = 3, + SQUAD_SIZE = 50, + SCOUT_SIZE = 7, + COACH_SIZE = 5, + PHYSIO_SIZE = 3 +}; + +// Database days of the week +enum ENUM_DAYS { + DB_SUN, + DB_MON, + DB_TUE, + DB_WED, + DB_THUR, + DB_FRI, + DB_SAT, +}; + +// Maximum sizes in CM 01/02 +enum ENUM_MAX_SIZES { + MAX_FRIENDLY_COMPETITIONS = 127, // Max number of friendly competitions + MAX_HUMAN_PLAYERS = 16, // Max number of human players + MAX_TACTIC_TRAINING = 4, // Maximum number of tactics in training at once + TEAM_SZ = 20 // Maximum size of a squad for a match +}; + +// Staff playing squad +enum ENUM_PLAYING_SQUAD { + INVALID_SQUAD = 0, + CLUB_SENIOR_SQUAD = 0x01, + CLUB_RESERVE_SQUAD = 0x02, + NATION_MAIN_SQUAD = 0x04, + NATION_B_SQUAD = 0x08 +}; + +// Pointers +enum ENUM_POINTER { + NO_MATCH = - 127, // 127 is the lowest value a char can be so this value is used in case it will be stored as a char + BLANK, + PROTECTED, + NONE = -1, + FREE_AGENT = NONE, + NO_NATION = NONE, + NO_STAFF = NONE, + VALID = 0 +}; + +// Club professional status +enum ENUM_PROFESSIONAL_STATUS { + PROFESSIONAL = 1, + SEMI_PRO, + AMATEUR +}; + +// Struct lengths +enum ENUM_TABLE_SIZES { + DB_CITY_SIZE = 56, + DB_CLUB_SIZE = 581, + DB_CLUB_COMP_SIZE = 107, + DB_CLUB_COMP_HISTORY_SIZE = 26, + DB_COLOUR_SIZE = 58, + DB_CONTINENT_SIZE = 198, + DB_INDEX_SIZE = 67, + DB_NAME_SIZE = 60, + DB_NATION_SIZE = 290, + DB_NON_PLAYER_SIZE = 68, + DB_OFFICIAL_SIZE = 43, + DB_PLAYER_SIZE = 70, + DB_STADIUM_SIZE = 78, + DB_STAFF_SIZE = 110, + DB_STAFF_COMP_SIZE = 101, + DB_STAFF_COMP_HISTORY_SIZE = 58, + DB_STAFF_HISTORY_SIZE = 17, + DB_STAFF_PREFERENCES_SIZE = 52, +}; + +// Text lengths +enum ENUM_TEXT_LENGTHS { + MAX_INDEX_NAME = 50, + SHORT_TEXT_LENGTH = 26, + STANDARD_TEXT_LENGTH = 51, + LONG_TEXT_LENGTH = 101 +}; + +#endif // DATABASE_TYPES + diff --git a/data_types/date.cpp b/data_types/date.cpp new file mode 100644 index 0000000..5459421 --- /dev/null +++ b/data_types/date.cpp @@ -0,0 +1,111 @@ +#include "date.h" +#include + + +/* ==================== */ +/* CM/SI Date */ +/* ==================== */ + +// --- Default Constructor --- // +Date::Date() : + m_Day(0), + m_Year(0), + m_LeapYear(0) +{ +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Get as QDate --- // +QDate Date::get() +{ + return QDate(m_Year, 1, 1).addDays(m_Day); +} + +// --- Get date formatted for matching against --- // +QString Date::getMatchText(int &yearAdjustment, bool &useFullDate) +{ + // Pad single day and month digits and under-size years with leading zeros + if(useFullDate) { + QDate d = this->get(); + int year = d.year(); + + if(year > 1900) + year+=yearAdjustment; + + return QString("%1.%2.%3") + .arg(d.day(), 2, 10, QChar('0')) + .arg(d.month(), 2, 10, QChar('0')) + .arg(year, 4, 10, QChar('0')); + } + else { + int year = m_Year; + + if(year > 1900) + year+=yearAdjustment; + + return QString::number(year); + } +} + +// --- Get year --- // +short Date::year() +{ + return m_Year; +} + +/* ================== */ +/* Set Data */ +/* ================== */ + +// --- Set from QDate --- // +void Date::set(const QDate &date, const int yearAdjustment) +{ + m_Day = static_cast(date.dayOfYear() - 1); + m_Year = static_cast(date.year()) - static_cast(yearAdjustment); + m_LeapYear = (date.isLeapYear(date.year())); +} + +// --- Set from QString --- // +void Date::set(const QString &value, const int yearAdjustment) +{ + // Split the string into a list + QStringList list = value.split("."); + + // Sanity check + if(list.size() < 3) + return; + + // Create a date from the list + QDate date((list[2].toInt() - yearAdjustment), + list[1].toInt(), + list[0].toInt()); + + // Set the date + this->set(date); +} + +// --- Set from QVariant --- // +void Date::set(const QVariant &value, const int yearAdjustment) +{ + QString tmp = value.toString().simplified(); // simplified() ensures all non-ASCII whitespace is converted to ASCII + tmp.replace(" ", ""); // Remove any whitespace + + // Abort if the string does not contain a date with '.' separators + if(tmp.count(".") != 2) + return; + + // Split the string into a list + QStringList list = tmp.split("."); + + // Create a date from the list + QDate date((list[2].toInt() - yearAdjustment), + list[1].toInt(), + list[0].toInt()); + + // Set the date + this->set(date); +} diff --git a/data_types/date.h b/data_types/date.h new file mode 100644 index 0000000..ff545a9 --- /dev/null +++ b/data_types/date.h @@ -0,0 +1,31 @@ +#ifndef DATE_H +#define DATE_H + +#include +#include +#include + +// --- CM_DATE --- // +class Date +{ +private: + short m_Day; // days from Jan 1st. + short m_Year; + long m_LeapYear; // =1 if year is a leapyear, 0 otherwise. + +public: + // Constructor + Date(); + + // Get data + QDate get(); + QString getMatchText(int &yearAdjustment, bool &useFullDate); + short year(); + + // Set data + void set(const QDate &date, const int yearAdjustment = 0); + void set(const QString &value, const int yearAdjustment = 0); + void set(const QVariant &value, const int yearAdjustment = 0); +}; + +#endif // DATE_H diff --git a/data_types/job.cpp b/data_types/job.cpp new file mode 100644 index 0000000..6482929 --- /dev/null +++ b/data_types/job.cpp @@ -0,0 +1,20 @@ +#include "job.h" + +Job::Job() +{ + +} + + +/* ================== */ +/* Set Data */ +/* ================== */ + +// --- Set job --- // +void Job::set(int i) +{ + if(i < INVALID_JOB || i > MEDIA_PUNDIT) + return; + else + m_Value = static_cast(i); +} diff --git a/data_types/job.h b/data_types/job.h new file mode 100644 index 0000000..ca2fff0 --- /dev/null +++ b/data_types/job.h @@ -0,0 +1,42 @@ +#ifndef JOB_H +#define JOB_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "cm_char.h" + +// --- Job for club/nation --- // +class Job : public CM_Char +{ +public: + Job(); + + // Set data + void set(int i); + + // Values + enum ENUM_JOB { + INVALID_JOB, + CHAIRMAN, + MANAGING_DIRECTOR, + GENERAL_MANAGER, + DIRECTOR_OF_FOOTBALL, + MANAGER, + ASSISTANT_MANAGER, + RESERVE_TEAM_MANAGER, + COACH, + SCOUT, + PHYSIO, + PLAYER, + PLAYER_MANAGER, + PLAYER_ASSISTANT_MANAGER, + PLAYER_RESERVE_TEAM_MANAGER, + PLAYER_COACH, + PLAYER_RETIRED, + MEDIA_PUNDIT, + }; +}; + +#endif // JOB_H diff --git a/data_types/player_squad_number.cpp b/data_types/player_squad_number.cpp new file mode 100644 index 0000000..b721f61 --- /dev/null +++ b/data_types/player_squad_number.cpp @@ -0,0 +1,31 @@ +#include "player_squad_number.h" + +PlayerSquadNumber::PlayerSquadNumber() +{ + m_Value = 0; +} + +// --- Set data (int) --- // +void PlayerSquadNumber::set(int i) +{ + if(i < 1 || i > 50) + m_Value = 0; + else + m_Value = static_cast(i); +} + +// --- Set from QVariant --- // +void PlayerSquadNumber::set(const QVariant &value) +{ + QString tmp = value.toString(); + + if(tmp.isEmpty() || tmp.contains("-")) + return; + + const char i = static_cast(value.toInt()); + + if(i < 1 || i > 50) + m_Value = 0; + else + m_Value = static_cast(i); +} diff --git a/data_types/player_squad_number.h b/data_types/player_squad_number.h new file mode 100644 index 0000000..2be2cfb --- /dev/null +++ b/data_types/player_squad_number.h @@ -0,0 +1,21 @@ +#ifndef PLAYER_SQUAD_NUMBER_H +#define PLAYER_SQUAD_NUMBER_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "cm_char.h" + +// --- Squad/shirt number --- // +class PlayerSquadNumber : public CM_Char +{ +public: + PlayerSquadNumber(); + + // Set data + void set(int i); + void set(const QVariant &value); +}; + +#endif // PLAYER_SQUAD_NUMBER_H diff --git a/data_types/ptr_club.cpp b/data_types/ptr_club.cpp new file mode 100644 index 0000000..c49ce00 --- /dev/null +++ b/data_types/ptr_club.cpp @@ -0,0 +1,97 @@ +#include "ptr_club.h" +#include "../database/club.h" + +PtrClub::PtrClub() : + DataPointer() +{ + +} + +PtrClub::PtrClub(const int i) : + DataPointer(i) +{ + +} + +// --- Add to roster --- // +bool PtrClub::addToRoster(const int staffId, const Job &job) +{ + PtrStaff staff(staffId); + + if(id < VALID) + return true; + + else if(id < Club::dbDom.size()) + return Club::dbDom[id].addToRoster(staff, job); + + else { + int natId = id - Club::dbDom.size(); + + if(natId < Club::dbInt.size()) + return Club::dbInt[natId].addToRoster(staff, job); + } + + return false; +} + +// --- Get text with no accents --- // +QString PtrClub::getNoAccentsText() const +{ + if(id < VALID) + return s_NoneText; + + else if(id < Club::dbDom.size()) + return String(Club::dbDom[id].getLongName()).getNoAccentsString(); + + else { + int natId = id - Club::dbDom.size(); + + if(natId < Club::dbInt.size()) + return String(Club::dbInt[natId].getLongName()).getNoAccentsString(); + } + + return s_OutOfRangeText; +} + +// --- Get CSV-friendly text --- // +QString PtrClub::getSafeText() const +{ + return this->getText().remove(","); +} + +// --- Get default display text --- // +QString PtrClub::getText() const +{ + if(id < VALID) + return s_NoneText; + + else if(id < Club::dbDom.size()) + return Club::dbDom[id].getLongName(); + + else { + int natId = id - Club::dbDom.size(); + + if(natId < Club::dbInt.size()) + return Club::dbInt[natId].getLongName(); + } + + return s_OutOfRangeText; +} + +// --- Set id --- // +void PtrClub::set(const int &i) +{ + if(i < VALID) + id = NONE; + else if(i < Club::dbDom.size()) + id = i; +} + +// --- Set id --- // +void PtrClub::set(const QVariant &value) +{ + if(value.toInt() < VALID) + id = NONE; + else if(value.toInt() < Club::dbDom.size()) + id = value.toInt(); +} diff --git a/data_types/ptr_club.h b/data_types/ptr_club.h new file mode 100644 index 0000000..aa54737 --- /dev/null +++ b/data_types/ptr_club.h @@ -0,0 +1,30 @@ +#ifndef PTR_CLUB_H +#define PTR_CLUB_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "data_pointer.h" +#include "job.h" + +// --- Club pointer --- // +class PtrClub : public DataPointer +{ +public: + // Constructor + PtrClub(); + PtrClub(const int i); + + // Get data + QString getNoAccentsText() const; + QString getSafeText() const; + QString getText() const; + + // Set data + bool addToRoster(const int staffId, const Job &job); + void set(const int &i); + void set(const QVariant &value); +}; + +#endif // PTR_CLUB_H diff --git a/data_types/ptr_club_comp.cpp b/data_types/ptr_club_comp.cpp new file mode 100644 index 0000000..8a06b72 --- /dev/null +++ b/data_types/ptr_club_comp.cpp @@ -0,0 +1,31 @@ +#include "ptr_club_comp.h" +#include "../database/club_comp.h" + +// --- Get default display text --- // +QString PtrClubComp::getText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < ClubComp::dbDom.size()) + return ClubComp::dbDom[id].getLongName(); + else + return s_OutOfRangeText; +} + +// --- Set id --- // +void PtrClubComp::set(const int &i) +{ + if(i < VALID) + id = NONE; + else if(i < ClubComp::dbDom.size()) + id = i; +} + +// --- Set id --- // +void PtrClubComp::set(const QVariant &value) +{ + if(value.toInt() < VALID) + id = NONE; + else if(value.toInt() < ClubComp::dbDom.size()) + id = value.toInt(); +} diff --git a/data_types/ptr_club_comp.h b/data_types/ptr_club_comp.h new file mode 100644 index 0000000..f42e760 --- /dev/null +++ b/data_types/ptr_club_comp.h @@ -0,0 +1,22 @@ +#ifndef PTR_CLUB_COMP_H +#define PTR_CLUB_COMP_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "data_pointer.h" + +// --- Club Comp pointer --- // +class PtrClubComp : public DataPointer +{ +public: + // Get data + QString getText() const; + + // Set data + void set(const int &i); + void set(const QVariant &value); +}; + +#endif // PTR_CLUB_COMP_H diff --git a/data_types/ptr_name.cpp b/data_types/ptr_name.cpp new file mode 100644 index 0000000..294f317 --- /dev/null +++ b/data_types/ptr_name.cpp @@ -0,0 +1,265 @@ +#include "ptr_name.h" +#include "../database/name.h" + + +/* ===================== */ +/* Common Name */ +/* ===================== */ + +// --- Default constructor --- // +PtrCommonName::PtrCommonName() : + DataPointer() +{ + +} + +// --- Increment name counter --- // +void PtrCommonName::increment() +{ + if(id < VALID || id >= CommonName::db.size()) + return; + else + CommonName::db[id].incrementCount(); +} + +// --- Get text for matching against --- // +QString PtrCommonName::getMatchText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < CommonName::db.size()) + return String(CommonName::db[id].getName()).getMatchString(); + else + return s_OutOfRangeText; +} + +// --- Get text with no accents --- // +QString PtrCommonName::getNoAccentsText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < CommonName::db.size()) + return String(CommonName::db[id].getName()).getNoAccentsString(); + else + return s_OutOfRangeText; +} + +// --- Get CSV-friendly text --- // +QString PtrCommonName::getSafeText() const +{ + return this->getText().remove(","); +} + +// --- Get default display text --- // +QString PtrCommonName::getText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < CommonName::db.size()) + return CommonName::db[id].getName(); + else + return s_OutOfRangeText; +} + +// --- Return whether id is none --- // +bool PtrCommonName::isNone() const +{ + if(id <= 0) // Id of zero for CommonName is also _none + return true; + else if(this->getText().isEmpty()) // Some CommonNames appear to be blank/empty (treat them as _none) + return true; + else + return false; +} + +// --- Set id --- // +void PtrCommonName::set(const int &i) +{ + if(i < 0) + id = NONE; + else if(i < CommonName::db.size()) + id = i; +} + +// --- Set id --- // +void PtrCommonName::set(const QVariant &value) +{ + if(value.toInt() < 0) + id = NONE; + else if(value.toInt() < CommonName::db.size()) + id = value.toInt(); +} + +// --- Set id from string (and add if not present in the database) --- // +void PtrCommonName::set(const QString &name, const int &nation) +{ + id = CommonName::set(name, nation); +} + + +/* ==================== */ +/* First Name */ +/* ==================== */ + +// --- Default constructor --- // +PtrFirstName::PtrFirstName() : + DataPointer() +{ + +} + +// --- Increment name counter --- // +void PtrFirstName::increment() +{ + if(id < VALID || id >= FirstName::db.size()) + return; + else + FirstName::db[id].incrementCount(); +} + +// --- Get text for matching against --- // +QString PtrFirstName::getMatchText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < FirstName::db.size()) + return String(FirstName::db[id].getName()).getMatchString(); + else + return s_OutOfRangeText; +} + +// --- Get text with no accents --- // +QString PtrFirstName::getNoAccentsText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < FirstName::db.size()) + return String(FirstName::db[id].getName()).getNoAccentsString(); + else + return s_OutOfRangeText; +} + +// --- Get CSV-friendly text --- // +QString PtrFirstName::getSafeText() const +{ + return this->getText().remove(","); +} + +// --- Get default display text --- // +QString PtrFirstName::getText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < FirstName::db.size()) + return FirstName::db[id].getName(); + else + return s_OutOfRangeText; +} + +// --- Set id --- // +void PtrFirstName::set(const int &i) +{ + if(i < 0) + id = NONE; + else if(i < FirstName::db.size()) + id = i; +} + +// --- Set id --- // +void PtrFirstName::set(const QVariant &value) +{ + if(value.toInt() < 0) + id = NONE; + else if(value.toInt() < FirstName::db.size()) + id = value.toInt(); +} + +// --- Set id from string (and add if not present in the database) --- // +void PtrFirstName::set(const QString &name, const int &nation) +{ + id = FirstName::set(name, nation); +} + + +/* ===================== */ +/* Second Name */ +/* ===================== */ + +// --- Default constructor --- // +PtrSecondName::PtrSecondName() : + DataPointer() +{ + +} + +// --- Increment name counter --- // +void PtrSecondName::increment() +{ + if(id < VALID || id >= SecondName::db.size()) + return; + else + SecondName::db[id].incrementCount(); +} + +// --- Get text for matching against --- // +QString PtrSecondName::getMatchText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < SecondName::db.size()) + return String(SecondName::db[id].getName()).getMatchString(); + else + return s_OutOfRangeText; +} + +// --- Get text with no accents --- // +QString PtrSecondName::getNoAccentsText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < SecondName::db.size()) + return String(SecondName::db[id].getName()).getNoAccentsString(); + else + return s_OutOfRangeText; +} + +// --- Get CSV-friendly text --- // +QString PtrSecondName::getSafeText() const +{ + return this->getText().remove(","); +} + +// --- Get default display text --- // +QString PtrSecondName::getText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < SecondName::db.size()) + return SecondName::db[id].getName(); + else + return s_OutOfRangeText; +} + +// --- Set id --- // +void PtrSecondName::set(const int &i) +{ + if(i < 0) + id = NONE; + else if(i < SecondName::db.size()) + id = i; +} + +// --- Set id --- // +void PtrSecondName::set(const QVariant &value) +{ + if(value.toInt() < 0) + id = NONE; + else if(value.toInt() < SecondName::db.size()) + id = value.toInt(); +} + +// --- Set id from string (and add if not present in the database) --- // +void PtrSecondName::set(const QString &name, const int &nation) +{ + id = SecondName::set(name, nation); +} diff --git a/data_types/ptr_name.h b/data_types/ptr_name.h new file mode 100644 index 0000000..c159849 --- /dev/null +++ b/data_types/ptr_name.h @@ -0,0 +1,77 @@ +#ifndef PTR_NAME_H +#define PTR_NAME_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "data_pointer.h" + +// --- Common Name pointer --- // +class PtrCommonName : public DataPointer +{ +public: + // Constructor + PtrCommonName(); + + // Counter + void increment(); + + // Get data + QString getMatchText() const; + QString getNoAccentsText() const; + QString getSafeText() const; + QString getText() const; + bool isNone() const; + + // Set data + void set(const int &i); + void set(const QVariant &value); + void set(const QString &name, const int &nation); +}; + +// --- First Name pointer --- // +class PtrFirstName : public DataPointer +{ +public: + // Constructor + PtrFirstName(); + + // Counter + void increment(); + + // Get data + QString getMatchText() const; + QString getNoAccentsText() const; + QString getSafeText() const; + QString getText() const; + + // Set data + void set(const int &i); + void set(const QVariant &value); + void set(const QString &name, const int &nation); +}; + +// --- Second Name pointer --- // +class PtrSecondName : public DataPointer +{ +public: + // Constructor + PtrSecondName(); + + // Counter + void increment(); + + // Get data + QString getMatchText() const; + QString getNoAccentsText() const; + QString getSafeText() const; + QString getText() const; + + // Set data + void set(const int &i); + void set(const QVariant &value); + void set(const QString &name, const int &nation); +}; + +#endif // PTR_NAME_H diff --git a/data_types/ptr_nation.cpp b/data_types/ptr_nation.cpp new file mode 100644 index 0000000..2b85e82 --- /dev/null +++ b/data_types/ptr_nation.cpp @@ -0,0 +1,61 @@ +#include "ptr_nation.h" +#include "../database/nation.h" + +PtrNation::PtrNation(const int i) : + DataPointer(i) +{ + +} + +// --- Increment club counter --- // +void PtrNation::incrementClubCount() +{ + if(id < VALID || id >= Nation::db.size()) + return; + else + Nation::db[id].incrementClubCount(); +} + +// --- Increment staff counter --- // +void PtrNation::incrementStaffCount() +{ + if(id < VALID || id >= Nation::db.size()) + return; + else + Nation::db[id].incrementStaffCount(); +} + +// --- Get CSV-friendly text --- // +QString PtrNation::getSafeText() const +{ + return this->getText().remove(","); +} + +// --- Get default display text --- // +QString PtrNation::getText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < Nation::db.size()) + return Nation::db[id].getLongName(); + else + return s_OutOfRangeText; +} + +// --- Set id --- // +void PtrNation::set(const int &i) +{ + if(i < 0) + id = NO_NATION; + else if(i < Nation::db.size()) + id = i; +} + +// --- Set id --- // +void PtrNation::set(const QVariant &value) +{ + if(value.toInt() < 0) + id = NO_NATION; + else if(value.toInt() < Nation::db.size()) + id = value.toInt(); +} diff --git a/data_types/ptr_nation.h b/data_types/ptr_nation.h new file mode 100644 index 0000000..3ea74fe --- /dev/null +++ b/data_types/ptr_nation.h @@ -0,0 +1,30 @@ +#ifndef PTR_NATION_H +#define PTR_NATION_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "data_pointer.h" + +// --- Club pointer --- // +class PtrNation : public DataPointer +{ +public: + PtrNation() {} + PtrNation(const int i); + + // Counter + void incrementClubCount(); + void incrementStaffCount(); + + // Get data + QString getSafeText() const; + QString getText() const; + + // Set data + void set(const int &i); + void set(const QVariant &value); +}; + +#endif // PTR_NATION_H diff --git a/data_types/ptr_non_player.cpp b/data_types/ptr_non_player.cpp new file mode 100644 index 0000000..d257f9b --- /dev/null +++ b/data_types/ptr_non_player.cpp @@ -0,0 +1,23 @@ +#include "ptr_non_player.h" + +PtrNonPlayer::PtrNonPlayer() +{ + +} + +PtrNonPlayer::PtrNonPlayer(const int i) : + DataPointer(i) +{ + +} + +// --- Get pointer to underlying data --- // +NonPlayer *PtrNonPlayer::data() +{ + if(id < VALID) + return nullptr; + else if(id < NonPlayer::db.size()) + return &(NonPlayer::db[id]); + else + return nullptr; +} diff --git a/data_types/ptr_non_player.h b/data_types/ptr_non_player.h new file mode 100644 index 0000000..d2a797e --- /dev/null +++ b/data_types/ptr_non_player.h @@ -0,0 +1,22 @@ +#ifndef PTR_NON_PLAYER_H +#define PTR_NON_PLAYER_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "data_pointer.h" +#include "../database/non_player.h" + +// --- Player pointer --- // +class PtrNonPlayer : public DataPointer +{ +public: + PtrNonPlayer(); + PtrNonPlayer(const int i); + + // Get data + NonPlayer *data(); +}; + +#endif // PTR_NON_PLAYER_H diff --git a/data_types/ptr_player.cpp b/data_types/ptr_player.cpp new file mode 100644 index 0000000..aec55d1 --- /dev/null +++ b/data_types/ptr_player.cpp @@ -0,0 +1,23 @@ +#include "ptr_player.h" + +PtrPlayer::PtrPlayer() +{ + +} + +PtrPlayer::PtrPlayer(const int i) : + DataPointer(i) +{ + +} + +// --- Get pointer to underlying data --- // +Player *PtrPlayer::data() +{ + if(id < VALID) + return nullptr; + else if(id < Player::db.size()) + return &(Player::db[id]); + else + return nullptr; +} diff --git a/data_types/ptr_player.h b/data_types/ptr_player.h new file mode 100644 index 0000000..8dea6a9 --- /dev/null +++ b/data_types/ptr_player.h @@ -0,0 +1,22 @@ +#ifndef PTR_PLAYER_H +#define PTR_PLAYER_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "data_pointer.h" +#include "../database/player.h" + +// --- Player pointer --- // +class PtrPlayer : public DataPointer +{ +public: + PtrPlayer(); + PtrPlayer(const int i); + + // Get data + Player *data(); +}; + +#endif // PTR_PLAYER_H diff --git a/data_types/ptr_preferences.cpp b/data_types/ptr_preferences.cpp new file mode 100644 index 0000000..9ebfb37 --- /dev/null +++ b/data_types/ptr_preferences.cpp @@ -0,0 +1,12 @@ +#include "ptr_preferences.h" + +PtrPreferences::PtrPreferences() +{ + +} + +PtrPreferences::PtrPreferences(const int i) : + DataPointer(i) +{ + +} diff --git a/data_types/ptr_preferences.h b/data_types/ptr_preferences.h new file mode 100644 index 0000000..4a3e3fc --- /dev/null +++ b/data_types/ptr_preferences.h @@ -0,0 +1,18 @@ +#ifndef PTR_PREFERENCES_H +#define PTR_PREFERENCES_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "data_pointer.h" + +// --- Staff preferences pointer --- // +class PtrPreferences : public DataPointer +{ +public: + PtrPreferences(); + PtrPreferences(const int i); +}; + +#endif // PTR_PREFERENCES_H diff --git a/data_types/ptr_staff.cpp b/data_types/ptr_staff.cpp new file mode 100644 index 0000000..396135d --- /dev/null +++ b/data_types/ptr_staff.cpp @@ -0,0 +1,42 @@ +#include "ptr_staff.h" +#include "../database/staff.h" + +PtrStaff::PtrStaff() +{ + +} + +PtrStaff::PtrStaff(const int i) : + DataPointer(i) +{ + +} + +// --- Get default display text --- // +QString PtrStaff::getText() const +{ + if(id < VALID) + return s_NoneText; + else if(id < Staff::db.size()) + return Staff::db[id].getDisplayText(); + else + return s_OutOfRangeText; +} + +// --- Set id --- // +void PtrStaff::set(const int &i) +{ + if(i < VALID) + id = NONE; + else if(i < Staff::db.size()) + id = i; +} + +// --- Set id --- // +void PtrStaff::set(const QVariant &value) +{ + if(value.toInt() < VALID) + id = NONE; + else if(value.toInt() < Staff::db.size()) + id = value.toInt(); +} diff --git a/data_types/ptr_staff.h b/data_types/ptr_staff.h new file mode 100644 index 0000000..97cce45 --- /dev/null +++ b/data_types/ptr_staff.h @@ -0,0 +1,25 @@ +#ifndef PTR_STAFF_H +#define PTR_STAFF_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "data_pointer.h" + +// --- Staff pointer --- // +class PtrStaff : public DataPointer +{ +public: + PtrStaff(); + PtrStaff(const int i); + + // Get data + QString getText() const; + + // Set data + void set(const int &i); + void set(const QVariant &value); +}; + +#endif // PTR_STAFF_H diff --git a/data_types/reputation.cpp b/data_types/reputation.cpp new file mode 100644 index 0000000..ff525e1 --- /dev/null +++ b/data_types/reputation.cpp @@ -0,0 +1,289 @@ +#include "reputation.h" + +// --- Static data --- // +QVector *Reputation::s_Values = Reputation::initValues(); + +// --- Default constructor -- // +Reputation::Reputation() : + CM_Short(1) +{ + +} + +// --- Set from QVariant --- // +void Reputation::set(const QVariant &value) +{ + QString tmp = value.toString(); + + if(tmp.isEmpty() || tmp.contains("-")) + return; + + short i = static_cast(value.toInt()); + + if(i < 1) + i = 1; + else if(i > 200) + i = 200; + + m_Value = i; +} + +// --- Initialise transfer value <--> reputation table --- // +QVector *Reputation::initValues() +{ + QVector *v = new QVector; + v->reserve(201); // 0 - 200 + + v->append(0); // Rep = 0 + v->append(25000); // Rep = 1 + v->append(50000); // Rep = 2 + v->append(75000); + v->append(100000); + v->append(125000); + v->append(150000); + v->append(175000); + v->append(200000); + v->append(225000); + v->append(250000); + v->append(275000); + v->append(300000); + v->append(325000); + v->append(350000); + v->append(375000); + v->append(400000); + v->append(425000); + v->append(450000); + v->append(475000); + v->append(500000); + v->append(550000); + v->append(600000); + v->append(650000); + v->append(700000); + v->append(750000); + v->append(800000); + v->append(850000); + v->append(900000); + v->append(950000); + v->append(1000000); + v->append(1100000); + v->append(1200000); + v->append(1300000); + v->append(1400000); + v->append(1500000); + v->append(1600000); + v->append(1700000); + v->append(1800000); + v->append(1900000); + v->append(2000000); + v->append(2100000); + v->append(2200000); + v->append(2300000); + v->append(2400000); + v->append(2500000); + v->append(2600000); + v->append(2700000); + v->append(2800000); + v->append(2900000); + v->append(3000000); + v->append(3100000); + v->append(3200000); + v->append(3300000); + v->append(3400000); + v->append(3500000); + v->append(3600000); + v->append(3700000); + v->append(3800000); + v->append(3900000); + v->append(4000000); + v->append(4100000); + v->append(4200000); + v->append(4300000); + v->append(4400000); + v->append(4500000); + v->append(4600000); + v->append(4700000); + v->append(4800000); + v->append(4900000); + v->append(5000000); + v->append(5100000); + v->append(5200000); + v->append(5300000); + v->append(5400000); + v->append(5500000); + v->append(5600000); + v->append(5700000); + v->append(5800000); + v->append(5900000); + v->append(6000000); + v->append(6100000); + v->append(6200000); + v->append(6300000); + v->append(6400000); + v->append(6500000); + v->append(6600000); + v->append(6700000); + v->append(6800000); + v->append(6900000); + v->append(7000000); + v->append(7100000); + v->append(7200000); + v->append(7300000); + v->append(7400000); + v->append(7500000); + v->append(7600000); + v->append(7700000); + v->append(7800000); + v->append(7900000); + v->append(8000000); + v->append(8200000); + v->append(8400000); + v->append(8600000); + v->append(8800000); + v->append(9000000); + v->append(9200000); + v->append(9400000); + v->append(9600000); + v->append(9800000); + v->append(10000000); + v->append(10500000); + v->append(11000000); + v->append(11500000); + v->append(12000000); + v->append(12500000); + v->append(13000000); + v->append(13500000); + v->append(14000000); + v->append(14500000); + v->append(15000000); + v->append(15500000); + v->append(16000000); + v->append(16500000); + v->append(17000000); + v->append(17500000); + v->append(18000000); + v->append(18500000); + v->append(19000000); + v->append(19500000); + v->append(20000000); + v->append(20500000); + v->append(21000000); + v->append(21500000); + v->append(22000000); + v->append(22500000); + v->append(23000000); + v->append(23500000); + v->append(24000000); + v->append(24500000); + v->append(25000000); + v->append(25500000); + v->append(26000000); + v->append(26500000); + v->append(27000000); + v->append(27500000); + v->append(28000000); + v->append(28500000); + v->append(29000000); + v->append(29500000); + v->append(30000000); + v->append(30500000); + v->append(31000000); + v->append(31500000); + v->append(32000000); + v->append(32500000); + v->append(33000000); + v->append(33500000); + v->append(34000000); + v->append(34500000); + v->append(35000000); + v->append(35500000); + v->append(36000000); + v->append(36500000); + v->append(37000000); + v->append(37500000); + v->append(38000000); + v->append(38500000); + v->append(39000000); + v->append(39500000); + v->append(40000000); + v->append(42000000); + v->append(44000000); + v->append(46000000); + v->append(48000000); + v->append(50000000); + v->append(52000000); + v->append(54000000); + v->append(56000000); + v->append(58000000); + v->append(60000000); + v->append(62000000); + v->append(64000000); + v->append(66000000); + v->append(68000000); + v->append(70000000); + v->append(72000000); + v->append(74000000); + v->append(76000000); + v->append(78000000); + v->append(80000000); + v->append(82000000); + v->append(84000000); + v->append(86000000); + v->append(88000000); + v->append(90000000); + v->append(92000000); + v->append(94000000); + v->append(96000000); + v->append(98000000); + v->append(100000000); // Rep = 200 + + return v; +} + +// --- Set from transfer value --- // +bool Reputation::setFromTransferValue(const unsigned int &euros) +{ + // Temporary rep value + short r = 200; // Use 200 as this will be used if the loop below fails to find a match + + // Find nearest match + const short count = static_cast(s_Values->size()); + for(short i = 0; i < count; ++i) { + + if(euros < s_Values->at(i)) { + r = i-1; + break; + } + } + + // Use a rep of 1 if the temporary figure is less than 1 + if(r > 0) { + m_Value = r; + return true; + } + else { + m_Value = 1; + return false; + } +} + +// --- Set from transfer value (int convenience function) --- // +bool Reputation::setFromTransferValue(const int &euros) +{ + // Abort if a negative value was passed + if(euros < 0) + return false; + + const unsigned int i = static_cast(euros); + return this->setFromTransferValue(i); +} + +// --- Set from transfer value (QVariant convenience function) --- // +bool Reputation::setFromTransferValue(const QVariant &euros) +{ + // Abort if a negative value was passed + if(euros.toString().isEmpty() || euros.toInt() < 0) + return false; + + const unsigned int i = euros.toUInt(); + return this->setFromTransferValue(i); +} diff --git a/data_types/reputation.h b/data_types/reputation.h new file mode 100644 index 0000000..5b29939 --- /dev/null +++ b/data_types/reputation.h @@ -0,0 +1,30 @@ +#ifndef REPUTATION_H +#define REPUTATION_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "cm_short.h" + +// --- Reputation (short) --- // +class Reputation : public CM_Short +{ +private: + // Transfer value <--> reputation table + static QVector *s_Values; + static QVector *initValues(); + +public: + // Constructor + Reputation(); + + // Set data + void set(const QVariant &value); + bool setFromTransferValue(const int &euros); + bool setFromTransferValue(const unsigned int &euros); + bool setFromTransferValue(const QVariant &euros); +}; + +#endif // REPUTATION_H diff --git a/data_types/squad.cpp b/data_types/squad.cpp new file mode 100644 index 0000000..47fea7a --- /dev/null +++ b/data_types/squad.cpp @@ -0,0 +1,21 @@ +#include "squad.h" + +Squad::Squad() : + CM_Char() +{ + m_Value = INVALID_SQUAD; +} + + +/* ================== */ +/* Set Data */ +/* ================== */ + +// --- Set job --- // +void Squad::set(int i) +{ + if(i < INVALID_SQUAD || i > NATION_B_SQUAD) + return; + else + m_Value = static_cast(i); +} diff --git a/data_types/squad.h b/data_types/squad.h new file mode 100644 index 0000000..6e8b250 --- /dev/null +++ b/data_types/squad.h @@ -0,0 +1,29 @@ +#ifndef SQUAD_H +#define SQUAD_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Base class +#include "cm_char.h" + +// --- Playing Squad --- // +class Squad : public CM_Char +{ +public: + Squad(); + + // Set data + void set(int i); + + // Values + enum ENUM_SQUAD { + INVALID_SQUAD = 0, + CLUB_SENIOR_SQUAD = 0x01, + CLUB_RESERVE_SQUAD = 0x02, + NATION_MAIN_SQUAD = 0x04, + NATION_B_SQUAD = 0x08 + }; +}; + +#endif // SQUAD_H diff --git a/data_types/staff_classification.cpp b/data_types/staff_classification.cpp new file mode 100644 index 0000000..ac637cd --- /dev/null +++ b/data_types/staff_classification.cpp @@ -0,0 +1,50 @@ +#include "staff_classification.h" + +/* ============================== */ +/* Staff Classification */ +/* ============================== */ + +// --- Default constructor --- // +StaffClassification::StaffClassification() +{ + +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Is non-player --- // +bool StaffClassification::isNonPlayer() +{ + if(m_Value == NON_PLAYER || m_Value == NON_PLAYER_AND_PLAYER) + return true; + else + return false; +} + +// --- Is player --- // +bool StaffClassification::isPlayer() +{ + if( m_Value == PLAYER || + m_Value == NON_PLAYER_AND_PLAYER || + m_Value == YOUTH_PLAYER) + return true; + else + return false; +} + + +/* ================== */ +/* Set Data */ +/* ================== */ + +// --- Set classification --- // +void StaffClassification::set(int i) +{ + if(i < INVALID_CLASSIFICATION || i > SPARE_NON_PLAYER) + return; + else + m_Value = static_cast(i); +} diff --git a/data_types/staff_classification.h b/data_types/staff_classification.h new file mode 100644 index 0000000..884f18a --- /dev/null +++ b/data_types/staff_classification.h @@ -0,0 +1,32 @@ +#ifndef STAFF_CLASSIFICATION_H +#define STAFF_CLASSIFICATION_H + +#include "cm_char.h" + +// --- Staff Classification --- // +class StaffClassification : public CM_Char +{ +public: + // Constructor + StaffClassification(); + + // Get data + bool isNonPlayer(); + bool isPlayer(); + + // Set data + void set(int i); + + // Values + enum ENUM_VALUES { + INVALID_CLASSIFICATION = 0, + NON_PLAYER, + PLAYER, + NON_PLAYER_AND_PLAYER, + YOUTH_PLAYER, + NEW_HUMAN_MANAGER, + SPARE_NON_PLAYER, + }; +}; + +#endif // STAFF_CLASSIFICATION_H diff --git a/data_types/string.cpp b/data_types/string.cpp new file mode 100644 index 0000000..4df07ef --- /dev/null +++ b/data_types/string.cpp @@ -0,0 +1,305 @@ +#include "string.h" + +// Qt headers +#include + +// --- Static data --- // +QVector String::s_AccentedCharacters = String::getAccentedCharacterVector(); +QVector String::s_UnaccentedCharacters = String::getUnaccentedCharacterVector(); +QVector String::s_EncodedCharacters = String::getEncodedCharacterVector(); +QVector String::s_UtfCharacters = String::getUtfCharacterVector(); +bool String::s_IgnoreAccents = true; + + +/* =============== */ +/* String */ +/* =============== */ + +// --- Default constructor --- // +String::String() : + m_Text() +{ +} + +// --- Constructor (QString) --- // +String::String(const QString text) : + m_Text(text) +{ + +} + +// --- Constructor (char array / SI Font / CM database text) --- // +String::String(const char text[]) +{ + m_Text = QString::fromLatin1(text); + + const int size = s_EncodedCharacters.size(); + for(int i = 0; i < size; ++i) + m_Text.replace(s_EncodedCharacters[i], s_UtfCharacters[i]); +} + + +/* ================= */ +/* Get data */ +/* ================= */ + +// --- Get string --- // +QString String::get() +{ + return m_Text; +} + +// --- Get date string formatted for matching against --- // +QString String::getMatchDateString() +{ + QString text = m_Text.toLower(); + + // Replace slashes with dots + text.replace("/", "."); + text.replace("\\", "."); + + // Do not process if the size of the date is too small (x.x.xxxx = 8 characters minimum) + if(text.size() < 8) + return text; + + // Add preceding zero if the day number is a single digit + // e.g. 3.04.1990 -> 03.04.1990 + if(text.at(1) == QStringLiteral(".")) + text.prepend(QString::number(0)); + + // Add preceding zero if the month number is a single digit + // e.g. 03.4.1990 -> 03.04.1990 + if(text.at(4) == QStringLiteral(".")) + text.insert(3, QString::number(0)); + + return text; +} + +// --- Get string formatted for matching against --- // +QString String::getMatchString() +{ + // Lower case + QString text = m_Text.toLower(); + + // Remove accents + if(s_IgnoreAccents) { + const int size = s_AccentedCharacters.size(); + for(int i = 0; i < size; ++i) + text.replace(s_AccentedCharacters[i], s_UnaccentedCharacters[i]); + } + + // Replace commas and semi-colons with underscores (for CSV compatibility) + text.replace(",", "_"); + text.replace(";", "_"); + + return text; +} + +// --- Get string formatted with no accents --- // +QString String::getNoAccentsString() +{ + // Lower case + QString text = m_Text; + + // Remove accents + if(s_IgnoreAccents) { + const int size = s_AccentedCharacters.size(); + for(int i = 0; i < size; ++i) + text.replace(s_AccentedCharacters[i], s_UnaccentedCharacters[i]); + } + + // Replace commas and semi-colons with underscores (for CSV compatibility) + text.replace(",", "_"); + text.replace(";", "_"); + + return text; +} + +// --- Convert the string to a char array / SI Font / CM database text --- // +void String::toSIFont(char *SITextArray, int length) +{ + QString tmp(m_Text); + + if(tmp.size() >= length) + tmp.chop(length - 1); + + const int size = s_UtfCharacters.size(); + for(int i = 0; i < size; ++i) + m_Text.replace(s_UtfCharacters[i], s_EncodedCharacters[i]); + + // Fill the SI Font char array with zeros and then copy the processed string + memset(SITextArray,'\x00', length); + strncpy_s(SITextArray, length, tmp.toLatin1().constData(), tmp.size()); +} + + +/* ============================== */ +/* String Conversion Data */ +/* ============================== */ + +// --- Get accented text vector --- // +QVector String::getAccentedCharacterVector() +{ + QVector v; + + v.push_back(0x00DF); // ß - Lowercase + v.push_back(0x00E0); // à - Lowercase + v.push_back(0x00E1); // á - Lowercase + v.push_back(0x00E2); // â - Lowercase + v.push_back(0x00E3); // ã - Lowercase + v.push_back(0x00E4); // ä - Lowercase + v.push_back(0x00E5); // å - Lowercase + v.push_back(0x00E6); // æ - Lowercase + v.push_back(0x00E7); // ç - Lowercase + v.push_back(0x00E8); // è - Lowercase + v.push_back(0x00E9); // é - Lowercase + v.push_back(0x00EA); // ê - Lowercase + v.push_back(0x00EB); // ë - Lowercase + v.push_back(0x00EC); // ì - Lowercase + v.push_back(0x00ED); // í - Lowercase + v.push_back(0x00EE); // î - Lowercase + v.push_back(0x00EF); // ï - Lowercase + v.push_back(0x00F0); // ð - Lowercase + v.push_back(0x00F1); // ñ - Lowercase + v.push_back(0x00F2); // ò - Lowercase + v.push_back(0x00F3); // ó - Lowercase + v.push_back(0x00F4); // ô - Lowercase + v.push_back(0x00F5); // õ - Lowercase + v.push_back(0x00F6); // ö - Lowercase + v.push_back(0x00F8); // ø - Lowercase + v.push_back(0x00F9); // ù - Lowercase + v.push_back(0x00FA); // ú - Lowercase + v.push_back(0x00FB); // û - Lowercase + v.push_back(0x00FC); // ü - Lowercase + v.push_back(0x00FD); // ý - Lowercase + v.push_back(0x00FE); // þ - Lowercase + v.push_back(0x00FF); // ÿ - Lowercase + v.push_back(0x010D); // c - Caron - Lowercase + v.push_back(0x010F); // d - Caron - Lowercase + v.push_back(0x011B); // e - Caron - Lowercase + v.push_back(0x0148); // n - Caron - Lowercase + v.push_back(0x0159); // r - Caron - Lowercase + v.push_back(0x0161); // s - Caron - Lowercase + v.push_back(0x016F); // u - Ring Above - Lowercase + v.push_back(0x017E); // z - Caron - Lowercase + + return v; +} + +// --- Get unaccented text vector --- // +QVector String::getUnaccentedCharacterVector() +{ + QVector v; + + v.push_back(0x0073); // ß - Lowercase + v.push_back(0x0061); // à - Lowercase + v.push_back(0x0061); // á - Lowercase + v.push_back(0x0061); // â - Lowercase + v.push_back(0x0061); // ã - Lowercase + v.push_back(0x0061); // ä - Lowercase + v.push_back(0x0061); // å - Lowercase + v.push_back(0x0061); // æ - Lowercase + v.push_back(0x0063); // ç - Lowercase + v.push_back(0x0065); // è - Lowercase + v.push_back(0x0065); // é - Lowercase + v.push_back(0x0065); // ê - Lowercase + v.push_back(0x0065); // ë - Lowercase + v.push_back(0x0069); // ì - Lowercase + v.push_back(0x0069); // í - Lowercase + v.push_back(0x0069); // î - Lowercase + v.push_back(0x0069); // ï - Lowercase + v.push_back(0x006F); // ð - Lowercase + v.push_back(0x006E); // ñ - Lowercase + v.push_back(0x006F); // ò - Lowercase + v.push_back(0x006F); // ó - Lowercase + v.push_back(0x006F); // ô - Lowercase + v.push_back(0x006F); // õ - Lowercase + v.push_back(0x006F); // ö - Lowercase + v.push_back(0x006F); // ø - Lowercase + v.push_back(0x0075); // ù - Lowercase + v.push_back(0x0075); // ú - Lowercase + v.push_back(0x0075); // û - Lowercase + v.push_back(0x0075); // ü - Lowercase + v.push_back(0x0079); // ý - Lowercase + v.push_back(0x0079); // þ - Lowercase + v.push_back(0x0079); // ÿ - Lowercase + v.push_back(0x0063); // c - Caron - Lowercase + v.push_back(0x0064); // d - Caron - Lowercase + v.push_back(0x0065); // e - Caron - Lowercase + v.push_back(0x006E); // n - Caron - Lowercase + v.push_back(0x0072); // r - Caron - Lowercase + v.push_back(0x0073); // s - Caron - Lowercase + v.push_back(0x0075); // u - Ring Above - Lowercase + v.push_back(0x007A); // z - Caron - Lowercase + + return v; +} + +// --- Get encoded text vector --- // +QVector String::getEncodedCharacterVector() +{ + QVector v; + + v.push_back(0x00BC); // C - Caron - Uppercase + v.push_back(0x00A0); // c - Caron - Lowercase + v.push_back(0x00BE); // D - Caron - Uppercase + v.push_back(0x00BB); // d - Caron - Lowercase + v.push_back(0x0086); // E - Caron - Uppercase + v.push_back(0x0090); // e - Caron - Lowercase + v.push_back(0x00B1); // N - Caron - Uppercase + v.push_back(0x008F); // n - Caron - Lowercase + v.push_back(0x00B3); // R - Caron - Uppercase + v.push_back(0x00A7); // r - Caron - Lowercase + v.push_back(0x008A); // S - Caron - Uppercase <--- ??? Duplicate? + v.push_back(0x00BD); // S - Caron - Uppercase + v.push_back(0x009A); // s - Caron - Lowercase + v.push_back(0x0087); // U - Ring Above - Uppercase + v.push_back(0x009F); // u - Ring Above - Lowercase + v.push_back(0x008E); // Z - Caron - Uppercase + v.push_back(0x009E); // z - Caron - Lowercase + + return v; +} + +// --- Get UTF text vector --- // +QVector String::getUtfCharacterVector() +{ + QVector v; + + v.push_back(0x010C); // C - Caron - Uppercase + v.push_back(0x010D); // c - Caron - Lowercase + v.push_back(0x010E); // D - Caron - Uppercase + v.push_back(0x010F); // d - Caron - Lowercase + v.push_back(0x011A); // E - Caron - Uppercase + v.push_back(0x011B); // e - Caron - Lowercase + v.push_back(0x0147); // N - Caron - Uppercase + v.push_back(0x0148); // n - Caron - Lowercase + v.push_back(0x0158); // R - Caron - Uppercase + v.push_back(0x0159); // r - Caron - Lowercase + v.push_back(0x02A5); // S - Caron - Uppercase <--- ??? Duplicate? + v.push_back(0x0160); // S - Caron - Uppercase + v.push_back(0x0161); // s - Caron - Lowercase + v.push_back(0x016E); // U - Ring Above - Uppercase + v.push_back(0x016F); // u - Ring Above - Lowercase + v.push_back(0x017D); // Z - Caron - Uppercase + v.push_back(0x017E); // z - Caron - Lowercase + + return v; +} + + +/* ================ */ +/* Settings */ +/* ================ */ + +// --- Ignore accented characters --- // +void String::ignoreAccents(bool ignore) +{ + s_IgnoreAccents = ignore; +} + +// --- Use accented characters --- // +void String::useAccents(bool use) +{ + s_IgnoreAccents = !use; +} diff --git a/data_types/string.h b/data_types/string.h new file mode 100644 index 0000000..1e0391a --- /dev/null +++ b/data_types/string.h @@ -0,0 +1,49 @@ +#ifndef STRING_H +#define STRING_H + +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + + +// --- QString <--> SI String wrapper/interface class --- // +class String +{ +private: + // Text string + QString m_Text; + + // Settings + static bool s_IgnoreAccents; + + // Static char conversion data + static QVector getAccentedCharacterVector(); + static QVector getUnaccentedCharacterVector(); + static QVector getEncodedCharacterVector(); + static QVector getUtfCharacterVector(); + static QVector s_AccentedCharacters; + static QVector s_UnaccentedCharacters; + static QVector s_EncodedCharacters; + static QVector s_UtfCharacters; + +public: + // Constructor + String(); + String(const QString text); + String(const char text[]); + + // Get data + QString get(); + QString getMatchDateString(); + QString getMatchString(); + QString getNoAccentsString(); + void toSIFont(char *SITextArray, int length); + + // Settings + static void ignoreAccents(bool ignore = true); + static void useAccents(bool use = true); +}; + +#endif // STRING_H diff --git a/data_types/wage.cpp b/data_types/wage.cpp new file mode 100644 index 0000000..8df72af --- /dev/null +++ b/data_types/wage.cpp @@ -0,0 +1,24 @@ +#include "wage.h" + +// --- Default constructor --- // +Wage::Wage() : + CM_Int(100) +{ + +} + +// --- Set wage from world reputation --- // +void Wage::setFromWorldReputation(const short &rep) +{ + if(rep > 110) { + m_Value = (rep - 100) * 1000; + } + else if(rep > 0) { + m_Value = rep * 100; + } + + // Ensure that any wages are at least 100 + if(rep > 0 && m_Value < 100) { + m_Value = 100; + } +} diff --git a/data_types/wage.h b/data_types/wage.h new file mode 100644 index 0000000..d29d30a --- /dev/null +++ b/data_types/wage.h @@ -0,0 +1,17 @@ +#ifndef WAGE_H +#define WAGE_H + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "cm_int.h" + +// --- Wage --- // +class Wage : public CM_Int +{ +public: + Wage(); + void setFromWorldReputation(const short &rep); +}; + +#endif // WAGE_H diff --git a/data_types/weather.h b/data_types/weather.h new file mode 100644 index 0000000..7489146 --- /dev/null +++ b/data_types/weather.h @@ -0,0 +1,79 @@ +#ifndef WEATHER_H +#define WEATHER_H + +#pragma pack(1) + +// WEATHER STRUCTURE DEFINITIONS // +// This structure defines the current weather settings for an area. + +enum ENUM_CITY_WEATHER { + SPRING = 0, + SUMMER, + AUTUMN, + WINTER +}; + +enum ENUM_WEATHER_SPECIAL { + SPECIAL_HAIL = 1, + SPECIAL_SLEET, + SPECIAL_SNOW +}; + +typedef struct +{ + char wind; + char precipitation; + char temperature; + char special; +} WEATHER_CONDITION; + +// Wind defintion structure, this structure should give the precentage scale +// upon which each of the various wind type will happen within a season. +// Eg. calm - 3 // 0 - 3 % means calm weather occurs +// breezy - 20 +// gusty - 75 +// strong - 90 +// gale - 100 +typedef struct +{ + char calm; + char breezy; + char gusty; + char strong; + char gale; +} WIND; + +// Precipitation weather structure, this structure gives the percentage chance +// of each of the various types of precipitations happening. +typedef struct +{ + char dry; + char wet; + char drizzle; + char shower; + char down_pour; +} PRECIPITATION; + +// Temperature weather structure, this structure gives the percentage chance +// of each of the various types of precipitations happening. +typedef struct +{ + char freezing; + char cold; + char mild; + char fine; + char warm; + char hot; + char very_hot; +} TEMPERATURE; + +// This structure defines a seasons weather conditions within CM3. +typedef struct +{ + short WeatherSeasonStartDay; + WIND WeatherSeasonWind; + PRECIPITATION WeatherSeasonPrecipitation; + TEMPERATURE WeatherSeasonTemperature; +} WEATHER_SEASON; + +#endif // WEATHER_H diff --git a/data_types/year.cpp b/data_types/year.cpp new file mode 100644 index 0000000..fbd3077 --- /dev/null +++ b/data_types/year.cpp @@ -0,0 +1,45 @@ +#include "year.h" + + +/* ==================== */ +/* CM/SI Year */ +/* ==================== */ + +// --- Default Constructor --- // +Year::Year() : + m_Year(0) +{ + +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Get as short --- // +short Year::get() +{ + return m_Year; +} + +// --- Get year formatted for matching against --- // +QString Year::getMatchText(int &yearAdjustment) +{ + if(m_Year > 1900) + return QString::number(m_Year+=static_cast(yearAdjustment)); + else + return QString::number(1900); +} + + +/* ================== */ +/* Set Data */ +/* ================== */ + +// --- Validate a year according to another year --- // +void Year::validate(const short year) +{ + if(year > 1900) + m_Year = year; +} diff --git a/data_types/year.h b/data_types/year.h new file mode 100644 index 0000000..adafea1 --- /dev/null +++ b/data_types/year.h @@ -0,0 +1,24 @@ +#ifndef YEAR_H +#define YEAR_H + +#include + +// --- Year --- // +class Year +{ +private: + short m_Year; + +public: + // Constructor + Year(); + + // Get data + short get(); + QString getMatchText(int &yearAdjustment); + + // Set data + void validate(const short year); +}; + +#endif // YEAR_H diff --git a/database/city.cpp b/database/city.cpp new file mode 100644 index 0000000..e9b4385 --- /dev/null +++ b/database/city.cpp @@ -0,0 +1,51 @@ +#include "city.h" + +// --- Static data --- // +QVector City::db; + + +/* =================== */ +/* City Data */ +/* =================== */ + +// --- Default constructor --- // +City::City() +{ + +} + + +/* ===================== */ +/* Export Data */ +/* ===================== */ + +// --- Export data --- // +bool City::exportData(Spreadsheet &s) +{ + // Header + QStringList header; + header << "Name" << "Nation" << "Longitude" << "Latitude"; + s.addHeader(header); + + // Rows + for(City &itr : db) + { + QStringList row; + row << itr.getName() << itr.Nation.getSafeText() + << QString::number(itr.Longitude) << QString::number(itr.Latitude); + s.add(row); + } + + return true; +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Get name text --- // +QString City::getName() +{ + return String(Name).get(); +} diff --git a/database/city.h b/database/city.h new file mode 100644 index 0000000..dd2283c --- /dev/null +++ b/database/city.h @@ -0,0 +1,43 @@ +#ifndef CITY_H +#define CITY_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/city_weather.h" +#include "../data_types/data_types.h" +#include "../data_types/ptr_nation.h" +#include "../data_types/string.h" +#include "../spreadsheet/spreadsheet.h" + +// --- City.dat --- // +class City +{ +private: + int ID; + char Name[ SHORT_TEXT_LENGTH ]; + char GenderName; + PtrNation Nation; + double Latitude; + double Longitude; + char Attraction; + int Weather; + CityWeather WeatherCity; + +public: + // Constructor + City(); + + // Container + static QVector db; + + // Export data + static bool exportData(Spreadsheet &s); + + // Get data + QString getName(); +}; + +#endif // CITY_H diff --git a/database/club.cpp b/database/club.cpp new file mode 100644 index 0000000..b5adede --- /dev/null +++ b/database/club.cpp @@ -0,0 +1,422 @@ +#include "club.h" + +// --- Static data --- // +QVector Club::dbDom; +QVector Club::dbInt; + + +/* =================== */ +/* Club Data */ +/* =================== */ + +// --- Default constructor --- // +Club::Club() +{ + +} + + +/* ========================= */ +/* Data Validation */ +/* ========================= */ + +// --- Validate data --- // +void Club::validate() +{ + const int szDom = dbDom.size(); + + for(int i = 0; i < szDom; ++i) { + // Nation counter + dbDom[i].Nation.incrementClubCount(); + } +} + + +/* ===================== */ +/* Export Data */ +/* ===================== */ + +// --- Export data --- // +bool Club::exportData(Spreadsheet &s) +{ + // Header + QStringList header; + header << "Type" + << "Long Name" << "Short Name" << "Nation" + << "Division" << "Reserve Division" + << "CSV Long Name" << "CSV Short Name"; + s.addHeader(header); + + // Rows (domestic) + for(Club &itr : dbDom) + { + QStringList row; + row << "Dom"; // Domestic team + itr.exportRow(row); + s.add(row); + } + + // Rows (international) + for(Club &itr : dbInt) + { + QStringList row; + row << "Nat"; // National team + itr.exportRow(row); + s.add(row); + } + + return true; +} + +// --- Export roster data --- // +bool Club::exportRosters(Spreadsheet &s) +{ + // Header + QStringList header; + header << "Club"; + + for(int i = 0; i < SQUAD_SIZE; ++i) + header << ("Player " + QString::number(i)); + + s.addHeader(header); + + // Rows (domestic) + for(Club &itr : dbDom) { + QStringList row; + row << itr.getShortName(); + + for(int i = 0; i < SQUAD_SIZE; ++i) + row << itr.SquadList[i].getText(); + + s.add(row); + } + + // Rows (international) + for(Club &itr : dbInt) { + QStringList row; + row << itr.getShortName(); + + for(int i = 0; i < SQUAD_SIZE; ++i) + row << itr.SquadList[i].getText(); + + s.add(row); + } + + return true; +} + +// --- Export row of club data --- // +void Club::exportRow(QStringList &row) +{ + // Text buffers + String longName(Name); + String shortName(NameShort); + + row << longName.get() + << shortName.get() + << Nation.getSafeText() + << Division.getText() + << ReserveDivision.getText() + << longName.getMatchString() + << shortName.getMatchString(); +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Get name text --- // +QString Club::getLongName() +{ + return String(Name).get(); +} + +// --- Get short name text --- // +QString Club::getShortName() +{ + return String(NameShort).get(); +} + + +/* ==================== */ +/* Match Data */ +/* ==================== */ + +// --- Populate a hash with domestic club data --- // +void Club::createHash(QHash &hash, bool useLongNames) +{ + + // Add free agent text (text must be lowercase) + // Only added if the hash is empty (if it is not empty, it suggests that this function + // has been called previously and thus the free agent text has already been added) + if(hash.isEmpty()) { + hash.insert("-", FREE_AGENT); + hash.insert("_none", FREE_AGENT); + hash.insert("_fa", FREE_AGENT); + hash.insert("_free", FREE_AGENT); + hash.insert("_free_agent", FREE_AGENT); + hash.insert("_free_transfer", FREE_AGENT); + } + + // For speed, the loops are embedded within the if statement + // (rather than embedding the if statements within a loop) + + // Long names + if(useLongNames) { + for(Club itr : dbDom) { + hash.insert(String(itr.Name).getMatchString(), itr.ID); + } + } + // Short names + else { + for(Club itr : dbDom) { + hash.insert(String(itr.NameShort).getMatchString(), itr.ID); + } + } +} + + +/* ================= */ +/* Rosters */ +/* ================= */ + +// --- Add staff to roster --- // +bool Club::addToRoster(const PtrStaff &staff, const Job &job) +{ + const int j = static_cast(job.get()); + + // Abort if invalid + if(j == Job::INVALID_JOB) + return false; + + // Flags used for mixed player/non-player roles + bool playerSuccess = false; + bool nonPlayerSuccess = false; + + switch(j) { + + case Job::CHAIRMAN: + if(Chairman.get() >= VALID) + return false; + else + Chairman = staff; + return true; + + case Job::MANAGING_DIRECTOR: + case Job::GENERAL_MANAGER: + case Job::DIRECTOR_OF_FOOTBALL: + + for(int i = 0; i < DIRECTOR_SIZE; ++i) { + if(DirectorList[i].get() < VALID) { + DirectorList[i] = staff; + return true; + } + } + + return false; + + case Job::MANAGER: + if(Manager.get() >= VALID) + return false; + else + Manager = staff; + return true; + + case Job::ASSISTANT_MANAGER: + if(AssistantManager.get() >= VALID) + return false; + else + AssistantManager = staff; + return true; + + case Job::RESERVE_TEAM_MANAGER: + if(Manager.get() >= VALID) + return false; + else + Manager = staff; + return true; + + case Job::COACH: + + for(int i = 0; i < COACH_SIZE; ++i) { + if(CoachList[i].get() < VALID) { + CoachList[i] = staff; + return true; + } + } + + return false; + + case Job::SCOUT: + + for(int i = 0; i < SCOUT_SIZE; ++i) { + if(ScoutList[i].get() < VALID) { + ScoutList[i] = staff; + return true; + } + } + + return false; + + case Job::PHYSIO: + + for(int i = 0; i < PHYSIO_SIZE; ++i) { + if(PhysioList[i].get() < VALID) { + PhysioList[i] = staff; + return true; + } + } + + return false; + + case Job::PLAYER: + + for(int i = 0; i < SQUAD_SIZE; ++i) { + if(SquadList[i].get() < VALID) { + SquadList[i] = staff; + return true; + } + } + + return false; + + case Job::PLAYER_MANAGER: + + if(Manager.get() >= VALID) + return false; + else { + Manager = staff; + nonPlayerSuccess = true; + } + + for(int i = 0; i < SQUAD_SIZE; ++i) { + if(SquadList[i].get() < VALID) { + SquadList[i] = staff; + playerSuccess = true; + break; + } + } + + return (playerSuccess && nonPlayerSuccess); + + case Job::PLAYER_ASSISTANT_MANAGER: + + if(AssistantManager.get() >= VALID) + return false; + else { + AssistantManager = staff; + nonPlayerSuccess = true; + } + + for(int i = 0; i < SQUAD_SIZE; ++i) { + if(SquadList[i].get() < VALID) { + SquadList[i] = staff; + playerSuccess = true; + break; + } + } + + return (playerSuccess && nonPlayerSuccess); + + case Job::PLAYER_RESERVE_TEAM_MANAGER: + + if(Manager.get() >= VALID) + return false; + else { + Manager = staff; + nonPlayerSuccess = true; + } + + for(int i = 0; i < SQUAD_SIZE; ++i) { + if(SquadList[i].get() < VALID) { + SquadList[i] = staff; + playerSuccess = true; + break; + } + } + + return (playerSuccess && nonPlayerSuccess); + + case Job::PLAYER_COACH: + + for(int i = 0; i < COACH_SIZE; ++i) { + if(CoachList[i].get() < VALID) { + CoachList[i] = staff; + nonPlayerSuccess = true; + break; + } + } + + for(int i = 0; i < SQUAD_SIZE; ++i) { + if(SquadList[i].get() < VALID) { + SquadList[i] = staff; + playerSuccess = true; + break; + } + } + + return (playerSuccess && nonPlayerSuccess); + + case Job::PLAYER_RETIRED: + case Job::MEDIA_PUNDIT: + return false; + + default: + return false; + } + + return false; +} + +// --- Clear all rosters --- // +void Club::clearRosters() +{ + // Domestic teams + int sz = dbDom.size(); + + for(int i = 0; i < sz; ++ i) { + dbDom[i].Chairman = NO_STAFF; + dbDom[i].Manager = NO_STAFF; + dbDom[i].AssistantManager = NO_STAFF; + + for(int x = 0; x < DIRECTOR_SIZE; ++x) + dbDom[i].DirectorList[x] = NO_STAFF; + + for(int x = 0; x < SQUAD_SIZE; ++x) + dbDom[i].SquadList[x] = NO_STAFF; + + for(int x = 0; x < COACH_SIZE; ++x) + dbDom[i].CoachList[x] = NO_STAFF; + + for(int x = 0; x < SCOUT_SIZE; ++x) + dbDom[i].ScoutList[x] = NO_STAFF; + + for(int x = 0; x < PHYSIO_SIZE; ++x) + dbDom[i].PhysioList[x] = NO_STAFF; + } + + // International teams + sz = dbInt.size(); + + for(int i = 0; i < sz; ++ i) { + dbInt[i].Chairman = NO_STAFF; + dbInt[i].Manager = NO_STAFF; + dbInt[i].AssistantManager = NO_STAFF; + + for(int x = 0; x < DIRECTOR_SIZE; ++x) + dbInt[i].DirectorList[x] = NO_STAFF; + + for(int x = 0; x < SQUAD_SIZE; ++x) + dbInt[i].SquadList[x] = NO_STAFF; + + for(int x = 0; x < COACH_SIZE; ++x) + dbInt[i].CoachList[x] = NO_STAFF; + + for(int x = 0; x < SCOUT_SIZE; ++x) + dbInt[i].ScoutList[x] = NO_STAFF; + + for(int x = 0; x < PHYSIO_SIZE; ++x) + dbInt[i].PhysioList[x] = NO_STAFF; + } +} diff --git a/database/club.h b/database/club.h new file mode 100644 index 0000000..325a671 --- /dev/null +++ b/database/club.h @@ -0,0 +1,107 @@ +#ifndef CLUB_H +#define CLUB_H + +#include +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/string.h" +#include "../data_types/data_types.h" +#include "../data_types/job.h" +#include "../data_types/ptr_club.h" +#include "../data_types/ptr_club_comp.h" +#include "../data_types/ptr_nation.h" +#include "../data_types/ptr_staff.h" +#include "../spreadsheet/spreadsheet.h" + +// --- Club.dat --- // +class Club +{ +private: + // Database data + int ID; + char Name[ STANDARD_TEXT_LENGTH ]; + char GenderName; + char NameShort[ SHORT_TEXT_LENGTH ]; + char GenderNameShort; + PtrNation Nation; + PtrClubComp Division; + PtrClubComp LastDivision; + char LastPosition; + PtrClubComp ReserveDivision; + char ProfessionalStatus; + int Cash; + int Stadium; + char OwnStadium; + int ReserveStadium; + char HomeMatchDay; + int Attendance; + int MinAttendance; + int MaxAttendance; + char Training; + short Reputation; // Version 0x02 - Changed char->short + char PLC; + int ForegroundColour1; + int BackgroundColour1; + int ForegroundColour2; + int BackgroundColour2; + int ForegroundColour3; + int BackgroundColour3; + PtrStaff FavouriteStaff1; + PtrStaff FavouriteStaff2; + PtrStaff FavouriteStaff3; + PtrStaff DislikedStaff1; + PtrStaff DislikedStaff2; + PtrStaff DislikedStaff3; + PtrClub Rivals1; + PtrClub Rivals2; + PtrClub Rivals3; + PtrStaff Chairman; + PtrStaff DirectorList[ DIRECTOR_SIZE ]; + PtrStaff Manager; + PtrStaff AssistantManager; + PtrStaff SquadList[ SQUAD_SIZE ]; + PtrStaff CoachList[ COACH_SIZE ]; + PtrStaff ScoutList[ SCOUT_SIZE ]; + PtrStaff PhysioList[ PHYSIO_SIZE ]; + + // Runtime data + int EuroFlag; + char EuroSeeding; + int TeamSelected[ TEAM_SZ ]; + int TacticTraining[ MAX_TACTIC_TRAINING ]; + int TacticSelected; + char HasLinkedClub; + +public: + // Constructor + Club(); + + // Containers (domestic & international) + static QVector dbDom; + static QVector dbInt; + + // Data validation + static void validate(); + + // Export data + static bool exportData(Spreadsheet &s); + static bool exportRosters(Spreadsheet &s); + void exportRow(QStringList &row); + + // Get data + QString getLongName(); + QString getShortName(); + + // Match data + static void createHash(QHash &hash, bool useLongNames = true); + + // Rosters + bool addToRoster(const PtrStaff &staff, const Job &job); + static void clearRosters(); +}; + +#endif // CLUB_H diff --git a/database/club_comp.cpp b/database/club_comp.cpp new file mode 100644 index 0000000..991f1a8 --- /dev/null +++ b/database/club_comp.cpp @@ -0,0 +1,27 @@ +#include "club_comp.h" + +// --- Static data --- // +QVector ClubComp::dbDom; +QVector ClubComp::dbInt; + + +/* =============================== */ +/* Club Competition Data */ +/* =============================== */ + +// --- Default constructor --- // +ClubComp::ClubComp() +{ + +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Get name text for models --- // +QString ClubComp::getLongName() +{ + return String(Name).get(); +} diff --git a/database/club_comp.h b/database/club_comp.h new file mode 100644 index 0000000..f7285c9 --- /dev/null +++ b/database/club_comp.h @@ -0,0 +1,44 @@ +#ifndef CLUB_COMP_H +#define CLUB_COMP_H + +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/string.h" +#include "../data_types/data_types.h" +#include "../data_types/ptr_nation.h" + +// --- Club_comp.dat --- // +class ClubComp +{ +private: + int ID; + char Name[ STANDARD_TEXT_LENGTH ]; + char GenderName; + char NameShort[ SHORT_TEXT_LENGTH ]; + char GenderNameShort; + char NameThreeLetter[ 4 ]; + char Scope; + char Selected; + int Continent; + PtrNation Nation; + int ForegroundColour; + int BackgroundColour; + short Reputation; // Version 0x02 - Changed char->short + +public: + // Constructor + ClubComp(); + + // Containers (domestic & international) + static QVector dbDom; + static QVector dbInt; + + // Get data + QString getLongName(); +}; + +#endif // CLUB_COMP_H diff --git a/database/club_comp_history.cpp b/database/club_comp_history.cpp new file mode 100644 index 0000000..7073de8 --- /dev/null +++ b/database/club_comp_history.cpp @@ -0,0 +1,17 @@ +#include "club_comp_history.h" + +// --- Static data --- // +QVector ClubCompHistory::dbDom; +QVector ClubCompHistory::dbInt; + + +/* ======================================= */ +/* Club Competition History Data */ +/* ======================================= */ + +// --- Default constructor --- // +ClubCompHistory::ClubCompHistory() +{ + +} + diff --git a/database/club_comp_history.h b/database/club_comp_history.h new file mode 100644 index 0000000..3634dbb --- /dev/null +++ b/database/club_comp_history.h @@ -0,0 +1,34 @@ +#ifndef CLUB_COMP_HISTORY_H +#define CLUB_COMP_HISTORY_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/data_types.h" +#include "../data_types/ptr_club.h" +#include "../data_types/ptr_club_comp.h" + +// --- Club_comp_history.dat --- // +class ClubCompHistory +{ +private: + int ID; + PtrClubComp Comp; + short Year; + PtrClub Winners; + PtrClub RunnersUp; + PtrClub ThirdPlaced; + PtrClub Hosts; + +public: + // Constructor + ClubCompHistory(); + + // Containers (domestic & international) + static QVector dbDom; + static QVector dbInt; +}; + +#endif // CLUB_COMP_HISTORY_H diff --git a/database/colour.cpp b/database/colour.cpp new file mode 100644 index 0000000..395b891 --- /dev/null +++ b/database/colour.cpp @@ -0,0 +1,14 @@ +#include "colour.h" + +// --- Static data --- // +QVector Colour::db; + + +/* ===================== */ +/* Colour Data */ +/* ===================== */ + +// --- Default constructor --- // +Colour::Colour() +{ +} diff --git a/database/colour.h b/database/colour.h new file mode 100644 index 0000000..0cce47c --- /dev/null +++ b/database/colour.h @@ -0,0 +1,29 @@ +#ifndef COLOUR_H +#define COLOUR_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/data_types.h" + +// --- Colour.dat --- // +class Colour +{ +private: + int ID; + char Name[ STANDARD_TEXT_LENGTH ]; + unsigned char RedIntensity; + unsigned char GreenIntensity; + unsigned char BlueIntensity; + +public: + // Constructor + Colour(); + + // Container + static QVector db; +}; + +#endif // COLOUR_H diff --git a/database/continent.cpp b/database/continent.cpp new file mode 100644 index 0000000..6d95907 --- /dev/null +++ b/database/continent.cpp @@ -0,0 +1,14 @@ +#include "continent.h" + +// --- Static data --- // +QVector Continent::db; + + +/* ======================== */ +/* Continent Data */ +/* ======================== */ + +// --- Default constructor --- // +Continent::Continent() +{ +} diff --git a/database/continent.h b/database/continent.h new file mode 100644 index 0000000..fdc651d --- /dev/null +++ b/database/continent.h @@ -0,0 +1,34 @@ +#ifndef CONTINENT_H +#define CONTINENT_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/data_types.h" + +// --- Continent.dat --- // +class Continent +{ +private: + int ID; + char Name[ SHORT_TEXT_LENGTH ]; + char GenderName; + char NameThreeLetter[ 4 ]; + char NameContinentality[ SHORT_TEXT_LENGTH ]; + char FederationName[ LONG_TEXT_LENGTH ]; + char GenderFederationName; + char FederationNameShort[ SHORT_TEXT_LENGTH ]; + char GenderFederationNameShort; + double RegionalStrength; + +public: + // Constructor + Continent(); + + // Container + static QVector db; +}; + +#endif // CONTINENT_H diff --git a/database/database.cpp b/database/database.cpp new file mode 100644 index 0000000..0ce07af --- /dev/null +++ b/database/database.cpp @@ -0,0 +1,386 @@ +#include "database.h" + +// Qt headers +#include +#include + +// Database headers +#include "city.h" +#include "club.h" +#include "club_comp.h" +#include "club_comp_history.h" +#include "colour.h" +#include "continent.h" +#include "name.h" +#include "nation.h" +#include "non_player.h" +#include "official.h" +#include "player.h" +#include "stadium.h" +#include "staff.h" +#include "staff_comp.h" +#include "staff_comp_history.h" +#include "staff_history.h" +#include "staff_preferences.h" + + +/* ================== */ +/* Database */ +/* ================== */ + +// --- Default constructor --- // +Database::Database() +{ +} + +// --- Destructor --- // +Database::~Database() +{ +} + + +/* ========================= */ +/* Data Validation */ +/* ========================= */ + +// --- Validate all of the data in the database --- // +void Database::validate(ProgressWindow *p) +{ + bool newProgressWindow; + const int validationCount = 8; + + if(p == nullptr) { + p = new ProgressWindow("Validating database", validationCount); + newProgressWindow = true; + } + else { + p->increaseRange(validationCount); + newProgressWindow = false; + } + + // Reset/clear club rosters + p->increment("Resetting club rosters"); + Club::clearRosters(); + + // Reset name counters + p->increment("Resetting name counters"); + CommonName::resetCounters(); + FirstName::resetCounters(); + SecondName::resetCounters(); + + // Set name string text + p->increment("Setting name string data"); + CommonName::setStringText(); + FirstName::setStringText(); + SecondName::setStringText(); + + // Reset nation counters + p->increment("Resetting nation counters"); + Nation::resetCounters(); + + // Clubs (including nation counters) + p->increment("Validating club data"); + Club::validate(); + + // Officials (including name counters) + p->increment("Validating official data"); + Official::validate(); + + // Staff (including name & nation counters) + p->increment("Validating staff data"); + Staff::validate(); + + // Index + p->increment("Updating index.dat"); + // Offset tracker + int offset = 0; + + // Set table sizes + Index::db[Index::CLUB_TABLE].set(Club::dbDom.size()); + Index::db[Index::NATION_CLUBS_TABLE].set(Club::dbInt.size()); + Index::db[Index::COLOUR_TABLE].set(Colour::db.size()); + Index::db[Index::CONTINENT_TABLE].set(Continent::db.size()); + Index::db[Index::NATION_TABLE].set(Nation::db.size()); + Index::db[Index::OFFICIALS_TABLE].set(Official::db.size()); + Index::db[Index::STADIUM_TABLE].set(Stadium::db.size()); + Index::db[Index::STAFF_TABLE].set(Staff::db.size()); + offset += (Index::db[Index::STAFF_TABLE].getRecordCount() * DB_STAFF_SIZE); + Index::db[Index::NON_PLAYER_TABLE].set(NonPlayer::db.size(), offset); + offset += (Index::db[Index::NON_PLAYER_TABLE].getRecordCount() * DB_NON_PLAYER_SIZE); + Index::db[Index::PLAYER_TABLE].set(Player::db.size(), offset); + Index::db[Index::STAFF_COMP_TABLE].set(StaffComp::db.size()); + Index::db[Index::CITY_TABLE].set(City::db.size()); + Index::db[Index::CLUB_COMP_TABLE].set(ClubComp::dbDom.size()); + Index::db[Index::NATION_COMP_TABLE].set(ClubComp::dbInt.size()); + Index::db[Index::FIRST_NAME_TABLE].set(FirstName::db.size()); + Index::db[Index::SECOND_NAME_TABLE].set(SecondName::db.size()); + Index::db[Index::COMMON_NAME_TABLE].set(CommonName::db.size()); + Index::db[Index::STAFF_HISTORY_TABLE].set(StaffHistory::db.size()); + Index::db[Index::STAFF_COMP_HISTORY_TABLE].set(StaffCompHistory::db.size()); + Index::db[Index::CLUB_COMP_HISTORY_TABLE].set(ClubCompHistory::dbDom.size()); + Index::db[Index::NATION_COMP_HISTORY_TABLE].set(ClubCompHistory::dbInt.size()); + offset += (Index::db[Index::PLAYER_TABLE].getRecordCount() * DB_PLAYER_SIZE); + Index::db[Index::STAFF_PREFERENCES_TABLE].set(StaffPreferences::db.size(), offset); + + // Clean up progress bar + if(newProgressWindow) { + p->complete(); + delete p; + } +} + +/* ============================ */ +/* File I/O: Dat File */ +/* ============================ */ + +// --- Read a dat file --- // +template bool Database::read(QString filename, + QVector &data, + int recordSize, + int skipBytes, + int recordCount) +{ + m_Progress->increment(filename); + + QFile f(m_Folder + filename); + + if(!f.open(QIODevice::ReadOnly)) { + m_FileErrorList << filename; + return false; + } + + else { + data.clear(); + + f.seek(skipBytes); + + // If the record count has not been passed to the function, calculate it using the file size divided by record size. + // This prevents the issue where a file (e.g. nation_comp_history.dat has an extra byte at the end of the file which + // is then read as a garbage record (and will then be saved as a garbage record). Such extraneous bytes will be ignored + // because the record count is rounded down to the nearest whole number when cast to an integer per the below calculation. + if(recordCount < 0) + recordCount = static_cast(f.size() / recordSize); + + // Read the defined number of records + for(int i = 0; i < recordCount; ++i) { + T buffer; + f.read((char*)&buffer, recordSize); + data.push_back(buffer); + } + + f.close(); + return true; + } +} + +// --- Read an index.dat file --- // +bool Database::readIndex(QString filename, QVector &data, int recordSize) +{ + m_Progress->increment(filename); + + QFile f(m_Folder + filename); + + if(!f.open(QIODevice::ReadOnly)) { + m_FileErrorList << filename; + return false; + } + + else { + data.clear(); + + // Read header + f.read((char*)&Index::s_Header, Index::INDEX_HEADER_SIZE); + + // Read records + while(!f.atEnd()) { + Index buffer; + f.read((char*)&buffer, recordSize); + data.push_back(buffer); + } + + f.close(); + return true; + } +} + +// --- Write a dat file --- // +template bool Database::write(QString filename, + QVector &data, + int recordSize, + bool append) +{ + m_Progress->increment(filename); + + QFile f(m_Folder + filename); + + // Truncate if the append bool is false + if(append == false && !f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + m_FileErrorList << filename; + return false; + } + + // Append if the append bool is true + else if(append == true && !f.open(QIODevice::WriteOnly | QIODevice::Append)) { + m_FileErrorList << filename; + return false; + } + + // Write the data + else { + for(T itr : data) + f.write((char*)&itr, recordSize); + + f.close(); + return true; + } +} + +// --- Write an index.dat file --- // +bool Database::writeIndex(QString filename, QVector &data, int recordSize) +{ + m_Progress->increment(filename); + + QFile f(m_Folder + filename); + + if(!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + m_FileErrorList << filename; + return false; + } + + else { + // Write header + f.write((char*)&Index::s_Header, Index::INDEX_HEADER_SIZE); + + // Write records + for(Index itr : data) + f.write((char*)&itr, recordSize); + + f.close(); + return true; + } +} + + +/* ======================== */ +/* Error Checking */ +/* ======================== */ + +// --- Check for errors --- // +bool Database::errorCheck() +{ + if(m_FileErrorList.size() <= 0) + return true; + + QString message("The following file(s) could not be accessed:\n"); + + for(QString str : m_FileErrorList) + message.append("\n" + str); + + QMessageBox m; + m.setWindowTitle("File Error"); + m.setText(message); + m.setModal(true); + m.exec(); + + return false; +} + + +/* ============================ */ +/* File I/O: Database */ +/* ============================ */ + +// --- Load the database --- // +bool Database::load(const QString &folder) +{ + // Clear error list and set database folder path + m_FileErrorList.clear(); + m_Folder = folder; + m_Folder.remove("index.dat"); // Remove any reference to index.dat if this was included in the folder string + + // Progress dialog + m_Progress = new ProgressWindow(); + m_Progress->set("Loading Database", FILE_COUNT); + + // Read the data + this->readIndex("index.dat", Index::db, DB_INDEX_SIZE); + this->read("city.dat", City::db, DB_CITY_SIZE); + this->read("club.dat", Club::dbDom, DB_CLUB_SIZE); + this->read("club_comp.dat", ClubComp::dbDom, DB_CLUB_COMP_SIZE); + this->read("club_comp_history.dat", ClubCompHistory::dbDom, DB_CLUB_COMP_HISTORY_SIZE); + this->read("colour.dat", Colour::db, DB_COLOUR_SIZE); + this->read("common_names.dat", CommonName::db, DB_NAME_SIZE); + this->read("continent.dat", Continent::db, DB_CONTINENT_SIZE); + this->read("first_names.dat", FirstName::db, DB_NAME_SIZE); + this->read("nat_club.dat", Club::dbInt, DB_CLUB_SIZE); + this->read("nation.dat", Nation::db, DB_NATION_SIZE); + this->read("nation_comp.dat", ClubComp::dbInt, DB_CLUB_COMP_SIZE); + this->read("nation_comp_history.dat", ClubCompHistory::dbInt, DB_CLUB_COMP_HISTORY_SIZE); + this->read("officials.dat", Official::db, DB_OFFICIAL_SIZE); + this->read("second_names.dat", SecondName::db, DB_NAME_SIZE); + this->read("stadium.dat", Stadium::db, DB_STADIUM_SIZE); + this->read("staff.dat", Staff::db, DB_STAFF_SIZE, + Index::db[Index::STAFF_TABLE].getOffset(), Index::db[Index::STAFF_TABLE].getRecordCount()); + this->read("staff.dat", NonPlayer::db, DB_NON_PLAYER_SIZE, + Index::db[Index::NON_PLAYER_TABLE].getOffset(), Index::db[Index::NON_PLAYER_TABLE].getRecordCount()); + this->read("staff.dat", Player::db, DB_PLAYER_SIZE, + Index::db[Index::PLAYER_TABLE].getOffset(), Index::db[Index::PLAYER_TABLE].getRecordCount()); + this->read("staff.dat", StaffPreferences::db, DB_STAFF_PREFERENCES_SIZE, + Index::db[Index::STAFF_PREFERENCES_TABLE].getOffset(), Index::db[Index::STAFF_PREFERENCES_TABLE].getRecordCount()); + this->read("staff_comp.dat", StaffComp::db, DB_STAFF_COMP_SIZE); + this->read("staff_comp_history.dat", StaffCompHistory::db, DB_STAFF_COMP_HISTORY_SIZE); + this->read("staff_history.dat", StaffHistory::db, DB_STAFF_HISTORY_SIZE); + + // Validate the database data + this->validate(m_Progress); + + m_Progress->complete(); + delete m_Progress; + return this->errorCheck(); +} + +// --- Save the database --- // +bool Database::save(const QString &folder) +{ + // Clear error list and set database folder path + m_FileErrorList.clear(); + m_Folder = folder; + m_Folder.remove("index.dat"); // Remove any reference to index.dat if this was included in the folder string + + // Progress dialog + m_Progress = new ProgressWindow(); + m_Progress->set("Loading Database", FILE_COUNT); + + // Validate the database data + this->validate(m_Progress); + + // Write the data + this->writeIndex("index.dat", Index::db, DB_INDEX_SIZE); + this->write("city.dat", City::db, DB_CITY_SIZE); + this->write("club.dat", Club::dbDom, DB_CLUB_SIZE); + this->write("club_comp.dat", ClubComp::dbDom, DB_CLUB_COMP_SIZE); + this->write("club_comp_history.dat", ClubCompHistory::dbDom, DB_CLUB_COMP_HISTORY_SIZE); + this->write("colour.dat", Colour::db, DB_COLOUR_SIZE); + this->write("common_names.dat", CommonName::db, DB_NAME_SIZE); + this->write("continent.dat", Continent::db, DB_CONTINENT_SIZE); + this->write("first_names.dat", FirstName::db, DB_NAME_SIZE); + this->write("nat_club.dat", Club::dbInt, DB_CLUB_SIZE); + this->write("nation.dat", Nation::db, DB_NATION_SIZE); + this->write("nation_comp.dat", ClubComp::dbInt, DB_CLUB_COMP_SIZE); + this->write("nation_comp_history.dat", ClubCompHistory::dbInt, DB_CLUB_COMP_HISTORY_SIZE); + this->write("officials.dat", Official::db, DB_OFFICIAL_SIZE); + this->write("second_names.dat", SecondName::db, DB_NAME_SIZE); + this->write("stadium.dat", Stadium::db, DB_STADIUM_SIZE); + // Staff.dat tables must be written in the correct order: + this->write("staff.dat", Staff::db, DB_STAFF_SIZE, false); // 1) Staff (truncate) + this->write("staff.dat", NonPlayer::db, DB_NON_PLAYER_SIZE, true); // 2) Non-player attributes (append) + this->write("staff.dat", Player::db, DB_PLAYER_SIZE, true); // 3) Player attributes (append) + this->write("staff.dat", StaffPreferences::db, DB_STAFF_PREFERENCES_SIZE, true); // 4) Staff preferences (append) + this->write("staff_comp.dat", StaffComp::db, DB_STAFF_COMP_SIZE); + this->write("staff_comp_history.dat", StaffCompHistory::db, DB_STAFF_COMP_HISTORY_SIZE); + this->write("staff_history.dat", StaffHistory::db, DB_STAFF_HISTORY_SIZE); + + m_Progress->complete(); + delete m_Progress; + return this->errorCheck(); +} diff --git a/database/database.h b/database/database.h new file mode 100644 index 0000000..4a31bbd --- /dev/null +++ b/database/database.h @@ -0,0 +1,75 @@ +#ifndef DATABASE_H +#define DATABASE_H + +#include +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +// Database headers +#include "index.h" + +// UI headers +#include "progress_window.h" + +// --- Database --- // +class Database +{ +private: + // Error checking + bool errorCheck(); + + // File I/O: Dat File + template bool read(QString filename, QVector &data, int recordSize, int skipBytes = 0, int recordCount = -1); + bool readIndex(QString filename, QVector &data, int recordSize); + template bool write(QString filename, QVector &data, int recordSize, bool append = false); + bool writeIndex(QString filename, QVector &data, int recordSize); + + // Progress dialog + ProgressWindow *m_Progress; + + // Struct lengths + enum ENUM_TABLE_SIZES { + DB_CITY_SIZE = 56, + DB_CLUB_SIZE = 581, + DB_CLUB_COMP_SIZE = 107, + DB_CLUB_COMP_HISTORY_SIZE = 26, + DB_COLOUR_SIZE = 58, + DB_CONTINENT_SIZE = 198, + DB_INDEX_SIZE = 67, + DB_NAME_SIZE = 60, + DB_NATION_SIZE = 290, + DB_NON_PLAYER_SIZE = 68, + DB_OFFICIAL_SIZE = 43, + DB_PLAYER_SIZE = 70, + DB_STADIUM_SIZE = 78, + DB_STAFF_SIZE = 110, + DB_STAFF_COMP_SIZE = 101, + DB_STAFF_COMP_HISTORY_SIZE = 58, + DB_STAFF_HISTORY_SIZE = 17, + DB_STAFF_PREFERENCES_SIZE = 52, + }; + + // Database file count (index.dat + 22 tables) + enum ENUM_FILE_COUNT { FILE_COUNT = 23 }; + +public: + // Constructor + Database(); + ~Database(); + + // Data validation + void validate(ProgressWindow *p = nullptr); + + // File I/O: Database + bool load(const QString &folder); + bool save(const QString &folder); + + // File I/O (members) + QStringList m_FileErrorList; + QString m_Folder; +}; + +#endif // DATABASE_H diff --git a/database/index.cpp b/database/index.cpp new file mode 100644 index 0000000..30db803 --- /dev/null +++ b/database/index.cpp @@ -0,0 +1,52 @@ +#include "index.h" +#include "data_types/string.h" + +// --- Static data --- // +QVector Index::db; +char Index::s_Header[INDEX_HEADER_SIZE]; + + +/* ==================== */ +/* Index Data */ +/* ==================== */ + +// --- Default constructor --- // +Index::Index() +{ + +} + + +/* ================= */ +/* Get Data */ +/* ================= */ + +// --- File Name --- // +QString Index::getFileName() +{ + return String(Filename).get(); +} + +// --- Offset --- // +int Index::getOffset() +{ + return Offset; +} + +// --- Record count --- // +int Index::getRecordCount() +{ + return TableSize; +} + + +/* ================= */ +/* Set Data */ +/* ================= */ + +// --- Count/size and offset --- // +void Index::set(const int size, const int offset) +{ + TableSize = size; + Offset = offset; +} diff --git a/database/index.h b/database/index.h new file mode 100644 index 0000000..ee53c36 --- /dev/null +++ b/database/index.h @@ -0,0 +1,74 @@ +#ifndef INDEX_H +#define INDEX_H + +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) +#include "data_types/data_types.h" + +// Forward declare model so that it can be a friend of the class +class IndexModel; + +// --- Index.dat --- // +class Index +{ +private: + char Filename[ MAX_INDEX_NAME + 1 ]; + int FileId; + int TableSize; + int Offset; + int Version; + +public: + // Index header + enum ENUM_HEADER_SIZE { INDEX_HEADER_SIZE = 8 }; + static char s_Header[INDEX_HEADER_SIZE]; + + // Constructor + Index(); + + // Container + static QVector db; + + // Get data + QString getFileName(); + int getOffset(); + int getRecordCount(); + + // Set data + void set(const int size, const int offset = 0); + + // Friend class + friend class IndexModel; + + enum ENUM_INDEX_POSITIONS { + CLUB_TABLE, + NATION_CLUBS_TABLE, + COLOUR_TABLE, + CONTINENT_TABLE, + NATION_TABLE, + OFFICIALS_TABLE, + STADIUM_TABLE, + STAFF_TABLE, + //YOUTH_PLAYER_TABLE = 8, // Not used + NON_PLAYER_TABLE, + PLAYER_TABLE, + STAFF_COMP_TABLE, + CITY_TABLE, + CLUB_COMP_TABLE, + NATION_COMP_TABLE, + FIRST_NAME_TABLE, + SECOND_NAME_TABLE, + COMMON_NAME_TABLE, + STAFF_HISTORY_TABLE, + STAFF_COMP_HISTORY_TABLE, + CLUB_COMP_HISTORY_TABLE, + NATION_COMP_HISTORY_TABLE, + STAFF_PREFERENCES_TABLE, // MUX with youth player table + INDEX_TABLE_COUNT + }; +}; + +#endif // INDEX_H diff --git a/database/name.cpp b/database/name.cpp new file mode 100644 index 0000000..35bfbee --- /dev/null +++ b/database/name.cpp @@ -0,0 +1,214 @@ +#include "name.h" + +// --- Static data --- // +QVector CommonName::db; +QVector FirstName::db; +QVector SecondName::db; + + +/* =================== */ +/* Name Data */ +/* =================== */ + +// --- Default constructor (base class) --- // +Name::Name() : + Count(0) +{ + StringText.reserve(STANDARD_TEXT_LENGTH); +} + +// --- Default constructor (Common name) --- // +CommonName::CommonName() : + Name() +{ + ID = CommonName::db.size(); // Allocate next ID to item +} + +// --- Default constructor (First name) --- // +FirstName::FirstName() : + Name() +{ + ID = FirstName::db.size(); // Allocate next ID to item +} + +// --- Default constructor (Second name) --- // +SecondName::SecondName() : + Name() +{ + ID = SecondName::db.size(); // Allocate next ID to item +} + + +/* ================== */ +/* Counters */ +/* ================== */ + +// --- Decrement count --- // +void Name::decrementCount() +{ + --Count; +} + +// --- Increment count --- // +void Name::incrementCount() +{ + if(Count < 127) + ++Count; +} + +// --- Reset count --- // +void Name::resetCount() +{ + Count = 0; +} + +// --- Reset all name counters --- // +void CommonName::resetCounters() +{ + for(int i = 0; i < db.size(); ++i) + db[i].resetCount(); +} + +// --- Reset all name counters --- // +void FirstName::resetCounters() +{ + for(int i = 0; i < db.size(); ++i) + db[i].resetCount(); +} + +// --- Reset all name counters --- // +void SecondName::resetCounters() +{ + for(int i = 0; i < db.size(); ++i) + db[i].resetCount(); +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Get name text for models --- // +QString Name::getName() +{ + return StringText; +} + + +/* ================== */ +/* Set Data */ +/* ================== */ + +// --- Set name (and add if not present) --- // +int CommonName::set(const QString &name, const int &nation) +{ + // Skip any blank names + if(name.isEmpty()) + { + return -1; + } + + // Check for pre-existing name + const int sz = CommonName::db.size(); + for(int i = 0; i < sz; ++i) + { + if(CommonName::db[i].StringText == name && CommonName::db[i].ID == nation) + return i; + } + + // Add as a new name + CommonName n; + n.Nation.set(nation); + + String str(name); + str.toSIFont(n.NameText, STANDARD_TEXT_LENGTH); + n.StringText = str.get(); + + CommonName::db.push_back(n); + return n.ID; +} + +// --- Set name (and add if not present) --- // +int FirstName::set(const QString &name, const int &nation) +{ + // Skip any blank names + if(name.isEmpty()) + { + return -1; + } + + // Check for pre-existing name + const int sz = FirstName::db.size(); + for(int i = 0; i < sz; ++i) + { + if(FirstName::db[i].StringText == name && FirstName::db[i].Nation == nation) + return i; + } + + // Add as a new name + FirstName n; + n.Nation.set(nation); + + String str(name); + str.toSIFont(n.NameText, STANDARD_TEXT_LENGTH); + n.StringText = str.get(); + + FirstName::db.push_back(n); + return n.ID; +} + +// --- Set name (and add if not present) --- // +int SecondName::set(const QString &name, const int &nation) +{ + // Skip any blank names + if(name.isEmpty()) + { + return -1; + } + + // Check for pre-existing name + const int sz = SecondName::db.size(); + for(int i = 0; i < sz; ++i) + { + if(SecondName::db[i].StringText == name && SecondName::db[i].Nation == nation) + return i; + } + + // Add as a new name + SecondName n; + n.Nation.set(nation); + + String str(name); + str.toSIFont(n.NameText, STANDARD_TEXT_LENGTH); + n.StringText = str.get(); + + SecondName::db.push_back(n); + return n.ID; +} + +// --- Set string text for each DB item --- // +void CommonName::setStringText() +{ + const int sz = db.size(); + for(int i = 0; i < sz; ++i) { + db[i].StringText = String(db[i].NameText).get(); + } +} + +// --- Set string text for each DB item --- // +void FirstName::setStringText() +{ + const int sz = db.size(); + for(int i = 0; i < sz; ++i) { + db[i].StringText = String(db[i].NameText).get(); + } +} + +// --- Set string text for each DB item --- // +void SecondName::setStringText() +{ + const int sz = db.size(); + for(int i = 0; i < sz; ++i) { + db[i].StringText = String(db[i].NameText).get(); + } +} diff --git a/database/name.h b/database/name.h new file mode 100644 index 0000000..a286764 --- /dev/null +++ b/database/name.h @@ -0,0 +1,94 @@ +#ifndef NAME_H +#define NAME_H + +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/string.h" +#include "../data_types/data_types.h" +#include "../data_types/ptr_nation.h" + + +// --- Name base class --- // +class Name +{ +protected: + char NameText[ STANDARD_TEXT_LENGTH ]; + int ID; + PtrNation Nation; + char Count; + + // Additional data + QString StringText; // QString representation of the raw NameText char array + +public: + // Constructor + Name(); + + // Counters + void decrementCount(); + void incrementCount(); + void resetCount(); + + // Get data + QString getName(); +}; + +// --- Common_names.dat --- // +class CommonName : public Name +{ +public: + // Constructor + CommonName(); + + // Containers + static QVector db; + + // Counters + static void resetCounters(); + + // Set data + static int set(const QString &name, const int &nation); + static void setStringText(); +}; + +// --- First_names.dat --- // +class FirstName : public Name +{ +public: + // Constructor + FirstName(); + + // Containers + static QVector db; + + // Counters + static void resetCounters(); + + // Set data + static int set(const QString &name, const int &nation); + static void setStringText(); +}; + +// --- Second_names.dat --- // +class SecondName : public Name +{ +public: + // Constructor + SecondName(); + + // Containers + static QVector db; + + // Counters + static void resetCounters(); + + // Set data + static int set(const QString &name, const int &nation); + static void setStringText(); +}; + +#endif // NAME_H diff --git a/database/nation.cpp b/database/nation.cpp new file mode 100644 index 0000000..c46b4a9 --- /dev/null +++ b/database/nation.cpp @@ -0,0 +1,113 @@ +#include "nation.h" +#include "../data_types/string.h" + +// --- Static data --- // +QVector Nation::db; + + +/* ===================== */ +/* Nation Data */ +/* ===================== */ + +// --- Default constructor --- // +Nation::Nation() +{ +} + + +/* ================== */ +/* Counters */ +/* ================== */ + +// --- Decrement club count --- // +void Nation::decrementClubCount() +{ + --NumberClubs; +} + +// --- Decrement staff count --- // +void Nation::decrementStaffCount() +{ + --NumberStaff; +} + +// --- Increment club count --- // +void Nation::incrementClubCount() +{ + ++NumberClubs; +} + +// --- Increment staff count --- // +void Nation::incrementStaffCount() +{ + ++NumberStaff; +} + +// --- Reset club count --- // +void Nation::resetClubCount() +{ + NumberClubs = 0; +} + +// --- Reset staff count --- // +void Nation::resetStaffCount() +{ + NumberStaff = 0; +} + +// --- Update all club and staff counts --- // +void Nation::resetCounters() +{ + // Reset all counts + for(Nation &itr : db) { + itr.NumberClubs = 0; + itr.NumberStaff = 0; + } +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Get name text --- // +QString Nation::getLongName() +{ + return String(Name).get(); +} + +// --- Get short name text --- // +QString Nation::getShortName() +{ + return String(NameShort).get(); +} + + +/* ==================== */ +/* Match Data */ +/* ==================== */ + +// --- Populate a hash with nation data --- // +void Nation::createHash(QHash &hash, bool useLongNames) +{ + // For speed, the loops are embedded within the if statement + // (rather than embedding the if statements within a loop) + + // Long names + if(useLongNames) { + for(Nation itr : db) { + hash.insert(String(itr.Name).getMatchString(), itr.ID); + } + } + // Short names + else { + for(Nation itr : db) { + hash.insert(String(itr.NameShort).getMatchString(), itr.ID); + } + } + + // Add no nation text (text must be lowercase + hash.insert("", NO_NATION); + hash.insert("-", NO_NATION); + hash.insert("_none", NO_NATION); +} diff --git a/database/nation.h b/database/nation.h new file mode 100644 index 0000000..040e53a --- /dev/null +++ b/database/nation.h @@ -0,0 +1,97 @@ +#ifndef NATION_H +#define NATION_H + +#include +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/data_types.h" + +// --- Nation.dat --- // +class Nation +{ +private: + // Database data + int ID; + char Name[ STANDARD_TEXT_LENGTH ]; + char GenderName; + char NameShort[ SHORT_TEXT_LENGTH ]; + char GenderNameShort; + char NameThreeLetter[ 4 ]; + char NameNationality[ SHORT_TEXT_LENGTH ]; + int Continent; + char Region; + char ActualRegion; + char FirstLanguage; + char SecondLanguage; + char ThirdLanguage; + int CapitalCity; + char StateOfDevelopment; + char GroupMembership; + int NationalStadium; + char GameImportance; + char LeagueStandard; + short NumberClubs; + int NumberStaff; // Version 0x02 - Added + short SeasonUpdateDay; // Version 0x02 - Added + short Reputation; // Version 0x02 - Changed char->short + int ForegroundColour1; + int BackgroundColour1; + int ForegroundColour2; + int BackgroundColour2; + int ForegroundColour3; + int BackgroundColour3; + double FIFACoefficient; + double FIFACoefficient91; + double FIFACoefficient92; + double FIFACoefficient93; + double FIFACoefficient94; + double FIFACoefficient95; + double FIFACoefficient96; + double UEFACoefficient91; + double UEFACoefficient92; + double UEFACoefficient93; + double UEFACoefficient94; + double UEFACoefficient95; + double UEFACoefficient96; + int Rivals1; + int Rivals2; + int Rivals3; + + // Runtime data + char LeagueSelected; + int ShortlistOffset; // Version 0x02 - Added + char GamesPlayed; // Version 0x02 - Moved to runtime + +public: + // Constructor + Nation(); + + // Container + static QVector db; + + // Club counter + void decrementClubCount(); + void incrementClubCount(); + void resetClubCount(); + + // Staff counter + void decrementStaffCount(); + void incrementStaffCount(); + void resetStaffCount(); + + // Counters + static void resetCounters(); + + // Get data + QString getLongName(); + QString getShortName(); + + // Match data + static void createHash(QHash &hash, bool useLongNames = true); +}; + +#endif // NATION_H diff --git a/database/non_player.cpp b/database/non_player.cpp new file mode 100644 index 0000000..a05c866 --- /dev/null +++ b/database/non_player.cpp @@ -0,0 +1,14 @@ +#include "non_player.h" + +// --- Static data --- // +QVector NonPlayer::db; + + +/* ========================= */ +/* Non Player Data */ +/* ========================= */ + +// --- Default constructor --- // +NonPlayer::NonPlayer() +{ +} diff --git a/database/non_player.h b/database/non_player.h new file mode 100644 index 0000000..b46022c --- /dev/null +++ b/database/non_player.h @@ -0,0 +1,67 @@ +#ifndef NON_PLAYER_H +#define NON_PLAYER_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/attribute.h" +#include "../data_types/data_types.h" + +// Forward declaration for purposes of friend class +class Staff; + +// --- Staff.dat - Non-player attributes --- // +class NonPlayer +{ +private: + int ID; + short CurrentAbility; + short PotentialAbility; + short HomeReputation; // Version 0x02 - Changed char->short + short CurrentReputation; // Version 0x02 - Changed char->short + short WorldReputation; // Version 0x02 - Changed char->short + Attribute Attacking; + Attribute Business; + Attribute Coaching; + Attribute CoachingGks; + Attribute CoachingTechnique; + Attribute Directness; + Attribute Discipline; + Attribute FreeRoles; + Attribute Interference; + Attribute Judgement; + Attribute JudgingPotential; + Attribute ManHandling; + Attribute Marking; + Attribute Motivating; + Attribute Offside; + Attribute Patience; + Attribute Physiotherapy; + Attribute Pressing; + Attribute Resources; + Attribute Tactics; + Attribute Youngsters; + int Goalkeeper; + int Sweeper; + int Defender; + int DefensiveMidfielder; + int Midfielder; + int AttackingMidfielder; + int Attacker; + int WingBack; + char FormationPreferred; + +public: + // Constructor + NonPlayer(); + + // Container + static QVector db; + + // Friend class + friend class Staff; +}; + +#endif // NON_PLAYER_H diff --git a/database/official.cpp b/database/official.cpp new file mode 100644 index 0000000..379c3ed --- /dev/null +++ b/database/official.cpp @@ -0,0 +1,34 @@ +#include "official.h" + +// --- Static data --- // +QVector Official::db; + + +/* ======================== */ +/* Officials Data */ +/* ======================== */ + +// --- Default constructor --- // +Official::Official() +{ +} + + +/* ========================= */ +/* Data Validation */ +/* ========================= */ + +// --- Validate data --- // +void Official::validate() +{ + const int sz = db.size(); + + for(int i = 0; i < sz; ++i) { + // Validate year of birth + db[i].YearOfBirth.validate(db[i].DateOfBirth.year()); + + // Name counters + db[i].FirstName.increment(); + db[i].SecondName.increment(); + } +} diff --git a/database/official.h b/database/official.h new file mode 100644 index 0000000..83a940e --- /dev/null +++ b/database/official.h @@ -0,0 +1,48 @@ +#ifndef OFFICIAL_H +#define OFFICIAL_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/data_types.h" +#include "../data_types/date.h" +#include "../data_types/ptr_name.h" +#include "../data_types/ptr_nation.h" +#include "../data_types/year.h" + +// --- Colour.dat --- // +class Official +{ +private: + int ID; + PtrFirstName FirstName; + PtrSecondName SecondName; + Date DateOfBirth; + Year YearOfBirth; + PtrNation Nation; + int City; + short CurrentAbility; + short PotentialAbility; + short Reputation; + char AllowingFlow; + char Discipline; + char ImportantMatches; + char Pressure; + char Refereeing; + char RunningLine; + char Timekeeping; + +public: + // Constructor + Official(); + + // Container + static QVector db; + + // Data validation + static void validate(); +}; + +#endif // OFFICIAL_H diff --git a/database/player.cpp b/database/player.cpp new file mode 100644 index 0000000..58ec873 --- /dev/null +++ b/database/player.cpp @@ -0,0 +1,44 @@ +#include "player.h" + +// --- Static data --- // +QVector Player::db; + + +/* ===================== */ +/* Player Data */ +/* ===================== */ + +// --- Default constructor --- // +Player::Player() : + ID(db.size()) // Allocate next ID to item +{ + // Default squad number + SquadNumber.set(0); + + // Default ability + CurrentAbility.set(0); + PotentialAbility.set(0); + + // Default positional ratings + Goalkeeper.set(1); + Sweeper.set(1); + Defender.set(1); + DefensiveMidfielder.set(1); + Midfielder.set(1); + AttackingMidfielder.set(1); + Attacker.set(1); + WingBack.set(1); +} + + +/* ================== */ +/* Add Data */ +/* ================== */ + +// --- Add new entry --- // +int Player::addNewItem() +{ + Player tmp; + db.push_back(tmp); + return tmp.ID; +} diff --git a/database/player.h b/database/player.h new file mode 100644 index 0000000..30a2ee8 --- /dev/null +++ b/database/player.h @@ -0,0 +1,104 @@ +#ifndef PLAYER_H +#define PLAYER_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/attribute.h" +#include "../data_types/ability.h" +#include "../data_types/data_types.h" +#include "../data_types/player_squad_number.h" +#include "../data_types/reputation.h" + +// Forward declaration for purposes of friend class +class Staff; +class TransferImporter; + +// --- Staff.dat - Player attributes --- // +class Player +{ +private: + // Database data + int ID; + PlayerSquadNumber SquadNumber; + AbilityCurrent CurrentAbility; + AbilityPotential PotentialAbility; + Reputation HomeReputation; // Version 0x02 - Changed char->short + Reputation CurrentReputation; // Version 0x02 - Changed char->short + Reputation WorldReputation; // Version 0x02 - Changed char->short + Attribute Goalkeeper; + Attribute Sweeper; + Attribute Defender; + Attribute DefensiveMidfielder; + Attribute Midfielder; + Attribute AttackingMidfielder; + Attribute Attacker; + Attribute WingBack; + Attribute RightSide; + Attribute LeftSide; + Attribute Central; + Attribute FreeRole; + Attribute Acceleration; + Attribute Aggression; + Attribute Agility; + Attribute Anticipation; + Attribute Balance; + Attribute Bravery; // Value + Attribute Consistency; + Attribute Corners; + Attribute Crossing; + Attribute Decisions; + Attribute Dirtiness; + Attribute Dribbling; + Attribute Finishing; + Attribute Flair; + Attribute FreeKicks; + Attribute Handling; + Attribute Heading; + Attribute ImportantMatches; + Attribute InjuryProneness; + Attribute Jumping; + Attribute Leadership; + Attribute LeftFoot; + Attribute LongShots; + Attribute Marking; + Attribute Movement; + Attribute NaturalFitness; + Attribute OneOnOnes; + Attribute Pace; + Attribute Passing; + Attribute Penalties; + Attribute Positioning; + Attribute Reflexes; + Attribute RightFoot; + Attribute Stamina; + Attribute Strength; + Attribute Tackling; + Attribute Teamwork; + Attribute Technique; + Attribute ThrowIns; + Attribute Versatility; + Attribute Vision; + Attribute WorkRate; + + // runtime data + Attribute Morale; + +public: + // Constructor + Player(); + + // Container + static QVector db; + + // Friend class + friend class Staff; + friend class TransferImporter; + + // Add data + static int addNewItem(); +}; + +#endif // PLAYER_H diff --git a/database/stadium.cpp b/database/stadium.cpp new file mode 100644 index 0000000..eca3b14 --- /dev/null +++ b/database/stadium.cpp @@ -0,0 +1,14 @@ +#include "stadium.h" + +// --- Static data --- // +QVector Stadium::db; + + +/* ====================== */ +/* Stadium Data */ +/* ====================== */ + +// --- Default constructor --- // +Stadium::Stadium() +{ +} diff --git a/database/stadium.h b/database/stadium.h new file mode 100644 index 0000000..9131a2d --- /dev/null +++ b/database/stadium.h @@ -0,0 +1,34 @@ +#ifndef STADIUM_H +#define STADIUM_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/data_types.h" + +// --- Stadium.dat --- // +class Stadium +{ +private: + int ID; + char Name[ STANDARD_TEXT_LENGTH ]; + char GenderName; + int City; + int Capacity; + int SeatingCapacity; + int ExpansionCapacity; + int NearbyStadium; + char Covered; + char UnderSoilHeating; + +public: + // Constructor + Stadium(); + + // Container + static QVector db; +}; + +#endif // STADIUM_H diff --git a/database/staff.cpp b/database/staff.cpp new file mode 100644 index 0000000..63821a1 --- /dev/null +++ b/database/staff.cpp @@ -0,0 +1,303 @@ +#include "staff.h" +#include + +// --- Static data --- // +QVector Staff::db; + + +/* ==================== */ +/* Staff Data */ +/* ==================== */ + +// --- Default constructor --- // +Staff::Staff() : + ID(db.size()) // Allocate next ID to item +{ + // Default to a player type + Classification.set(StaffClassification::PLAYER); + JobForClub.set(Job::PLAYER); + PlayingSquad.set(Squad::CLUB_SENIOR_SQUAD); +} + + +/* ========================= */ +/* Data Validation */ +/* ========================= */ + +// --- Validate data --- // +void Staff::validate() +{ + const int sz = db.size(); + + for(int i = 0; i < sz; ++i) { + // Validate year of birth + db[i].YearOfBirth.validate(db[i].DateOfBirth.year()); + + // Name counters + db[i].CommonName.increment(); + db[i].FirstName.increment(); + db[i].SecondName.increment(); + + // Nation counters + db[i].Nation.incrementStaffCount(); + + // Club rosters + db[i].ClubContracted.addToRoster(i, db[i].JobForClub); + db[i].NationContracted.addToRoster(i, db[i].JobForNation); + } +} + + +/* ===================== */ +/* Export Data */ +/* ===================== */ + +// --- Export staff list with extended data --- // +bool Staff::exportExtendedList(Spreadsheet &s) +{ + // Header + QStringList header; + header << "First name" << "Second name" << "Common name" << "DOB" + << "Nation1" << "Nation2" + << "Date joined club" << "Contract end" + << "Est. Value" + << "Club" + << "GK" << "SW" << "D" << "DM" << "M" << "AM" << "ST" << "WB" << "FR" + << "Right" << "Left" << "Center" + << "Right Foot" << "Left Foot" + << "Int Caps" << "Int Goals" + << "Current Ability" << "Potential Ability" + << "World Reputation" << "Home Reputation" << "Current Reputation" + << "Squad no."; + + s.addHeader(header); + + // Temp data match text settings + int yearAdjustment = 0; + bool useFullDate = true; + + // Rows + for(Staff &itr : db) { + QStringList row; + + // Common staff data + row << itr.FirstName.getSafeText() << itr.SecondName.getSafeText() << itr.CommonName.getSafeText() + << itr.DateOfBirth.getMatchText(yearAdjustment, useFullDate) + << itr.Nation.getSafeText() << itr.SecondNation.getSafeText() + << itr.DateJoinedClub.getMatchText(yearAdjustment, useFullDate) << itr.ContractExpiresClub.getMatchText(yearAdjustment, useFullDate) + << itr.EstimatedValue.getText() + << itr.ClubContracted.getSafeText(); + + // Player data + Player *p = itr.PlayerData.data(); + + if(p != nullptr) { + row << p->Goalkeeper.getText() << p->Sweeper.getText() << p->Defender.getText() + << p->DefensiveMidfielder.getText() << p->Midfielder.getText() << p->AttackingMidfielder.getText() + << p->Attacker.getText() << p->WingBack.getText() << p->FreeRole.getText() + << p->RightSide.getText() << p->LeftSide.getText() << p->Central.getText() + << p->RightFoot.getText() << p->LeftFoot.getText(); + } + else { + row << "" << "" << "" << "" << "" << "" << "" << "" << "" + << "" << "" << "" + << "" << ""; + } + + // International data + row << itr.InternationalApps.getText() << itr.InternationalGoals.getText(); + + // Remaining player data + if(p != nullptr) { + row << p->CurrentAbility.getText() << p->PotentialAbility.getText() + << p->WorldReputation.getText() << p->HomeReputation.getText() << p->CurrentReputation.getText() + << p->SquadNumber.getText(); + } + + s.add(row); + } + + return true; +} + +// --- Export staff list in the form of a Transfer Importer spreadsheet --- // +bool Staff::exportTransferList(Spreadsheet &s) +{ + // Header + QStringList header; + header << "First name" << "Second name" << "Common name" << "DOB" + << "Nation1" << "Nation2" + << "Contract start" << "Contract end" + << "Value" + << "Club" << "On loan from" + << "GK" << "SW" << "D" << "DM" << "M" << "AM" << "ST" << "WB" << "FR" + << "Right" << "Left" << "Center" + << "Right Foot" << "Left Foot" + << "Int Caps" << "Int Goals" + << "Current Ability" << "Potential Ability" + << "Home Reputation" << "Current Reputation" + << "Squad no."; + + s.addHeader(header); + + // Temp data match text settings + int yearAdjustment = 0; + bool useFullDate = true; + + // Rows + for(Staff &itr : db) { + QStringList row; + + // Common staff data + row << itr.FirstName.getSafeText() << itr.SecondName.getSafeText() << itr.CommonName.getSafeText() + << itr.DateOfBirth.getMatchText(yearAdjustment, useFullDate) + << itr.Nation.getSafeText() << itr.SecondNation.getSafeText() + << itr.DateJoinedClub.getMatchText(yearAdjustment, useFullDate) << itr.ContractExpiresClub.getMatchText(yearAdjustment, useFullDate) + << "" + << itr.ClubContracted.getSafeText() << ""; + + // Player data + Player *p = itr.PlayerData.data(); + + if(p != nullptr) { + row << p->Goalkeeper.getText() << p->Sweeper.getText() << p->Defender.getText() + << p->DefensiveMidfielder.getText() << p->Midfielder.getText() << p->AttackingMidfielder.getText() + << p->Attacker.getText() << p->WingBack.getText() << p->FreeRole.getText() + << p->RightSide.getText() << p->LeftSide.getText() << p->Central.getText() + << p->RightFoot.getText() << p->LeftFoot.getText(); + } + else { + row << "" << "" << "" << "" << "" << "" << "" << "" << "" + << "" << "" << "" + << "" << ""; + } + + // International data + row << itr.InternationalApps.getText() << itr.InternationalGoals.getText(); + + // Remaining player data + if(p != nullptr) { + row << p->CurrentAbility.getText() << p->PotentialAbility.getText() + << p->HomeReputation.getText() << p->CurrentReputation.getText() + << p->SquadNumber.getText(); + } + + s.add(row); + } + + return true; +} + +// --- Export staff list in the form of a Transfer Importer spreadsheet (NO ACCENTS) --- // +bool Staff::exportTransferList_NoAccents(Spreadsheet &s) +{ + // Header + QStringList header; + header << "First name" << "Second name" << "Common name" << "DOB" + << "Nation1" << "Nation2" + << "Contract start" << "Contract end" + << "Value" + << "Club" << "On loan from" + << "GK" << "SW" << "D" << "DM" << "M" << "AM" << "ST" << "WB" << "FR" + << "Right" << "Left" << "Center" + << "Right Foot" << "Left Foot" + << "Int Caps" << "Int Goals" + << "Current Ability" << "Potential Ability" + << "Home Reputation" << "Current Reputation" + << "Squad no."; + + s.addHeader(header); + + // Temp data match text settings + int yearAdjustment = 0; + bool useFullDate = true; + + // Rows + for(Staff &itr : db) { + QStringList row; + + // Common staff data + row << itr.FirstName.getNoAccentsText() << itr.SecondName.getNoAccentsText() << itr.CommonName.getNoAccentsText() + << itr.DateOfBirth.getMatchText(yearAdjustment, useFullDate) + << itr.Nation.getSafeText() << itr.SecondNation.getSafeText() + << itr.DateJoinedClub.getMatchText(yearAdjustment, useFullDate) << itr.ContractExpiresClub.getMatchText(yearAdjustment, useFullDate) + << "" + << itr.ClubContracted.getNoAccentsText() << ""; + + // Player data + Player *p = itr.PlayerData.data(); + + if(p != nullptr) { + row << p->Goalkeeper.getText() << p->Sweeper.getText() << p->Defender.getText() + << p->DefensiveMidfielder.getText() << p->Midfielder.getText() << p->AttackingMidfielder.getText() + << p->Attacker.getText() << p->WingBack.getText() << p->FreeRole.getText() + << p->RightSide.getText() << p->LeftSide.getText() << p->Central.getText() + << p->RightFoot.getText() << p->LeftFoot.getText(); + } + else { + row << "" << "" << "" << "" << "" << "" << "" << "" << "" + << "" << "" << "" + << "" << ""; + } + + // International data + row << itr.InternationalApps.getText() << itr.InternationalGoals.getText(); + + // Remaining player data + if(p != nullptr) { + row << p->CurrentAbility.getText() << p->PotentialAbility.getText() + << p->HomeReputation.getText() << p->CurrentReputation.getText() + << p->SquadNumber.getText(); + } + + s.add(row); + } + + return true; +} + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Get data --- // +QString Staff::getDisplayText() +{ + if(this->CommonName.isNone()) + return QString("%1 %2").arg(this->FirstName.getText()).arg(this->SecondName.getText()); + else + return this->CommonName.getText(); +} + + +/* ==================== */ +/* Match Data */ +/* ==================== */ + +// --- Populate a hash with staff data (with a main dob hash and a failsafe yob hash) --- // +void Staff::createHash(QHash &dobHash, QHash &yobHash, unsigned char scope, + int yearAdjustment, bool useFullDateOfBirth) +{ + for(Staff itr : db) { + // Only add staff within the scope + if( (scope & SCOPE_PLAYERS && itr.Classification.isPlayer()) || + (scope & SCOPE_NON_PLAYERS && itr.Classification.isNonPlayer())) { + + // Date of birth + QString dob = itr.DateOfBirth.getMatchText(yearAdjustment, useFullDateOfBirth); + QString yob = itr.YearOfBirth.getMatchText(yearAdjustment); + + // Add common name if available + if(itr.CommonName.isPointer()) { + dobHash.insert(QString("%1 %2").arg(itr.CommonName.getMatchText()).arg(dob), itr.ID); + yobHash.insert(QString("%1 %2").arg(itr.CommonName.getMatchText()).arg(yob), itr.ID); + } + + // Add full name if available + if(itr.FirstName.isPointer() && itr.SecondName.isPointer()) { + dobHash.insert(QString("%1 %2 %3").arg(itr.FirstName.getMatchText()).arg(itr.SecondName.getMatchText()).arg(dob), itr.ID); + yobHash.insert(QString("%1 %2 %3").arg(itr.FirstName.getMatchText()).arg(itr.SecondName.getMatchText()).arg(yob), itr.ID); + } + } + } +} diff --git a/database/staff.h b/database/staff.h new file mode 100644 index 0000000..97522d7 --- /dev/null +++ b/database/staff.h @@ -0,0 +1,105 @@ +#ifndef STAFF_H +#define STAFF_H + +#include +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/attribute.h" +#include "../data_types/cm_int.h" +#include "../data_types/cm_uchar.h" +#include "../data_types/data_types.h" +#include "../data_types/date.h" +#include "../data_types/job.h" +#include "../data_types/ptr_club.h" +#include "../data_types/ptr_name.h" +#include "../data_types/ptr_nation.h" +#include "../data_types/ptr_non_player.h" +#include "../data_types/ptr_player.h" +#include "../data_types/ptr_preferences.h" +#include "../data_types/squad.h" +#include "../data_types/staff_classification.h" +#include "../data_types/year.h" +#include "../data_types/wage.h" +#include "../spreadsheet/spreadsheet.h" + +// Forward declaration for purposes of friend class +class TransferImporter; + +// --- Staff.dat - Staff --- // +class Staff +{ +private: + // Database data + int ID; + PtrFirstName FirstName; + PtrSecondName SecondName; + PtrCommonName CommonName; + Date DateOfBirth; + Year YearOfBirth; + PtrNation Nation; + PtrNation SecondNation; + CM_UChar InternationalApps; + CM_UChar InternationalGoals; + PtrClub NationContracted; + Job JobForNation; + Date DateJoinedNation; + Date ContractExpiresNation; + PtrClub ClubContracted; + Job JobForClub; + Date DateJoinedClub; + Date ContractExpiresClub; + Wage EstimatedWage; + CM_Int EstimatedValue; + Attribute Adaptability; + Attribute Ambition; + Attribute Determination; + Attribute Loyalty; + Attribute Pressure; + Attribute Professionalism; + Attribute Sportsmanship; + Attribute Temperament; + Squad PlayingSquad; + StaffClassification Classification; + CM_Char ClubValuation; // This is possibly a 0-20 range + PtrPlayer PlayerData; + PtrPreferences Preferences; // Version 0x02 - New ptr type + PtrNonPlayer NonPlayerData; + + // Runtime data + char EuroSquadFlag; + +public: + // Constructor + Staff(); + + // Container + static QVector db; + + // Data validation + static void validate(); + + // Export data + static bool exportExtendedList(Spreadsheet &s); + static bool exportTransferList(Spreadsheet &s); + static bool exportTransferList_NoAccents(Spreadsheet &s); + + // Get data + QString getDisplayText(); + + // Match data + static void createHash(QHash &dobHash, QHash &yobHash, unsigned char scope, + int yearAdjustment = 0, bool useFullDateOfBirth = true); + + enum ENUM_MATCH_SCOPE { + SCOPE_NON_PLAYERS = 1, + SCOPE_PLAYERS = SCOPE_NON_PLAYERS * 2 // Use powers of two for effective bitwise operators + }; + + // Friend class + friend class TransferImporter; +}; + +#endif // STAFF_H diff --git a/database/staff_comp.cpp b/database/staff_comp.cpp new file mode 100644 index 0000000..ed0d598 --- /dev/null +++ b/database/staff_comp.cpp @@ -0,0 +1,14 @@ +#include "staff_comp.h" + +// --- Static data --- // +QVector StaffComp::db; + + +/* ================================ */ +/* Staff Competition Data */ +/* ================================ */ + +// --- Default constructor --- // +StaffComp::StaffComp() +{ +} diff --git a/database/staff_comp.h b/database/staff_comp.h new file mode 100644 index 0000000..9320c2f --- /dev/null +++ b/database/staff_comp.h @@ -0,0 +1,34 @@ +#ifndef STAFF_COMP_H +#define STAFF_COMP_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/data_types.h" + +// --- Staff_comp.dat --- // +class StaffComp +{ +private: + int ID; + char Name[ STANDARD_TEXT_LENGTH ]; + char GenderName; + char NameShort[ SHORT_TEXT_LENGTH ]; + char GenderNameShort; + int Continent; + int Nation; + int ForegroundColour; + int BackgroundColour; + short Reputation; // Version 0x02 - Changed char->short + +public: + // Constructor + StaffComp(); + + // Container + static QVector db; +}; + +#endif // STAFF_COMP_H diff --git a/database/staff_comp_history.cpp b/database/staff_comp_history.cpp new file mode 100644 index 0000000..35cb325 --- /dev/null +++ b/database/staff_comp_history.cpp @@ -0,0 +1,14 @@ +#include "staff_comp_history.h" + +// --- Static data --- // +QVector StaffCompHistory::db; + + +/* ======================================== */ +/* Staff Competition History Data */ +/* ======================================== */ + +// --- Default constructor --- // +StaffCompHistory::StaffCompHistory() +{ +} diff --git a/database/staff_comp_history.h b/database/staff_comp_history.h new file mode 100644 index 0000000..0a9f326 --- /dev/null +++ b/database/staff_comp_history.h @@ -0,0 +1,41 @@ +#ifndef STAFF_COMP_HISTORY_H +#define STAFF_COMP_HISTORY_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/data_types.h" +#include "../data_types/ptr_name.h" +#include "../data_types/ptr_staff.h" + +// --- Staff_comp_history.dat --- // +class StaffCompHistory +{ +private: + int ID; + int StaffComp; + short Year; + PtrFirstName FirstPlacedFirstName; + PtrSecondName FirstPlacedSecondName; + PtrStaff FirstPlacedIndex; + int FirstPlacedInfo; + PtrFirstName SecondPlacedFirstName; + PtrSecondName SecondPlacedSecondName; + PtrStaff SecondPlacedIndex; + int SecondPlacedInfo; + PtrFirstName ThirdPlacedFirstName; + PtrSecondName ThirdPlacedSecondName; + PtrStaff ThirdPlacedIndex; + int ThirdPlacedInfo; + +public: + // Constructor + StaffCompHistory(); + + // Container + static QVector db; +}; + +#endif // STAFF_COMP_HISTORY_H diff --git a/database/staff_history.cpp b/database/staff_history.cpp new file mode 100644 index 0000000..1ce93ab --- /dev/null +++ b/database/staff_history.cpp @@ -0,0 +1,14 @@ +#include "staff_history.h" + +// --- Static data --- // +QVector StaffHistory::db; + + +/* =================================== */ +/* Staff Career History Data */ +/* =================================== */ + +// --- Default constructor --- // +StaffHistory::StaffHistory() +{ +} diff --git a/database/staff_history.h b/database/staff_history.h new file mode 100644 index 0000000..661799c --- /dev/null +++ b/database/staff_history.h @@ -0,0 +1,33 @@ +#ifndef STAFF_HISTORY_H +#define STAFF_HISTORY_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/data_types.h" +#include "../data_types/ptr_club.h" +#include "../data_types/ptr_staff.h" + +// --- Staff_history.dat --- // +class StaffHistory +{ +private: + int ID; + PtrStaff Staff; + short Year; + PtrClub Club; + char OnLoan; + unsigned char Apps; + unsigned char Goals; + +public: + // Constructor + StaffHistory(); + + // Container + static QVector db; +}; + +#endif // STAFF_HISTORY_H diff --git a/database/staff_preferences.cpp b/database/staff_preferences.cpp new file mode 100644 index 0000000..0fd5b76 --- /dev/null +++ b/database/staff_preferences.cpp @@ -0,0 +1,15 @@ +#include "staff_preferences.h" + +// --- Static data --- // +QVector StaffPreferences::db; + + +/* ================================ */ +/* Staff Preferences Data */ +/* ================================ */ + +// --- Default constructor --- // +StaffPreferences::StaffPreferences() : + ID(db.size()) // Allocate next ID to item +{ +} diff --git a/database/staff_preferences.h b/database/staff_preferences.h new file mode 100644 index 0000000..a377077 --- /dev/null +++ b/database/staff_preferences.h @@ -0,0 +1,42 @@ +#ifndef STAFF_PREFERENCES_H +#define STAFF_PREFERENCES_H + +#include + +#pragma warning(disable: 4103) +#pragma pack(1) + +#include "../data_types/data_types.h" +#include "../data_types/ptr_club.h" +#include "../data_types/ptr_staff.h" + +// --- Staff.dat - Staff preferences --- // +class StaffPreferences +{ +private: + long ID; + PtrClub FavouriteClubs1; + PtrClub FavouriteClubs2; + PtrClub FavouriteClubs3; + PtrClub DislikedClubs1; + PtrClub DislikedClubs2; + PtrClub DislikedClubs3; + PtrStaff FavouriteStaff1; + PtrStaff FavouriteStaff2; + PtrStaff FavouriteStaff3; + PtrStaff DislikedStaff1; + PtrStaff DislikedStaff2; + PtrStaff DislikedStaff3; + +public: + // Constructor + StaffPreferences(); + + // Container + static QVector db; + + // Friend class + friend class TransferImporter; +}; + +#endif // STAFF_PREFERENCES_H diff --git a/delimiter_repair.txt b/delimiter_repair.txt new file mode 100644 index 0000000..7efdec0 --- /dev/null +++ b/delimiter_repair.txt @@ -0,0 +1,2 @@ +korea, north +Korea, South \ No newline at end of file diff --git a/exporter/exporter.cpp b/exporter/exporter.cpp new file mode 100644 index 0000000..6c207d0 --- /dev/null +++ b/exporter/exporter.cpp @@ -0,0 +1,132 @@ +#include "exporter.h" +#include "../progress_window.h" +#include + +// --- Static data --- // +unsigned short Exporter::s_PositionCounter = 0; + + +/* ============================ */ +/* Exporter Interface */ +/* ============================ */ + +// --- Default constructor --- // +Exporter::Exporter(QWidget *parent) : + QWidget(parent) +{ + // Model + m_Model = new ExporterModel(m_ExporterItems); + + // Data widget mapper + m_Mapper = new QDataWidgetMapper(this); + m_Mapper->setModel(m_Model); + + // ListView + m_ListView = new QListView(this); + m_ListView->setUniformItemSizes(true); // As only uniform sizes are used, the ListView can be optimised using setUniformItemSizes() + m_ListView->setModel(m_Model); + m_ListView->setModelColumn(ExporterModel::TITLE); + + // Connect the widget mapper to the list view + QObject::connect(m_ListView, &QListView::clicked, + this, &Exporter::onModelChanged); + + // Title + m_Title = new QLineEdit("Title", this); + m_Title->setReadOnly(true); + m_Mapper->addMapping(m_Title, ExporterModel::TITLE); + + // Description + m_Description = new QLineEdit("Description", this); + m_Description->setReadOnly(true); + m_Mapper->addMapping(m_Description, ExporterModel::DESCRIPTION); + + // Id + m_Id = new QLineEdit("ID", this); + m_Id->setReadOnly(true); + m_Mapper->addMapping(m_Id, ExporterModel::ID); + + // Button + m_Button = new QPushButton("Export Data", this); + QObject::connect(m_Button, &QPushButton::clicked, + this, &Exporter::onClick); + + // Layout + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(m_ListView, 0, 0, 3, 1); + layout->addWidget(m_Id, 0, 1); + layout->addWidget(m_Title, 0, 2); + layout->addWidget(m_Description, 1, 1, 1, 2); + layout->addWidget(m_Button, 2, 1, 1, 2); + this->setLayout(layout); +} + + +/* ================== */ +/* Add Data */ +/* ================== */ + +// --- Add ExporterItem --- // +void Exporter::add(bool exportFunction(Spreadsheet &), QString title, QString description) +{ + // New ExporterItem + ExporterItem *item = new ExporterItem(exportFunction, title, description); + + // Position counter + item->setId(s_PositionCounter); + ++s_PositionCounter; + + // Add to QVector + m_ExporterItems.push_back(item); + + // Move to first index (this is a bit of a hack) + m_Mapper->toFirst(); +} + + +/* ===================== */ +/* Export Data */ +/* ===================== */ + +// --- Export data --- // +void Exporter::onClick() +{ + // Vector position/id + unsigned short id = m_Id->text().toShort(); + + // Abort if the selected item is out of range + if(id < 0 || id >= m_ExporterItems.size()) + return; + + Spreadsheet s; + + // Show open dialog and abort if the user cancels + if(!s.create(QString(), m_Title->text())) + return; + + ProgressWindow *p = new ProgressWindow("Preparing data", 2); + p->increment(); + + // Write the file if export function pointer returned true + if(m_ExporterItems[id]->exportSpreadsheet(s)) { + p->complete(); + delete p; + s.write(); + } + else { + p->complete(); + delete p; + QMessageBox::critical(0, "Export Error", "An error was encountered when attempting to export the data."); + } +} + + +/* =============== */ +/* Model */ +/* =============== */ + +// --- Model --> mapper slot --- // +void Exporter::onModelChanged(const QModelIndex &index) +{ + m_Mapper->setCurrentModelIndex(index); +} diff --git a/exporter/exporter.h b/exporter/exporter.h new file mode 100644 index 0000000..a7736f5 --- /dev/null +++ b/exporter/exporter.h @@ -0,0 +1,58 @@ +#ifndef EXPORTER_H +#define EXPORTER_H + +// Qt headers +#include +#include +#include +#include +#include +#include +#include + +// Exporter headers +#include "exporter_item.h" +#include "exporter_model.h" + +// Other headers +#include "../spreadsheet/spreadsheet.h" + +// --- Exporter interface --- // +class Exporter : public QWidget +{ +private: + // Underlying data + QVector m_ExporterItems; + + // Vector position counter + static unsigned short s_PositionCounter; + + // Model data + QDataWidgetMapper *m_Mapper; + ExporterModel *m_Model; + + // User interface + QPushButton *m_Button; + QLineEdit *m_Description; + QLineEdit *m_Id; + QLineEdit *m_Title; + QListView *m_ListView; + +private slots: + // Export + void onClick(); + + // Model + void onModelChanged(const QModelIndex &index); + +public: + // Constructor + Exporter(QWidget *parent = 0); + + // Add data + void add(bool (exportFunction)(Spreadsheet &), + QString title, + QString description); +}; + +#endif // EXPORTER_H diff --git a/exporter/exporter_item.cpp b/exporter/exporter_item.cpp new file mode 100644 index 0000000..8ffc12b --- /dev/null +++ b/exporter/exporter_item.cpp @@ -0,0 +1,64 @@ +#include "exporter_item.h" + +/* ======================= */ +/* Exporter Item */ +/* ======================= */ + +// --- Default constructor --- // +ExporterItem::ExporterItem() : + m_ExportSpreadsheet(nullptr) +{ + +} + +// --- Actual constructor --- // +ExporterItem::ExporterItem(bool (exportFunction)(Spreadsheet &), QString title, QString description) : + m_ExportSpreadsheet(exportFunction), + m_Title(title), + m_Description(description) +{ + +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Get description --- // +QString ExporterItem::description() +{ + return m_Description; +} + +// --- Export the spreadsheet via the function pointer --- // +bool ExporterItem::exportSpreadsheet(Spreadsheet &s) +{ + if(m_ExportSpreadsheet == nullptr) + return false; + else + return m_ExportSpreadsheet(s); +} + +// --- Get id --- // +unsigned short ExporterItem::id() +{ + return m_Id; +} + +// --- Get title --- // +QString ExporterItem::title() +{ + return m_Title; +} + + +/* ================== */ +/* Set Data */ +/* ================== */ + +// --- Set id --- // +void ExporterItem::setId(unsigned short id) +{ + m_Id = id; +} diff --git a/exporter/exporter_item.h b/exporter/exporter_item.h new file mode 100644 index 0000000..02c1a94 --- /dev/null +++ b/exporter/exporter_item.h @@ -0,0 +1,37 @@ +#ifndef EXPORTER_ITEM_H +#define EXPORTER_ITEM_H + +#include + +#include "../spreadsheet/spreadsheet.h" + +// --- Exporter item --- // +class ExporterItem +{ +private: + // Id + unsigned short m_Id; + + // Exporter data + QString m_Title; + QString m_Description; + bool (*m_ExportSpreadsheet)(Spreadsheet &); + +public: + // Constructor + ExporterItem(); + ExporterItem(bool (exportFunction)(Spreadsheet &), + QString title, + QString description); + + // Get data + QString description(); + bool exportSpreadsheet(Spreadsheet &s); + unsigned short id(); + QString title(); + + // Set data + void setId(unsigned short id); +}; + +#endif // EXPORTER_ITEM_H diff --git a/exporter/exporter_model.cpp b/exporter/exporter_model.cpp new file mode 100644 index 0000000..a92e2e4 --- /dev/null +++ b/exporter/exporter_model.cpp @@ -0,0 +1,100 @@ +#include "exporter_model.h" + +/* ===================== */ +/* Index Model */ +/* ===================== */ + +// --- Constructor --- // +ExporterModel::ExporterModel(QVector &data) : + m_Data(&data) +{ + +} + + +/* ================ */ +/* Counts */ +/* ================ */ + +// --- Column count --- // +int ExporterModel::columnCount(const QModelIndex & /*parent*/) const +{ + return COLUMN_COUNT; +} + +// --- Row/QVector count --- // +int ExporterModel::rowCount(const QModelIndex & /*parent*/) const +{ + return m_Data->size(); +} + + +/* ============== */ +/* Data */ +/* ============== */ + +// --- Get data --- // +QVariant ExporterModel::data(const QModelIndex &index, int role) const +{ + if(role == Qt::DisplayRole || role == Qt::EditRole) + { + ExporterItem *i = (*m_Data)[index.row()]; + + switch(index.column()) { + + case TITLE: + return i->title(); + + case DESCRIPTION: + return i->description(); + + case ID: + return i->id(); + } + } + + return QVariant(); +} + + +/* ================ */ +/* Header */ +/* ================ */ + +// --- Get header --- // +QVariant ExporterModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + // Horizontal header + if(orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + switch(section) { + + case TITLE: + return "Title"; + + case DESCRIPTION: + return "Description"; + + case ID: + return "ID"; + } + } + + // Vertical header (i.e. row numbers) + else if(orientation == Qt::Vertical && role == Qt::DisplayRole) + return section+1; + + return QVariant(); +} + + +/* =============== */ +/* Flags */ +/* =============== */ + +// --- Flags --- // +Qt::ItemFlags ExporterModel::flags(const QModelIndex &index) const +{ + // Read only + return QAbstractTableModel::flags(index); +} diff --git a/exporter/exporter_model.h b/exporter/exporter_model.h new file mode 100644 index 0000000..19be6f7 --- /dev/null +++ b/exporter/exporter_model.h @@ -0,0 +1,44 @@ +#ifndef EXPORTER_MODEL_H +#define EXPORTER_MODEL_H + +// Qt headers +#include +#include + +// Exporter headers +#include "exporter_item.h" + +// --- Exporter Model --- // +class ExporterModel : public QAbstractTableModel +{ +private: + // Pointer to underlying data + QVector *m_Data; + +public: + // Constructor + ExporterModel(QVector &data); + + // Counts + int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + + // Data + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + + // Header + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + + // Flags + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + + // Column numbers + enum ENUM_COLUMNS { + TITLE = 0, + DESCRIPTION, + ID, + COLUMN_COUNT + }; +}; + +#endif // EXPORTERMODEL_H diff --git a/icons/app.ico b/icons/app.ico new file mode 100644 index 0000000..a086c74 Binary files /dev/null and b/icons/app.ico differ diff --git a/icons/document-open.png b/icons/document-open.png new file mode 100644 index 0000000..421bce1 Binary files /dev/null and b/icons/document-open.png differ diff --git a/icons/document-save-as.png b/icons/document-save-as.png new file mode 100644 index 0000000..dc749f1 Binary files /dev/null and b/icons/document-save-as.png differ diff --git a/icons/document-save.png b/icons/document-save.png new file mode 100644 index 0000000..72644b5 Binary files /dev/null and b/icons/document-save.png differ diff --git a/icons/row-buffer.png b/icons/row-buffer.png new file mode 100644 index 0000000..00401dd Binary files /dev/null and b/icons/row-buffer.png differ diff --git a/import_template.csv b/import_template.csv new file mode 100644 index 0000000..f0531bd --- /dev/null +++ b/import_template.csv @@ -0,0 +1 @@ +First Name,Last Name,Common Name,DOB,Nation1,Nation2,Contract start,Contract end,Value,Club,On loan from,GK,SW,D,DM,M,AM,ST,WB,FR,Right,Left,Centre,Right Foot,Left Foot,Int Caps,Int Goals,Current Ability,Potential Ability,Home Reputation,Current Reputation diff --git a/importer/transfer_importer.cpp b/importer/transfer_importer.cpp new file mode 100644 index 0000000..07a9c67 --- /dev/null +++ b/importer/transfer_importer.cpp @@ -0,0 +1,1109 @@ +#include "transfer_importer.h" +#include "vlookup_hash.h" +#include "../database/club.h" +#include "../database/nation.h" +#include "../database/staff.h" +#include "../database/staff_preferences.h" + +// Qt headers +#include +#include +#include +#include +#include + +// --- Static data --- // +QString TransferImporter::s_ProtectedPlayersFileName = QStringLiteral("vlookup_protected_players.csv"); + + +/* =========================== */ +/* Transfer Importer */ +/* =========================== */ + +// --- Default constructor --- // +TransferImporter::TransferImporter(Settings &settings, + int posSettings, + int posImportPath, + QWidget *parent) : + m_ColumnAdjustment(0), + m_Settings(&settings), + m_SettingsPos(posSettings), + m_SettingImportPathPos(posImportPath), + m_Spreadsheet(nullptr), + QWidget(parent) +{ + // Create user interface + this->createControlWidget(); + this->createProgressWidget(); + this->createStatusWidget(); + this->createSettingsWidget(); + + // Main layout + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(m_ControlWidget, 0, 0); + layout->addWidget(m_ProgressWidget, 1, 0); + layout->addWidget(m_StatusWidget, 2, 0); + layout->addWidget(m_SettingsWidget, 0, 1, 3, 1); + this->setLayout(layout); +} + +/* ======================== */ +/* Control Widget */ +/* ======================== */ + +// --- Create control widget --- // +void TransferImporter::createControlWidget() +{ + // Widget & layout + m_ControlWidget = new QWidget(this); + QGridLayout *layout = new QGridLayout(m_ControlWidget); + + // Open button + m_OpenButton = new QPushButton("Open File", m_ControlWidget); + QObject::connect(m_OpenButton, &QPushButton::clicked, + this, &TransferImporter::onOpen); + + // Save button + m_SaveButton = new QPushButton("Save File", m_ControlWidget); + m_SaveButton->setEnabled(false); // Initially disabled as no file will be open to start with + QObject::connect(m_SaveButton, &QPushButton::clicked, + this, &TransferImporter::onSave); + + // Import button + m_ImportButton = new QPushButton("Import Data", m_ControlWidget); + m_ImportButton->setEnabled(false); // Initially disabled as no file will be open to start with + QObject::connect(m_ImportButton, &QPushButton::clicked, + this, &TransferImporter::onImport); + + // Close button + m_CloseButton = new QPushButton("Close File", m_ControlWidget); + m_CloseButton->setEnabled(false); // Initially disabled as no file will be open to start with + QObject::connect(m_CloseButton, &QPushButton::clicked, + this, &TransferImporter::onClose); + + + // Apply layout + layout->addWidget(m_OpenButton, 0, 0); + layout->addWidget(m_SaveButton, 0, 1); + layout->addWidget(m_ImportButton, 0, 2); + layout->addWidget(m_CloseButton, 0, 3); + m_ControlWidget->setLayout(layout); +} + + +/* ============== */ +/* Data */ +/* ============== */ + +// --- Import the spreadsheet data --- // +void TransferImporter::onImport() +{ + // Temporarily disable the relevant buttons + m_CloseButton->setEnabled(false); + m_ImportButton->setEnabled(false); + m_SaveButton->setEnabled(false); + m_OpenButton->setEnabled(false); + + // Progress bar + this->resetProgressBar(); + m_ProgressBar->setRange(0, m_Spreadsheet->data()->size()); + m_ProgressLabel->setText("Importing Data"); + + // Start timer + QTime timer; + timer.start(); + + // Process the rows of data (loop by index for optimum speed) + const unsigned int rowSize = static_cast(m_Spreadsheet->data()->size()); + for(unsigned int row = 0; row < rowSize; ++row) { + + /* DISPLAY NAME IN PROGRESS DIALOG + * This is useful for debugging but is VERY VERY slow!! + + // Display name + QString displayName; + + switch(m_ColumnAdjustment) + { + case 2: + displayName = QString("%1 %2 %3") + .arg(m_Spreadsheet->string(row, NAME)) + .arg(m_Spreadsheet->string(row, NAME+1)) + .arg(m_Spreadsheet->string(row, NAME+2)); + break; + + case 1: + displayName = QString("%1 %2") + .arg(m_Spreadsheet->string(row, NAME)) + .arg(m_Spreadsheet->string(row, NAME+1)); + break; + default: + displayName = m_Spreadsheet->string(row, NAME); + break; + } + + // Progress bar + this->incrementProgressBar(displayName); + + */ + + // Progress bar + this->incrementProgressBar(); // Comment out this line if the above Display Name is being used + + // Skip any rows with a blank DOB column + if(m_Spreadsheet->string(row, DATE_OF_BIRTH).isEmpty()) + continue; + + // Columns positions + const unsigned int posFurthestRightColumn = m_Spreadsheet->data()->at(row).size()-1; // Furthest right column + const unsigned int posIsProtected = posFurthestRightColumn; + const unsigned int posLoanClubId = posFurthestRightColumn -1; + const unsigned int posClubId = posFurthestRightColumn - 2; + const unsigned int posNationSecondaryId = posFurthestRightColumn - 3; + const unsigned int posNationPrimaryId = posFurthestRightColumn - 4; + const unsigned int posStaffId = posFurthestRightColumn - 5; + + // Id numbers + int staffId = m_Spreadsheet->number(row, posStaffId); + + // Skip any invalid staff Ids (there should not be any) + if(staffId == Spreadsheet::NOT_A_NUMBER) { + continue; + } + + // Determine relevant club ID to use according to whether the player is on loan + QVariant club = m_Spreadsheet->variant(row, posClubId); + QVariant loanClub = m_Spreadsheet->variant(row, posLoanClubId); + + if(loanClub.toInt() >= VALID) // Use the loan club if the column is not blank + club = loanClub; + + // Determine whether the player is protected + const bool isProtected = m_Spreadsheet->variant(row, posIsProtected).toBool(); + + // For flagging whether new staff to be added + bool isNewStaff = (staffId < VALID); + + /* Add new staff */ + if(isNewStaff) { + // Staff data + Staff s; + staffId = s.ID; + + // Date & year of birth + s.DateOfBirth.set(m_Spreadsheet->matchDateString(row, DATE_OF_BIRTH), m_YearAdjustment->value()); + s.YearOfBirth.validate(s.DateOfBirth.year()); + + // Player data + Player p; + s.PlayerData = p.ID; + + // Staff preferences data + StaffPreferences sp; + s.Preferences = sp.ID; + + // Add to database + Player::db.push_back(p); + StaffPreferences::db.push_back(sp); + Staff::db.push_back(s); + } + + /* Edit new and existing staff */ + // Staff pointer + Staff *staff = &(Staff::db[staffId]); + + // Nationality + staff->Nation.setNoCheck(m_Spreadsheet->variant(row, posNationPrimaryId)); + staff->SecondNation.setNoCheck(m_Spreadsheet->variant(row, posNationSecondaryId)); + + // Contract + staff->ClubContracted.setNoCheck(club); + staff->DateJoinedClub.set(m_Spreadsheet->variant(row, CONTRACT_START), m_YearAdjustment->value()); + staff->ContractExpiresClub.set(m_Spreadsheet->variant(row, CONTRACT_END), m_YearAdjustment->value()); + + // International stats + staff->InternationalApps.set(m_Spreadsheet->variant(row, INT_CAPS)); + staff->InternationalGoals.set(m_Spreadsheet->variant(row, INT_GOALS)); + + // Value + staff->EstimatedValue.set(m_Spreadsheet->variant(row, VALUE), m_ExchangeRate->value()); + + // Attributes + Player *player = staff->PlayerData.data(); + + if(player != nullptr) { + + // Protected player items + if(!isProtected) + { + // Positions + player->Goalkeeper.set(m_Spreadsheet->variant(row, GK)); + player->Sweeper.set(m_Spreadsheet->variant(row, SW)); + player->Defender.set(m_Spreadsheet->variant(row, D)); + player->DefensiveMidfielder.set(m_Spreadsheet->variant(row, DM)); + player->Midfielder.set(m_Spreadsheet->variant(row, M)); + player->AttackingMidfielder.set(m_Spreadsheet->variant(row, AM)); + player->Attacker.set(m_Spreadsheet->variant(row, ST)); + player->WingBack.set(m_Spreadsheet->variant(row, WB)); + player->FreeRole.set(m_Spreadsheet->variant(row, FR)); + + // Sides + player->RightSide.set(m_Spreadsheet->variant(row, SIDE_RIGHT)); + player->LeftSide.set(m_Spreadsheet->variant(row, SIDE_LEFT)); + player->Central.set(m_Spreadsheet->variant(row, SIDE_CENTRE)); + + // Footedness + player->RightFoot.set(m_Spreadsheet->variant(row, FOOT_RIGHT)); + player->LeftFoot.set(m_Spreadsheet->variant(row, FOOT_LEFT)); + + // Ability + player->CurrentAbility.set(m_Spreadsheet->variant(row, ABILITY_CURRENT)); + player->PotentialAbility.set(m_Spreadsheet->variant(row, ABILITY_POTENTIAL)); + + // Reputation + player->HomeReputation.set(m_Spreadsheet->variant(row, REPUTATION_HOME)); + player->CurrentReputation.set(m_Spreadsheet->variant(row, REPUTATION_CURRENT)); + } + + // Unprotected player items: + + // World Reputation + bool resultWR = player->WorldReputation.setFromTransferValue(m_Spreadsheet->variant(row, VALUE)); + + // Wage (only if World Rep was successfully updated) + if(resultWR == true) + staff->EstimatedWage.setFromWorldReputation(player->WorldReputation.get()); + + // Squad number + player->SquadNumber.set(m_Spreadsheet->variant(row, SQUAD_NUMBER)); + } + + /* Setting name of new staff */ + /* This can only be done once nationality has been set */ + + /* First name in first column, second name in second column and common name in third column */ + if(isNewStaff && m_ColumnAdjustment == 2) + { + const QString n1 = m_Spreadsheet->string(row, NAME).simplified(); + const QString n2 = m_Spreadsheet->string(row, NAME+1).simplified(); + const QString cn = m_Spreadsheet->string(row, NAME+2).simplified(); + staff->FirstName.set(n1, staff->Nation.get()); + staff->SecondName.set(n2, staff->Nation.get()); + staff->CommonName.set(cn, staff->Nation.get()); + } + + /* First name in first column and second name in second column */ + else if(isNewStaff && m_ColumnAdjustment == 1) + { + const QString n1 = m_Spreadsheet->string(row, NAME).simplified(); + const QString n2 = m_Spreadsheet->string(row, NAME+1).simplified(); + + // Common name + if(n1.isEmpty() || n2.isEmpty()) + { + const QString cn = QString("%1%2").arg(n1).arg(n2); + staff->CommonName.set(cn, staff->Nation.get()); + } + // Full name + else + { + staff->FirstName.set(n1, staff->Nation.get()); + staff->SecondName.set(n2, staff->Nation.get()); + } + } + + /* Full name in first column (i.e. m_ColumnAdjustment is zero) */ + else if(isNewStaff) + { + // Name buffer + const QString n = m_Spreadsheet->string(row, NAME).simplified(); // Removes any whitespaces + + // Index of first space in text + const int space = n.indexOf(" ", 0, Qt::CaseInsensitive); + + // Common name (no spaces in text) + if(space < 0) + { + staff->CommonName.set(n, staff->Nation.get()); + } + // Full name + else + { + const QString n1 = n.left(space); + const QString n2 = n.right(n.size()-space-1); + staff->FirstName.set(n1, staff->Nation.get()); + staff->SecondName.set(n2, staff->Nation.get()); + } + } + } + + // Stop timer + this->setTimerLabels(timer.elapsed(), rowSize, + m_TimerItem[IMPORT_TOTAL], + m_TimerItem[IMPORT_PER_RECORD]); + + // Stop/complete the progress bar + this->stopProgressBar(); + m_ProgressLabel->setText("Import Complete"); + + // Enable and disable the relevant buttons + m_CloseButton->setEnabled(true); + m_ImportButton->setEnabled(false); + m_SaveButton->setEnabled(true); + m_SaveButton->setFocus(); +} + +// --- Process the data and identify the relevant ID numbers --- // +void TransferImporter::process(const QTime &timer) +{ + // Apply settings + String::ignoreAccents(m_RadioButton[IGNORE_ACCENTS]->isChecked()); + + // Counters + int counterPlayerMatches = 0; + int counterPlayerUnmatched = 0; + int counterPlayerProtected = 0; + int counterErrorRows = 0; + + // Progress bar + m_ProgressBar->setRange(0, 4 + m_Spreadsheet->data()->size()); + + // Hash tables + QHash clubs; + QHash nations; + QHash staffMain; + QHash staffFailSafe; + + // Prefix/suffix table + QVector clubPrefixSuffix; + + // VLookup + VLookupHash vlookup(VLookupHash::CLUBS); + + // Club hash table + this->incrementProgressBar("Creating club list"); + + if(m_RadioButton[CLUB_NAMES_SHORT_AND_LONG]->isChecked()) + Club::createHash(clubs, true); // Add DB long names (if the relevant RadioButton is checked) + + Club::createHash(clubs, false); // Add DB short names + vlookup.load(clubs); // Add VLookup names + vlookup.set(VLookupHash::CLUBS_PREFIX_SUFFIX); // Club prefix/suffixes + vlookup.load(clubPrefixSuffix); // Add prefix/suffixes + + // Nation hash table + this->incrementProgressBar("Creating nation list"); + Nation::createHash(nations); // Add DB names + vlookup.set(VLookupHash::NATIONS); + vlookup.load(nations); // Add VLookup names + + // Staff hash tables + this->incrementProgressBar("Creating staff list"); + Staff::createHash(staffMain, staffFailSafe, Staff::SCOPE_PLAYERS, m_YearAdjustment->value()); + + // Load protected players list + this->incrementProgressBar("Creating protected staff list"); + this->loadProtectedPlayersList(staffMain); + + // Add header columns + m_Spreadsheet->addHeader("Staff ID", 0); + m_Spreadsheet->addHeader("Nation1 ID", 0); + m_Spreadsheet->addHeader("Nation2 ID", 0); + m_Spreadsheet->addHeader("Club ID", 0); + m_Spreadsheet->addHeader("Loan Club ID", 0); + m_Spreadsheet->addHeader("Protected?", 0); + + // Prepare progress bar label + m_ProgressLabel->setText("Processing spreadsheet"); + + // Process the rows of data (loop by index for optimum speed) + const unsigned int rowSize = static_cast(m_Spreadsheet->data()->size()); + for(unsigned int row = 0; row < rowSize; ++row) { + + // Progress bar + this->incrementProgressBar(); + + // Staff + int staffId = BLANK; + QString staffText; + + // Set name (where the full name is in the first column) + if(m_ColumnAdjustment == 0) + { + staffText = m_Spreadsheet->matchString(row, NAME); + } + + // Set name where: + // (1) the first name is in the first column; + // (2) the second name is in the second column; and + // (3) the common name is in the third column + else + { + const QString firstname = m_Spreadsheet->matchString(row, NAME); + const QString secondname = m_Spreadsheet->matchString(row, NAME+1); + const QString commonname = m_Spreadsheet->matchString(row, NAME+2); + + // Use first name + surname if no common name is present + if(commonname.isEmpty()) + { + staffText = QString("%1 %2") + .arg(firstname) + .arg(secondname); + } + // Use common name if one has been entered + else + { + staffText = commonname; + } + + } + + // Match the staff against the database + if(!staffText.isEmpty()) { // Only proceed if the cell is not empty + // Staff date of birth + QString yob = m_Spreadsheet->string(row, DATE_OF_BIRTH).right(4); + QString dob = m_Spreadsheet->matchDateString(row, DATE_OF_BIRTH); + + // Match the staff against the database + staffId = staffMain.value(QString("%1 %2").arg(staffText).arg(dob), NO_MATCH); + + // Use failsafe hash (i.e. year of birth) if no match found using main hash (i.e. date of birth) + if(staffId == NO_MATCH) + staffId = staffFailSafe.value(QString("%1 %2").arg(staffText).arg(yob), NO_MATCH); + } + + // Check for protected staff (vector will be empty if the setting is disabled) + bool isProtected = (staffId >= VALID && m_ProtectedPlayers.contains(staffId)); + + // Club + int clubId = BLANK; + QString clubText = m_Spreadsheet->matchString(row, CLUB); + + // Match the club against the database + if(!clubText.isEmpty()) { // Only proceed if the cell is not empty + clubId = clubs.value(clubText, NO_MATCH); + + // Try prefix/suffixes if no exact match can be found + if(clubId == NO_MATCH) + clubId = vlookup.lookupPrefixSuffix(clubText, clubPrefixSuffix, clubs, NO_MATCH); + } + + // Loan Club + int loanClubId = BLANK; + QString loanClubText = m_Spreadsheet->matchString(row, ON_LOAN_FROM); + + // Match the club against the database + if(!loanClubText.isEmpty()) { // Only proceed if the cell is not empty + loanClubId = clubs.value(loanClubText, NO_MATCH); + + // Try prefix/suffixes if no exact match can be found + if(loanClubId == NO_MATCH) + loanClubId = vlookup.lookupPrefixSuffix(loanClubText, clubPrefixSuffix, clubs, NO_MATCH); + } + + // Primary nation (blank means no nationality) + QString primaryNationText = m_Spreadsheet->matchString(row, NATION_1); + int primaryNationId = nations.value(primaryNationText, NO_MATCH); + + // Primary nation (blank means no nationality) + QString secondaryNationText = m_Spreadsheet->matchString(row, NATION_2); + int secondaryNationId = nations.value(secondaryNationText, NO_MATCH); + + // Add data to spreadsheet + m_Spreadsheet->add(row, staffId); + m_Spreadsheet->add(row, primaryNationId); + m_Spreadsheet->add(row, secondaryNationId); + m_Spreadsheet->add(row, clubId); + m_Spreadsheet->add(row, loanClubId); + m_Spreadsheet->add(row, isProtected); + + // Increment counters + if(staffId == NO_MATCH) + ++counterPlayerUnmatched; + else + ++counterPlayerMatches; + + if(isProtected) + ++counterPlayerProtected; + + if(clubId == NO_MATCH || loanClubId == NO_MATCH || primaryNationId == NO_MATCH || secondaryNationId == NO_MATCH) + ++counterErrorRows; + } + + // Stop timer + this->setTimerLabels(timer.elapsed(), m_Spreadsheet->data()->size(), + m_TimerItem[PROCESS_TOTAL], + m_TimerItem[PROCESS_PER_RECORD]); + + // Set summary items + m_SummaryItem[SUMMARY_PLAYER_MATCHES]->setValue(counterPlayerMatches); + m_SummaryItem[SUMMARY_PLAYER_UNMATCHED]->setValue(counterPlayerUnmatched); + m_SummaryItem[SUMMARY_PLAYER_PROTECTED]->setValue(counterPlayerProtected); + m_SummaryItem[SUMMARY_ERRORS]->setValue(counterErrorRows); + + // Stop/complete the progress bar + this->stopProgressBar(); +} + + +/* ================== */ +/* File I/O */ +/* ================== */ + +// --- Close CSV file --- // +void TransferImporter::onClose() +{ + // Reset summary items to zero + for(SummaryItem *itr : m_SummaryItem) + itr->setValue(0); + + // Reset the timer values + this->resetTimerLabels(); + + // Enable and disable the relevant buttons + m_CloseButton->setEnabled(false); + m_ImportButton->setEnabled(false); + m_SaveButton->setEnabled(false); + m_OpenButton->setEnabled(true); + m_OpenButton->setFocus(); + this->resetProgressBar(); + + // Enable the settings + m_ExchangeRate->setEnabled(true); + m_YearAdjustment->setEnabled(true); + for(QRadioButton *itr : m_RadioButton) + itr->setEnabled(true); + + // Delete the spreadsheet pointer + delete m_Spreadsheet; + m_Spreadsheet = nullptr; // Null pointer allows us to track whether the pointer is deleted +} + +// --- Open CSV file --- // +void TransferImporter::onOpen() +{ + // Save the settings: Radio buttons + for(unsigned int i = 0; i < RADIO_BUTTON_COUNT; ++i) { + m_Settings->set(m_RadioButton[i]->isChecked(), m_SettingsPos + i); + } + + // Save the settings: Exchange rate + m_Settings->set(m_ExchangeRate->value(), m_SettingsPos + RADIO_BUTTON_COUNT); + + // Save the settings: Year adjustment + m_Settings->set(m_YearAdjustment->value(), m_SettingsPos + RADIO_BUTTON_COUNT + 1); + + // Reset progress bar and disable settings + this->resetProgressBar(); + m_ExchangeRate->setEnabled(false); + m_YearAdjustment->setEnabled(false); + for(QRadioButton *itr : m_RadioButton) + itr->setEnabled(false); + + // Reset the timer values + this->resetTimerLabels(); + + // Spreadsheet + m_Spreadsheet = new Spreadsheet(true); + + if(m_Spreadsheet->open(m_Settings->text(m_SettingImportPathPos), 1, true)) { + + // Update import folder path setting + m_Settings->set(m_Spreadsheet->getFilePath(), m_SettingImportPathPos); + + // Temporarily disable the relevant buttons + m_CloseButton->setEnabled(false); + m_ImportButton->setEnabled(false); + m_SaveButton->setEnabled(false); + m_OpenButton->setEnabled(false); + + // Detect and set the column adjustment + this->detectColumnAdjustment(); + + // Process the data (and pass the spreadsheet's timer) + this->process(m_Spreadsheet->time()); + } + // Call onClose() if opening the spreadsheet was cancelled or failed + else { + this->onClose(); + return; + } + + // Enable and disable the relevant buttons + m_CloseButton->setEnabled(true); + m_ImportButton->setEnabled(true); + m_SaveButton->setEnabled(true); + m_SaveButton->setFocus(); +} + +// --- Save CSV file --- // +void TransferImporter::onSave() +{ + m_Spreadsheet->save(); +} + + +/* ========================= */ +/* Progress Widget */ +/* ========================= */ + +// --- Create control widget --- // +void TransferImporter::createProgressWidget() +{ + // Widget & layout + m_ProgressWidget = new QWidget(this); + QVBoxLayout *layout = new QVBoxLayout(m_ProgressWidget); + + // Progress label + m_ProgressLabel = new QLabel(m_ProgressWidget); + m_ProgressLabel->setObjectName("TextSummaryLarge"); + layout->addWidget(m_ProgressLabel, 0, Qt::AlignHCenter | Qt::AlignBottom); + + // Progress bar + m_ProgressBar = new QProgressBar(m_ProgressWidget); + //m_ProgressBar->setObjectName("TextSummaryLarge"); + layout->addWidget(m_ProgressBar); + + // Correct the label width so that it matches the bar width + m_ProgressLabel->setMinimumWidth(m_ProgressBar->width()); + + // Apply layout + m_ProgressWidget->setLayout(layout); +} + +// --- Increment the progress bar (for fast processing) --- // +void TransferImporter::incrementProgressBar() +{ + m_ProgressBar->setValue(m_ProgressBar->value()+1); + QApplication::processEvents(); +} + +// --- Increment the progress bar (standard, with a label) --- // +void TransferImporter::incrementProgressBar(const QString &label) +{ + m_ProgressBar->setWindowModality(Qt::ApplicationModal); + m_ProgressLabel->setText(label); + m_ProgressBar->setValue(m_ProgressBar->value()+1); + QApplication::processEvents(); +} + +// --- Reset the progress bar --- // +void TransferImporter::resetProgressBar() +{ + m_ProgressBar->setWindowModality(Qt::NonModal); + m_ProgressLabel->clear(); + m_ProgressBar->setValue(0); + m_ProgressBar->setEnabled(true); +} + +// --- Stop the progress bar --- // +void TransferImporter::stopProgressBar() +{ + m_ProgressLabel->setText("Complete"); + m_ProgressBar->setValue(m_ProgressBar->maximum()); + m_ProgressBar->setEnabled(false); +} + + +/* ================================ */ +/* Protected Players List */ +/* ================================ */ + +// --- Create a blank protected players list --- // +void TransferImporter::createBlankProtectedPlayersList() +{ + QStringList header; + header << QStringLiteral("First Name") + << QStringLiteral("Second Name") + << QStringLiteral("Date of Birth") + << QStringLiteral("Staff ID"); + + Spreadsheet s; + s.addHeader(header); + s.write(s_ProtectedPlayersFileName); +} + +// --- Load protected players list --- // +void TransferImporter::loadProtectedPlayersList(QHash &staffList) +{ + // Clear the vector + m_ProtectedPlayers.clear(); + + // Abort if the setting is disabled + if(m_RadioButton[PROTECTED_PLAYERS_DISABLED]->isChecked()) + return; + + // Load the spreadsheet (create a blank file if the file cannot be found) + Spreadsheet s; + s.setSilent(true); + if(!s.read(s_ProtectedPlayersFileName)) { + this->createBlankProtectedPlayersList(); + return; + } + + enum ENUM_PROTECTED_COLUMN_POSITIONS { + PR_FIRST_NAME = 0, + PR_SECOND_NAME, + PR_DATE_OF_BIRTH, + PR_STAFF_ID + }; + + // Process the rows of data (loop by index for optimum speed) + const unsigned int rowSize = static_cast(s.data()->size()); + for(unsigned int row = 0; row < rowSize; ++row) { + + QString staff = QString("%1 %2 %3") + .arg(s.matchString(row, PR_FIRST_NAME)) + .arg(s.matchString(row, PR_SECOND_NAME)) + .arg(s.matchDateString(row, PR_DATE_OF_BIRTH)); + + // Match the staff against the database + int id = staffList.value(staff, NO_MATCH); + + // Add id to spreadsheet + s.set(row, PR_STAFF_ID, id); + + // Skip if no valid match found + if(id < VALID) + continue; + else + m_ProtectedPlayers.push_back(id); + } + + // Save the modified spreadsheet + s.write(); + + // Sort the protected players list + std::sort(m_ProtectedPlayers.begin(), m_ProtectedPlayers.end()); +} + + +/* ========================= */ +/* Settings Widget */ +/* ========================= */ + +// --- Create settings widget --- // +void TransferImporter::createSettingsWidget() +{ + // Widget & layout + m_SettingsWidget = new QWidget(this); + m_SettingsWidget->setFixedWidth(250); + QVBoxLayout *layout = new QVBoxLayout(m_SettingsWidget); + layout->setContentsMargins(0,0,0,0); + + // Create radio buttons + for(int i = 0; i < RADIO_BUTTON_COUNT; ++i) + m_RadioButton.push_back(nullptr); // Ensure that all buttons are initialised otherwise use of "nullptr" + // could cause a crash + + // Create heading and description labels + for(int i = 0; i < SETTINGS_COUNT; ++i) { + + QGroupBox *group = new QGroupBox(m_SettingsWidget); + group->setObjectName("TextSettingGroup"); + layout->addWidget(group); + m_SettingsGroup.push_back(group); + + QLabel *desc = new QLabel(m_SettingsWidget); + desc->setObjectName("TextSettingDescription"); + desc->setWordWrap(true); + desc->setContentsMargins(0,0,0,0); + m_SettingsDescription.push_back(desc); + + if(i < EXCHANGE_RATE) // Don't add a button group for Exchange Rate or Year Adjustment + m_ButtonGroup.push_back(new QButtonGroup(m_SettingsWidget)); + } + + // Accent matching + m_SettingsGroup[ACCENTS]->setTitle("Accent Matching"); + m_SettingsDescription[ACCENTS]->setText("Accented text matching sensitivity"); + m_RadioButton[IGNORE_ACCENTS] = new QRadioButton("Ignore accents"); + m_RadioButton[USE_ACCENTS] = new QRadioButton("Use accents"); + m_ButtonGroup[ACCENTS]->addButton(m_RadioButton[IGNORE_ACCENTS]); + m_ButtonGroup[ACCENTS]->addButton(m_RadioButton[USE_ACCENTS]); + + QGridLayout *layoutAccents = new QGridLayout(m_SettingsGroup[ACCENTS]); + layoutAccents->addWidget(m_SettingsDescription[ACCENTS], 0, 0, 1, 2); + layoutAccents->addWidget(m_RadioButton[IGNORE_ACCENTS]); + layoutAccents->addWidget(m_RadioButton[USE_ACCENTS]); + m_SettingsGroup[ACCENTS]->setLayout(layoutAccents); + + // Club lookup + m_SettingsGroup[CLUB_LOOKUP]->setTitle("Club Name Lookup"); + m_SettingsDescription[CLUB_LOOKUP]->setText("Use lookup data from vlookup_clubs.csv"); + m_RadioButton[CLUB_LOOKUP_DISABLED] = new QRadioButton("Disabled"); + m_RadioButton[CLUB_LOOKUP_ENABLED] = new QRadioButton("Enabled"); + m_ButtonGroup[CLUB_LOOKUP]->addButton(m_RadioButton[CLUB_LOOKUP_DISABLED]); + m_ButtonGroup[CLUB_LOOKUP]->addButton(m_RadioButton[CLUB_LOOKUP_ENABLED]); + + QGridLayout *layoutClubLookup = new QGridLayout(m_SettingsGroup[CLUB_LOOKUP]); + layoutClubLookup->addWidget(m_SettingsDescription[CLUB_LOOKUP], 0, 0, 1, 2); + layoutClubLookup->addWidget(m_RadioButton[CLUB_LOOKUP_ENABLED]); + layoutClubLookup->addWidget(m_RadioButton[CLUB_LOOKUP_DISABLED]); + m_SettingsGroup[CLUB_LOOKUP]->setLayout(layoutClubLookup); + + // Club names + m_SettingsGroup[CLUB_NAMES]->setTitle("Club Name Source"); + m_SettingsDescription[CLUB_NAMES]->setText("Match against long and/or short club names"); + m_RadioButton[CLUB_NAMES_SHORT_ONLY] = new QRadioButton("Short Only"); + m_RadioButton[CLUB_NAMES_SHORT_AND_LONG] = new QRadioButton("Short and Long"); + m_ButtonGroup[CLUB_NAMES]->addButton(m_RadioButton[CLUB_NAMES_SHORT_ONLY]); + m_ButtonGroup[CLUB_NAMES]->addButton(m_RadioButton[CLUB_NAMES_SHORT_AND_LONG]); + + QGridLayout *layoutClubNames = new QGridLayout(m_SettingsGroup[CLUB_NAMES]); + layoutClubNames->addWidget(m_SettingsDescription[CLUB_NAMES], 0, 0, 1, 2); + layoutClubNames->addWidget(m_RadioButton[CLUB_NAMES_SHORT_ONLY]); + layoutClubNames->addWidget(m_RadioButton[CLUB_NAMES_SHORT_AND_LONG]); + m_SettingsGroup[CLUB_NAMES]->setLayout(layoutClubNames); + + // First name lookup + m_SettingsGroup[FIRST_NAME_LOOKUP]->setTitle("First Name Lookup"); + m_SettingsDescription[FIRST_NAME_LOOKUP]->setText("Attempt to match using similar first names"); + m_RadioButton[FIRST_NAME_LOOKUP_DISABLED] = new QRadioButton("Disabled"); + m_RadioButton[FIRST_NAME_LOOKUP_ENABLED] = new QRadioButton("Enabled"); + m_ButtonGroup[FIRST_NAME_LOOKUP]->addButton(m_RadioButton[FIRST_NAME_LOOKUP_ENABLED]); + m_ButtonGroup[FIRST_NAME_LOOKUP]->addButton(m_RadioButton[FIRST_NAME_LOOKUP_DISABLED]); + + QGridLayout *layoutFirstNameLookup = new QGridLayout(m_SettingsGroup[FIRST_NAME_LOOKUP]); + layoutFirstNameLookup->addWidget(m_SettingsDescription[FIRST_NAME_LOOKUP], 0, 0, 1, 2); + layoutFirstNameLookup->addWidget(m_RadioButton[FIRST_NAME_LOOKUP_ENABLED]); + layoutFirstNameLookup->addWidget(m_RadioButton[FIRST_NAME_LOOKUP_DISABLED]); + m_SettingsGroup[FIRST_NAME_LOOKUP]->setLayout(layoutFirstNameLookup); + + // Protected players + m_SettingsGroup[PROTECTED_PLAYERS]->setTitle("Protected Players"); + m_SettingsDescription[PROTECTED_PLAYERS]->setText("Use lookup data from vlookup_protected_players.csv"); + m_RadioButton[PROTECTED_PLAYERS_DISABLED] = new QRadioButton("Disabled"); + m_RadioButton[PROTECTED_PLAYERS_ENABLED] = new QRadioButton("Enabled"); + m_ButtonGroup[PROTECTED_PLAYERS]->addButton(m_RadioButton[PROTECTED_PLAYERS_ENABLED]); + m_ButtonGroup[PROTECTED_PLAYERS]->addButton(m_RadioButton[PROTECTED_PLAYERS_DISABLED]); + + QGridLayout *layoutProtectedPlayers = new QGridLayout(m_SettingsGroup[PROTECTED_PLAYERS]); + layoutProtectedPlayers->addWidget(m_SettingsDescription[PROTECTED_PLAYERS], 0, 0, 1, 2); + layoutProtectedPlayers->addWidget(m_RadioButton[PROTECTED_PLAYERS_ENABLED]); + layoutProtectedPlayers->addWidget(m_RadioButton[PROTECTED_PLAYERS_DISABLED]); + m_SettingsGroup[PROTECTED_PLAYERS]->setLayout(layoutProtectedPlayers); + + // Exchange rate + m_SettingsGroup[EXCHANGE_RATE]->setTitle("Exchange Rate"); + m_SettingsDescription[EXCHANGE_RATE]->setText("The number of £££ per 1 unit of currency"); + m_ExchangeRate = new QDoubleSpinBox(m_SettingsGroup[EXCHANGE_RATE]); + m_ExchangeRate->setDecimals(5); + m_ExchangeRate->setSingleStep(0.00001); + m_ExchangeRate->setPrefix("£ "); + + QVBoxLayout *layoutExchangeRate = new QVBoxLayout(m_SettingsGroup[EXCHANGE_RATE]); + layoutExchangeRate->addWidget(m_SettingsDescription[EXCHANGE_RATE]); + layoutExchangeRate->addWidget(m_ExchangeRate); + m_SettingsGroup[EXCHANGE_RATE]->setLayout(layoutExchangeRate); + + // Year adjustment + m_SettingsGroup[YEAR_ADJUSTMENT]->setTitle("Year Adjustment"); + m_SettingsDescription[YEAR_ADJUSTMENT]->setText("Years in file minus years in DB = value below"); + m_YearAdjustment = new QSpinBox(m_SettingsGroup[YEAR_ADJUSTMENT]); + + QVBoxLayout *layoutYearAdjustment = new QVBoxLayout(m_SettingsGroup[YEAR_ADJUSTMENT]); + layoutYearAdjustment->addWidget(m_SettingsDescription[YEAR_ADJUSTMENT]); + layoutYearAdjustment->addWidget(m_YearAdjustment); + m_SettingsGroup[YEAR_ADJUSTMENT]->setLayout(layoutYearAdjustment); + + // Apply layout + m_SettingsWidget->setLayout(layout); + + // Initialise the default settings + this->applyDefaultSettings(); + + // Apply any saved settings + if(m_Settings->count() > m_SettingsPos) { + // Radio buttons + for(unsigned int i = 0; i < RADIO_BUTTON_COUNT; ++i) + m_RadioButton[i]->setChecked(m_Settings->get(m_SettingsPos + i).toBool()); + + // Exchange rate + m_ExchangeRate->setValue(m_Settings->get(m_SettingsPos + RADIO_BUTTON_COUNT).toDouble()); + + // Year adjustment + m_YearAdjustment->setValue(m_Settings->get(m_SettingsPos + RADIO_BUTTON_COUNT + 1).toInt()); + } +} + +// --- Apply/initialise the defaul settings --- // +void TransferImporter::applyDefaultSettings() +{ + m_ExchangeRate->setValue(1); + m_YearAdjustment->setValue(14); + m_RadioButton[IGNORE_ACCENTS]->setChecked(true); + m_RadioButton[CLUB_LOOKUP_ENABLED]->setChecked(true); + m_RadioButton[CLUB_NAMES_SHORT_ONLY]->setChecked(true); + m_RadioButton[FIRST_NAME_LOOKUP_ENABLED]->setChecked(true); + m_RadioButton[PROTECTED_PLAYERS_ENABLED]->setChecked(true); +} + + +/* ===================== */ +/* Spreadsheet */ +/* ===================== */ + +// --- Detect column adjustment (i.e. whether one or three name columns) --- // +void TransferImporter::detectColumnAdjustment() +{ + // Use the second column heading as the sample + const QString sampleColB = m_Spreadsheet->headerString(0,1).toLower(); + const QString sampleColC = m_Spreadsheet->headerString(0,2).toLower(); + + // If the third column heading contains a "name" type heading then the first two columns contain forename and surname. + // This means a column adjustment of two because the date of birth is in the fourth column, etc. + // | Col 0 | Col 1 | Col 2 | Col 1 + 2 = 3 | Col 2 + 2 = 4 | + // | Forename | Surname | Commonname| Date of Birth | Primary Nationality | + if(sampleColC.contains("name")) { + m_ColumnAdjustment = 2; + } + + // If the second column heading contains a "surname" type heading then the first column contains the forename only. + // This means a column adjustment of two because the date of birth is in the fourth column, etc. + // | Col 0 | Col 1 | Col 1 + 1 = 2 | Col 2 + 1 = 3 | + // | Forename | Surname | Date of Birth | Primary Nationality | + else if(sampleColB.contains("name")) { + m_ColumnAdjustment = 1; + } + + // Default adjustment of zero (i.e. assumption that the second column contains the date of birth) + else { + m_ColumnAdjustment = 0; + } + + // Set the adjusted column positions + NAME = COL_NAME; // This column is never adjusted as it is always first + DATE_OF_BIRTH = COL_DATE_OF_BIRTH + m_ColumnAdjustment; + NATION_1 = COL_NATION_1 + m_ColumnAdjustment; + NATION_2 = COL_NATION_2 + m_ColumnAdjustment; + CONTRACT_START = COL_CONTRACT_START + m_ColumnAdjustment; + CONTRACT_END = COL_CONTRACT_END + m_ColumnAdjustment; + VALUE = COL_VALUE + m_ColumnAdjustment; + CLUB = COL_CLUB + m_ColumnAdjustment; + ON_LOAN_FROM = COL_ON_LOAN_FROM + m_ColumnAdjustment; + GK = COL_GK + m_ColumnAdjustment; + SW = COL_SW + m_ColumnAdjustment; + D = COL_D + m_ColumnAdjustment; + DM = COL_DM + m_ColumnAdjustment; + M = COL_M + m_ColumnAdjustment; + AM = COL_AM + m_ColumnAdjustment; + ST = COL_ST + m_ColumnAdjustment; + WB = COL_WB + m_ColumnAdjustment; + FR = COL_FR + m_ColumnAdjustment; + SIDE_RIGHT = COL_SIDE_RIGHT + m_ColumnAdjustment; + SIDE_LEFT = COL_SIDE_LEFT + m_ColumnAdjustment; + SIDE_CENTRE = COL_SIDE_CENTRE + m_ColumnAdjustment; + FOOT_RIGHT = COL_FOOT_RIGHT + m_ColumnAdjustment; + FOOT_LEFT = COL_FOOT_LEFT + m_ColumnAdjustment; + INT_CAPS = COL_INT_CAPS + m_ColumnAdjustment; + INT_GOALS = COL_INT_GOALS + m_ColumnAdjustment; + ABILITY_CURRENT = COL_ABILITY_CURRENT + m_ColumnAdjustment; + ABILITY_POTENTIAL = COL_ABILITY_POTENTIAL + m_ColumnAdjustment; + REPUTATION_HOME = COL_REPUTATION_HOME + m_ColumnAdjustment; + REPUTATION_CURRENT = COL_REPUTATION_CURRENT + m_ColumnAdjustment; + SQUAD_NUMBER = COL_SQUAD_NUMBER + m_ColumnAdjustment; +} + + +/* ======================= */ +/* Status Widget */ +/* ======================= */ + +// --- Create status widget --- // +void TransferImporter::createStatusWidget() +{ + // Widget & layout + m_StatusWidget = new QWidget(this); + QGridLayout *layout = new QGridLayout(m_StatusWidget); + + // Add summary items + for(int i = 0; i < SUMMARY_COUNT; ++i) { + SummaryItem *tmp = new SummaryItem(m_StatusWidget); + layout->addWidget(tmp, 0, i); + m_SummaryItem.push_back(tmp); + } + + // Add timer items + for(int i = 0; i < TIMER_COUNT; ++i) { + SummaryItem *tmp = new SummaryItem(this); + layout->addWidget(tmp, 1, i); + m_TimerItem.push_back(tmp); + } + + m_StatusWidget->setLayout(layout); + + // Player matches + m_SummaryItem[SUMMARY_PLAYER_MATCHES]->setType(SummaryItem::SUCCESS); + m_SummaryItem[SUMMARY_PLAYER_MATCHES]->setText("Players matched"); + + // Player matches + m_SummaryItem[SUMMARY_PLAYER_PROTECTED]->setType(SummaryItem::INFO); + m_SummaryItem[SUMMARY_PLAYER_PROTECTED]->setText("Protected players"); + + // Players not matched + m_SummaryItem[SUMMARY_PLAYER_UNMATCHED]->setType(SummaryItem::INFO); + m_SummaryItem[SUMMARY_PLAYER_UNMATCHED]->setText("Players to be created"); + + // Club/nation errors + m_SummaryItem[SUMMARY_ERRORS]->setType(SummaryItem::ERROR); + m_SummaryItem[SUMMARY_ERRORS]->setText("Rows with errors"); + + // Import time per record + m_TimerItem[IMPORT_PER_RECORD]->setType(SummaryItem::SUBTLE); + m_TimerItem[IMPORT_PER_RECORD]->setText("Import\n(Per Record)"); + + // Import time total + m_TimerItem[IMPORT_TOTAL]->setType(SummaryItem::SUBTLE); + m_TimerItem[IMPORT_TOTAL]->setText("Import\n(Total)"); + + // Process time per record + m_TimerItem[PROCESS_PER_RECORD]->setType(SummaryItem::SUBTLE); + m_TimerItem[PROCESS_PER_RECORD]->setText("Loading\n(Per Record)"); + + // Process time total + m_TimerItem[PROCESS_TOTAL]->setType(SummaryItem::SUBTLE); + m_TimerItem[PROCESS_TOTAL]->setText("Loading\n(Total)"); +} + + +/* =============== */ +/* Timer */ +/* =============== */ + +// --- Reset timer labels --- // +void TransferImporter::resetTimerLabels() +{ + m_TimerItem[IMPORT_PER_RECORD]->setValue("--:--.---"); + m_TimerItem[IMPORT_TOTAL]->setValue("--:--.---"); + m_TimerItem[PROCESS_PER_RECORD]->setValue("--:--.---"); + m_TimerItem[PROCESS_TOTAL]->setValue("--:--.---"); +} + +// --- Calculate time and set timer labels --- // +void TransferImporter::setTimerLabels(const int &msecs, const int &recordCount, SummaryItem *total, SummaryItem *perRecord) +{ + QString tmp; + // Total time + QTime time(0,0,0); + time = time.addMSecs(msecs); + tmp = QString("%1:%2.%3") + .arg(time.minute(), 2, 10, QLatin1Char('0')) + .arg(time.second(), 2, 10, QLatin1Char('0')) + .arg(time.msec(), 3, 10, QLatin1Char('0')); + total->setValue(tmp); + + // Time per record + time.setHMS(0,0,0,0); + time = time.addMSecs(msecs / recordCount); + tmp = QString("%1:%2.%3") + .arg(time.minute(), 2, 10, QLatin1Char('0')) + .arg(time.second(), 2, 10, QLatin1Char('0')) + .arg(time.msec(), 3, 10, QLatin1Char('0')); + + // If the average time per record is less than 1msec then indicate this with the "~" (approximate) symbol + if((msecs / recordCount) < 1) + tmp.prepend("~"); + + perRecord->setValue(tmp); +} diff --git a/importer/transfer_importer.h b/importer/transfer_importer.h new file mode 100644 index 0000000..9270d48 --- /dev/null +++ b/importer/transfer_importer.h @@ -0,0 +1,212 @@ +#ifndef TRANSFER_IMPORTER_H +#define TRANSFER_IMPORTER_H + +// Qt headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Custom headers +#include "settings.h" +#include "spreadsheet/spreadsheet.h" +#include "ui/summary_item.h" + +// --- Transfer importer and player creator --- // +class TransferImporter : public QWidget +{ + Q_OBJECT + +public: + // Constructor + TransferImporter(Settings &settings, + int posSettings, + int posImportPath, + QWidget *parent = 0); + + // Settings + enum ENUM_SETTINGS { + ACCENTS, + CLUB_LOOKUP, + CLUB_NAMES, + FIRST_NAME_LOOKUP, + PROTECTED_PLAYERS, + EXCHANGE_RATE, + YEAR_ADJUSTMENT, + SETTINGS_COUNT + }; + + enum ENUM_RADIO_BUTTONS { + IGNORE_ACCENTS, + USE_ACCENTS, + CLUB_LOOKUP_DISABLED, + CLUB_LOOKUP_ENABLED, + CLUB_NAMES_SHORT_ONLY, + CLUB_NAMES_SHORT_AND_LONG, + FIRST_NAME_LOOKUP_DISABLED, + FIRST_NAME_LOOKUP_ENABLED, + PROTECTED_PLAYERS_DISABLED, + PROTECTED_PLAYERS_ENABLED, + RADIO_BUTTON_COUNT + }; + +private slots: + // Data functions + void onImport(); + + // File I/O + void onClose(); + void onOpen(); + void onSave(); + +private: + // Data functions + void process(const QTime &timer); + + // Left-hand pane: Control widget + QPushButton *m_CloseButton; + QPushButton *m_ImportButton; + QPushButton *m_OpenButton; + QPushButton *m_SaveButton; + QWidget *m_ControlWidget; + void createControlWidget(); + + // Left-hand pane: Progress widget + QProgressBar *m_ProgressBar; + QLabel *m_ProgressLabel; + QWidget *m_ProgressWidget; + void createProgressWidget(); + void incrementProgressBar(); + void incrementProgressBar(const QString &label); + void resetProgressBar(); + void stopProgressBar(); + + // Left-hand pane: Status widget + QVector m_SummaryItem; + QWidget *m_StatusWidget; + void createStatusWidget(); + + enum ENUM_SUMMARY_ITEMS { + SUMMARY_PLAYER_MATCHES, + SUMMARY_PLAYER_UNMATCHED, + SUMMARY_PLAYER_PROTECTED, + SUMMARY_ERRORS, + SUMMARY_COUNT + }; + + // Protected players + QVector m_ProtectedPlayers; + static QString s_ProtectedPlayersFileName; + void createBlankProtectedPlayersList(); + void loadProtectedPlayersList(QHash &staffList); + + // Right-hand pane: Settings widget + QWidget *m_SettingsWidget; + void applyDefaultSettings(); + void createSettingsWidget(); + + // Settings + Settings *m_Settings; + const int m_SettingsPos; + const int m_SettingImportPathPos; + QVector m_ButtonGroup; + QVector m_RadioButton; + QVector m_SettingsDescription; + QVector m_SettingsGroup; + QDoubleSpinBox *m_ExchangeRate; + QSpinBox *m_YearAdjustment; + + // Timer + QVector m_TimerItem; + void resetTimerLabels(); + void setTimerLabels(const int &msecs, const int &recordCount, SummaryItem *total, SummaryItem *perRecord); + + enum ENUM_TIMER_LABELS { + PROCESS_PER_RECORD, + PROCESS_TOTAL, + IMPORT_PER_RECORD, + IMPORT_TOTAL, + TIMER_COUNT + }; + + // Spreadsheet + Spreadsheet *m_Spreadsheet; + void detectColumnAdjustment(); + + // Columns + unsigned int m_ColumnAdjustment; + + // Unadjusted column positions + enum ENUM_COLUMN_POSITIONS { + COL_NAME, + COL_DATE_OF_BIRTH, + COL_NATION_1, + COL_NATION_2, + COL_CONTRACT_START, + COL_CONTRACT_END, + COL_VALUE, + COL_CLUB, + COL_ON_LOAN_FROM, + COL_GK, + COL_SW, + COL_D, + COL_DM, + COL_M, + COL_AM, + COL_ST, + COL_WB, + COL_FR, + COL_SIDE_RIGHT, + COL_SIDE_LEFT, + COL_SIDE_CENTRE, + COL_FOOT_RIGHT, + COL_FOOT_LEFT, + COL_INT_CAPS, + COL_INT_GOALS, + COL_ABILITY_CURRENT, + COL_ABILITY_POTENTIAL, + COL_REPUTATION_HOME, + COL_REPUTATION_CURRENT, + COL_SQUAD_NUMBER + }; + + // Adjusted column positions + unsigned short NAME; + unsigned short DATE_OF_BIRTH; + unsigned short NATION_1; + unsigned short NATION_2; + unsigned short CONTRACT_START; + unsigned short CONTRACT_END; + unsigned short VALUE; + unsigned short CLUB; + unsigned short ON_LOAN_FROM; + unsigned short GK; + unsigned short SW; + unsigned short D; + unsigned short DM; + unsigned short M; + unsigned short AM; + unsigned short ST; + unsigned short WB; + unsigned short FR; + unsigned short SIDE_RIGHT; + unsigned short SIDE_LEFT; + unsigned short SIDE_CENTRE; + unsigned short FOOT_RIGHT; + unsigned short FOOT_LEFT; + unsigned short INT_CAPS; + unsigned short INT_GOALS; + unsigned short ABILITY_CURRENT; + unsigned short ABILITY_POTENTIAL; + unsigned short REPUTATION_HOME; + unsigned short REPUTATION_CURRENT; + unsigned short SQUAD_NUMBER; +}; + +#endif // TRANSFER_IMPORTER_H diff --git a/importer/vlookup_hash.cpp b/importer/vlookup_hash.cpp new file mode 100644 index 0000000..dde5b6f --- /dev/null +++ b/importer/vlookup_hash.cpp @@ -0,0 +1,288 @@ +#include "vlookup_hash.h" + +// Qt headers +#include + + +/* ================================= */ +/* Additional VLookup Hash */ +/* ================================= */ + +// --- Constructor --- // +VLookupHash::VLookupHash(QString filename, char type) : + m_FileName(filename), + m_Type(type), + m_MatchedCount(0), + m_UnmatchedCount(0) +{ + +} + +// --- Constructor (using type) --- // +VLookupHash::VLookupHash(char type) : + m_MatchedCount(0), + m_UnmatchedCount(0) +{ + this->set(type); +} + +/* ====================== */ +/* Default Data */ +/* ====================== */ + +// --- Use default data if available --- // +void VLookupHash::initDefaultData(Spreadsheet &s) +{ + // Use default data or return if none available for the type + switch(m_Type) { + + case CLUBS: + s.addHeader("Find:", "Replace with:"); + this->initDefaultClubData(s); + break; + + case NATIONS: + s.addHeader("Find:", "Replace with:"); + this->initDefaultNationData(s); + break; + + case CLUBS_PREFIX_SUFFIX: + s.addHeader("Prefix/Suffix name:"); + this->initDefaultClubPrefixSuffixData(s); + break; + + default: + return; + } + + // Save default data + s.write(m_FileName); +} + +// --- Default data: Clubs --- // +void VLookupHash::initDefaultClubData(Spreadsheet &/*s*/) +{ + +} + +// --- Default data: Club prefixes/suffixes --- // +void VLookupHash::initDefaultClubPrefixSuffixData(Spreadsheet &s) +{ + s.add("afc"); + s.add("fc"); +} + +// --- Default data: Nations --- // +void VLookupHash::initDefaultNationData(Spreadsheet &s) +{ + s.add("American Virgin Islands", "US Virgin Islands"); + s.add("Antigua and Barbuda", "Antigua & Barbuda"); + s.add("Bonaire", "_none"); + s.add("Botsuana", "Botswana"); + s.add("Cape Verde", "Cape Verde Islands"); + s.add("Cayman-Inseln", "Cayman Islands"); + s.add("Chinese Taipei (Taiwan)", "Chinese Taipei"); + s.add("Congo DR", "Democratic Republic of Congo"); + s.add("Congo", "The Congo"); + s.add("Cookinseln", "Cook Islands"); + s.add("Cote d'Ivoire", "Ivory Coast"); + s.add("Falkland Islands", "_none"); + s.add("Faroe Island", "Faroe Islands"); + s.add("Federated States of Micronesia", "_none"); + s.add("French Guiana", "_none"); + s.add("Greenland", "_none"); + s.add("Guadeloupe", "_none"); + s.add("Guine", "Guinea"); + s.add("Hongkong", "Hong Kong"); + s.add("Ireland", "Republic of Ireland"); + s.add("Kiribati", "_none"); + s.add("Korea North", "North Korea"); + s.add("Korea South", "South Korea"); + s.add("Macao", "Macau"); + s.add("Macedonia", "FYR of Macedonia"); + s.add("Mariana Islands", "_none"); + s.add("Marshall Islands", "_none"); + s.add("Martinique", "_none"); + s.add("Monaco", "_none"); + s.add("Nauru", "_none"); + s.add("Netherlands", "Holland"); + s.add("Neukaledonien", "New Caledonia"); + s.add("Niue", "_none"); + s.add("Osttimor", "Timor"); + s.add("Palästina", "Palestine"); + s.add("Palau", "_none"); + s.add("Philippines", "The Philippines"); + s.add("Réunion", "_none"); + s.add("Saint-Martin", "_none"); + s.add("Sao Tome and Principe", "São Tomé & Principe"); + s.add("Sint Maarten", "_none"); + s.add("Southern Sudan", "South Sudan"); + s.add("St. Kitts &Nevis", "St Kitts & Nevis"); + s.add("St. Lucia", "Saint Lucia"); + s.add("St. Vincent & Grenadinen", "St Vincent & The Grenadines"); + s.add("Suriname", "Surinam"); + s.add("Tibet", "_none"); + s.add("Trinidad and Tobago", "Trinidad & Tobago"); + s.add("Turks- and Caicosinseln", "Turks and Caicos Islands"); + s.add("Tuvalu", "_none"); + s.add("Vatican", "_none"); + s.add("Zanzibar", "_none"); +} + + +/* ================== */ +/* File I/O */ +/* ================== */ + +// --- Load a spreadsheet containing vlookup data and add to a hash --- // +void VLookupHash::load(QHash &hash) +{ + Spreadsheet s; + s.setSilent(true); + + // Initialise default data if file could not be opened + if(!s.read(m_FileName)) { + this->initDefaultData(s); + } + + // Reset counters + m_MatchedCount = 0; + m_UnmatchedCount = 0; + + // Add the data + const int size = s.data()->size(); + for(int i = 0; i < size; ++i) { + int id = hash.value(s.matchString(i, REPLACE), NO_MATCH); + + if(id != NO_MATCH) { + hash.insert(s.matchString(i, FIND), id); + ++m_MatchedCount; + } + else { + ++m_UnmatchedCount; + } + } +} + +// --- Load a spreadsheet containing vlookup prefix/suffix data and add to a vector --- // +void VLookupHash::load(QVector &vector) +{ + Spreadsheet s; + s.setSilent(true); + + // Initialise default data if file could not be opened + if(!s.read(m_FileName)) { + this->initDefaultData(s); + } + + // Add the data + const int size = s.data()->size(); + for(int i = 0; i < size; ++i) { + vector.push_back(s.matchString(i, PREFIX_SUFFIX)); + } + + // Set counters + m_MatchedCount = size; + m_UnmatchedCount = 0; +} + + +/* ================ */ +/* Lookup */ +/* ================ */ + +// --- Look up a hash using prefix/suffix strings --- // +int VLookupHash::lookupPrefixSuffix(const QString &targetString, + QVector &vectorPrefixSuffix, QHash &hash, + const int noMatchValue) +{ + // Buffers + int result = noMatchValue; + QString target; + + // Iterate over vector + const int size = vectorPrefixSuffix.size(); + for(int i = 0; i < size; ++i) { + + QString prefix = QString("%1 ").arg(vectorPrefixSuffix.at(i)); + QString suffix = QString(" %1").arg(vectorPrefixSuffix.at(i)); + + // Add suffix + target = QString("%1%2").arg(targetString).arg(suffix); + result = hash.value(target, noMatchValue); + + if(result != noMatchValue) + break; + + // Add prefix + target = QString("%1%2").arg(prefix).arg(targetString); + result = hash.value(target, noMatchValue); + + if(result != noMatchValue) + break; + + // String size + const int stringSize = targetString.size(); + const int prefixSuffixSize = prefix.size(); // Both prefix and suffix are the same string size + + // Remove suffix + if(targetString.endsWith(suffix)) { + target = targetString.left(stringSize - prefixSuffixSize); + result = hash.value(target, noMatchValue); + + if(result != noMatchValue) + break; + } + + // Remove prefix + else if(targetString.startsWith(prefix)) { + target = targetString.right(stringSize - prefixSuffixSize); + result = hash.value(target, noMatchValue); + + if(result != noMatchValue) + break; + } + } + + return result; +} + + + +/* ================== */ +/* Set Data */ +/* ================== */ + +// --- Set data --- // +void VLookupHash::set(QString filename, char type) +{ + m_FileName = filename; + m_Type = type; +} + +// --- Set data (based upon type) --- // +void VLookupHash::set(char type) +{ + m_Type = type; + + switch(m_Type) { + case CLUBS: + m_FileName = "vlookup_clubs.csv"; + break; + + case CLUB_COMPS: + m_FileName = "vlookup_club_comps.csv"; + break; + + case NATIONS: + m_FileName = "vlookup_nations.csv"; + break; + + case CLUBS_PREFIX_SUFFIX: + m_FileName = "vlookup_clubs_prefix_suffix.csv"; + break; + + default: + return; + } +} diff --git a/importer/vlookup_hash.h b/importer/vlookup_hash.h new file mode 100644 index 0000000..4d1dce7 --- /dev/null +++ b/importer/vlookup_hash.h @@ -0,0 +1,76 @@ +#ifndef VLOOKUP_HASH_H +#define VLOOKUP_HASH_H + +// Qt headers +#include +#include +#include + +// Custom headers +#include "../spreadsheet/spreadsheet.h" + +// --- Additional VLookup Hash --- // +class VLookupHash +{ +private: + // Counts + unsigned int m_MatchedCount; + unsigned int m_UnmatchedCount; + + // Default data + void initDefaultData(Spreadsheet &s); + void initDefaultClubData(Spreadsheet &/*s*/); + void initDefaultClubPrefixSuffixData(Spreadsheet &s); + void initDefaultNationData(Spreadsheet &s); + + // File I/O + QString m_FileName; + + // Underlying data + //QHash *m_Hash; + //QVector *m_Vector; + + // Lookup type + char m_Type; + + // Match flag + enum ENUM_MATCH_FLAG { + NO_MATCH = -99999999 + }; + + // Spreadsheet columns + enum ENUM_SPREADSHEET_COLUMNS { + PREFIX_SUFFIX = 0, + FIND = 0, + REPLACE + }; + +public: + // Lookup types + enum ENUM_LOOKUP_TYPES { + NONE, + CLUBS, + CLUB_COMPS, + NATIONS, + CLUBS_PREFIX_SUFFIX + }; + + // Constructor + VLookupHash(QString filename, char type = NONE); + VLookupHash(char type); + + // File I/O + void load(QHash &hash); + void load(QVector &vector); + + // Lookup + int lookupPrefixSuffix(const QString &targetString, + QVector &vectorPrefixSuffix, QHash &hash, + const int noMatchValue); + + // Set data + void set(QString filename, char type); + void set(char type); +}; + +#endif // VLOOKUP_HASH_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..2b1b756 --- /dev/null +++ b/main.cpp @@ -0,0 +1,30 @@ +#include "mainwindow.h" +#include +#include "ui/style_sheet.h" + +int main(int argc, char *argv[]) +{ + // HIDPI support + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + + // Application details + QCoreApplication::setApplicationName("Transfer Tool"); + QCoreApplication::setApplicationVersion(APP_VERSION); + QCoreApplication::setOrganizationName("EHM The Blue Line"); + QCoreApplication::setOrganizationDomain("ehmtheblueline.com"); + + // Application + QApplication a(argc, argv); + + // Stylesheet + StyleSheet *s = new StyleSheet(); + s->get(); + delete s; + + // Main window + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..8f7d539 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,306 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +// Qt headers +#include +#include +#include +#include +#include +#include + +// Application headers +#include "spreadsheet/spreadsheet.h" + +// Database headers +#include "database/city.h" +#include "database/club.h" +#include "database/index.h" +#include "database/staff.h" + +// Model headers +#include "models/index_model.h" + + +/* ===================== */ +/* Main Window */ +/* ===================== */ + +// --- Default constructor --- // +// --- THIS IS THE ESSENTIALLY THE ENTRY POINT OF THE PROGRAM --- // +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + // Setup the .ui + ui->setupUi(this); + + // Application details + this->setWindowTitle(QString("%1 v%2").arg(qApp->applicationName()).arg(qApp->applicationVersion())); + qApp->setWindowIcon(QIcon("cmxf.ico")); + + // Initialise the window + this->createWindow(); + + // Add the toolbar buttons + this->createToolBarButtons(); + + // Initialise the status bar + this->createStatusBar(); +} + +// --- Default destructor --- // +MainWindow::~MainWindow() +{ + delete ui; +} + + +/* ================== */ +/* File I/O */ +/* ================== */ + +// --- Slot: Open --- // +void MainWindow::onOpen() +{ + QString f = QFileDialog::getOpenFileName(this, tr("Open Database"), + m_Settings.text(DATABASE_PATH), + "CM 01/02 Database (index.dat)"); + + // Abort if Open dialog cancelled + if(f.isEmpty()) + return; + else + m_Settings.set(f, DATABASE_PATH); + + // Load the database + if(m_Database.load(f)) { + this->showTabs(); + } +} + +// --- Slot: Save --- // +void MainWindow::onSave() +{ + const bool result = m_Database.save(m_Settings.text(DATABASE_PATH)); + + if(result) + QMessageBox::information(this, + QStringLiteral("Database Saved"), + QString("The database was successfully saved to %1").arg(m_Settings.text(DATABASE_PATH))); + else + QMessageBox::critical(this, + QStringLiteral("Error"), + QString("An error was encountered when attempting to save to %1") + .arg(m_Settings.text(DATABASE_PATH))); +} + +// --- Slot: Save As --- // +void MainWindow::onSaveAs() +{ + QString f = QFileDialog::getSaveFileName(this, tr("Save Database"), + m_Settings.text(DATABASE_PATH), + "CM 01/02 Database (index.dat)"); + + // Abort if Save dialog cancelled + if(f.isEmpty()) + return; + else { + m_Settings.set(f, DATABASE_PATH); + this->onSave(); + } +} + + +/* ===================== */ +/* Spreadsheet */ +/* ===================== */ + +// --- Slot: Read buffer change --- // +void MainWindow::onReadBufferChange(const int &i) +{ + Spreadsheet::setReadBufferSize(i); +} + + +/* ======================== */ +/* User Interface */ +/* ======================== */ + +// --- Create the exporter user interface --- // +void MainWindow::createExporter(Exporter *e) +{ + e->add(&City::exportData, + "Cities", + "List of cities."); + + e->add(&Club::exportData, + "Clubs", + "List of clubs."); + + e->add(&Club::exportRosters, + "Club Rosters", + "Club roster list in the form of Staff Id numbers. This is primarily for testing purposes."); + + e->add(&Staff::exportExtendedList, + "Staff List (Extended)", + "Staff list with extended data."); + + e->add(&Staff::exportTransferList, + "Staff List (Transfer Importer Format)", + "Staff list formatted as a Transfer Importer spreadsheet."); + + e->add(&Staff::exportTransferList_NoAccents, + "Staff List (Transfer Importer Format) (NO ACCENTS)", + "Staff list formatted as a Transfer Importer spreadsheet (no accents in staff and club names)."); +} + +// --- Create the status bar --- // +void MainWindow::createStatusBar() +{ + +} + +// --- Create the toolbar buttons --- // +void MainWindow::createToolBarButtons() +{ + // Create the toolbar + m_ToolBar = new QToolBar(this); + this->addToolBar(m_ToolBar); + m_ToolBar->setFloatable(false); + m_ToolBar->setMovable(true); + + // Open + m_ButtonFileOpen = new QToolButton(m_ToolBar); + m_ButtonFileOpen->setIcon(QIcon(":/buttons/document-open.png")); + m_ButtonFileOpen->setText("Open"); + m_ButtonFileOpen->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_ToolBar->addWidget(m_ButtonFileOpen); + QObject::connect(m_ButtonFileOpen, &QToolButton::clicked, + this, &MainWindow::onOpen); + + // Separator line + m_ToolBar->addSeparator(); + + // Save + m_ButtonFileSave = new QToolButton(m_ToolBar); + m_ButtonFileSave->setIcon(QIcon(":/buttons/document-save.png")); + m_ButtonFileSave->setText("Save"); + m_ButtonFileSave->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_ToolBar->addWidget(m_ButtonFileSave); + QObject::connect(m_ButtonFileSave, &QToolButton::clicked, + this, &MainWindow::onSave); + + // Save As + m_ButtonFileSaveAs = new QToolButton(m_ToolBar); + m_ButtonFileSaveAs->setIcon(QIcon(":/buttons/document-save-as.png")); + m_ButtonFileSaveAs->setText("Save As"); + m_ButtonFileSaveAs->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_ToolBar->addWidget(m_ButtonFileSaveAs); + QObject::connect(m_ButtonFileSaveAs, &QToolButton::clicked, + this, &MainWindow::onSaveAs); + + // Separator line + m_ToolBar->addSeparator(); + + // Spreadsheet row buffer size + QToolButton *labelRowBufferSize = new QToolButton(m_ToolBar); + labelRowBufferSize->setIcon(QIcon(":/buttons/row-buffer.png")); + labelRowBufferSize->setText("Row Buffer:"); + labelRowBufferSize->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_ToolBar->addWidget(labelRowBufferSize); + + m_SpinBoxRowReadBufferSize = new QSpinBox(m_ToolBar); + m_SpinBoxRowReadBufferSize->setRange(100, 1000000); + m_SpinBoxRowReadBufferSize->setValue(Spreadsheet::readBufferSize()); + m_ToolBar->addWidget(m_SpinBoxRowReadBufferSize); + QObject::connect(m_SpinBoxRowReadBufferSize, static_cast(&QSpinBox::valueChanged), + this, &MainWindow::onReadBufferChange); + + // Separator line + m_ToolBar->addSeparator(); +} + +// --- Create the window user interface --- // +void MainWindow::createWindow() +{ + // Window minimum dimensions + this->setMinimumSize(1152, 648); + + // Main window user interface + m_TabWidget = new QTabWidget(ui->centralWidget); + + // Set the layout of the main window + QGridLayout *layout = new QGridLayout(ui->centralWidget); + layout->addWidget(m_TabWidget); + ui->centralWidget->setLayout(layout); + + // Introductory text + m_IntroLabel = new QTextEdit(m_TabWidget); + m_TabWidget->addTab(m_IntroLabel, "User Guide"); + m_IntroLabel->setReadOnly(true); + m_IntroLabel->setContentsMargins(10,10,10,10); + + // CSS + m_IntroLabel->document()->setDefaultStyleSheet( + QStringLiteral("p { font-size: 10px; }" + "h1 { clear: both; }" + "h3 { clear: both; color: #4e4e4e; font-style: italic; }" + "li { margin-bottom: 10px; }")); + + // About + m_IntroLabel->insertHtml(QStringLiteral("

Credits

" + "

Written by: Archibalduk - " + "www.ehmtheblueline.com

" + "

Testing by: Dermotron - " + "www.champman0102.co.uk

" + "

Icons

" + "

All icons are designed by " + "www.icons8.com and are used under the Creative Commons Attribution-NoDerivs" + "3.0 Unported licence.

" + "

Licence

" + "

This application is licensed under the GNU General Public License Version 3 (GPLv3). " + "In summary, this means that this is a free and open source program. You may use the source " + "code from this program in your own programs provided you also release your program " + "under the GPLv3 (which means that your program must also be free and open source). ")); + + m_IntroLabel->insertHtml(QString("I used Qt %1 and Microsoft Visual C++ %2 to develop and build this program.




") + .arg(QT_VERSION_STR).arg(_MSC_VER)); + + QFile file("readme.html"); + if(file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QTextStream stream(&file); + m_IntroLabel->insertHtml(stream.readAll()); + } + else + m_IntroLabel->insertHtml("

Click on Open to load a database

"); + + // Scroll to the top of the introductory text area + m_IntroLabel->moveCursor(QTextCursor::Start); + + file.close(); +} + +// --- Add and show the various tabs --- // +void MainWindow::showTabs() +{ + // Transfer importer + TransferImporter *transferImporter = new TransferImporter(m_Settings, + TRANSFER_IMPORTER_SETTINGS_START_POS, + IMPORT_PATH, + m_TabWidget); + m_TabWidget->addTab(transferImporter, "Transfer Import"); + m_TabWidget->setCurrentIndex(1); // Display the Transfer Import tab + + // Index table + IndexModel *indexModel = new IndexModel(Index::db); + m_IndexTable = new QTableView(m_TabWidget); + m_IndexTable->setModel(indexModel); + m_TabWidget->addTab(m_IndexTable, "Index"); + + // Data exporter + Exporter *exporter = new Exporter(this); + this->createExporter(exporter); + m_TabWidget->addTab(exporter, "Data Export"); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..cb45f80 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,86 @@ +#ifndef MAIN_WINDOW_H +#define MAIN_WINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include + +// Importer headers +#include "importer/transfer_importer.h" + +// Database headers +#include "database/database.h" + +// Other custom headers +#include "exporter/exporter.h" +#include "settings.h" + +namespace Ui { +class MainWindow; +} + +// --- Main window --- // +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + // Constructor + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + // File I/O + void onOpen(); + void onSave(); + void onSaveAs(); + + // Spreadsheet + void onReadBufferChange(const int &i); + +private: + // Database + Database m_Database; + + // Settings + Settings m_Settings; + + enum ENUM_SETTINGS { + DATABASE_PATH, + EXPORT_PATH, + IMPORT_PATH, + TRANSFER_IMPORTER_SETTINGS_START_POS, + TRANSFER_IMPORTER_SETTINGS_END_POS = (TransferImporter::SETTINGS_COUNT - 1) + }; + + // Spreadsheet + QSpinBox *m_SpinBoxRowReadBufferSize; + + // Tables + QTableView *m_IndexTable; + + // Tool buttons + QToolButton *m_ButtonFileOpen; + QToolButton *m_ButtonFileSave; + QToolButton *m_ButtonFileSaveAs; + + // User interfece (functions) + void createExporter(Exporter *e); + void createStatusBar(); + void createToolBarButtons(); + void createWindow(); + void showTabs(); + + // User interface (members) + QTextEdit *m_IntroLabel; + QTabWidget *m_TabWidget; + QToolBar *m_ToolBar; + Ui::MainWindow *ui; +}; + +#endif // MAIN_WINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..4879724 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,21 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + diff --git a/models/index_model.cpp b/models/index_model.cpp new file mode 100644 index 0000000..f77eb14 --- /dev/null +++ b/models/index_model.cpp @@ -0,0 +1,112 @@ +#include "index_model.h" + +/* ===================== */ +/* Index Model */ +/* ===================== */ + +// --- Constructor --- // +IndexModel::IndexModel(QVector &data) : + m_Data(&data) +{ + +} + + +/* ================ */ +/* Counts */ +/* ================ */ + +// --- Column count --- // +int IndexModel::columnCount(const QModelIndex & /*parent*/) const +{ + return COLUMN_COUNT; +} + +// --- Row/QVector count --- // +int IndexModel::rowCount(const QModelIndex & /*parent*/) const +{ + return m_Data->size(); +} + + +/* ============== */ +/* Data */ +/* ============== */ + +// --- Get data --- // +QVariant IndexModel::data(const QModelIndex &index, int role) const +{ + if(role == Qt::DisplayRole || role == Qt::EditRole) + { + Index *i = &(*m_Data)[index.row()]; + + switch(index.column()) { + + case FILE_NAME: + return i->getFileName(); + + case FILE_ID: + return i->FileId; + + case TABLE_SIZE: + return i->TableSize; + + case OFFSET: + return i->Offset; + + case VERSION: + return i->Version; + } + } + + return QVariant(); +} + + +/* ================ */ +/* Header */ +/* ================ */ + +// --- Get header --- // +QVariant IndexModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + // Horizontal header + if(orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + switch(section) { + + case FILE_NAME: + return "Filename"; + + case FILE_ID: + return "ID"; + + case TABLE_SIZE: + return "Records"; + + case OFFSET: + return "Offest"; + + case VERSION: + return "Version"; + } + } + + // Vertical header (i.e. row numbers) + else if(orientation == Qt::Vertical && role == Qt::DisplayRole) + return section+1; + + return QVariant(); +} + + +/* =============== */ +/* Flags */ +/* =============== */ + +// --- Flags --- // +Qt::ItemFlags IndexModel::flags(const QModelIndex &index) const +{ + // Read only + return QAbstractTableModel::flags(index); +} diff --git a/models/index_model.h b/models/index_model.h new file mode 100644 index 0000000..0d6853b --- /dev/null +++ b/models/index_model.h @@ -0,0 +1,46 @@ +#ifndef INDEX_MODEL_H +#define INDEX_MODEL_H + +// Qt headers +#include +#include + +// Recorder headers +#include "../database/index.h" + +// --- Index Model --- // +class IndexModel : public QAbstractTableModel +{ +private: + // Pointer to underlying data + QVector *m_Data; + +public: + // Constructor + IndexModel(QVector &data); + + // Counts + int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + + // Data + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + + // Header + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + + // Flags + Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; + + // Column numbers + enum ENUM_COLUMNS { + FILE_NAME = 0, + FILE_ID, + TABLE_SIZE, + OFFSET, + VERSION, + COLUMN_COUNT + }; +}; + +#endif // INDEX_MODEL_H diff --git a/progress_window.cpp b/progress_window.cpp new file mode 100644 index 0000000..1942ee9 --- /dev/null +++ b/progress_window.cpp @@ -0,0 +1,167 @@ +#include "progress_window.h" + +// --- Static setting: Hide progress window --- // +bool ProgressWindow::s_HideWindow = false; + +/* ========================= */ +/* Progress Window */ +/* ========================= */ + +// --- Default constructor --- // +ProgressWindow::ProgressWindow(QString t, unsigned int r, bool ok) : + counter(0), + title(t), + useOkButton(ok) +{ + // Construct progress dialog window + win = new QProgressDialog(title, "OK", 0, max, 0, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); + win->setWindowTitle(title); + win->setLabelText("Processing..."); + + // Dimensions + win->setMinimumWidth(250); + + // Initialise the progress counter to zero + win->setValue(0); + + // Set range and refresh threshold + this->setRange(r+2); + + // Hide the OK button if appropriate + if(!useOkButton) + win->setCancelButton(0); + + // Window behaviour + win->setAutoReset(false); + win->setModal(true); + + // Display the progress dialog window (if not hidden) + if(s_HideWindow == false) + win->show(); + else // Else don't show the window and reset the Hide Window setting for next time + s_HideWindow = false; +} + + +/* =========================== */ +/* Progress Controls */ +/* =========================== */ + +// --- Complete the progress bar --- // +void ProgressWindow::complete(QString message) +{ + win->setValue(max); + win->setLabelText(message); + + if(useOkButton == false) + win->close(); + + QApplication::processEvents(); +} + +// --- Hide the progress window --- // +void ProgressWindow::hide() +{ + s_HideWindow = true; + win->accept(); +} + +// --- Increment the progress bar --- // +void ProgressWindow::increment(QString label) +{ + win->setLabelText(label); + win->setValue(++counter); + + QApplication::processEvents(); +} + +// --- Increment the progress bar without updating the label text --- // +void ProgressWindow::incrementSilent(int i) +{ + counter += i; + win->setValue(counter); + QApplication::processEvents(); +} + +// --- Increment the progress bar but only refresh every 1 percent --- // +void ProgressWindow::incrementStep() +{ + ++counter; + win->setValue(counter); + + if(step % counter == 0) + QApplication::processEvents(); +} + +// --- Increment the progress bar but only refresh every 1 percent --- // +void ProgressWindow::incrementSteps(unsigned int i) +{ + counter += i; + win->setValue(counter); + + if(step % counter == 0) + QApplication::processEvents(); +} + +// --- Set the progress bar to a specific value but only refresh every 1 percent --- // +void ProgressWindow::setSteps(unsigned int i) +{ + win->setValue(i); + + if(step % i == 0) + QApplication::processEvents(); +} + +// --- Set the progress bar to a specific value --- // +void ProgressWindow::setValue(int i) +{ + win->setValue(i); + QApplication::processEvents(); +} + + +/* =========================== */ +/* Progress Settings */ +/* =========================== */ + +// --- Setup the progress bar ready to start --- // +void ProgressWindow::set(QString t, unsigned int r, bool ok) +{ + // Initialise the progress counter to zero + win->setValue(0); + QApplication::processEvents(); + + setTitle(t); + setRange(r+2); + + useOkButton = ok; +} + +// --- Set the max range --- // +void ProgressWindow::setRange(unsigned int r) +{ + // Set range + max = r+2; + win->setRange(0, max); + + // Step refresh threshold + if(max > 2) + step = max / 50; // 100/50 = Update every 2 per cent. + else + step = 1; +} + +/* ===================== */ +/* Close Event */ +/* ===================== */ + +// --- Reset the dialog window on close --- // +void ProgressWindow::closeEvent(QCloseEvent *event) +{ + if(counter == max) { + win->setValue(0); + event->accept(); + } + else + event->ignore(); +} diff --git a/progress_window.h b/progress_window.h new file mode 100644 index 0000000..b6c6c20 --- /dev/null +++ b/progress_window.h @@ -0,0 +1,55 @@ +#ifndef PROGRESS_WINDOW_H +#define PROGRESS_WINDOW_H + +#include +#include +#include +#include + +#pragma warning(disable: 4103) // Ignore pragma pack warnings. Packing is needed for compatibility with CM and EHM +#pragma pack(1) + +class ProgressWindow +{ +private: + QProgressDialog *win; + + unsigned int counter; + unsigned int max; + unsigned int step; + bool useOkButton; + QString title; + + static bool s_HideWindow; + +public: + // Default constructor + ProgressWindow(QString t = "Progress Dialog Window", unsigned int r = 100, bool ok = false); + + // Default destructor + ~ProgressWindow() {} + + // Progress controls + void abort() { win->cancel(); } + void complete(QString message = "Process Complete"); + void hide(); + void increaseRange(unsigned int r) { setRange(max + r);} + void increment(QString label = "Processing. Please Wait..."); + void incrementSilent(int i = 1); + void incrementStep(); + void incrementSteps(unsigned int i); + void setSteps(unsigned int i); + void setValue(int i); + + // Progress settings + static void hideWindow(bool b = true) { s_HideWindow = b; } + void set(QString t, unsigned int r = 100, bool ok = false); + void setLabel(QString label) { win->setLabelText(label); } + void setRange(unsigned int r); + void setTitle(QString t) { title = t; win->setWindowTitle(title); } + +protected: + void closeEvent(QCloseEvent *event); +}; + +#endif // PROGRESS_WINDOW_H diff --git a/readme.html b/readme.html new file mode 100644 index 0000000..def427b --- /dev/null +++ b/readme.html @@ -0,0 +1,76 @@ +

Step by Step Guide

+

Step 1: Open the database

+

Click on Open to open a database.

+

Step 2: Open the spreadsheet

+

On the Transfer Import tab click on Open File and select the spreadsheet you would like to import. The Tool will then process your spreadsheet.

+

Note: Before opening the spreadsheet, ensure that the Row Buffer size at the top of the Tool's window is greater than or equal to the number of rows in your spreadsheet. A smaller Row Buffer may slow down the import process slightly.

+

Step 3: Check for errors

+

A process summary will be displayed in coloured boxes once the Tool has finished processing the spreadsheet as follows: +

  • Players matched: The number of players in the spreadsheet which the Tool has identified as matching players in the database;
  • +
  • Players to be created: The number of players in the spreadsheet which the Tool could not match against players in the database. Consequently those players will be added as new players in the database;
  • +
  • Protected players: The number of players identified as being protected (see Protected Players and vlookup_protected_players.csv below); and
  • +
  • Rows with errors: The number of rows that contain a club and/or nation which could not be found in the database. These rows will not be processed if you proceed with the import.

+

If the number of players to be created is more than you expected or if there are rows with errors: +

  1. Click on Save File to save a copy of your spreadsheet;
  2. +
  3. Open your saved spreadsheet and scroll to the far right columns of the spreadsheet where you will find the following additional columns: +
    • Staff ID: An ID of -127 means that the player will be created. Any other number refers to the player's Staff ID in the database;
    • +
    • Nation 1 ID & Nation 2 ID: An ID of -127 means that the chosen nation does not exist and this should be corrected in the spreadsheet. A -126 means that the cell is blank (this is not an error - it just means that the existing Nation ID value in the database will not be modified). A -1 simply means no nation selected (this is not an error) and any other number refers to the nation's Nation ID in the database;
    • +
    • Club ID & Loan Club ID: An ID of -127 means that the chosen club does not exist and this should be corrected in the spreadsheet. A -126 means that the cell is blank (this is not an error - it just means that the existing Club ID value in the database will not be modified). A -1 simply means no club selected (i.e. free agent - this is not an error) and any other number refers to the club's Club ID in the database; and
    • +
    • Protected: 1 = the player is protected and 0 = the player is not protected.
  4. +
  5. If you decide that the spreadsheet is fine and there are no errors then proceed with Step 4 below.
  6. +
  7. If there errors that you need to correct then do the following: +
    1. Click on the Close File button in the Transfer Import screen of the Tool. This will cancel the import process;
    2. +
    3. Correct the errors in the spreadsheet (you can either edit your original spreadsheet or the one you saved from the Tool - it doesn't matter which); and
    4. +
    5. Start again from Step 2 above.

+

Step 4: Import the data

+

Once you are happy that the spreadsheet has no errors, click on the Import Data button to import the spreadsheet into the database.

+

Step 5: Close the spreadsheet

+

Click on the Close File button in order to close the spreadsheet once the import process is complete.

+

Step 6: Save the database

+

Click on Save or Save As to save your database.

+ +

Important Information

+

Non-Playing Staff

+

The Tool can be used to edit the contract data of non-playing staff but it cannot be used to (1) edit Non-Player Ability or Non-Player Reputation or (2) create new non-players.

+

Contract Start Date

+

This column is imported as Date Joined Club in the database.

+

First & Second Name vs Common Name

+

To edit an existing player, you can either enter his first and second name or his common name. You cannot edit an existing player's name or common name.

+

When creating a new player, you can set their first and second name and/or their common name.

+

Transfer Value, Wage and World Reputation

+

Note that the Tool will automatically set a player's Wage and World Reputation based upon the Transfer Value entered into the spreadsheet (see the Transfer Value column). If you do not want this to happen then do not enter any Value.

+ +

Settings

+

Row Buffer

+

This is used to fine-tune the performance of the Tool. The smaller the buffer, the less RAM used by the Tool (in theory). The buffer should be set to a value which is greater than or equal to the number of rows in the spreadsheet to be imported. If the buffer is less than the number of rows in the spreadsheet then the Tool may load the spreadsheet at a lower speed because it will need to continually reallocate memory for each row over and above the buffer whilst loading the spreadsheet (the effect of the speed decrease might only be marginal however). Unless you have very little RAM or you are importing a huge spreadsheet then you can usually leave this setting at the default value.

+

Accent Matching

+

Determines whether accents are taken into account when attempting to match any text from the imported spreadsheet against text in the database (e.g. person names, club names, competition names, etc). Use the enabled setting to apply strict accent matching. This means that "Dembele" would not match against "Dembélé". Use the disabled setting to relax matching such that "Dembele" in the spreadsheet would be treated as a match against "Dembélé". This means that you do not need to worry about using the correct accented characters in the spreadsheet.

+

Club Name Lookup

+

Determines whether the club names used in the imported spreadsheet will be corrected using the data from the vlookup_clubs.csv file which is located in the same folder as the Transfer Tool.exe file. Use the enabled setting to allow lookup correction. This is essentially a find and replace function. A "Find" and a "Replace" setting is specified in the vlookup_clubs.csv which the Tool uses to find and replace club names in the imported spreadsheet. This is intended to be a quick and easy way of correcting club names in the imported spreadsheet. Use the disabled setting to prevent any club name lookup.

+

Club Name Source

+

Determines whether the club names in the imported spreadsheet will be matched against short names and long names or short names only from the database.

+

First Name Lookup

+

This setting does not do anything yet.

+

Protected Players

+

Determines whether the players listed in the vlookup_protected_players.csv file will be protected when importing changes from a spreadsheet. Using the enabled will prevent protected players from having any of their protected details modified by the Transfer Tool. The disabled setting prevents this behaviour, meaning that no players will be protected.

+

The protected details are: Footedness, Positions, Sides, Reputation/Value and Current & Potential Ability.

+

Exchange Rate

+

This setting can be used to correct transfer value data in the imported spreadsheet if it has been entered using a foreign currency. Use an Exchange Rate of £1.00 if no exchange rate correction is to be applied.

+

Year Adjustment

+

This setting allows the Transfer Tool to match up the dates of birth in the database against the dates of birth in the imported spreadsheet where different year adjustments have been used. To calculate the adjustment, take the year from the spreadsheet and subtract the year in the database. For example, if David de Gea's year of birth in the database is 1976 and it is 1990 in the spreadsheet, the adjustment to be entered is 14 (1990 - 1976 = 14).

+

Vertical Lookup Settings Files

+

The vertical lookup files are all located in the same folder as the Transfer Tool.exe file. These files are used to apply corrections to the spreadsheet import file similar to the Excel VLookup function or a find and replace function. Each vlookup file contains a Find: and a Replace: column. The Find: column is used to list out the text you want to find and correct. The Replace: column is used to list out the corrected text to be inserted in place (this text should match the appropriate text from the database).

+

vlookup_clubs.csv

+

Find and replace club names.

+

vlookup_clubs_prefix_suffix.csv

+

This file is different from the other vlookup files. Rather than searching and replacing, this file sets out club name prefixes and suffixes which are to be disregarded by the Transfer Tool when attempting to match club names. The prefixes/suffixes are only used as a last resort - the Transfer Tool will in the first instance try to match against the full club short/long name and only if it cannot find a match will it try again with the prefixes/suffixes disregarded. For instance, if "AFC" is contained in the vlookup file, you can use "Wimbledon" in the imported spreadsheed even if the club is called "AFC Wimbledon" in the database (or vice-versa). This is because "AFC" will be a disregarded prefix/suffix.

+

vlookup_nations.csv

+

Find and replace nation names.

+

vlookup_protected_players.csv

+

List the names and dates of birth of the players that are to be protected for the purposes of the Protected Players setting. The Staff ID column is populated automatically by the Transfer Tool (it will insert the IDs from the most recently loaded database and will auto-update them each time a new database is opened - do not attempt to set the ID numbers manually as they will be ignored and overwritten by the Tool).

+ +

Other Settings Files

+

delimiter_repair.txt

+

Files using the csv format should use commas and semi-colons only for the purposes of indicating the end of a column/cell. However, it is easy to overlook this and mistakes can creep in. To mitigate against this, the delimiter_repair.txt file is used to auto-correct common mistakes. One such common mistake is to enter "Korea South" as "Korea, South". By listing this error in this file, the Transfer Tool will automatically remove the comma/semi-colon for you.

+

settings.dat

+

Internal settings for the Transfer Tool. Nothing to see here! ;-)

\ No newline at end of file diff --git a/settings.cpp b/settings.cpp new file mode 100644 index 0000000..3489219 --- /dev/null +++ b/settings.cpp @@ -0,0 +1,128 @@ +#include "settings.h" + +#include +#include +#include + +// --- Static data --- // +QString Settings::s_FileName("settings.dat"); // Settings file name +unsigned int Settings::s_Version = 1002; // Version control. Increase this value each time the file structure changes significantly + + +/* ================== */ +/* Settings */ +/* ================== */ + +// --- Default constructor (load settings) --- // +Settings::Settings() +{ + QFile f(s_FileName); + QDataStream in(&f); + + if(!f.open(QIODevice::ReadOnly)) + return; + + // Read version number + unsigned int version; + in >> version; + + // Only read the settings if the version is compatible + if(version == s_Version) + in >> m_Settings; + + f.close(); +} + +// --- Default destructor (save settings) --- // +Settings::~Settings() +{ + QFile f(s_FileName); + QDataStream out(&f); + + if(!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) + return; + + out << s_Version; + out << m_Settings; + + f.close(); +} + + +/* ===================== */ +/* Get Data */ +/* ===================== */ + +// --- Get size of QVariantList --- // +int Settings::count() +{ + return m_Settings.count(); +} + + +/* ===================== */ +/* Get Setting */ +/* ===================== */ + +// --- Get setting (variant) --- // +QVariant Settings::get(unsigned int pos) +{ + if(pos < static_cast(m_Settings.size())) + return m_Settings[pos]; + else + return QVariant(); +} + +// --- Get setting (string) --- // +QString Settings::text(unsigned int pos) +{ + if(pos < static_cast(m_Settings.size())) + return m_Settings[pos].toString(); + else + return QString(); +} + + +/* ===================== */ +/* Set Setting */ +/* ===================== */ + +// --- Set setting from bool --- // +void Settings::set(const bool &value, unsigned int pos) +{ + // Fill the QVariantList if the pos is beyond the current size of the list + while(static_cast(m_Settings.size()) <= pos ) + m_Settings << QVariant(); + + m_Settings[pos].setValue(value); +} + +// --- Set setting from double --- // +void Settings::set(const double &value, unsigned int pos) +{ + // Fill the QVariantList if the pos is beyond the current size of the list + while(static_cast(m_Settings.size()) <= pos ) + m_Settings << QVariant(); + + m_Settings[pos].setValue(value); +} + +// --- Set setting from int --- // +void Settings::set(const int &value, unsigned int pos) +{ + // Fill the QVariantList if the pos is beyond the current size of the list + while(static_cast(m_Settings.size()) <= pos ) + m_Settings << QVariant(); + + m_Settings[pos].setValue(value); +} + +// --- Set setting from string --- // +void Settings::set(const QString &text, unsigned int pos) +{ + // Fill the QVariantList if the pos is beyond the current size of the list + while(static_cast(m_Settings.size()) <= pos ) + m_Settings << QVariant(); + + m_Settings[pos].setValue(text); +} diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..2adb5c0 --- /dev/null +++ b/settings.h @@ -0,0 +1,40 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include +#include +#include + +// --- Application settings --- // +class Settings +{ +public: + // Constructor/Destructor + Settings(); + ~Settings(); + + // Get data + int count(); + + // Get setting + QVariant get(unsigned int pos); + QString text(unsigned int pos); + + // Set setting + void set(const bool &value, unsigned int pos); + void set(const double &value, unsigned int pos); + void set(const int &value, unsigned int pos); + void set(const QString &text, unsigned int pos); + +private: + // Settings + QVariantList m_Settings; + + // Settings file + static QString s_FileName; + + // Version + static unsigned int s_Version; +}; + +#endif // SETTINGS_H diff --git a/spreadsheet/spreadsheet - Copy.cpp b/spreadsheet/spreadsheet - Copy.cpp new file mode 100644 index 0000000..7903f94 --- /dev/null +++ b/spreadsheet/spreadsheet - Copy.cpp @@ -0,0 +1,560 @@ +#include "spreadsheet.h" +#include "../data_types/string.h" +#include "../progress_window.h" + +// Qt headers +#include +#include +#include +#include +#include +#include + +#include + +// --- Static data --- // +const int Spreadsheet::s_BufferSize = 150; +QChar Spreadsheet::s_Delimiter = Spreadsheet::getSystemDelimiter(); + + +/* ===================== */ +/* Spreadsheet */ +/* ===================== */ + +// --- Default constructor --- // +Spreadsheet::Spreadsheet(bool repairColumns) : + m_HideProgress(false), + m_Repair(repairColumns) +{ + +} + +// --- Default destructor --- // +Spreadsheet::~Spreadsheet() +{ + +} + + +/* ================== */ +/* Add Data */ +/* ================== */ + +// --- Add a QStringList as a new row of data --- // +void Spreadsheet::add(const QStringList &list) +{ + m_Data.push_back(list); +} + +// --- Add a variable number of QStrings as a new row (C++11 variadic function) --- // +/*void Spreadsheet::add(const QString values...) +{ + QStringList list; + + const int count = sizeof(values); + + va_list(args); + va_start(args, count); + + for(int i = 0; i < count; ++i) { + list << va_arg(args, QString); + } + + va_end(args); + + m_Data.push_back(list); +}*/ + +// --- Add one column to a new row (for convenience but slower than using the QStringList method) --- // +void Spreadsheet::add(const QString col1) +{ + QStringList list; + list << col1; + m_Data.push_back(list); +} + +// --- Add two columns to a new row (for convenience but slower than using the QStringList method) --- // +void Spreadsheet::add(const QString col1, const QString col2) +{ + QStringList list; + list << col1 << col2; + m_Data.push_back(list); +} + +// --- Add integer value as a new column to a row --- // +void Spreadsheet::add(unsigned int &row, const int &value) +{ + if(row < static_cast(m_Data.size())) + m_Data[row] << QString::number(value); +} + +// --- Add string value as a new column to a row --- // +void Spreadsheet::add(unsigned int &row, QString value) +{ + if(row < static_cast(m_Data.size())) + m_Data[row] << value; +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Pointer to data vector --- // +const QVector *Spreadsheet::data() +{ + return &m_Data; +} + +// --- Row cell date string formatted for matching against --- // +QString Spreadsheet::matchDateString(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Data.size()) && col < static_cast(m_Data[row].size())) + return String(m_Data[row][col]).getMatchDateString(); + else + return QString(); +} + + +// --- Row cell string formatted for matching against --- // +QString Spreadsheet::matchString(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Data.size()) && col < static_cast(m_Data[row].size())) + return String(m_Data[row][col]).getMatchString(); + else + return QString(); +} + +// --- Row cell number --- // +int Spreadsheet::number(unsigned int row, unsigned int col) +{ + // Out of range + if(row >= static_cast(m_Data.size())) + return NOT_A_NUMBER; + + // Not a number + else if(m_Data[row][col].isEmpty() || m_Data[row][col] == "-") // If empty or contains only a dash + return NOT_A_NUMBER; + + // Return as an integer + else + return m_Data[row][col].toInt(); +} + +// --- Pointer to row string list --- // +QStringList *Spreadsheet::row(unsigned int row) +{ + if(row < static_cast(m_Data.size())) + return &m_Data[row]; + else + return nullptr; +} + +// --- Row cell string --- // +QString Spreadsheet::string(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Data.size()) && col < static_cast(m_Data[row].size())) + return m_Data[row][col]; + else + return QString(); +} + +// --- Row cell variant --- // +QVariant Spreadsheet::variant(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Data.size()) && col < static_cast(m_Data[row].size())) + return m_Data[row][col]; + else + return QVariant(); +} + + +/* ================== */ +/* Set Data */ +/* ================== */ + +// --- Set integer value of a column of a row --- // +void Spreadsheet::set(unsigned int &row, unsigned int col, const int &value) +{ + // Abort if invalid row + if(row >= static_cast(m_Data.size())) + return; + + // Update existing column if it already exists + if(col < static_cast(m_Data[row].size())) + m_Data[row][col] = QString::number(value); + // Add if the column does not exist + else + m_Data[row] << QString::number(value); +} + + +/* =================== */ +/* Delimiter */ +/* =================== */ + +// --- Get standard delimiter based upon the system locale --- // +QChar Spreadsheet::getSystemDelimiter() +{ + QLocale locale; + return (locale.decimalPoint() == '.') ? ',' : ';'; +} + + +/* ================== */ +/* File I/O */ +/* ================== */ + +// --- Get file path --- // +QString Spreadsheet::getFilePath() +{ + int pos = m_FilePath.lastIndexOf("/"); + return m_FilePath.left(pos+1); +} + +// --- Load delimiter repair list --- // +void Spreadsheet::loadRepairStrings(QVector &repairSearch, QVector &repairReplace) +{ + // Open file + QFile f("delimiter_repair.txt"); + if(!f.open(QIODevice::ReadOnly | QIODevice::Text)) + return; + + // Text stream (for reading in the text from the file) + QTextStream s; + s.setCodec(QTextCodec::codecForName("Latin-1")); // ASCII + s.setDevice(&f); + + // Read the rows + while(!s.atEnd()) { + QString row = s.readLine(); + + // Search string + repairSearch.push_back(row); + + // Replacement string (i.e. string without the delimiter) + row.remove(s_Delimiter); + repairReplace.push_back(row); + } + + f.close(); + return; +} + +// --- Open file via a dialog window --- // +bool Spreadsheet::open(QString path, unsigned int headerCount) +{ + qDebug() << "Spreadsheet::open" << path; + + path = QFileDialog::getOpenFileName(0, "Open Comma/Tab Separated File", + path, + "Spreadsheet (*.csv *.txt)"); + + // Abort if the user has clicked Cancel + if(path.isEmpty()) + return false; + + // Read the file + m_Time.start(); + return this->read(path, headerCount); +} + +// --- Read file --- // +bool Spreadsheet::read(QString &path, unsigned int headerCount) +{ + m_FilePath = path; + + // Open file + QFile f(m_FilePath); + if(!f.open(QIODevice::ReadOnly | QIODevice::Text)) + return false; + + // Text stream (for reading in the text from the file) + QTextStream s; + s.setCodec(QTextCodec::codecForName("Latin-1")); // ASCII + s.setDevice(&f); + + // Detect the file delimiter + QString dSample = s.readLine(); + int comma = dSample.count(','); + int semicolon = dSample.count(';'); + int tab = dSample.count('\t'); + + // Set delimiter to comma or semicolon according to which appears the most in the first line + QChar d = (comma > semicolon) ? ',' : ';'; + + // Use tab as a delimiter if there are more tabs than commas and semicolons + if(tab > (comma + semicolon)) + d = '\t'; + + // Return the stream to the beginning of the file + s.seek(0); + + // Row counter + unsigned int count = 0; + + // Progress dialog + ProgressWindow p; + p.hideWindow(m_HideProgress); + p.set("Loading spreadsheet", f.size()); + + // Counters (for reserving sizes of QVector and QStringList + int columnReserve = 50; // QStringList + int rowReserve = 20000; // QVector + m_Data.reserve(rowReserve); + + // The conditional statement below could alternatively be placed within the while loop as this would make the code tidier + // and easier to maintain. But to keep the csv parsing as fast as possible (which is important when dealing with large + // files), it is better to place the while loop within the conditional statement. + + // Parse the csv (no column repair) + if(!m_Repair) { + while(!s.atEnd()) { + QString row = s.readLine(); + + // Pointer to the relevant string list + QVector *data = (count < headerCount) ? &m_Header : &m_Data; + + // Remove any text qualifiers (i.e. speech marks) + row.remove(QChar(0x22)); + + // Parse the row and add it to the string list + QStringList list; + list.reserve(columnReserve); + list = row.split(d); + data->append(list); + + // Update column counter + if(list.size() > columnReserve) + columnReserve = list.size(); + + // Increment the progress dialog + p.setSteps(s.pos()); + + // Increment the row counter + ++count; + } + } + // Parse the csv (with column repair) + else { + // Initialise the column repair vector + QVector repairSearch; + QVector repairReplace; + this->loadRepairStrings(repairSearch, repairReplace); + + while(!s.atEnd()) { + QString row = s.readLine(); + + // Check for any repairs + const int size = repairSearch.size(); + for(int i = 0; i < size; ++i) { + row.replace(repairSearch[i], repairReplace[i], Qt::CaseInsensitive); + } + + // Pointer to the relevant string list + QVector *data = (count < headerCount) ? &m_Header : &m_Data; + + // Remove any text qualifiers (i.e. speech marks) + row.remove(QChar(0x22)); + + // Parse the row and add it to the string list + QStringList list; + list.reserve(columnReserve); + list = row.split(d); + data->append(list); + + // Update column counter + if(list.size() > columnReserve) + columnReserve = list.size(); + + // Increment the progress dialog + p.setSteps(s.pos()); + + // Increment the row counter + ++count; + } + } + + p.complete(); + f.close(); + return true; +} + +// --- Create file/buffer via a dialog window but do not write any data to the hard drive --- // +bool Spreadsheet::create(QString path) +{ + return this->saveDialog(path); +} + +// --- Save file via a dialog window --- // +bool Spreadsheet::saveDialog(QString &path) +{ + // Use existing path if passed path is empty + if(path.isEmpty()) + path = m_FilePath; + + path = QFileDialog::getSaveFileName(0, "Save Comma/Tab Separated File", + path, + "Spreadsheet (*.csv *.txt)"); + + // Abort if the user has clicked Cancel + if(path.isEmpty()) + return false; + else { + m_FilePath = path; + return true; + } +} +// --- Save file via a dialog window and write the data to the hard drive --- // +bool Spreadsheet::save(QString path) +{ + if(saveDialog(path) == true) { + m_Time.start(); + return this->write(); + } + else + return false; +} + +// --- Write file --- // +bool Spreadsheet::write(QString path) +{ + if(!path.isEmpty()) + m_FilePath = path; + + // Create file + QFile f(m_FilePath); + if(!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + return false; + + // Text stream (for writing text to the file) + QTextStream s; + s.setCodec(QTextCodec::codecForName("Latin-1")); // ASCII + s.setDevice(&f); + + // Progress dialog + ProgressWindow p; + p.hideWindow(m_HideProgress); + p.set("Saving spreadsheet", m_Header.size() + m_Data.size()); + + // Write the csv (iterate first over header and then over data strings) + for(int i = 0; i < 2; ++i) { + + // Pointer to the relevant string list + QVector *data = (i == 0) ? &m_Header : &m_Data; + + // Iterate over each row (index-based iteration should be the fastest way of writing, albeit the code is a little harder to read) + const int rowSize = data->size(); + for(int row = 0; row < rowSize; ++row) { + + // Iterate over each cell + const int colSize = data->at(row).size()-1; // Subtract 1 as the final element will be added after the loop + for(int col = 0; col < colSize; ++col) { + s << data->at(row).at(col) << s_Delimiter; + } + + // Add final cell + s << data->at(row).at(colSize) << "\n"; + + // Flush buffer to the file every x rows + if(row % s_BufferSize == 0) + s.flush(); + + // Increment the counter + p.incrementStep(); + } + } + + p.complete(); + f.close(); + return true; +} + + +/* ================ */ +/* Header */ +/* ================ */ + +// --- Add a QStringList as a new header row of data --- // +void Spreadsheet::addHeader(const QStringList &list) +{ + m_Header.push_back(list); +} + +// --- Add one column to a new header row (for convenience but slower than using the QStringList method) --- // +void Spreadsheet::addHeader(const QString col1) +{ + QStringList list; + list << col1; + m_Header.push_back(list); +} + +// --- Add two columns to a new header row (for convenience but slower than using the QStringList method) --- // +void Spreadsheet::addHeader(const QString col1, const QString col2) +{ + QStringList list; + list << col1 << col2; + m_Header.push_back(list); +} + +// --- Add string value as a new column to a header row --- // +void Spreadsheet::addHeader(QString value, unsigned int row) +{ + if(row < static_cast(m_Header.size())) + m_Header[row] << value; +} + +// --- Pointer to header vector --- // +const QVector *Spreadsheet::header() +{ + return &m_Header; +} + +// --- Pointer to header row string list --- // +QStringList *Spreadsheet::header(unsigned int row) +{ + if(row < static_cast(m_Header.size())) + return &m_Header[row]; + else + return nullptr; +} + +// --- Header cell string --- // +QString Spreadsheet::headerString(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Header.size()) && col < static_cast(m_Header[row].size())) + return m_Header[row][col]; + else + return QString(); +} + +// --- Header cell variant --- // +QVariant Spreadsheet::headerVariant(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Header.size()) && col < static_cast(m_Header[row].size())) + return m_Header[row][col]; + else + return QVariant(); +} + + +/* ========================== */ +/* Progress Display */ +/* ========================== */ + +// --- Show/hide the progress window --- // +void Spreadsheet::setSilent(bool silent) +{ + m_HideProgress = silent; +} + + +/* =============== */ +/* Timer */ +/* =============== */ + +// --- Get timer --- // +QTime Spreadsheet::time() +{ + return m_Time; +} diff --git a/spreadsheet/spreadsheet - Copy.h b/spreadsheet/spreadsheet - Copy.h new file mode 100644 index 0000000..355cae3 --- /dev/null +++ b/spreadsheet/spreadsheet - Copy.h @@ -0,0 +1,109 @@ +#ifndef SPREADSHEET_H +#define SPREADSHEET_H + +#include +#include +#include +#include +#include +#include + +/* --- BENCHMARKS --- + * + * Using a sample of 1,000 TransferImporter records + * + * QVector 21.743 secs (total) / 0.021 secs per record + * QVector secs (total) / secs per record + * + */ + +// --- CSV spreadsheet --- // +class Spreadsheet +{ +private: + // Data + QVector m_Header; + QVector m_Data; + + // Delimiter / separator + static QChar s_Delimiter; + static QChar getSystemDelimiter(); + + // File I/O (functions) + void loadRepairStrings(QVector &repairSearch, QVector &repairReplace); + bool saveDialog(QString &path); + + // File I/O (members) + static const int s_BufferSize; + QString m_FilePath; + + // Header count + unsigned int m_HeaderCount; + + // Progress display + bool m_HideProgress; + + // Repair columns by removing delimiter characters within columns + bool m_Repair; + + // Timer + QTime m_Time; + +public: + // Constructor + Spreadsheet(bool repairColumns = false); + + // Destructor + ~Spreadsheet(); + + // Add Data + void add(const QStringList &list); + //void add(const QString values...); + void add(const QString col1); + void add(const QString col1, const QString col2); + void add(unsigned int &row, const int &value); + void add(unsigned int &row, QString value); + + // Get data + const QVector *data(); + QString matchDateString(unsigned int row, unsigned int col); + QString matchString(unsigned int row, unsigned int col); + int number(unsigned int row, unsigned int col); + QStringList *row(unsigned int row); + QString string(unsigned int row, unsigned int col); + QVariant variant(unsigned int row, unsigned int col); + + // Set data + void set(unsigned int &row, unsigned int col, const int &value); + + // File I/O + QString getFilePath(); + bool open(QString path = "", unsigned int headerCount = 1); + bool read(QString &path, unsigned int headerCount = 1); + bool create(QString path = ""); + bool save(QString path = ""); + bool write(QString path = ""); + + // Header + void addHeader(const QStringList &list); + void addHeader(const QString col1); + void addHeader(const QString col1, const QString col2); + void addHeader(QString value, unsigned int row); + const QVector *header(); + QStringList *header(unsigned int row); + QString headerString(unsigned int row, unsigned int col); + QVariant headerVariant(unsigned int row, unsigned int col); + + // Progress display + void setSilent(bool silent); + + // Timer + QTime time(); + + // Flags + enum ENUM_FLAGS { + NOT_A_NUMBER = -999999999 + }; +}; + +#endif // SPREADSHEET_H diff --git a/spreadsheet/spreadsheet.cpp b/spreadsheet/spreadsheet.cpp new file mode 100644 index 0000000..7ce3520 --- /dev/null +++ b/spreadsheet/spreadsheet.cpp @@ -0,0 +1,670 @@ +#include "spreadsheet.h" +#include "../data_types/string.h" +#include "../progress_window.h" + +// Qt headers +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// --- Static data --- // +QChar Spreadsheet::s_Delimiter = Spreadsheet::getSystemDelimiter(); +int Spreadsheet::s_ReadBufferSize = 95000 + Spreadsheet::BUFFER_MARGIN; + +/* ===================== */ +/* Spreadsheet */ +/* ===================== */ + +// --- Default constructor --- // +Spreadsheet::Spreadsheet(bool repairColumns) : + m_WriteBufferSize(150), + m_HideProgress(false), + m_Repair(repairColumns) +{ + // Load default path setting + QSettings settings; + m_FilePath = settings.value("spreadsheet_path", "C:\\").toString(); +} + +// --- Default destructor --- // +Spreadsheet::~Spreadsheet() +{ + // Save default path setting + QSettings settings; + settings.setValue("spreadsheet_path", m_FilePath); +} + + +/* ================== */ +/* Add Data */ +/* ================== */ + +// --- Add a QStringList as a new row of data --- // +void Spreadsheet::add(const QStringList &list) +{ + QVector tmp; + + const int size = list.size(); + for(int i = 0; i < size; ++i) + tmp.push_back(list[i]); + + m_Data.push_back(tmp); +} + +// --- Add a variable number of QStrings as a new row (C++11 variadic function) --- // +/*void Spreadsheet::add(const QString values...) +{ + QStringList list; + + const int count = sizeof(values); + + va_list(args); + va_start(args, count); + + for(int i = 0; i < count; ++i) { + list << va_arg(args, QString); + } + + va_end(args); + + m_Data.push_back(list); +}*/ + +// --- Add one column to a new row (for convenience but slower than using the QStringList method) --- // +void Spreadsheet::add(const QString col1) +{ + QVector tmp; + tmp.push_back(col1); + + m_Data.push_back(tmp); +} + +// --- Add two columns to a new row (for convenience but slower than using the QStringList method) --- // +void Spreadsheet::add(const QString col1, const QString col2) +{ + QVector tmp; + tmp.push_back(col1); + tmp.push_back(col2); + + m_Data.push_back(tmp); +} + +// --- Add integer value as a new column to a row --- // +void Spreadsheet::add(unsigned int &row, const int &value) +{ + if(row < static_cast(m_Data.size())) + m_Data[row] << QString::number(value); +} + +// --- Add string value as a new column to a row --- // +void Spreadsheet::add(unsigned int &row, QString value) +{ + if(row < static_cast(m_Data.size())) + m_Data[row] << value; +} + + +/* ================ */ +/* Buffer */ +/* ================ */ + +// --- Get row buffer size --- // +int Spreadsheet::readBufferSize() +{ + return s_ReadBufferSize - BUFFER_MARGIN; // Discount buffer margin +} + +// --- Set row buffer size when reading --- // +void Spreadsheet::setReadBufferSize(const int &size) +{ + s_ReadBufferSize = size + BUFFER_MARGIN; // Allow for a small margin of error +} + + +/* ================== */ +/* Get Data */ +/* ================== */ + +// --- Pointer to data vector --- // +const QVector> *Spreadsheet::data() +{ + return &m_Data; +} + +// --- Row cell date string formatted for matching against --- // +QString Spreadsheet::matchDateString(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Data.size()) && col < static_cast(m_Data[row].size())) + return String(m_Data[row][col]).getMatchDateString(); + else + return QString(); +} + + +// --- Row cell string formatted for matching against --- // +QString Spreadsheet::matchString(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Data.size()) && col < static_cast(m_Data[row].size())) + return String(m_Data[row][col]).getMatchString(); + else + return QString(); +} + +// --- Row cell number --- // +int Spreadsheet::number(unsigned int row, unsigned int col) +{ + // Out of range + if(row >= static_cast(m_Data.size())) + return NOT_A_NUMBER; + + // Not a number + else if(m_Data[row][col].isEmpty() || m_Data[row][col] == "-") // If empty or contains only a dash + return NOT_A_NUMBER; + + // Return as an integer + else + return m_Data[row][col].toInt(); +} + +// --- Pointer to row string list --- // +QVector *Spreadsheet::row(unsigned int row) +{ + if(row < static_cast(m_Data.size())) + return &m_Data[row]; + else + return nullptr; +} + +// --- Row cell string --- // +QString Spreadsheet::string(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Data.size()) && col < static_cast(m_Data[row].size())) + return m_Data[row][col]; + else + return QString(); +} + +// --- Row cell variant --- // +QVariant Spreadsheet::variant(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Data.size()) && col < static_cast(m_Data[row].size())) + return m_Data[row][col]; + else + return QVariant(); +} + + +/* ================== */ +/* Set Data */ +/* ================== */ + +// --- Set integer value of a column of a row --- // +void Spreadsheet::set(unsigned int &row, unsigned int col, const int &value) +{ + // Abort if invalid row + if(row >= static_cast(m_Data.size())) + return; + + // Update existing column if it already exists + if(col < static_cast(m_Data[row].size())) + m_Data[row][col] = QString::number(value); + // Add if the column does not exist + else + m_Data[row] << QString::number(value); +} + + +/* =================== */ +/* Delimiter */ +/* =================== */ + +// --- Get standard delimiter based upon the system locale --- // +QChar Spreadsheet::getSystemDelimiter() +{ + QLocale locale; + return (locale.decimalPoint() == '.') ? ',' : ';'; +} + + +/* ================ */ +/* Errors */ +/* ================ */ + +// --- Show bad rows error message --- // +int Spreadsheet::showBadRowsError(const QVector &badRows) +{ + const int size = badRows.size(); + + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Warning); + msgBox.setText(QString("Unexpected characters found in %1 row(s).").arg(size)); + msgBox.setInformativeText("This suggests the presence of html code and/or corrupt rows. " + "Importing the data may cause the application to crash or for unintended data to be inserted " + "into the Championship Manager database.\n\n" + "Click on 'Show Details...' below for a list of affected row(s)."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + + // Add details of affected rows to the message box + QString list; + for(int i = 0; i < size; ++i) + list.append(QString("%1\n").arg(badRows[i])); + + list.chop(1); // Remove trailing newline character + msgBox.setDetailedText(list); + + return msgBox.exec(); +} + + +/* ================== */ +/* File I/O */ +/* ================== */ + +// --- Get file path --- // +QString Spreadsheet::getFilePath() +{ + int pos = m_FilePath.lastIndexOf("/"); + return m_FilePath.left(pos+1); +} + +// --- Load delimiter repair list --- // +void Spreadsheet::loadRepairStrings(QVector &repairSearch, QVector &repairReplace) +{ + // Open file + QFile f("delimiter_repair.txt"); + if(!f.open(QIODevice::ReadOnly | QIODevice::Text)) + return; + + // Read the rows + while(!f.atEnd()) { + QString row = QString::fromLatin1(f.readLine()); + + // Search string + repairSearch.push_back(row); + + // Replacement string (i.e. string without the delimiter) + row.remove(s_Delimiter); + repairReplace.push_back(row); + } + + f.close(); + return; +} + +// --- Open file via a dialog window --- // +bool Spreadsheet::open(QString path, unsigned int headerCount, const bool &errorChecking) +{ + //qDebug() << "Spreadsheet::open" << path; + + path = QFileDialog::getOpenFileName(0, "Open Comma/Tab Separated File", + path, + "Spreadsheet (*.csv *.txt)"); + + // Abort if the user has clicked Cancel + if(path.isEmpty()) + return false; + + // Read the file + m_Time.start(); + return this->read(path, headerCount, errorChecking); +} + +// --- Read file --- // +bool Spreadsheet::read(QString &path, unsigned int headerCount, const bool &errorChecking) +{ + m_FilePath = path; + + // Open file + QFile f(m_FilePath); + if(!f.open(QIODevice::ReadOnly | QIODevice::Text)) + return false; + + // Detect the file delimiter + QString dSample = f.readLine(); + int comma = dSample.count(','); + int semicolon = dSample.count(';'); + int tab = dSample.count('\t'); + + // Set delimiter to comma or semicolon according to which appears the most in the first line + QChar d = (comma > semicolon) ? ',' : ';'; + + // Use tab as a delimiter if there are more tabs than commas and semicolons + if(tab > (comma + semicolon)) + d = '\t'; + + // Return the stream to the beginning of the file + f.seek(0); + + // Row counter + unsigned int count = 0; + + // Progress dialog + ProgressWindow p; + p.hideWindow(m_HideProgress); + p.set("Loading spreadsheet", f.size()); + + // Counters (for reserving sizes of QVector and QStringList + m_Data.reserve(s_ReadBufferSize); + + // Error checking + const QRegExp badChars("[<>]"); + QVector badRows; + + // The conditional statement below could alternatively be placed within the while loop as this would make the code tidier + // and easier to maintain. But to keep the csv parsing as fast as possible (which is important when dealing with large + // files), it is better to place the while loop within the conditional statement. + + // Parse the csv (no column repair) + if(!m_Repair) { + while(!f.atEnd()) { + QString row = QString::fromLatin1(f.readLine()); + + // Pointer to the relevant string list + QVector> *data = (count < headerCount) ? &m_Header : &m_Data; + + // Remove any text qualifiers (i.e. speech marks) + row.remove(QChar(0x22)); + + // Check for bad characters + if(row.contains(badChars)) + badRows.push_back(count+1); + + // Skip empty rows + if(row.isEmpty()) + { + ++count; + continue; + } + + // Parse the row and add it to the string list + QStringList list; + list = row.split(d); + + // Convert the list to a QVector + QVector tmp; + tmp.reserve(list.size()+10); // Add ten for any additional columns to be added later + + const int size = list.size(); + for(int i = 0; i < size; ++i) + tmp.push_back(list[i]); + + // Remove end line marker from end of final cell + tmp[size-1].chop(1); + + data->push_back(tmp); + + // Increment the progress dialog + p.setSteps(f.pos()); + + // Increment the row counter + ++count; + } + } + // Parse the csv (with column repair) + else { + // Initialise the column repair vector + QVector repairSearch; + QVector repairReplace; + this->loadRepairStrings(repairSearch, repairReplace); + + while(!f.atEnd()) { + QString row = QString::fromLatin1(f.readLine()); + + // Check for any repairs + const int size = repairSearch.size(); + for(int i = 0; i < size; ++i) { + row.replace(repairSearch[i], repairReplace[i], Qt::CaseInsensitive); + } + + // Pointer to the relevant string list + QVector> *data = (count < headerCount) ? &m_Header : &m_Data; + + // Remove any text qualifiers (i.e. speech marks) + row.remove(QChar(0x22)); + + // Check for bad characters + if(row.contains(badChars)) + badRows.push_back(count+1); + + // Skip empty rows + if(row.isEmpty()) + { + ++count; + continue; + } + + // Parse the row and add it to the string list + QStringList list; + list = row.split(d); + + // Convert the list to a QVector + QVector tmp; + tmp.reserve(list.size()+10); // Add ten for any additional columns to be added later + + const int sz = list.size(); + for(int i = 0; i < sz; ++i) + tmp.push_back(list[i]); + + // Remove end line marker from end of final cell + tmp[sz-1].remove("\n"); + + data->push_back(tmp); + + // Increment the progress dialog + p.setSteps(f.pos()); + + // Increment the row counter + ++count; + } + } + + p.complete(); + f.close(); + + if(errorChecking && badRows.size() > 0) + this->showBadRowsError(badRows); + + return true; +} + +// --- Create file/buffer via a dialog window but do not write any data to the hard drive --- // +bool Spreadsheet::create(QString path, const QString &filename) +{ + return this->saveDialog(path, filename); +} + +// --- Save file via a dialog window --- // +bool Spreadsheet::saveDialog(QString &path, const QString &filename) +{ + // Use existing path if passed path is empty + if(path.isEmpty()) + path = m_FilePath; + + // Append default file name + if(!filename.isEmpty()) + { + path.append("\\"); + path.append(filename); + } + + path = QFileDialog::getSaveFileName(0, "Save Comma/Tab Separated File", + path, + "Spreadsheet (*.csv *.txt)"); + + // Abort if the user has clicked Cancel + if(path.isEmpty()) + return false; + else { + m_FilePath = path; + return true; + } +} +// --- Save file via a dialog window and write the data to the hard drive --- // +bool Spreadsheet::save(QString path) +{ + if(saveDialog(path) == true) { + m_Time.start(); + return this->write(); + } + else + return false; +} + +// --- Write file --- // +bool Spreadsheet::write(QString path) +{ + if(!path.isEmpty()) + m_FilePath = path; + + // Create file + QFile f(m_FilePath); + if(!f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + return false; + + // Text stream (for writing text to the file) + QTextStream s; + s.setCodec(QTextCodec::codecForName("Latin-1")); // ASCII + s.setDevice(&f); + + // Progress dialog + ProgressWindow p; + p.hideWindow(m_HideProgress); + p.set("Saving spreadsheet", m_Header.size() + m_Data.size()); + + // Write the csv (iterate first over header and then over data strings) + for(int i = 0; i < 2; ++i) { + + // Pointer to the relevant string list + QVector> *data = (i == 0) ? &m_Header : &m_Data; + + // Iterate over each row (index-based iteration should be the fastest way of writing, albeit the code is a little harder to read) + const int rowSize = data->size(); + for(int row = 0; row < rowSize; ++row) { + + // Iterate over each cell + const int colSize = data->at(row).size()-1; // Subtract 1 as the final element will be added after the loop + for(int col = 0; col < colSize; ++col) { + s << data->at(row).at(col) << s_Delimiter; + } + + // Add final cell + s << data->at(row).at(colSize) << "\n"; + + // Flush buffer to the file every x rows + if(row % m_WriteBufferSize == 0) + s.flush(); + + // Increment the counter + p.incrementStep(); + } + } + + p.complete(); + f.close(); + return true; +} + + +/* ================ */ +/* Header */ +/* ================ */ + +// --- Add a QStringList as a new header row of data --- // +void Spreadsheet::addHeader(const QStringList &list) +{ + QVector tmp; + + const int size = list.size(); + for(int i = 0; i < size; ++i) + tmp.push_back(list[i]); + + m_Header.push_back(tmp); +} + +// --- Add one column to a new header row (for convenience but slower than using the QStringList method) --- // +void Spreadsheet::addHeader(const QString col1) +{ + QVector tmp; + tmp.push_back(col1); + + m_Header.push_back(tmp); +} + +// --- Add two columns to a new header row (for convenience but slower than using the QStringList method) --- // +void Spreadsheet::addHeader(const QString col1, const QString col2) +{ + QVector tmp; + tmp.push_back(col1); + tmp.push_back(col2); + + m_Header.push_back(tmp); +} + +// --- Add string value as a new column to a header row --- // +void Spreadsheet::addHeader(QString value, unsigned int row) +{ + if(row < static_cast(m_Header.size())) + m_Header[row] << value; +} + +// --- Pointer to header vector --- // +const QVector> *Spreadsheet::header() +{ + return &m_Header; +} + +// --- Pointer to header row string list --- // +QVector *Spreadsheet::header(unsigned int row) +{ + if(row < static_cast(m_Header.size())) + return &m_Header[row]; + else + return nullptr; +} + +// --- Header cell string --- // +QString Spreadsheet::headerString(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Header.size()) && col < static_cast(m_Header[row].size())) + return m_Header[row][col]; + else + return QString(); +} + +// --- Header cell variant --- // +QVariant Spreadsheet::headerVariant(unsigned int row, unsigned int col) +{ + if(row < static_cast(m_Header.size()) && col < static_cast(m_Header[row].size())) + return m_Header[row][col]; + else + return QVariant(); +} + + +/* ========================== */ +/* Progress Display */ +/* ========================== */ + +// --- Show/hide the progress window --- // +void Spreadsheet::setSilent(bool silent) +{ + m_HideProgress = silent; +} + + +/* =============== */ +/* Timer */ +/* =============== */ + +// --- Get timer --- // +QTime Spreadsheet::time() +{ + return m_Time; +} diff --git a/spreadsheet/spreadsheet.h b/spreadsheet/spreadsheet.h new file mode 100644 index 0000000..d20fc81 --- /dev/null +++ b/spreadsheet/spreadsheet.h @@ -0,0 +1,109 @@ +#ifndef SPREADSHEET_H +#define SPREADSHEET_H + +#include +#include +#include +#include +#include +#include + +// --- CSV spreadsheet --- // +class Spreadsheet +{ +private: + // Data + QVector> m_Header; + QVector> m_Data; + + // Delimiter / separator + static QChar s_Delimiter; + static QChar getSystemDelimiter(); + + // Errors + int showBadRowsError(const QVector &badRows); + + // File I/O (functions) + void loadRepairStrings(QVector &repairSearch, QVector &repairReplace); + bool saveDialog(QString &path, const QString &filename = ""); + + // File I/O (members) + static int s_ReadBufferSize; + const int m_WriteBufferSize; + QString m_FilePath; + + // Header count + unsigned int m_HeaderCount; + + // Progress display + bool m_HideProgress; + + // Repair columns by removing delimiter characters within columns + bool m_Repair; + + // Timer + QTime m_Time; + +public: + // Constructor + Spreadsheet(bool repairColumns = false); + + // Destructor + ~Spreadsheet(); + + // Add Data + void add(const QStringList &list); + //void add(const QString values...); + void add(const QString col1); + void add(const QString col1, const QString col2); + void add(unsigned int &row, const int &value); + void add(unsigned int &row, QString value); + + // Buffers + static int readBufferSize(); + static void setReadBufferSize(const int &size); + + // Get data + const QVector > *data(); + QString matchDateString(unsigned int row, unsigned int col); + QString matchString(unsigned int row, unsigned int col); + int number(unsigned int row, unsigned int col); + QVector *row(unsigned int row); + QString string(unsigned int row, unsigned int col); + QVariant variant(unsigned int row, unsigned int col); + + // Set data + void set(unsigned int &row, unsigned int col, const int &value); + + // File I/O + QString getFilePath(); + bool open(QString path = "", unsigned int headerCount = 1, const bool &errorChecking = false); + bool read(QString &path, unsigned int headerCount = 1, const bool &errorChecking = false); + bool create(QString path = "", const QString &filename = ""); + bool save(QString path = ""); + bool write(QString path = ""); + + // Header + void addHeader(const QStringList &list); + void addHeader(const QString col1); + void addHeader(const QString col1, const QString col2); + void addHeader(QString value, unsigned int row); + const QVector > *header(); + QVector *header(unsigned int row); + QString headerString(unsigned int row, unsigned int col); + QVariant headerVariant(unsigned int row, unsigned int col); + + // Progress display + void setSilent(bool silent); + + // Timer + QTime time(); + + // Flags + enum ENUM_FLAGS { + NOT_A_NUMBER = -999999999, + BUFFER_MARGIN = 5 + }; +}; + +#endif // SPREADSHEET_H diff --git a/ui/style_sheet.cpp b/ui/style_sheet.cpp new file mode 100644 index 0000000..d2fe4bd --- /dev/null +++ b/ui/style_sheet.cpp @@ -0,0 +1,137 @@ +#include "style_sheet.h" + +// --- Get style sheet from a css file --- // +void StyleSheet::get() +{ + QString s; + QFile f(filename); + + // Check for an existing css file + if(f.open(QIODevice::ReadOnly)) { + s = QLatin1String(f.readAll()); + } + // Use default settings if no existing css file is found + else { + this->getDefaultStyleSheet(s); + this->save(s); + } + + qApp->setStyleSheet(s); +} + +// --- Save style sheet to a css file --- // +void StyleSheet::save(QString &s) +{ + QFile f(filename); + + if(f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) { + QTextStream stream(&f); + stream << s; + f.close(); + } +} + +// --- Default style sheet --- // +void StyleSheet::getDefaultStyleSheet(QString &s) +{ + s = "/* Global font styling */" "\n" + "* {" "\n" + "\t" "font-family: \"Segoe UI\", Verdana, Geneva, sans-serif;" "\n" + "}\n" + "\n" + "/* GROUP BOXES: */" "\n" + "#TextSettingGroup {" "\n" + "\t" "font-weight: bold;" "\n" + "}\n" + "/* TEXT: */" "\n" + "#TextSettingTitle {" "\n" + "\t" "font-weight: bold;" "\n" + "\t" "margin: 0px 0px 0px 0px;" "\n" + "\t" "padding: 0px;" "\n" + "}\n" + "#TextSettingDescription {" "\n" + "\t" "margin: 0px;" "\n" + "\t" "padding: 0px;" "\n" + "}\n" + "#TextSummaryLarge {" "\n" + "\t" "font-size: 12pt;" "\n" + "\t" "font-family: \"Segoe UI Light\", Verdana, Geneva, sans-serif;" "\n" + "\t" "color: #000000;" "\n" + "}\n" + "/* SUMMARY BOXES: */" "\n" + "#UI_SummaryError {" "\n" + "\t" "background-color: #e74c3c;" "\n" + "\t" "border: 5px solid #e74c3c;" "\n" + "\t" "border-radius: 6px;" "\n" + "\t" "color: #ffffff;" "\n" + "}\n" + "#UI_SummaryErrorText {" "\n" + "\t" "color: #ffffff;" "\n" + "\t" "font-size: 18pt;" "\n" + "\t" "font-family: \"Segoe UI Light\", Verdana, Geneva, sans-serif;" "\n" + "}\n" + "#UI_SummaryErrorValue {" "\n" + "\t" "color: #ffffff;" "\n" + "\t" "font-size: 24pt;" "\n" + "}\n" + "#UI_SummaryInfo {" "\n" + "\t" "background-color: #3498db;" "\n" + "\t" "border: 5px solid #3498db;" "\n" + "\t" "border-radius: 6px;" "\n" + "\t" "color: #ffffff;" "\n" + "}\n" + "#UI_SummaryInfoText {" "\n" + "\t" "color: #ffffff;" "\n" + "\t" "font-size: 18pt;" "\n" + "\t" "font-family: \"Segoe UI Light\", Verdana, Geneva, sans-serif;" "\n" + "}\n" + "#UI_SummaryInfoValue {" "\n" + "\t" "color: #ffffff;" "\n" + "\t" "font-size: 24pt;" "\n" + "}\n" + "#UI_SummarySuccess {" "\n" + "\t" "background-color: #2ecc71;" "\n" + "\t" "border: 3px solid #2ecc71;" "\n" + "\t" "border-radius: 6px;" "\n" + "\t" "color: #ffffff;" "\n" + "}\n" + "#UI_SummarySuccessText {" "\n" + "\t" "color: #ffffff;" "\n" + "\t" "font-size: 18pt;" "\n" + "\t" "font-family: \"Segoe UI Light\", Verdana, Geneva, sans-serif;" "\n" + "}\n" + "#UI_SummarySuccessValue {" "\n" + "\t" "color: #ffffff;" "\n" + "\t" "font-size: 24pt;" "\n" + "}\n" + "#UI_SummarySubtle {" "\n" + "\t" "background-color: #f5f5f5;" "\n" + "\t" "border: 5px solid #f5f5f5;" "\n" + "\t" "border-radius: 6px;" "\n" + "\t" "color: #ffffff;" "\n" + "}\n" + "#UI_SummarySubtleText {" "\n" + "\t" "color: #000000;" "\n" + "\t" "font-size: 18pt;" "\n" + "\t" "font-family: \"Segoe UI Light\", Verdana, Geneva, sans-serif;" "\n" + "}\n" + "#UI_SummarySubtleValue {" "\n" + "\t" "color: #000000;" "\n" + "\t" "font-size: 24pt;" "\n" + "}\n" + "#UI_SummaryWarning {" "\n" + "\t" "background-color: #f39c12;" "\n" + "\t" "border: 5px solid #f39c12;" "\n" + "\t" "border-radius: 6px;" "\n" + "\t" "color: #ffffff;" "\n" + "}\n" + "#UI_SummaryWarningText {" "\n" + "\t" "color: #ffffff;" "\n" + "\t" "font-size: 18pt;" "\n" + "\t" "font-family: \"Segoe UI Light\", Verdana, Geneva, sans-serif;" "\n" + "}\n" + "#UI_SummaryWarningValue {" "\n" + "\t" "color: #ffffff;" "\n" + "\t" "font-size: 24pt;" "\n" + "}"; +} diff --git a/ui/style_sheet.h b/ui/style_sheet.h new file mode 100644 index 0000000..c6481d4 --- /dev/null +++ b/ui/style_sheet.h @@ -0,0 +1,22 @@ +#ifndef STYLE_SHEET_H +#define STYLE_SHEET_H + +#include +#include +#include +#include + +class StyleSheet +{ +private: + QString filename; + + void getDefaultStyleSheet(QString &s); + void save(QString &s); + +public: + StyleSheet() : filename("stylesheet.css") {} + void get(); +}; + +#endif // STYLE_SHEET_H diff --git a/ui/summary_item.cpp b/ui/summary_item.cpp new file mode 100644 index 0000000..7373485 --- /dev/null +++ b/ui/summary_item.cpp @@ -0,0 +1,86 @@ +#include "summary_item.h" + + +/* ====================== */ +/* Summary Item */ +/* ====================== */ + +// --- Default constructor --- // +SummaryItem::SummaryItem(QWidget *parent, unsigned char type) : + QWidget(parent) +{ + this->setFixedSize(ITEM_WIDTH, ITEM_HEIGHT); + + m_Text = new QLabel(this); + m_Text->setWordWrap(true); + m_Text->setAlignment(Qt::AlignHCenter | Qt::AlignTop); + + m_Value = new QLabel(this); + m_Value->setAlignment(Qt::AlignHCenter | Qt::AlignBottom); + this->setType(type); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->addWidget(m_Value); + layout->addWidget(m_Text); + this->setLayout(layout); +} + +// --- Set text label --- // +void SummaryItem::setText(const QString &t) +{ + m_Text->setText(t); +} + +// --- Set value label --- // +void SummaryItem::setValue(const int &i) +{ + m_Value->setText(QLocale().toString(i)); +} + +// --- Set value label --- // +void SummaryItem::setValue(const QString &t) +{ + m_Value->setText(t); +} + +// --- Set item type --- // +void SummaryItem::setType(unsigned char type) +{ + switch(type) { + case INFO: + this->setObjectName("UI_SummaryInfo"); + m_Text->setObjectName("UI_SummaryInfoText"); + m_Value->setObjectName("UI_SummaryInfoValue"); + break; + + case SUCCESS: + this->setObjectName("UI_SummarySuccess"); + m_Text->setObjectName("UI_SummarySuccessText"); + m_Value->setObjectName("UI_SummarySuccessValue"); + break; + + case WARNING: + this->setObjectName("UI_SummaryWarning"); + m_Text->setObjectName("UI_SummaryWarningText"); + m_Value->setObjectName("UI_SummaryWarningValue"); + break; + + case ERROR: + this->setObjectName("UI_SummaryError"); + m_Text->setObjectName("UI_SummaryErrorText"); + m_Value->setObjectName("UI_SummaryErrorValue"); + break; + + case SUBTLE: + this->setObjectName("UI_SummarySubtle"); + m_Text->setObjectName("UI_SummarySubtleText"); + m_Value->setObjectName("UI_SummarySubtleValue"); + break; + + default: + this->setObjectName("UI_SummaryInfo"); + m_Text->setObjectName("UI_SummaryInfoText"); + m_Value->setObjectName("UI_SummaryInfoValue"); + break; + } +} diff --git a/ui/summary_item.h b/ui/summary_item.h new file mode 100644 index 0000000..c8bbb91 --- /dev/null +++ b/ui/summary_item.h @@ -0,0 +1,48 @@ +#ifndef SUMMARY_ITEM_H +#define SUMMARY_ITEM_H + +#include +#include +#include +#include +#include +#include +#include +#include + +// --- Summary Item --- // +class SummaryItem : public QWidget +{ +private: + // Text labels + QLabel *m_Text; + QLabel *m_Value; + +public: + // Default constructor + SummaryItem(QWidget *parent, unsigned char type = 0); + + // Set text and value + void setText(const QString &t); + void setValue(const int &i); + void setValue(const QString &t); + + // Set type / formatting + void setType(unsigned char type); + + enum ENUM_SUMMARY_ITEM_TYPE { + INFO, + SUCCESS, + WARNING, + ERROR, + SUBTLE + }; + + enum ENUM_DIMENSIONS { + ITEM_HEIGHT = 140, + ITEM_WIDTH = ITEM_HEIGHT + 40 + //ITEM_WIDTH = ITEM_HEIGHT * 2 + }; +}; + +#endif diff --git a/vlookup_clubs.csv b/vlookup_clubs.csv new file mode 100644 index 0000000..dc93a3f --- /dev/null +++ b/vlookup_clubs.csv @@ -0,0 +1 @@ +Find:,Replace with: diff --git a/vlookup_clubs_prefix_suffix.csv b/vlookup_clubs_prefix_suffix.csv new file mode 100644 index 0000000..592c1ca --- /dev/null +++ b/vlookup_clubs_prefix_suffix.csv @@ -0,0 +1,3 @@ +Prefix/Suffix name: +afc +fc diff --git a/vlookup_nations.csv b/vlookup_nations.csv new file mode 100644 index 0000000..68f721a --- /dev/null +++ b/vlookup_nations.csv @@ -0,0 +1,53 @@ +Find:,Replace with: +American Virgin Islands,US Virgin Islands +Antigua and Barbuda,Antigua & Barbuda +Bonaire,_none +Botsuana,Botswana +Cape Verde,Cape Verde Islands +Cayman-Inseln,Cayman Islands +Chinese Taipei (Taiwan),Chinese Taipei +Congo DR,Democratic Republic of Congo +Congo,The Congo +Cookinseln,Cook Islands +Cote d'Ivoire,Ivory Coast +Falkland Islands,_none +Faroe Island,Faroe Islands +Federated States of Micronesia,_none +French Guiana,_none +Greenland,_none +Guadeloupe,_none +Guine,Guinea +Hongkong,Hong Kong +Ireland,Republic of Ireland +Kiribati,_none +Korea North,North Korea +Korea South,South Korea +Macao,Macau +Macedonia,FYR of Macedonia +Mariana Islands,_none +Marshall Islands,_none +Martinique,_none +Monaco,_none +Nauru,_none +Netherlands,Holland +Neukaledonien,New Caledonia +Niue,_none +Osttimor,Timor +Palästina,Palestine +Palau,_none +Philippines,The Philippines +Réunion,_none +Saint-Martin,_none +Sao Tome and Principe,São Tomé & Principe +Sint Maarten,_none +Southern Sudan,South Sudan +St. Kitts &Nevis,St Kitts & Nevis +St. Lucia,Saint Lucia +St. Vincent & Grenadinen,St Vincent & The Grenadines +Suriname,Surinam +Tibet,_none +Trinidad and Tobago,Trinidad & Tobago +Turks- and Caicosinseln,Turks and Caicos Islands +Tuvalu,_none +Vatican,_none +Zanzibar,_none diff --git a/vlookup_protected_players.csv b/vlookup_protected_players.csv new file mode 100644 index 0000000..337073d --- /dev/null +++ b/vlookup_protected_players.csv @@ -0,0 +1 @@ +First Name,Second Name,Date of Birth,Staff ID