Compare commits

..

10 Commits

65 changed files with 825 additions and 369 deletions

View File

@ -1,32 +1,5 @@
# Changelog
## 0.3.0 - 2026-02-04
## 0.1.0 - 2026-02-05
### Added
- Basic JSON RESTful client (compatible with the GenericRestServer)
- No editing of existing items on the server yet
### Fixed
- Save changes when closing the EditItemDialog
## 0.2.1 - 2026-01-15
### Added
- Displaying QR code of current item in edit item dialog
## 0.2 - 2026-01-14
### Added
- Displaying editable table model (sortable by column)
- Modifying model data can be un-/redone
- Data is stored in JSON file and automatically loaded on application start
- Data can be imported/exported from/into CSV file
- Model rows containing specific data can be selected via "Find item(s)" dialog
## 0.1 - 2025-11-01
A simple Qt application separated into an UI frontend and backend core. With installer (for Linux for now) and option to trigger updater from within the application.
Initial release of BeetRound. Based on GenericQtClient v0.3.0 and adjusted the project name to the use case.

View File

@ -8,8 +8,8 @@ enable_testing()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(libs/GenericCore)
set (CORE_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/GenericCore)
add_subdirectory(libs/BeetRoundCore)
set (CORE_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/BeetRoundCore)
### 3rd party libraries
add_subdirectory(libs/3rdParty/Qt-QrCodeGenerator)
@ -18,7 +18,7 @@ set (QR_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/3rdParty/Qt-QrCodeGenerator)
configure_file(ApplicationConfig.h.in ApplicationConfig.h)
#Frontend applications
add_subdirectory(UIs/GenericWidgets)
add_subdirectory(UIs/BeetRoundWidgets)
### Tests
add_subdirectory(tests/GenericCoreTests)

View File

@ -1,19 +1,9 @@
# GenericQtClient
# BeetRound
This is a Qt application which can be used as a starting point for new software projects.
BeetRound is a software project to manage the yearly crop share auction (german: Bietrunde) of a Community-supported agriculture (CSA, german: Solidarische Landwirtschaft (SoLaWi)).
Common features most Qt software clients need will be already implemented and can be easily configured for the specific needs.
This is the management application. The project also includes a web server where the participants can submit their biddings.
## Implemented features:
- Separated UI frontend and backend core (in its own git submodules)
- Using Qt model/view framework with QT undo framework
- Saving/Loading JSON files
- CSV import/export
- installable and updateable via Qt updater framework
- only linux for now
- Qt 6 libraries must be installed on the machine to run
Further information will follow…
## Coming features:
- REST client
- Extensive use of sorting and filtering models to display data in different ways
- ...
This project is currently tailored for one specific CSA and will be expanded to broader use after the coming crop share auction in february.

View File

@ -34,6 +34,8 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
dialogs/edititemdialog.h dialogs/edititemdialog.cpp
dialogs/settingsdialog.h dialogs/settingsdialog.cpp
views/itemdetailmapper.h views/itemdetailmapper.cpp
widgets/comboboxdelegate.h widgets/comboboxdelegate.cpp
widgets/spinboxdelegate.h widgets/spinboxdelegate.cpp
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET ${TARGET_APP} APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
@ -63,7 +65,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(${TARGET_APP} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_include_directories(${TARGET_APP} PRIVATE ${CORE_LIB_DIR}/)
target_link_libraries(${TARGET_APP} PRIVATE GenericCore)
target_link_libraries(${TARGET_APP} PRIVATE BeetRoundCore)
target_include_directories(${TARGET_APP} PRIVATE ${QR_LIB_DIR}/src)
target_link_libraries(${TARGET_APP} PRIVATE qrcode)

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,149 @@
#include "newitemdialog.h"
#include <QGridLayout>
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include <QStringListModel>
#include "formats/jsonparser.h"
#include "model/metadata.h"
NewItemDialog::NewItemDialog(QWidget* parent)
: AbstractDialog(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, parent) {}
void NewItemDialog::createContent() {
if (m_contentContainer) {
delete m_contentContainer;
}
setWindowTitle(tr("New item..."));
m_contentContainer = new QWidget(this);
// REFACTOR use a data structure for input widgets which can be iterated through
// using a factory which iterates through the roles from metadata.h
// and create the input widgets based on the data type of this role
m_numberLabel = new QLabel(GET_HEADER_FOR_COLUMN(0));
m_numberBox = new QSpinBox();
m_numberBox->setMaximum(1000);
m_lastNameLabel = new QLabel(GET_HEADER_FOR_COLUMN(1));
m_lastNameEdit = new QLineEdit();
m_lastNameLabel->setBuddy(m_lastNameEdit);
m_firstNameLabel = new QLabel(GET_HEADER_FOR_COLUMN(2));
m_firstNameEdit = new QLineEdit();
m_firstNameLabel->setBuddy(m_firstNameEdit);
m_shareTypeLabel = new QLabel(GET_HEADER_FOR_COLUMN(3));
m_shareTypeBox = new QComboBox();
m_shareTypeLabel->setBuddy(m_shareTypeBox);
m_shareTypeModel = new QStringListModel(SHARE_TYPES, this);
m_shareTypeBox->setModel(m_shareTypeModel);
m_amountLabel = new QLabel(GET_HEADER_FOR_COLUMN(4));
m_amountSpinBox = new QDoubleSpinBox();
m_amountLabel->setBuddy(m_amountSpinBox);
m_amountSpinBox->setValue(1.0);
m_biddingTypeLabel = new QLabel(GET_HEADER_FOR_COLUMN(5));
m_biddingTypeBox = new QComboBox();
m_biddingTypeLabel->setBuddy(m_biddingTypeBox);
m_biddingTypeModel = new QStringListModel(BIDDING_TYPES, this);
m_biddingTypeBox->setModel(m_biddingTypeModel);
m_bidding1Label = new QLabel(GET_HEADER_FOR_COLUMN(6));
m_bidding1SpinBox = new QSpinBox();
m_bidding1SpinBox->setMaximum(500);
m_bidding2Label = new QLabel(GET_HEADER_FOR_COLUMN(7));
m_bidding2SpinBox = new QSpinBox();
m_bidding2SpinBox->setMaximum(500);
m_bidding3Label = new QLabel(GET_HEADER_FOR_COLUMN(8));
m_bidding3SpinBox = new QSpinBox();
m_bidding3SpinBox->setMaximum(500);
m_depotWish1Label = new QLabel(GET_HEADER_FOR_COLUMN(9));
m_depotWish1Edit = new QLineEdit();
m_depotWish1Label->setBuddy(m_depotWish1Edit);
m_depotWish2Label = new QLabel(GET_HEADER_FOR_COLUMN(10));
m_depotWish2Edit = new QLineEdit();
m_depotWish2Label->setBuddy(m_depotWish2Edit);
m_mailLabel = new QLabel(GET_HEADER_FOR_COLUMN(11));
m_mailEdit = new QLineEdit();
m_mailEdit->setMinimumWidth(200);
m_mailLabel->setBuddy(m_mailEdit);
/// layouting
QGridLayout* layout = new QGridLayout();
layout->addWidget(m_numberLabel, 0, 0, 1, 1);
layout->addWidget(m_numberBox, 0, 1, 1, 1);
layout->addWidget(m_lastNameLabel, 1, 0, 1, 1);
layout->addWidget(m_lastNameEdit, 1, 1, 1, 1);
layout->addWidget(m_firstNameLabel, 2, 0, 1, 1);
layout->addWidget(m_firstNameEdit, 2, 1, 1, 1);
layout->addWidget(m_shareTypeLabel, 3, 0, 1, 1);
layout->addWidget(m_shareTypeBox, 3, 1, 1, 1);
layout->addWidget(m_amountLabel, 4, 0, 1, 1);
layout->addWidget(m_amountSpinBox, 4, 1, 1, 1);
layout->addWidget(m_biddingTypeLabel, 5, 0, 1, 1);
layout->addWidget(m_biddingTypeBox, 5, 1, 1, 1);
layout->addWidget(m_bidding1Label, 6, 0, 1, 1);
layout->addWidget(m_bidding1SpinBox, 6, 1, 1, 1);
layout->addWidget(m_bidding2Label, 7, 0, 1, 1);
layout->addWidget(m_bidding2SpinBox, 7, 1, 1, 1);
layout->addWidget(m_bidding3Label, 8, 0, 1, 1);
layout->addWidget(m_bidding3SpinBox, 8, 1, 1, 1);
layout->addWidget(m_depotWish1Label, 9, 0, 1, 1);
layout->addWidget(m_depotWish1Edit, 9, 1, 1, 1);
layout->addWidget(m_depotWish2Label, 10, 0, 1, 1);
layout->addWidget(m_depotWish2Edit, 10, 1, 1, 1);
layout->addWidget(m_mailLabel, 11, 0, 1, 1);
layout->addWidget(m_mailEdit, 11, 1, 1, 1);
m_contentContainer->setLayout(layout);
m_outerLayout->insertWidget(0, m_contentContainer);
}
void NewItemDialog::accept() {
ModelItemValues itemValues;
// TODO (after refactoring data structure for input widgets) use iteration through the relevant
// roles and their input widgets
// itemValues.insert(LastNameRole, m_nameEdit->text());
itemValues.insert(GET_ROLE_FOR_COLUMN(0), m_numberBox->value());
itemValues.insert(GET_ROLE_FOR_COLUMN(1), m_lastNameEdit->text());
itemValues.insert(GET_ROLE_FOR_COLUMN(2), m_firstNameEdit->text());
itemValues.insert(GET_ROLE_FOR_COLUMN(3), m_shareTypeBox->currentText());
itemValues.insert(GET_ROLE_FOR_COLUMN(4), m_amountSpinBox->value());
itemValues.insert(GET_ROLE_FOR_COLUMN(5), m_biddingTypeBox->currentText());
itemValues.insert(GET_ROLE_FOR_COLUMN(6), m_bidding1SpinBox->value());
itemValues.insert(GET_ROLE_FOR_COLUMN(7), m_bidding2SpinBox->value());
itemValues.insert(GET_ROLE_FOR_COLUMN(8), m_bidding3SpinBox->value());
itemValues.insert(GET_ROLE_FOR_COLUMN(9), m_depotWish1Edit->text());
itemValues.insert(GET_ROLE_FOR_COLUMN(10), m_depotWish2Edit->text());
itemValues.insert(GET_ROLE_FOR_COLUMN(11), m_mailEdit->text());
const QByteArray jsonDoc = JsonParser::itemValuesListToJson({itemValues}, ITEMS_KEY_STRING);
emit addItems(jsonDoc);
resetContent();
AbstractDialog::accept();
}
void NewItemDialog::resetContent() {
m_numberBox->setValue(0);
m_lastNameEdit->setText("");
m_firstNameEdit->setText("");
m_shareTypeBox->setCurrentIndex(0);
m_amountSpinBox->setValue(1);
m_biddingTypeBox->setCurrentIndex(0);
m_bidding1SpinBox->setValue(0);
m_bidding2SpinBox->setValue(0);
m_bidding3SpinBox->setValue(0);
m_depotWish1Edit->setText("");
m_depotWish2Edit->setText("");
m_mailEdit->setText("");
}

View File

@ -0,0 +1,68 @@
#ifndef NEWITEMDIALOG_H
#define NEWITEMDIALOG_H
#include "abstractdialog.h"
#include <QComboBox>
class QStringListModel;
class QDoubleSpinBox;
class QLineEdit;
class QSpinBox;
class QLabel;
class NewItemDialog : public AbstractDialog {
Q_OBJECT
public:
NewItemDialog(QWidget* parent = nullptr);
void createContent() override;
signals:
void addItems(const QByteArray& jsonDoc);
public slots:
void accept() override;
// void reject() override;
private:
QLabel* m_numberLabel;
QSpinBox* m_numberBox;
QLabel* m_lastNameLabel;
QLineEdit* m_lastNameEdit;
QLabel* m_firstNameLabel;
QLineEdit* m_firstNameEdit;
QLabel* m_shareTypeLabel;
QComboBox* m_shareTypeBox;
QStringListModel* m_shareTypeModel = nullptr;
QLabel* m_amountLabel;
QDoubleSpinBox* m_amountSpinBox;
QLabel* m_biddingTypeLabel;
QComboBox* m_biddingTypeBox;
QStringListModel* m_biddingTypeModel = nullptr;
QLabel* m_bidding1Label;
QSpinBox* m_bidding1SpinBox;
QLabel* m_bidding2Label;
QSpinBox* m_bidding2SpinBox;
QLabel* m_bidding3Label;
QSpinBox* m_bidding3SpinBox;
QLabel* m_depotWish1Label;
QLineEdit* m_depotWish1Edit;
QLabel* m_depotWish2Label;
QLineEdit* m_depotWish2Edit;
QLabel* m_mailLabel;
QLineEdit* m_mailEdit;
void resetContent();
};
#endif // NEWITEMDIALOG_H

View File

@ -15,7 +15,10 @@
#include "dialogs/settingsdialog.h"
#include "genericcore.h"
#include "model/generalsortfiltermodel.h"
#include "model/metadata.h"
#include "model/tablemodel.h"
#include "widgets/comboboxdelegate.h"
#include "widgets/spinboxdelegate.h"
static QStandardPaths::StandardLocation standardLocation = QStandardPaths::HomeLocation;
static QString updateTextClean = "Do you want to update the application now?";
@ -46,11 +49,7 @@ MainWindow::MainWindow(QWidget* parent)
restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("windowState").toByteArray());
// m_tableModel = m_core->getModel();
// ui->tableView->setModel(m_tableModel.get());
m_proxyModel = m_core->getSortFilterModel();
ui->tableView->setModel((QAbstractItemModel*)m_proxyModel.get());
ui->tableView->setSortingEnabled(true);
setupModelViews();
createActions();
createHelpMenu();
@ -147,8 +146,11 @@ void MainWindow::onSelectionChanged(const QItemSelection& selected,
void MainWindow::onAboutClicked() {
const QString applicationName = APPLICATION_NAME;
const QString titlePrefix = tr("About ");
// TODO read the about text from a rich text / markdown somewhere else.
const QString aboutText =
tr(QString("<b>%1 v%2</b> is a template for Qt applications."
tr(QString("<b>%1 v%2</b> is a software project to manage the yearly crop share "
"auction (german: Bietrunde) of a Community-supported agriculture (CSA, german: "
"Solidarische Landwirtschaft (SoLaWi))."
"<br><br><a href=\"https://working-copy.org/\">Working-Copy_Collective website</a>"
"<br><br><a href=\"mailto:support@working-copy.org\">Mail to support</a>"
"<br><br>It uses the <a href=\"https://qt.io\">Qt Framework</a>.")
@ -340,6 +342,29 @@ void MainWindow::execSettingsDialog() {
delete settingsDialog;
}
void MainWindow::setupModelViews() {
// m_tableModel = m_core->getModel();
// ui->tableView->setModel(m_tableModel.get());
m_proxyModel = m_core->getSortFilterModel();
/// setting number delegates to combo boxes
SpinboxDelegate* spinboxDelegate = new SpinboxDelegate(this);
ui->tableView->setItemDelegateForColumn(0, spinboxDelegate);
ui->tableView->setItemDelegateForColumn(4, spinboxDelegate);
ui->tableView->setItemDelegateForColumn(6, spinboxDelegate);
ui->tableView->setItemDelegateForColumn(7, spinboxDelegate);
ui->tableView->setItemDelegateForColumn(8, spinboxDelegate);
/// setting type delegates to combo boxes
ComboboxDelegate* shareTypeDelegate = new ComboboxDelegate(SHARE_TYPES, this);
ComboboxDelegate* biddingTypeDelegate = new ComboboxDelegate(BIDDING_TYPES, this);
ui->tableView->setItemDelegateForColumn(3, shareTypeDelegate);
ui->tableView->setItemDelegateForColumn(5, biddingTypeDelegate);
ui->tableView->setModel((QAbstractItemModel*)m_proxyModel.get());
ui->tableView->setSortingEnabled(true);
}
void MainWindow::createActions() {
// TODO add generic menu actions (file/new, edit/cut, ...)
createFileActions();

View File

@ -7,8 +7,8 @@
QT_BEGIN_NAMESPACE
class QUndoStack;
class QUndoView;
namespace Ui {
class MainWindow;
}
@ -110,6 +110,7 @@ class MainWindow : public QMainWindow {
unique_ptr<EditItemDialog> m_editItemDialog;
/// Setup functions
void setupModelViews();
void createActions();
void createFileActions();
void createUndoActions();

View File

@ -1,6 +1,7 @@
#include "itemdetailmapper.h"
#include <QAbstractItemModel>
#include <QComboBox>
#include <QDataWidgetMapper>
#include <QGridLayout>
#include <QJsonArray>
@ -9,6 +10,7 @@
#include <QLineEdit>
#include <QPushButton>
#include <QSpinBox>
#include <QStringListModel>
#include <QTableView>
#include "model/metadata.h"
@ -34,42 +36,84 @@ ItemDetailMapper::ItemDetailMapper(QWidget* parent)
/// the individual widgets
// REFACTOR deduce label names and types from meta data & use a factory
m_nameLabel = new QLabel("&Name");
m_nameEdit = new QLineEdit();
m_nameLabel->setBuddy(m_nameEdit);
m_numberLabel = new QLabel(GET_HEADER_FOR_COLUMN(0));
m_numberBox = new QSpinBox();
m_numberBox->setMaximum(1000);
m_descriptionLabel = new QLabel("&Description");
m_descriptionEdit = new QLineEdit();
m_descriptionLabel->setBuddy(m_descriptionEdit);
m_lastNameLabel = new QLabel(GET_HEADER_FOR_COLUMN(1));
m_lastNameEdit = new QLineEdit();
m_lastNameLabel->setBuddy(m_lastNameEdit);
m_infoLabel = new QLabel("&Info");
m_infoEdit = new QLineEdit();
m_infoLabel->setBuddy(m_infoEdit);
m_firstNameLabel = new QLabel(GET_HEADER_FOR_COLUMN(2));
m_firstNameEdit = new QLineEdit();
m_firstNameLabel->setBuddy(m_firstNameEdit);
m_amountLabel = new QLabel("&Amount");
m_amountBox = new QSpinBox();
m_amountBox->setMaximum(1000);
m_amountLabel->setBuddy(m_amountBox);
m_shareTypeLabel = new QLabel(GET_HEADER_FOR_COLUMN(3));
m_shareTypeBox = new QComboBox();
m_shareTypeLabel->setBuddy(m_shareTypeBox);
m_shareTypeModel = new QStringListModel(SHARE_TYPES, this);
m_shareTypeBox->setModel(m_shareTypeModel);
m_factorLabel = new QLabel("&Factor");
m_factorBox = new QDoubleSpinBox();
m_factorBox->setMaximum(1000);
m_factorLabel->setBuddy(m_factorBox);
m_amountLabel = new QLabel(GET_HEADER_FOR_COLUMN(4));
m_amountSpinBox = new QDoubleSpinBox();
m_amountLabel->setBuddy(m_amountSpinBox);
m_biddingTypeLabel = new QLabel(GET_HEADER_FOR_COLUMN(5));
m_biddingTypeBox = new QComboBox();
m_biddingTypeLabel->setBuddy(m_biddingTypeBox);
m_biddingTypeModel = new QStringListModel(BIDDING_TYPES, this);
m_biddingTypeBox->setModel(m_biddingTypeModel);
m_bidding1Label = new QLabel(GET_HEADER_FOR_COLUMN(6));
m_bidding1SpinBox = new QSpinBox();
m_bidding1SpinBox->setMaximum(500);
m_bidding2Label = new QLabel(GET_HEADER_FOR_COLUMN(7));
m_bidding2SpinBox = new QSpinBox();
m_bidding2SpinBox->setMaximum(500);
m_bidding3Label = new QLabel(GET_HEADER_FOR_COLUMN(8));
m_bidding3SpinBox = new QSpinBox();
m_bidding3SpinBox->setMaximum(500);
m_depotWish1Label = new QLabel(GET_HEADER_FOR_COLUMN(9));
m_depotWish1Edit = new QLineEdit();
m_depotWish1Label->setBuddy(m_depotWish1Edit);
m_depotWish2Label = new QLabel(GET_HEADER_FOR_COLUMN(10));
m_depotWish2Edit = new QLineEdit();
m_depotWish2Label->setBuddy(m_depotWish2Edit);
m_mailLabel = new QLabel(GET_HEADER_FOR_COLUMN(11));
m_mailEdit = new QLineEdit();
m_mailEdit->setMinimumWidth(200);
m_mailLabel->setBuddy(m_mailEdit);
QGridLayout* layout = new QGridLayout();
layout->addWidget(m_nameLabel, 0, 0, 1, 1);
layout->addWidget(m_nameEdit, 0, 1, 1, 1);
layout->addWidget(m_descriptionLabel, 1, 0, 1, 1);
layout->addWidget(m_descriptionEdit, 1, 1, 1, 1);
layout->addWidget(m_infoLabel, 2, 0, 1, 1);
layout->addWidget(m_infoEdit, 2, 1, 1, 1);
layout->addWidget(m_amountLabel, 3, 0, 1, 1);
layout->addWidget(m_amountBox, 3, 1, 1, 1);
layout->addWidget(m_factorLabel, 4, 0, 1, 1);
layout->addWidget(m_factorBox, 4, 1, 1, 1);
layout->addWidget(m_numberLabel, 0, 0, 1, 1);
layout->addWidget(m_numberBox, 0, 1, 1, 1);
layout->addWidget(m_lastNameLabel, 1, 0, 1, 1);
layout->addWidget(m_lastNameEdit, 1, 1, 1, 1);
layout->addWidget(m_firstNameLabel, 2, 0, 1, 1);
layout->addWidget(m_firstNameEdit, 2, 1, 1, 1);
layout->addWidget(m_shareTypeLabel, 3, 0, 1, 1);
layout->addWidget(m_shareTypeBox, 3, 1, 1, 1);
layout->addWidget(m_amountLabel, 4, 0, 1, 1);
layout->addWidget(m_amountSpinBox, 4, 1, 1, 1);
layout->addWidget(m_biddingTypeLabel, 5, 0, 1, 1);
layout->addWidget(m_biddingTypeBox, 5, 1, 1, 1);
layout->addWidget(m_bidding1Label, 6, 0, 1, 1);
layout->addWidget(m_bidding1SpinBox, 6, 1, 1, 1);
layout->addWidget(m_bidding2Label, 7, 0, 1, 1);
layout->addWidget(m_bidding2SpinBox, 7, 1, 1, 1);
layout->addWidget(m_bidding3Label, 8, 0, 1, 1);
layout->addWidget(m_bidding3SpinBox, 8, 1, 1, 1);
layout->addWidget(m_depotWish1Label, 9, 0, 1, 1);
layout->addWidget(m_depotWish1Edit, 9, 1, 1, 1);
layout->addWidget(m_depotWish2Label, 10, 0, 1, 1);
layout->addWidget(m_depotWish2Edit, 10, 1, 1, 1);
layout->addWidget(m_mailLabel, 11, 0, 1, 1);
layout->addWidget(m_mailEdit, 11, 1, 1, 1);
layout->addWidget(m_previousButton, 5, 0, 1, 1);
layout->addWidget(m_nextButton, 5, 1, 1, 1);
layout->addWidget(m_previousButton, 12, 0, 1, 1);
layout->addWidget(m_nextButton, 12, 1, 1, 1);
setLayout(layout);
}
@ -81,11 +125,18 @@ void ItemDetailMapper::setModelMappings(QTableView* tableView) {
m_selectionModel = tableView->selectionModel();
m_mapper->setModel(m_model);
m_mapper->addMapping(m_nameEdit, 0);
m_mapper->addMapping(m_descriptionEdit, 1);
m_mapper->addMapping(m_infoEdit, 2);
m_mapper->addMapping(m_amountBox, 3);
m_mapper->addMapping(m_factorBox, 4);
m_mapper->addMapping(m_numberBox, 0);
m_mapper->addMapping(m_lastNameEdit, 1);
m_mapper->addMapping(m_firstNameEdit, 2);
m_mapper->addMapping(m_shareTypeBox, 3, "currentText");
m_mapper->addMapping(m_amountSpinBox, 4);
m_mapper->addMapping(m_biddingTypeBox, 5, "currentText");
m_mapper->addMapping(m_bidding1SpinBox, 6);
m_mapper->addMapping(m_bidding2SpinBox, 7);
m_mapper->addMapping(m_bidding3SpinBox, 8);
m_mapper->addMapping(m_depotWish1Edit, 9);
m_mapper->addMapping(m_depotWish2Edit, 10);
m_mapper->addMapping(m_mailEdit, 11);
m_mapper->setCurrentIndex(m_selectionModel->currentIndex().row());
@ -119,11 +170,11 @@ void ItemDetailMapper::rowsRemoved(const QModelIndex& parent, int start, int end
if (m_model->rowCount() == 0) {
setEnabled(false);
m_nameEdit->clear();
m_descriptionEdit->clear();
m_infoEdit->clear();
m_amountBox->clear();
m_factorBox->clear();
// m_nameEdit->clear();
// m_descriptionEdit->clear();
// m_infoEdit->clear();
// m_amountBox->clear();
// m_factorBox->clear();
}
updateButtons(m_mapper->currentIndex());

View File

@ -6,11 +6,13 @@
class QLabel;
class QLineEdit;
class QComboBox;
class QDoubleSpinBox;
class QSpinBox;
class QPushButton;
class QAbstractItemModel;
class QItemSelectionModel;
class QStringListModel;
class QTableView;
class ItemDetailMapper : public QWidget {
@ -45,21 +47,42 @@ class ItemDetailMapper : public QWidget {
std::unique_ptr<QDataWidgetMapper> m_mapper;
/// GUI elements
QLabel* m_nameLabel = nullptr;
QLineEdit* m_nameEdit = nullptr;
QLabel* m_numberLabel;
QSpinBox* m_numberBox;
QLabel* m_descriptionLabel = nullptr;
QLineEdit* m_descriptionEdit = nullptr;
QLabel* m_lastNameLabel;
QLineEdit* m_lastNameEdit;
QLabel* m_infoLabel = nullptr;
QLineEdit* m_infoEdit = nullptr;
QLabel* m_firstNameLabel;
QLineEdit* m_firstNameEdit;
QLabel* m_amountLabel = nullptr;
QSpinBox* m_amountBox = nullptr;
QLabel* m_shareTypeLabel;
QComboBox* m_shareTypeBox;
QStringListModel* m_shareTypeModel = nullptr;
QLabel* m_factorLabel = nullptr;
QDoubleSpinBox* m_factorBox = nullptr;
QLabel* m_amountLabel;
QDoubleSpinBox* m_amountSpinBox;
QLabel* m_biddingTypeLabel;
QComboBox* m_biddingTypeBox;
QStringListModel* m_biddingTypeModel = nullptr;
QLabel* m_bidding1Label;
QSpinBox* m_bidding1SpinBox;
QLabel* m_bidding2Label;
QSpinBox* m_bidding2SpinBox;
QLabel* m_bidding3Label;
QSpinBox* m_bidding3SpinBox;
QLabel* m_depotWish1Label;
QLineEdit* m_depotWish1Edit;
QLabel* m_depotWish2Label;
QLineEdit* m_depotWish2Edit;
QLabel* m_mailLabel;
QLineEdit* m_mailEdit;
/// Model mapper stuff
QPushButton* m_nextButton;
QPushButton* m_previousButton;
};

View File

@ -0,0 +1,66 @@
#include "comboboxdelegate.h"
#include <QComboBox>
#include <QStringListModel>
#include "model/metadata.h"
ComboboxDelegate::ComboboxDelegate(const QStringList items, QObject* parent)
: QStyledItemDelegate(parent)
, m_types(new QStringListModel(items)) {}
void ComboboxDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const {
QStyledItemDelegate::paint(painter, option, index);
}
QSize ComboboxDelegate::sizeHint(const QStyleOptionViewItem& option,
const QModelIndex& index) const {
return QStyledItemDelegate::sizeHint(option, index);
}
QWidget* ComboboxDelegate::createEditor(QWidget* parent,
const QStyleOptionViewItem& /*option*/,
const QModelIndex& /*index*/) const {
QComboBox* editor = new QComboBox(parent);
editor->setModel(m_types);
return editor;
// return QStyledItemDelegate::createEditor(parent, option, index);
}
void ComboboxDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
/// Get the value via index of the Model
const QAbstractItemModel* localModel = index.model();
const QString headerText = localModel->headerData(index.column(), Qt::Horizontal).toString();
const UserRoles role = GET_ROLE_FOR_COLUMN(index.column());
const bool isShareType = SHARE_TYPE_ROLES.contains(role);
const bool isBiddingType = BIDDING_TYPE_ROLES.contains(role);
/// Put the value into the SpinBox
if (isShareType) {
const QString valueString = index.model()->data(index, ShareTypeRole).toString();
int value = SHARE_TYPES.indexOf(valueString);
QComboBox* combobox = static_cast<QComboBox*>(editor);
combobox->setCurrentIndex(value);
// QStyledItemDelegate::setEditorData(editor, index);
} else if (isBiddingType) {
const QString valueString = index.model()->data(index, BiddingTypeRole).toString();
int value = BIDDING_TYPES.indexOf(valueString);
// Put the value into the SpinBox
QComboBox* combobox = static_cast<QComboBox*>(editor);
combobox->setCurrentIndex(value);
// QStyledItemDelegate::setEditorData(editor, index);
} else {
qCritical() << "Could not find the correct type role for index:" << index << "!!!";
QComboBox* combobox = static_cast<QComboBox*>(editor);
combobox->setCurrentIndex(-1);
}
}
void ComboboxDelegate::setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const {
QStyledItemDelegate::setModelData(editor, model, index);
}

View File

@ -0,0 +1,30 @@
#ifndef COMBOBOXDELEGATE_H
#define COMBOBOXDELEGATE_H
#include <QStyledItemDelegate>
class QStringListModel;
class ComboboxDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
explicit ComboboxDelegate(const QStringList items, QObject* parent = nullptr);
/// QAbstractItemDelegate interface
public:
void paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
QWidget* createEditor(QWidget* parent,
const QStyleOptionViewItem& option,
const QModelIndex&) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const override;
private:
QStringListModel* m_types = nullptr;
};
#endif // COMBOBOXDELEGATE_H

View File

@ -0,0 +1,69 @@
#include "spinboxdelegate.h"
#include <QSpinBox>
#include "model/metadata.h"
SpinboxDelegate::SpinboxDelegate(QObject* parent)
: QStyledItemDelegate(parent) {}
void SpinboxDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const {
QStyledItemDelegate::paint(painter, option, index);
}
QSize SpinboxDelegate::sizeHint(const QStyleOptionViewItem& option,
const QModelIndex& index) const {
return QStyledItemDelegate::sizeHint(option, index);
}
QWidget* SpinboxDelegate::createEditor(QWidget* parent,
const QStyleOptionViewItem& option,
const QModelIndex& index) const {
const QAbstractItemModel* localModel = index.model();
QString headerText = localModel->headerData(index.column(), Qt::Horizontal).toString();
const UserRoles role = GET_ROLE_FOR_COLUMN(index.column());
const bool isInt = INT_ROLES.contains(role);
if (isInt) {
QSpinBox* editor = new QSpinBox(parent);
editor->setMinimum(0);
editor->setMaximum(23000);
return editor;
} else {
QDoubleSpinBox* editor = new QDoubleSpinBox(parent);
editor->setMinimum(0);
editor->setMaximum(23000);
return editor;
}
// return QStyledItemDelegate::createEditor(parent, option, index);
}
void SpinboxDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
// Get the value via index of the Model
const QAbstractItemModel* localModel = index.model();
QString headerText = localModel->headerData(index.column(), Qt::Horizontal).toString();
const UserRoles role = GET_ROLE_FOR_COLUMN(index.column());
const bool isInt = INT_ROLES.contains(role);
if (isInt) {
int value = index.model()->data(index, Qt::EditRole).toInt();
// Put the value into the SpinBox
QSpinBox* spinbox = static_cast<QSpinBox*>(editor);
spinbox->setValue(value);
} else {
// Put the value into the SpinBox
qreal value = index.model()->data(index, Qt::EditRole).toReal();
QDoubleSpinBox* spinbox = static_cast<QDoubleSpinBox*>(editor);
spinbox->setValue(value);
}
// QStyledItemDelegate::setEditorData(editor, index);
}
void SpinboxDelegate::setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const {
QStyledItemDelegate::setModelData(editor, model, index);
}

View File

@ -0,0 +1,26 @@
#ifndef SPINBOXDELEGATE_H
#define SPINBOXDELEGATE_H
#include <QStyledItemDelegate>
class SpinboxDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
explicit SpinboxDelegate(QObject* parent = nullptr);
/// QAbstractItemDelegate interface
public:
void paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
QWidget* createEditor(QWidget* parent,
const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor,
QAbstractItemModel* model,
const QModelIndex& index) const override;
};
#endif // SPINBOXDELEGATE_H

View File

@ -1,81 +0,0 @@
#include "newitemdialog.h"
#include <QGridLayout>
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include "formats/jsonparser.h"
#include "model/metadata.h"
NewItemDialog::NewItemDialog(QWidget* parent)
: AbstractDialog(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, parent) {}
void NewItemDialog::createContent() {
if (m_contentContainer) {
delete m_contentContainer;
}
setWindowTitle(tr("New item..."));
m_contentContainer = new QWidget(this);
// REFACTOR use a data structure for input widgets which can be iterated through
// using a factory which iterates through the roles from metadata.h
// and create the input widgets based on the data type of this role
m_nameLabel = new QLabel("&Name");
m_nameEdit = new QLineEdit();
m_nameLabel->setBuddy(m_nameEdit);
m_descriptionLabel = new QLabel("&Description");
m_descriptionEdit = new QLineEdit();
m_descriptionLabel->setBuddy(m_descriptionEdit);
m_infoLabel = new QLabel("&Info");
m_infoEdit = new QLineEdit();
m_infoLabel->setBuddy(m_infoEdit);
m_amountLabel = new QLabel("&Amount");
m_amountBox = new QSpinBox();
m_amountBox->setMaximum(1000);
m_amountLabel->setBuddy(m_amountBox);
m_factorLabel = new QLabel("&Factor");
m_factorBox = new QDoubleSpinBox();
m_factorBox->setMaximum(1000);
m_factorLabel->setBuddy(m_factorBox);
QGridLayout* layout = new QGridLayout();
layout->addWidget(m_nameLabel, 0, 0, 1, 1);
layout->addWidget(m_nameEdit, 0, 1, 1, 1);
layout->addWidget(m_descriptionLabel, 1, 0, 1, 1);
layout->addWidget(m_descriptionEdit, 1, 1, 1, 1);
layout->addWidget(m_infoLabel, 2, 0, 1, 1);
layout->addWidget(m_infoEdit, 2, 1, 1, 1);
layout->addWidget(m_amountLabel, 3, 0, 1, 1);
layout->addWidget(m_amountBox, 3, 1, 1, 1);
layout->addWidget(m_factorLabel, 4, 0, 1, 1);
layout->addWidget(m_factorBox, 4, 1, 1, 1);
m_contentContainer->setLayout(layout);
m_outerLayout->insertWidget(0, m_contentContainer);
}
void NewItemDialog::accept() {
ModelItemValues itemValues;
// TODO (after refactoring data structure for input widgets) use iteration through the relevant
// roles and their input widgets
itemValues.insert(NameRole, m_nameEdit->text());
itemValues.insert(DescriptionRole, m_descriptionEdit->text());
itemValues.insert(InfoRole, m_infoEdit->text());
itemValues.insert(AmountRole, m_amountBox->value());
itemValues.insert(FactorRole, m_factorBox->value());
const QByteArray jsonDoc = JsonParser::itemValuesListToJson({itemValues}, ITEMS_KEY_STRING);
emit addItems(jsonDoc);
// resetContent();
AbstractDialog::accept();
}

View File

@ -1,43 +0,0 @@
#ifndef NEWITEMDIALOG_H
#define NEWITEMDIALOG_H
#include "abstractdialog.h"
class QDoubleSpinBox;
class QLineEdit;
class QSpinBox;
class QLabel;
class NewItemDialog : public AbstractDialog {
Q_OBJECT
public:
NewItemDialog(QWidget* parent = nullptr);
void createContent() override;
signals:
void addItems(const QByteArray& jsonDoc);
public slots:
void accept() override;
// void reject() override;
private:
QLabel* m_nameLabel = nullptr;
QLineEdit* m_nameEdit = nullptr;
QLabel* m_descriptionLabel = nullptr;
QLineEdit* m_descriptionEdit = nullptr;
QLabel* m_infoLabel = nullptr;
QLineEdit* m_infoEdit = nullptr;
QLabel* m_amountLabel = nullptr;
QSpinBox* m_amountBox = nullptr;
QLabel* m_factorLabel = nullptr;
QDoubleSpinBox* m_factorBox = nullptr;
};
#endif // NEWITEMDIALOG_H

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)
set(TARGET_APP "GenericCore")
set(TARGET_APP "BeetRoundCore")
project(${TARGET_APP} VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
@ -43,8 +43,8 @@ add_library(${TARGET_APP} STATIC
include_directories(${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(${TARGET_APP} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui)
target_link_libraries(GenericCore PRIVATE Qt${QT_VERSION_MAJOR}::Test)
target_link_libraries(GenericCore PRIVATE Qt${QT_VERSION_MAJOR}::Network)
target_link_libraries(${TARGET_APP} PRIVATE Qt${QT_VERSION_MAJOR}::Test)
target_link_libraries(${TARGET_APP} PRIVATE Qt${QT_VERSION_MAJOR}::Network)
target_compile_definitions(${TARGET_APP} PRIVATE ${TARGET_APP}_LIBRARY)

View File

@ -52,6 +52,11 @@ bool CsvParser::isCsvCompatible(const rapidcsv::Document& doc) {
qInfo() << "Checking CSV document for compatiblity...";
const std::vector<std::string> columnNames = doc.GetColumnNames();
for (const QString& headerName : GET_HEADER_NAMES()) {
if (OPTIONAL_CSV_HEADERS.contains(headerName)) {
/// no need to have a column for the optional values
continue;
}
/// these column must be found in CSV document
bool isHeaderNameFound = false;
if (std::find(columnNames.begin(), columnNames.end(), headerName) != columnNames.end()) {
qDebug() << QString("Header found in column names: %1").arg(headerName);
@ -86,14 +91,13 @@ QHash<QString, std::vector<std::string>> CsvParser::extractColumnValues(
const rapidcsv::Document& doc) {
QHash<QString, std::vector<std::string>> columnValueMap;
for (const QString& columnName : headerNames) {
// TODO add support for optional columns
// if (optionalCsvHeaderNames.contains(columnName)) {
// const std::vector<std::string> columnNames = doc.GetColumnNames();
// int columnIdx = doc.GetColumnIdx(columnName.toStdString());
// if (columnIdx == -1) {
// continue;
// }
// }
if (OPTIONAL_CSV_HEADERS.contains(columnName)) {
const std::vector<std::string> columnNames = doc.GetColumnNames();
int columnIdx = doc.GetColumnIdx(columnName.toStdString());
if (columnIdx == -1) {
continue;
}
}
const std::vector<std::string> columnValues =
doc.GetColumn<std::string>(columnName.toStdString());
columnValueMap.insert(columnName, columnValues);

View File

@ -86,14 +86,6 @@ QJsonArray JsonParser::extractItemArray(const QJsonDocument& doc, const QString&
ModelItemValues JsonParser::jsonObjectToItemValues(const QJsonObject& itemJsonObject) {
ModelItemValues values;
const UserRoles idRole = IdRole;
const QString idRoleName = ROLE_NAMES.value(idRole);
// QVariant idValue = data(idRole);
if (itemJsonObject.contains(idRoleName)) {
std::pair<int, QVariant> keyValuePair = getKeyValuePair(itemJsonObject, idRole);
values.insert(keyValuePair.first, keyValuePair.second);
}
QListIterator<UserRoles> i(USER_FACING_ROLES);
while (i.hasNext()) {
const UserRoles role = i.next();

View File

@ -26,11 +26,6 @@ EditItemCommand::EditItemCommand(TableModel* model,
.arg(roleName)
.arg(index.data(DEFAULT_ROLE).toString())
.arg(value.toString());
} else if (role == IdRole) {
commandText = QString("Setting '%1' of item '%2' to '%3'")
.arg(roleName)
.arg(index.data(DEFAULT_ROLE).toString())
.arg(value.toString());
} else {
qWarning() << "Role didn't match! Using a generic command text...";
commandText = QString("Edit item '%1'").arg(index.data(DEFAULT_ROLE).toString());

View File

@ -24,7 +24,7 @@ QItemSelection GeneralSortFilterModel::findItems(const QString& text) const {
}
QString GeneralSortFilterModel::getUuid(const QModelIndex& itemIndex) const {
return data(itemIndex, IdRole).toString();
return data(itemIndex, OnlineIdRole).toString();
}
QByteArray GeneralSortFilterModel::jsonDataForServer(const QModelIndex& proxyIndex) {

View File

@ -0,0 +1,151 @@
#ifndef METADATA_H
#define METADATA_H
#include <QDebug>
#include <QHash>
#include <QList>
#include <QString>
// TODO add namespace
/// model data
enum UserRoles {
LastNameRole = Qt::UserRole + 1,
FirstNameRole,
MembershipNumberRole,
Bidding1Role,
Bidding2Role,
Bidding3Role,
DepotWish1Role,
DepotWish2Role,
ShareAmountRole,
MailRole,
ShareTypeRole,
BiddingTypeRole,
OnlineIdRole,
AccessCodeRole,
// read-only (calculated)
FullNameRole,
FactoredBiddingRole,
ToStringRole,
JsonObjectRole
};
static UserRoles DEFAULT_ROLE = FullNameRole;
// TODO ?rename USER_FACING_ROLES -> MAIN_ROLES ?
static QList<UserRoles> USER_FACING_ROLES = {
LastNameRole, FirstNameRole, MembershipNumberRole, Bidding1Role, Bidding2Role,
Bidding3Role, DepotWish1Role, DepotWish2Role, ShareAmountRole, MailRole,
ShareTypeRole, BiddingTypeRole, OnlineIdRole, AccessCodeRole};
static QHash<int, QByteArray> ROLE_NAMES = {{MembershipNumberRole, "Mitglieds-nr."},
{LastNameRole, "Name"},
{FirstNameRole, "Vorname"},
{Bidding1Role, "Gebot 1"},
{Bidding2Role, "Gebot 2"},
{Bidding3Role, "Gebot 3"},
{DepotWish1Role, "Wunsch 1"},
{DepotWish2Role, "Wunsch 2"},
{ShareAmountRole, "Anzahl"},
{MailRole, "Mail"},
{ShareTypeRole, "Art"},
{BiddingTypeRole, "Bietart"},
{OnlineIdRole, "Online ID"},
{AccessCodeRole, "Access Code"},
{FactoredBiddingRole, "factoredBidding"}};
static const QList<QString> SHARE_TYPES = {"bezahlt", "teils/teils", "erarbeitet", ""};
static const QList<QString> BIDDING_TYPES = {"paper", "digital", "online", "offline", ""};
static QList<UserRoles> STRING_ROLES = {LastNameRole, FirstNameRole, DepotWish1Role,
DepotWish2Role, MailRole, ShareTypeRole,
BiddingTypeRole, OnlineIdRole, AccessCodeRole};
static QList<UserRoles> INT_ROLES = {
MembershipNumberRole,
Bidding1Role,
Bidding2Role,
Bidding3Role,
};
static QList<UserRoles> DOUBLE_ROLES = {
ShareAmountRole,
};
static const QList<UserRoles> TYPE_ROLES = {ShareTypeRole, BiddingTypeRole};
static const QList<UserRoles> SHARE_TYPE_ROLES = {ShareTypeRole};
static const QList<UserRoles> BIDDING_TYPE_ROLES = {BiddingTypeRole};
static const QStringList OPTIONAL_CSV_HEADERS = {"Bietart", "Online ID", "Access Code"};
/// JSON keys
static const QString ITEMS_KEY_STRING = "items";
static const QString ITEM_KEY_STRING = "item";
/// file naming
static const QString ITEMS_FILE_NAME = ITEMS_KEY_STRING + ".json";
/// functions
static UserRoles GET_ROLE_FOR_COLUMN(const int column) {
switch (column) {
case 0:
return MembershipNumberRole;
break;
case 1:
return LastNameRole;
break;
case 2:
return FirstNameRole;
break;
case 3:
return ShareTypeRole;
break;
case 4:
return ShareAmountRole;
break;
case 5:
return BiddingTypeRole;
break;
case 6:
return Bidding1Role;
break;
case 7:
return Bidding2Role;
break;
case 8:
return Bidding3Role;
break;
case 9:
return DepotWish1Role;
break;
case 10:
return DepotWish2Role;
break;
case 11:
return MailRole;
break;
case 12:
return OnlineIdRole;
break;
case 13:
return AccessCodeRole;
break;
default:
qWarning() << QString("No role found for column %1! Returning 'FullNameRole'...").arg(column);
return FullNameRole;
break;
}
}
static QList<QString> GET_HEADER_NAMES() {
QList<QString> result;
for (const UserRoles& role : USER_FACING_ROLES) {
const QString headerName = ROLE_NAMES.value(role);
result.append(headerName);
}
return result;
}
static QString GET_HEADER_FOR_COLUMN(const int column) {
const UserRoles role = GET_ROLE_FOR_COLUMN(column);
const QString headerName = ROLE_NAMES.value(role);
return headerName;
}
#endif // METADATA_H

View File

@ -8,8 +8,28 @@
ModelItem::ModelItem(const ModelItemValues values)
: m_values(values) {}
QVariant ModelItem::data(int role) const { return m_values.value(role); }
QVariant ModelItem::data(int role) const {
switch (role) {
case FullNameRole:
return fullName();
break;
case ToStringRole:
return toString();
break;
case JsonObjectRole:
return toJsonObject();
break;
case MembershipNumberRole:
case ShareAmountRole:
case Bidding1Role:
case Bidding2Role:
case Bidding3Role:
return getValueButReplaceZeroValueWithEmptyString(role);
break;
default:
return m_values.value(role);
}
}
bool ModelItem::setData(const QVariant& value, int role) {
bool valueChanged = false;
if (m_values.contains(role)) {
@ -44,6 +64,10 @@ bool ModelItem::setItemData(const QMap<int, QVariant>& changedValues) {
return valueChanged;
}
QString ModelItem::fullName() const {
return QString("%1 %2").arg(data(FirstNameRole).toString(), data(LastNameRole).toString());
}
QString ModelItem::toString() const {
QString result;
@ -52,15 +76,7 @@ QString ModelItem::toString() const {
const UserRoles role = i.next();
const QString roleName = ROLE_NAMES.value(role);
const QVariant value = data(role);
// result.append(value.toString());
result.append(QString("%1: %2\n").arg(roleName, data(role).toString()));
}
const UserRoles idRole = IdRole;
QVariant idValue = data(idRole);
if (!idValue.isNull()) {
const QString idRoleName = ROLE_NAMES.value(idRole);
result.append(QString("%1: %2\n").arg(idRoleName, idValue.toString()));
result.append(QString("%1: %2\n").arg(roleName, value.toString()));
}
return result;
}
@ -68,12 +84,6 @@ QString ModelItem::toString() const {
QJsonObject ModelItem::toJsonObject() const {
QJsonObject itemObject;
// TODO add UUID and dates (entry, modification, end)
const UserRoles idRole = IdRole;
QVariant idValue = data(idRole);
if (!idValue.isNull()) {
const QString idRoleName = ROLE_NAMES.value(idRole);
itemObject.insert(idRoleName, idValue.toString());
}
QListIterator<UserRoles> i(USER_FACING_ROLES);
while (i.hasNext()) {
@ -92,3 +102,12 @@ QJsonObject ModelItem::toJsonObject() const {
}
return itemObject;
}
QVariant ModelItem::getValueButReplaceZeroValueWithEmptyString(const int role) const {
QVariant localValue = m_values.value(role, QVariant());
if (localValue == 0) {
return QVariant();
} else {
return localValue;
}
}

View File

@ -14,11 +14,14 @@ class ModelItem {
// TODO change return value to list of changed roles
bool setItemData(const QMap<int, QVariant>& changedValues);
QString fullName() const;
QString toString() const;
QJsonObject toJsonObject() const;
private:
ModelItemValues m_values;
QVariant getValueButReplaceZeroValueWithEmptyString(const int role) const;
};
#endif // MODELITEM_H

View File

@ -21,11 +21,20 @@ QByteArray TableModel::generateExampleItems() {
QJsonObject itemObject;
// itemObject.insert("uuid", m_uuid.toString());
// itemObject.insert("entryDateUTC", m_entryDateUTC.toString(Qt::ISODate));
itemObject.insert(ROLE_NAMES.value(NameRole), QString("Item %1").arg(row));
itemObject.insert(ROLE_NAMES.value(DescriptionRole), QString("This is item %1").arg(row));
itemObject.insert(ROLE_NAMES.value(InfoRole), QString("Info of item %1").arg(row));
itemObject.insert(ROLE_NAMES.value(AmountRole), row);
itemObject.insert(ROLE_NAMES.value(FactorRole), row * 1.1);
itemObject.insert(ROLE_NAMES.value(MembershipNumberRole), row);
itemObject.insert(ROLE_NAMES.value(LastNameRole), QString("Nachname%1").arg(row));
itemObject.insert(ROLE_NAMES.value(FirstNameRole), QString("Vorname%1").arg(row));
itemObject.insert(ROLE_NAMES.value(Bidding1Role), 100 + row);
itemObject.insert(ROLE_NAMES.value(DepotWish1Role), QString("Depot X%1").arg(row));
itemObject.insert(ROLE_NAMES.value(DepotWish2Role), QString("Depot Y%1").arg(row));
itemObject.insert(ROLE_NAMES.value(MailRole),
QString("%1@%2.com")
.arg(itemObject.value(ROLE_NAMES.value(FirstNameRole)).toString(),
itemObject.value(ROLE_NAMES.value(LastNameRole)).toString()));
itemObject.insert(ROLE_NAMES.value(ShareAmountRole), 1);
itemObject.insert(ROLE_NAMES.value(ShareTypeRole), SHARE_TYPES.at(row % 3));
itemObject.insert(ROLE_NAMES.value(BiddingTypeRole), BIDDING_TYPES.at(row % 4));
// itemObject.insert(ROLE_NAMES.value(FactorRole), row * 1.1);
array.append(itemObject);
}
@ -73,22 +82,32 @@ QVariant TableModel::data(const QModelIndex& index, int role) const {
return QVariant();
}
int roleForColumn = GET_ROLE_FOR_COLUMN(column);
const int roleForColumn = GET_ROLE_FOR_COLUMN(column);
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
return m_items.at(row)->data(roleForColumn);
case NameRole:
case DescriptionRole:
case InfoRole:
case AmountRole:
case FactorRole:
case IdRole:
return m_items.at(row)->data(role);
case Qt::ToolTipRole:
return m_items.at(index.row())->data(ToStringRole);
break;
case MembershipNumberRole:
case LastNameRole:
case FirstNameRole:
case FullNameRole:
case Bidding1Role:
case Bidding2Role:
case Bidding3Role:
case DepotWish1Role:
case DepotWish2Role:
case ShareAmountRole:
case MailRole:
case ShareTypeRole:
case BiddingTypeRole:
case OnlineIdRole:
case AccessCodeRole:
case ToStringRole:
return m_items.at(row)->toString();
case ToJsonRole:
return m_items.at(row)->toJsonObject();
case JsonObjectRole:
return m_items.at(row)->data(role);
}
return QVariant();
@ -174,7 +193,7 @@ QList<QStringList> TableModel::getItemsAsStringLists() const {
// TODO use item selection as parameter to wrap multiple items into JSON data structure
QByteArray TableModel::jsonDataForServer(const QModelIndex& currentIndex) const {
const QJsonObject itemObject = data(currentIndex, ToJsonRole).toJsonObject();
const QJsonObject itemObject = data(currentIndex, JsonObjectRole).toJsonObject();
QJsonObject rootObject;
rootObject.insert(ITEM_KEY_STRING, itemObject);
const QJsonDocument jsonDoc(rootObject);
@ -350,8 +369,9 @@ QModelIndex TableModel::searchItemIndex(const ModelItemValues givenItemValues) c
bool TableModel::isItemEqualToItemValues(const QModelIndex& itemIndex,
const ModelItemValues givenItemValues) const {
/// do both have a UUID?
QVariant idOfItem = data(itemIndex, IdRole);
QVariant given = givenItemValues.value(IdRole);
const UserRoles idRole = OnlineIdRole;
QVariant idOfItem = data(itemIndex, idRole);
QVariant given = givenItemValues.value(idRole);
if (idOfItem.isValid() && given.isValid()) {
/// are the UUIDs the same?
if (idOfItem.toString() == given.toString()) {

View File

@ -1,77 +0,0 @@
#ifndef METADATA_H
#define METADATA_H
#include <QDebug>
#include <QHash>
#include <QList>
#include <QString>
// TODO add namespace
/// model data
enum UserRoles {
NameRole = Qt::UserRole + 1,
DescriptionRole,
InfoRole,
AmountRole,
FactorRole,
/// Non user facing
IdRole,
/// read only roles
ToStringRole,
ToJsonRole
};
static UserRoles DEFAULT_ROLE = NameRole;
// TODO ?rename USER_FACING_ROLES -> MAIN_ROLES ?
static QList<UserRoles> USER_FACING_ROLES = {NameRole, DescriptionRole, InfoRole, AmountRole,
FactorRole};
static QHash<int, QByteArray> ROLE_NAMES = {
{NameRole, "name"}, {DescriptionRole, "description"}, {InfoRole, "info"},
{AmountRole, "amount"}, {FactorRole, "factor"}, {ToStringRole, "ToString"},
{IdRole, "id"}};
static QList<UserRoles> STRING_ROLES = {NameRole, DescriptionRole, InfoRole, IdRole};
static QList<UserRoles> INT_ROLES = {AmountRole};
static QList<UserRoles> DOUBLE_ROLES = {FactorRole};
/// JSON keys
static const QString ITEMS_KEY_STRING = "items";
static const QString ITEM_KEY_STRING = "item";
/// file naming
static const QString ITEMS_FILE_NAME = ITEMS_KEY_STRING + ".json";
/// functions
static int GET_ROLE_FOR_COLUMN(const int column) {
switch (column) {
case 0:
return NameRole;
break;
case 1:
return DescriptionRole;
break;
case 2:
return InfoRole;
break;
case 3:
return AmountRole;
break;
case 4:
return FactorRole;
break;
default:
qWarning() << QString("No role found for column %1! Returning 'NameRole'...").arg(column);
return NameRole;
break;
}
}
static QList<QString> GET_HEADER_NAMES() {
QList<QString> result;
for (const UserRoles& role : USER_FACING_ROLES) {
const QString headerName = ROLE_NAMES.value(role);
result.append(headerName);
}
return result;
}
#endif // METADATA_H

View File

@ -24,7 +24,7 @@ target_include_directories(${TARGET_APP} PRIVATE ${CORE_LIB_DIR}/include)
target_link_libraries(${TARGET_APP}
PRIVATE
GTest::GTest
GenericCore)
BeetRoundCore)
target_link_libraries(${TARGET_APP} PUBLIC Qt${QT_VERSION_MAJOR}::Core)
add_test(core_gtests ${TARGET_APP})

View File

@ -2,7 +2,7 @@
#include <QString>
#include "../../libs/GenericCore/genericcore.h"
#include "../../libs/BeetRoundCore/genericcore.h"
QT_BEGIN_NAMESPACE
inline void PrintTo(const QString& qString, ::std::ostream* os) { *os << qUtf8Printable(qString); }
@ -10,7 +10,7 @@ QT_END_NAMESPACE
TEST(CoreTests, TestEqualString) {
const QString coreName("GenericCore");
const QString coreVersion("0.2.0");
const QString coreVersion("0.1.0");
const auto expected = QString("%1 (Version %2)").arg(coreName).arg(coreVersion);
auto core = std::make_unique<GenericCore>();
const auto actual = core->toString();