Compare commits

...

58 Commits

Author SHA1 Message Date
6408eaa10a Refactored the creation of control widgets. 2026-04-02 09:51:04 +02:00
24192df5d6 Using the new SpinBoxDelegate in int and double columns even if the cell is empty. 2026-03-14 09:35:42 +01:00
c7b0e4f3e5 Removed status message which overwrites message from the core on CSV import. 2026-03-12 10:38:31 +01:00
7fa8612313 Removed not used icon and renamed the application icon properly. 2026-03-06 11:07:05 +01:00
8dbceabb8a Removed messagehandler.h because it now lives in the GenericCore project. 2026-03-03 18:55:55 +01:00
2d8c97b5f0 NewItemDialog uses dynamically generated control widgets for model data as well. Added a WidgetHelper class to consolidate identical source code for ItemDetailMapper and NewItemDialog. 2026-03-02 19:25:27 +01:00
2fb7560e6e Minor refactoring of ItemDetailMapper. 2026-03-02 18:51:31 +01:00
560cadb2c0 All mapped control widgets (plus labels) are dynamically generated from meta data. 2026-03-02 17:11:31 +01:00
0e7f803d42 Refactored ItemDetailMapper to encapsulate widget creation and their mappings into functions to be able to iterate over meta data to auto generate them. (Only working for string roles for now) 2026-03-02 14:09:55 +01:00
d56af7231c Added the type role to the EditItemDialog. 2026-03-02 08:43:21 +01:00
385dc7ca76 Added a ComboBoxDelegate for the new "type" role. 2026-03-01 11:42:30 +01:00
5e3950f6ba Bumped version to 0.3.0. 2026-02-04 11:28:11 +01:00
34a34891b4 In SettingsDialog: Disabled the email and password input edits. 2026-02-03 12:12:51 +01:00
4c4d734b1b Added a SettingsDialog with a "Server" tab to configure the server settings. 2026-02-03 11:22:55 +01:00
0eef55fc32 Don't use the SettingsHandler directly. Go through the GenericCore instead. 2026-02-03 11:21:33 +01:00
d109eb31f8 Added "Server/Delete item" menu action. 2026-02-02 16:30:59 +01:00
a9f24ac8f2 Adjusted to GenericCore::postItemsToServer and using ModelItemsValues typedef in new item dialog. 2026-02-02 16:25:17 +01:00
67d9a3914d Retrieving JSON data from the proxy model and send it via core to the server. 2026-01-29 15:11:59 +01:00
518bebcbb7 Added "Server" menu with "fetch items" and "post items" actions to trigger communication with the RESTful server. 2026-01-29 09:00:12 +01:00
8f61c6bc2f Bugfix: Save changes when closing the EditItemDialog. 2026-01-19 18:40:39 +01:00
a966b26185 Getting rid of warning, that a widget has already a layout. 2026-01-19 14:43:07 +01:00
a8bf5b4032 Displaying a QR code in the EditItemDialog containing the full data of the current item as a string. 2026-01-15 14:06:02 +01:00
c83ba2da9d Bumped version to 0.2.0. 2026-01-14 10:27:38 +01:00
232b9ceb78 Added a find items dialog to select items based on if they contain a specific text. 2026-01-12 15:02:34 +01:00
e915846b8a Merge branch 'feature/proxySortFilterModel' into develop 2026-01-12 10:17:58 +01:00
8a0d37f8f9 Not using the TableModel directly anymore. Removing and appending items via GeneralSortFilterModel. 2026-01-12 10:15:05 +01:00
bd056a00ce Using the new QSortFilterProxyModel subclass of the core for the TableView. 2026-01-12 09:57:44 +01:00
5eee7a4a75 Informing the core, that unsaved changes to the items should be saved before starting the updater. 2026-01-06 10:17:15 +01:00
3cab6d6b86 Model data can be exported to CSV file. 2026-01-06 10:05:16 +01:00
78a55b6559 Model items can be imported from CSV file. 2026-01-04 16:52:03 +01:00
683f5d224c Changing submit policy of QDataWidgetMapper to AutoSubmit because if multiple changes are made at once not all of them are applied. 2026-01-01 09:59:12 +01:00
bcba8f8452 Using JsonParser to generate JSON document to pass in the addItems signal & using roles instead of their names (compiler will complain if a role is not existent anymore). 2025-12-31 10:47:28 +01:00
12e5596b34 Merge branch 'feature/saveLoadItemsInFromFile' into develop 2025-12-23 14:02:12 +01:00
8333be2044 Items can be saved via menu action and when exiting application with unclean undo stack. 2025-12-23 10:18:00 +01:00
6cd0519237 Using the application name set in the core (with "-dev" suffix in debug builds) as window title. 2025-12-23 10:08:11 +01:00
d94a928fc0 Merge branch 'feature/UndoRedoWithModelData' into develop 2025-12-22 15:53:16 +01:00
fd7e0975f5 Merge branch 'feature/UndoRedoWithModelData' into develop 2025-12-22 11:39:03 +01:00
b3f83ccdb0 Using QDataWidgetMapper in ItemDetailMapper to work with the model data (current index of the TableView). 2025-12-22 11:21:44 +01:00
8d523bb5bc Moved EditItemDialog content widget into separate widget class ItemDetailMapper. 2025-12-18 11:00:53 +01:00
467ab58fae Renamed source code folder "Dialogs" into "dialogs". 2025-12-17 10:39:34 +01:00
47338e5ae2 Added an EditItemDialog dummy, based on the NewItemDialog. 2025-12-17 10:33:18 +01:00
902ed1c84b Added a undo view and react to change of the cleanness of the undo stack. 2025-12-11 16:01:48 +01:00
ec0d72953f Merge branch 'develop' into feature/UndoRedoWithModelData 2025-12-09 09:59:16 +01:00
c80a692a28 Selected items can be deleted from model via menu action "Delete item(s)" 2025-12-08 15:05:04 +01:00
c51c06ba50 The "factor" value can be inserted as a floating point. 2025-12-08 13:28:01 +01:00
ea75e57dbf An item can be added to the model with accepting the NewItemDialog. 2025-12-08 13:26:42 +01:00
928b795f4e Added an AbstractDialog class and using it as base for a NewItemDialog class (no new item functionality yet). 2025-12-07 14:50:58 +01:00
370bcd5a41 Retrieve QUndoStack pointer from core and create actions for undo and redo. 2025-12-06 10:36:21 +01:00
c7012ceff5 Added menu action dummies for the menus "File" and "Edit". 2025-12-04 16:44:31 +01:00
5e54f52cd6 Removed obsolete TODO comment. 2025-12-03 11:09:13 +01:00
64a65b5916 Retrieving model from the core and showing its data in a QTableView. 2025-12-02 16:01:44 +01:00
9a30fb2aad Fix: Don't create a second "Help" menu for the "About" dialogs. 2025-11-04 21:44:06 +01:00
8105fac8d3 Updated Widgets-UI project version to 0.1.0 2025-11-04 21:23:11 +01:00
7dd9d3d95b Added "About" & "About Qt" dialogs. 2025-11-04 21:21:28 +01:00
c8825bfe14 Disable translations because it throws errors when compiling in Release mode. 2025-11-04 21:19:31 +01:00
43df76c88f Available update can be checked via menu action Help/"Check for update". 2025-10-31 15:31:33 +01:00
dd8847d63a Save and restore window geometry and state. 2025-10-31 15:23:21 +01:00
f359a1fb11 Displaying status messages from the core. Using non-smart pointer for GenericCore to connect to its signal. 2025-10-31 15:02:14 +01:00
24 changed files with 1646 additions and 174 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
set(TARGET_APP "GenericQtClient-Widgets") set(TARGET_APP "GenericQtClient-Widgets")
project(${TARGET_APP} VERSION 0.0.2 LANGUAGES CXX) project(${TARGET_APP} VERSION 0.3.0 LANGUAGES CXX)
set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
@ -27,15 +27,24 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(${TARGET_APP} qt_add_executable(${TARGET_APP}
MANUAL_FINALIZATION MANUAL_FINALIZATION
${PROJECT_SOURCES} ${PROJECT_SOURCES}
utils/messagehandler.h
assets/icons.qrc assets/icons.qrc
dialogs/abstractdialog.h dialogs/abstractdialog.cpp
dialogs/newitemdialog.h dialogs/newitemdialog.cpp
dialogs/edititemdialog.h dialogs/edititemdialog.cpp
dialogs/settingsdialog.h dialogs/settingsdialog.cpp
views/itemdetailmapper.h views/itemdetailmapper.cpp
widgets/controls/comboboxdelegate.h widgets/controls/comboboxdelegate.cpp
widgethelper.h widgethelper.cpp
widgets/controls/spinboxdelegate.h widgets/controls/spinboxdelegate.cpp
) )
# Define target properties for Android with Qt 6 as: # Define target properties for Android with Qt 6 as:
# set_property(TARGET ${TARGET_APP} APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR # set_property(TARGET ${TARGET_APP} APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android) # ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation # For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) # qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
else() else()
if(ANDROID) if(ANDROID)
add_library(${TARGET_APP} SHARED add_library(${TARGET_APP} SHARED
@ -52,10 +61,14 @@ endif()
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
endif() endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(${TARGET_APP} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) target_link_libraries(${TARGET_APP} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_include_directories(${TARGET_APP} PRIVATE ${CORE_LIB_DIR}/) target_include_directories(${TARGET_APP} PRIVATE ${CORE_LIB_DIR}/)
target_link_libraries(${TARGET_APP} PRIVATE GenericCore) target_link_libraries(${TARGET_APP} PRIVATE GenericCore)
target_include_directories(${TARGET_APP} PRIVATE ${QR_LIB_DIR}/src)
target_link_libraries(${TARGET_APP} PRIVATE qrcode)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,7 +1,6 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>software-application.png</file> <file>software-application.png</file>
<file>feature.png</file>
<file>no-picture-taking.png</file> <file>no-picture-taking.png</file>
</qresource> </qresource>
</RCC> </RCC>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,51 @@
#include "abstractdialog.h"
#include <QDialogButtonBox>
#include <QGuiApplication>
#include <QLabel>
#include <QScreen>
#include <QVBoxLayout>
AbstractDialog::AbstractDialog(QDialogButtonBox::StandardButtons buttons, QWidget* parent)
: QDialog(parent) {
setWindowTitle(tr("Dialog does what?..."));
setModal(true);
m_buttonBox = new QDialogButtonBox(buttons, this);
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &AbstractDialog::accept);
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &AbstractDialog::reject);
m_contentContainer = new QLabel("content", this);
m_outerLayout = new QVBoxLayout;
m_outerLayout->addWidget(m_contentContainer);
m_outerLayout->addWidget(m_buttonBox);
setLayout(m_outerLayout);
}
void AbstractDialog::show() {
centerInParent();
QWidget::show();
}
void AbstractDialog::accept() { QDialog::accept(); }
void AbstractDialog::reject() { QDialog::reject(); }
void AbstractDialog::centerInParent() {
// BUG the centering in the parent doesn't work the first time (and the later ones seem off too)
QWidget* parent = parentWidget();
if (parent) {
auto parentRect = parent->geometry();
move(parentRect.center() - rect().center());
} else {
QRect screenGeometry = QGuiApplication::screens().at(0)->geometry();
int x = (screenGeometry.width() - width()) / 2;
int y = (screenGeometry.height() - height()) / 2;
move(x, y);
}
}
void AbstractDialog::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); }

33
dialogs/abstractdialog.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef ABSTRACTDIALOG_H
#define ABSTRACTDIALOG_H
#include <QDialog>
#include <QDialogButtonBox>
class QGridLayout;
class QVBoxLayout;
class AbstractDialog : public QDialog {
Q_OBJECT
public:
AbstractDialog(QDialogButtonBox::StandardButtons buttons, QWidget* parent = nullptr);
virtual void createContent() = 0;
/// QDialog interface
public slots:
void show();
void accept() override;
void reject() override;
protected:
void centerInParent();
protected:
QWidget* m_contentContainer;
QVBoxLayout* m_outerLayout;
QDialogButtonBox* m_buttonBox = nullptr;
void closeEvent(QCloseEvent* event) override;
};
#endif // ABSTRACTDIALOG_H

View File

@ -0,0 +1,56 @@
#include "edititemdialog.h"
#include <QDialogButtonBox>
#include <QLabel>
#include <QVBoxLayout>
#include "../views/itemdetailmapper.h"
EditItemDialog::EditItemDialog(QTableView* tableView, QWidget* parent)
: AbstractDialog(QDialogButtonBox::Ok, parent)
, m_tableView(tableView)
, m_qrCodeDisplay(new QLabel("QR Code")) {}
void EditItemDialog::createContent() {
if (m_contentContainer) {
delete m_contentContainer;
}
setWindowTitle(tr("Edit item..."));
m_contentContainer = new QWidget(this);
QHBoxLayout* innerLayout = new QHBoxLayout();
m_contentContainer->setLayout(innerLayout);
m_detailMapper = new ItemDetailMapper(m_tableView, this);
innerLayout->addWidget(m_detailMapper);
updateQRCode();
connect(m_detailMapper, &ItemDetailMapper::contentChanged, this, &EditItemDialog::updateQRCode);
innerLayout->addWidget(m_qrCodeDisplay);
m_outerLayout->insertWidget(0, m_contentContainer);
}
void EditItemDialog::accept() {
m_detailMapper->submit();
QDialog::accept();
}
void EditItemDialog::reject() {
m_detailMapper->revert();
QDialog::reject();
}
void EditItemDialog::updateQRCode(const QString text) {
QImage unscaledImage;
if (text.isEmpty()) {
unscaledImage = QImage("://no-picture-taking.png");
} else {
unscaledImage = m_generator.generateQr(text);
}
QImage image = unscaledImage.scaled(250, 250);
m_qrCodeDisplay->setPixmap(QPixmap::fromImage(image));
m_qrCodeDisplay->setToolTip(text);
}

53
dialogs/edititemdialog.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef EDITITEMDIALOG_H
#define EDITITEMDIALOG_H
#include "QrCodeGenerator.h"
#include "abstractdialog.h"
class QDoubleSpinBox;
class QLineEdit;
class QSpinBox;
class QLabel;
class QTableView;
class ItemDetailMapper;
class EditItemDialog : public AbstractDialog {
Q_OBJECT
public:
EditItemDialog(QTableView* tableView, QWidget* parent = nullptr);
/// AbstractDialog interface
void createContent() override;
public slots:
void accept() override;
void reject() override;
private slots:
void updateQRCode(const QString text = "");
private:
QTableView* m_tableView = nullptr;
ItemDetailMapper* m_detailMapper;
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;
QLabel* m_qrCodeDisplay = nullptr;
QrCodeGenerator m_generator;
};
#endif // EDITITEMDIALOG_H

108
dialogs/newitemdialog.cpp Normal file
View File

@ -0,0 +1,108 @@
#include "newitemdialog.h"
#include <QComboBox>
#include <QGridLayout>
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include <QStringListModel>
#include "../widgethelper.h"
#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);
m_layout = new QGridLayout();
for (int i = 0; i < USER_FACING_ROLES.size(); ++i) {
setupWidgetPairForColumn(i);
}
m_contentContainer->setLayout(m_layout);
m_outerLayout->insertWidget(0, m_contentContainer);
}
void NewItemDialog::accept() {
addItemToModel();
resetContent();
AbstractDialog::accept();
}
void NewItemDialog::setupWidgetPairForColumn(const int column) {
// REFACTOR consolidate NewItemDialog::setupWidgetPairForColumn(...)
// with ItemDetailMapper::setupWidgetPairForColumn(...)
const UserRoles role = GET_ROLE_FOR_COLUMN(column);
QWidget* control = WidgetHelper::createControlWidget(role, this);
const QString string = QString("&%1").arg(GET_HEADER_FOR_COLUMN(column));
QLabel* label = new QLabel(string);
label->setBuddy(control);
m_layout->addWidget(label, column, 0);
m_layout->addWidget(control, column, 1);
m_controlWidgets.append(control);
}
void NewItemDialog::addItemToModel() {
ModelItemValues itemValues;
for (int i = 0; i < m_controlWidgets.size(); ++i) {
const UserRoles role = GET_ROLE_FOR_COLUMN(i);
if (STRING_ROLES.contains(role)) {
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(m_controlWidgets[i]);
itemValues.insert(role, lineEdit->text());
} else if (TYPE_ROLES.contains(role)) {
QComboBox* comboBox = dynamic_cast<QComboBox*>(m_controlWidgets[i]);
itemValues.insert(role, comboBox->currentText());
} else if (INT_ROLES.contains(role)) {
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(m_controlWidgets[i]);
itemValues.insert(role, spinBox->value());
} else if (DOUBLE_ROLES.contains(role)) {
QDoubleSpinBox* doubleSpinBox = dynamic_cast<QDoubleSpinBox*>(m_controlWidgets[i]);
itemValues.insert(role, doubleSpinBox->value());
} else {
qCritical() << "Could not reset content for control widget in column:" << i << "!";
}
}
const QByteArray jsonDoc = JsonParser::itemValuesListToJson({itemValues}, ITEMS_KEY_STRING);
emit addItems(jsonDoc);
}
void NewItemDialog::resetContent() {
for (int i = 0; i < m_controlWidgets.size(); ++i) {
const UserRoles role = GET_ROLE_FOR_COLUMN(i);
if (STRING_ROLES.contains(role)) {
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(m_controlWidgets[i]);
lineEdit->clear();
} else if (TYPE_ROLES.contains(role)) {
QComboBox* comboBox = dynamic_cast<QComboBox*>(m_controlWidgets[i]);
comboBox->setCurrentText("");
} else if (INT_ROLES.contains(role)) {
QSpinBox* spinBox = dynamic_cast<QSpinBox*>(m_controlWidgets[i]);
spinBox->setValue(0);
} else if (DOUBLE_ROLES.contains(role)) {
QDoubleSpinBox* doubleSpinBox = dynamic_cast<QDoubleSpinBox*>(m_controlWidgets[i]);
doubleSpinBox->setValue(0.0);
} else {
qCritical() << "Could not reset content for control widget in column:" << i << "!";
}
}
}

36
dialogs/newitemdialog.h Normal file
View File

@ -0,0 +1,36 @@
#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:
QGridLayout* m_layout;
QList<QWidget*> m_controlWidgets;
void setupWidgetPairForColumn(const int column);
void addItemToModel();
void resetContent();
};
#endif // NEWITEMDIALOG_H

View File

@ -0,0 +1,63 @@
#include "settingsdialog.h"
#include <QCoreApplication>
#include <QGridLayout>
#include <QLabel>
#include <QLineEdit>
#include <QTabWidget>
SettingsDialog::SettingsDialog(QWidget* parent)
: AbstractDialog(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, parent) {}
void SettingsDialog::createContent() {
if (m_contentContainer) {
delete m_contentContainer;
}
const QString dialogTitle = tr("Settings - ");
const QString applicationName = QCoreApplication::applicationName();
setWindowTitle(dialogTitle + applicationName);
setModal(true);
setGeometry(0, 0, 350, 250);
QGridLayout* serverLayout = new QGridLayout();
QLabel* urlLabel = new QLabel("Server URL:");
m_urlEdit = new QLineEdit();
serverLayout->addWidget(urlLabel, 0, 0);
serverLayout->addWidget(m_urlEdit, 0, 1);
QLabel* emailLabel = new QLabel("Email:");
m_emailEdit = new QLineEdit();
m_emailEdit->setEnabled(false);
serverLayout->addWidget(emailLabel, 1, 0);
serverLayout->addWidget(m_emailEdit, 1, 1);
QLabel* passwordLabel = new QLabel("Password:");
m_passwordEdit = new QLineEdit();
m_passwordEdit->setEnabled(false);
m_passwordEdit->setEchoMode(QLineEdit::Password);
serverLayout->addWidget(passwordLabel, 2, 0);
serverLayout->addWidget(m_passwordEdit, 2, 1);
QWidget* serverTab = new QWidget();
serverTab->setLayout(serverLayout);
QTabWidget* widget = new QTabWidget();
widget->addTab(serverTab, "Server");
m_contentContainer = widget;
m_outerLayout->insertWidget(0, m_contentContainer);
}
void SettingsDialog::fillContent(const QVariantMap& settings) {
m_urlEdit->setText(settings.value("url").toString());
m_emailEdit->setText(settings.value("email").toString());
m_passwordEdit->setText(settings.value("password").toString());
}
QVariantMap SettingsDialog::getSettings() const {
QVariantMap result;
result.insert("url", m_urlEdit->text());
result.insert("email", m_emailEdit->text());
result.insert("password", m_passwordEdit->text());
return result;
}

23
dialogs/settingsdialog.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef SETTINGSDIALOG_H
#define SETTINGSDIALOG_H
#include "abstractdialog.h"
class QLineEdit;
class SettingsDialog : public AbstractDialog {
Q_OBJECT
public:
SettingsDialog(QWidget* parent = nullptr);
void createContent() override;
void fillContent(const QVariantMap& settings);
QVariantMap getSettings() const;
private:
QLineEdit* m_urlEdit = nullptr;
QLineEdit* m_emailEdit = nullptr;
QLineEdit* m_passwordEdit = nullptr;
};
#endif // SETTINGSDIALOG_H

View File

@ -1,31 +1,565 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "./ui_mainwindow.h" #include "./ui_mainwindow.h"
#include "../../libs/GenericCore/genericcore.h" #include <QCloseEvent>
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QStandardPaths>
#include <QUndoStack>
#include <QUndoView>
#include "../../ApplicationConfig.h"
#include "dialogs/edititemdialog.h"
#include "dialogs/newitemdialog.h"
#include "dialogs/settingsdialog.h"
#include "genericcore.h"
#include "model/generalsortfiltermodel.h"
#include "model/metadata.h"
#include "model/tablemodel.h"
#include "widgets/controls/comboboxdelegate.h"
#include "widgets/controls/spinboxdelegate.h"
static QStandardPaths::StandardLocation standardLocation = QStandardPaths::HomeLocation;
static QString updateTextClean = "Do you want to update the application now?";
static QString updateTextDirty = "Do you want to save the tasks & update the application now?";
MainWindow::MainWindow(QWidget* parent) MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent) : QMainWindow(parent)
, ui(new Ui::MainWindow) { , ui(new Ui::MainWindow) {
ui->setupUi(this); ui->setupUi(this);
m_core = std::make_shared<GenericCore>(); m_core = std::make_unique<GenericCore>();
setWindowTitle(QCoreApplication::applicationName() + " [*]");
/// application icon /// application icon
const QString iconString = "://feature.png"; const QString iconString = "://software-application.png";
#ifdef QT_DEBUG #ifdef QT_DEBUG
QPixmap pixmap = QPixmap(iconString); QPixmap pixmap = QPixmap(iconString);
QTransform transform = QTransform(); QTransform transform = QTransform();
transform.rotate(180); transform.rotate(180);
QPixmap rotated = pixmap.transformed(transform); QPixmap rotated = pixmap.transformed(transform);
QIcon appIcon = QIcon(rotated);
setWindowIcon(QIcon(rotated)); setWindowIcon(QIcon(rotated));
#else #else
setWindowIcon(QIcon(iconString)); setWindowIcon(QIcon(iconString));
#endif #endif
const QVariantMap settings = m_core->getSettings("GUI");
restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("windowState").toByteArray());
setupModelViews();
createActions();
createHelpMenu();
createGuiDialogs();
connect(m_core.get(), &GenericCore::displayStatusMessage, this,
&MainWindow::displayStatusMessage);
connect(this, &MainWindow::displayStatusMessage, this, &MainWindow::showStatusMessage);
connect(this, &MainWindow::checkForUpdates, this,
&MainWindow::on_actionCheck_for_update_triggered, Qt::QueuedConnection);
// connect(ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
// &MainWindow::onSelectionChanged);
connect(ui->tableView->selectionModel(), &QItemSelectionModel::currentChanged, this,
&MainWindow::onCurrentChanged);
onSelectionChanged(QItemSelection(), QItemSelection());
onCurrentChanged(QModelIndex(), QModelIndex());
} }
MainWindow::~MainWindow() { delete ui; } MainWindow::~MainWindow() { delete ui; }
void MainWindow::closeEvent(QCloseEvent* event) {
if (isWindowModified()) {
QMessageBox msgBox;
msgBox.setWindowTitle(windowTitle() + " - Save dialog");
msgBox.setText("The document has been modified.");
msgBox.setInformativeText("Do you want to save your changes?");
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);
int ret = msgBox.exec();
switch (ret) {
case QMessageBox::Save:
emit saveItems();
event->accept();
break;
case QMessageBox::Discard:
event->accept();
break;
case QMessageBox::Cancel:
event->ignore();
break;
default:
/// should never be reached
qCritical() << "unexpected switch case in closeEvent:" << ret;
event->ignore();
break;
}
} else {
event->accept();
}
if (event->isAccepted()) {
qInfo() << "Saving GUI settings...";
m_core->applySettings({{"geometry", saveGeometry()}, {"windowState", saveState()}}, "GUI");
}
}
void MainWindow::showStatusMessage(const QString text) {
qInfo() << text;
ui->statusbar->showMessage(text);
}
void MainWindow::onCurrentChanged(const QModelIndex& current, const QModelIndex& previous) {
// Q_UNUSED(current);
Q_UNUSED(previous);
// QItemSelection localSelection = ui->tableView->selectionModel()->selection();
if (current == QModelIndex()) {
// qDebug() << "Nothing selected. Disabling delete action";
m_deleteItemAct->setEnabled(false);
} else {
// qDebug() << "Something selected. Enabling delete action";
m_deleteItemAct->setEnabled(true);
}
}
void MainWindow::onSelectionChanged(const QItemSelection& selected,
const QItemSelection& deselected) {
Q_UNUSED(selected);
Q_UNUSED(deselected);
QItemSelection localSelection = ui->tableView->selectionModel()->selection();
if (localSelection.empty()) {
// qDebug() << "Nothing selected. Disabling delete action";
m_deleteItemAct->setEnabled(false);
} else {
// qDebug() << "Something selected. Enabling delete action";
m_deleteItemAct->setEnabled(true);
}
}
void MainWindow::onAboutClicked() {
const QString applicationName = APPLICATION_NAME;
const QString titlePrefix = tr("About ");
const QString aboutText =
tr(QString("<b>%1 v%2</b> is a template for Qt applications."
"<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>.")
.arg(applicationName)
.arg(APPLICATION_VERSION)
.toLatin1());
QMessageBox::about(this, titlePrefix + applicationName, aboutText);
}
void MainWindow::on_actionCheck_for_update_triggered() {
showStatusMessage("Checking for update...");
const bool updateAvailable = m_core->isApplicationUpdateAvailable();
if (updateAvailable) {
const QString text = isWindowModified() ? updateTextDirty : updateTextClean;
const QMessageBox::StandardButton clickedButton = QMessageBox::question(
this, tr("Update available."), text, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (clickedButton == QMessageBox::Yes) {
m_core->triggerApplicationUpdate(true);
close();
}
}
}
void MainWindow::on_pushButton_clicked() { void MainWindow::on_pushButton_clicked() {
const QString prefix("Backend provided by: "); const QString prefix("Backend provided by: ");
ui->label->setText(prefix + m_core->toString()); ui->label->setText(prefix + m_core->toString());
} }
void MainWindow::openNewItemDialog() {
showStatusMessage(tr("Invoked 'Edit|New Item'"));
m_newItemDialog->show();
}
void MainWindow::openEditItemDialog() {
showStatusMessage(tr("Invoked 'Edit|Edit Item'"));
m_editItemDialog->show();
}
void MainWindow::deleteCurrentItem() {
showStatusMessage(tr("Invoked 'Edit|Delete Item'"));
// QItemSelection localSelection = ui->tableView->selectionModel()->selection();
const QModelIndex currentIndex = ui->tableView->selectionModel()->currentIndex();
if (currentIndex == QModelIndex()) {
qDebug() << "No current item. Nothing to remove.";
} else {
m_proxyModel->removeRows(currentIndex.row(), 1);
}
}
void MainWindow::deleteSelectedtItems() {
showStatusMessage(tr("Invoked 'Edit|Delete Item'"));
QItemSelection localSelection = ui->tableView->selectionModel()->selection();
if (localSelection.empty()) {
qDebug() << "No items selected. Nothing to remove.";
} else {
for (QList<QItemSelectionRange>::reverse_iterator iter = localSelection.rbegin(),
rend = localSelection.rend();
iter != rend; ++iter) {
// qInfo() << "iter:" << *iter;
// const QModelIndex parentIndex = iter->parent();
const int topRow = iter->top();
const int bottomRow = iter->bottom();
const int nRows = bottomRow - topRow + 1;
m_proxyModel->removeRows(topRow, nRows);
}
}
}
void MainWindow::onCleanStateChanged(bool clean) {
qInfo() << "Slot onCleanStateChanged triggered: clean:" << clean;
setWindowModified(!clean);
if (!clean) {
ui->statusbar->clearMessage();
}
}
void MainWindow::onShowUndoViewToggled(bool checked) {
// qInfo() << "Slot onShowUndoViewToggled toggled: checked:" << checked;
// m_undoView->setVisible(checked);
/// workaround until m_showUndoViewAction is checkable
qInfo() << "Slot onShowUndoViewToggled triggered, toggleing undo view...";
Q_UNUSED(checked);
if (m_modelUndoView->isVisible()) {
m_modelUndoView->setVisible(false);
} else {
m_modelUndoView->setVisible(true);
}
}
void MainWindow::saveItems() {
showStatusMessage(tr("Invoked 'File|Save'"));
m_core->saveItems();
}
void MainWindow::importCSV() {
showStatusMessage(tr("Invoked 'File|Import CSV'"));
const QString csvFilePath = QFileDialog::getOpenFileName(
this, tr("Import CSV"), QStandardPaths::standardLocations(standardLocation).first(),
tr("CSV Files (*.csv)"));
if (QFileInfo::exists(csvFilePath)) {
m_core->importCSVFile(csvFilePath);
} else {
qWarning() << "Selected CSV file path doesn't exist. Doing nothing...";
showStatusMessage(tr("Could't find CSV file!"));
}
}
void MainWindow::exportCSV() {
showStatusMessage(tr("Invoked 'File|Export'"));
const QString filter = tr("CSV Files (*.csv)");
const QString location = QStandardPaths::standardLocations(standardLocation).first();
QFileDialog dialog(this, "Export CSV File", location, "Comma-separated file (*.csv)");
dialog.setDefaultSuffix(".csv");
dialog.setAcceptMode(QFileDialog::AcceptSave);
if (dialog.exec()) {
const QString csvFilePath = dialog.selectedFiles().first();
const bool successful = m_core->exportCSVFile(csvFilePath);
if (successful) {
const QString message = QString(tr("CSV exported to: %1")).arg(csvFilePath);
showStatusMessage(message);
}
} else {
qWarning() << "Selected CSV file path doesn't exist. Doing nothing...";
}
}
void MainWindow::findItems() {
showStatusMessage(tr("Invoked 'Edit|Find items'"));
bool ok;
QString text = QInputDialog::getText(this, tr("Find items"), tr("Enter the text to search for:"),
QLineEdit::Normal, "", &ok);
if (ok && !text.isEmpty()) {
const QItemSelection itemsToSelect = m_proxyModel->findItems(text);
if (itemsToSelect.empty()) {
ui->tableView->setCurrentIndex(QModelIndex());
ui->tableView->selectionModel()->select(QModelIndex(), QItemSelectionModel::ClearAndSelect);
} else {
ui->tableView->setCurrentIndex(itemsToSelect.first().topLeft());
ui->tableView->selectionModel()->select(itemsToSelect, QItemSelectionModel::ClearAndSelect);
}
}
}
void MainWindow::fetchItems() {
showStatusMessage(tr("Invoked 'Server|Fetch items'"));
emit m_core->fetchItemsFromServer();
}
void MainWindow::postItems() {
showStatusMessage(tr("Invoked 'Server|Post items'"));
const QModelIndex currentIndex = ui->tableView->currentIndex();
const QByteArray jsonData = m_proxyModel->jsonDataForServer(currentIndex);
emit m_core->postItemToServer(jsonData);
}
void MainWindow::deleteItem() {
showStatusMessage(tr("Invoked 'Server|Delete items'"));
const QModelIndex currentIndex = ui->tableView->currentIndex();
// const QByteArray jsonData = m_proxyModel->jsonDataForServer(currentIndex);
const QString currentId = m_proxyModel->getUuid(currentIndex);
emit m_core->deleteItemFromServer(currentId);
}
void MainWindow::execSettingsDialog() {
showStatusMessage(tr("Invoked 'Tools|Settings'"));
QVariantMap oldSettings = m_core->getSettings("Server");
// SettingsDialog* settingsDialog = new SettingsDialog(settingMap, this);
SettingsDialog* settingsDialog = new SettingsDialog(this);
settingsDialog->createContent();
settingsDialog->fillContent(oldSettings);
int returnCode = settingsDialog->exec();
if (returnCode == QDialog::Accepted) {
qDebug() << "Settings dialog accepted, writing settings...";
const QVariantMap settings = settingsDialog->getSettings();
m_core->applySettings(settings, "Server");
// TODO use signal-slot connection Core::syncServerSetupChanged(bool enabled) ->
// MainWindow::onSyncServerSetupChanged(bool enabled)
// enableDisableServerActions();
} else {
qDebug() << "Settings dialog rejected";
}
delete settingsDialog;
}
void MainWindow::setupModelViews() {
// m_tableModel = m_core->getModel();
// ui->tableView->setModel(m_tableModel.get());
m_proxyModel = m_core->getSortFilterModel();
// TODO iterate over INT_ROLES and DOUBLE_ROLES to set spinbox delegate
/// setting number delegates to combo boxes
SpinboxDelegate* spinboxDelegate = new SpinboxDelegate(this);
const int amountColumn = GET_COLUMN_FOR_ROLE(AmountRole);
ui->tableView->setItemDelegateForColumn(amountColumn, spinboxDelegate);
const int factorColumn = GET_COLUMN_FOR_ROLE(FactorRole);
ui->tableView->setItemDelegateForColumn(factorColumn, spinboxDelegate);
// TODO iterate over TYPE_ROLES to set combobox delegate
/// setting type delegates to combo boxes
const int typeColumn = GET_COLUMN_FOR_ROLE(TypeRole);
ComboboxDelegate* shareTypeDelegate = new ComboboxDelegate(TYPES, this);
ui->tableView->setItemDelegateForColumn(typeColumn, shareTypeDelegate);
ui->tableView->setModel((QAbstractItemModel*)m_proxyModel.get());
ui->tableView->setSortingEnabled(true);
}
void MainWindow::createActions() {
// TODO add generic menu actions (file/new, edit/cut, ...)
createFileActions();
createUndoActions();
createEditActions();
createServerActions();
createToolsActions();
}
void MainWindow::createFileActions() {
m_newFileAct = make_unique<QAction>(tr("&New File"), this);
m_newFileAct->setShortcut(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_N));
m_newFileAct->setStatusTip(tr("Create a new file"));
// connect(m_newAct, &QAction::triggered, this, &MainWindow::newFile);
m_newFileAct->setEnabled(false);
ui->menu_File->addAction(m_newFileAct.get());
m_openAct = make_unique<QAction>(tr("&Open..."), this);
m_openAct->setShortcuts(QKeySequence::Open);
m_openAct->setStatusTip(tr("Open an existing file"));
// connect(m_openAct, &QAction::triggered, this, &MainWindow::open);
m_openAct->setEnabled(false);
ui->menu_File->addAction(m_openAct.get());
m_saveAct = make_unique<QAction>(tr("&Save"), this);
m_saveAct->setShortcuts(QKeySequence::Save);
m_saveAct->setStatusTip(tr("Save the document to disk"));
connect(m_saveAct.get(), &QAction::triggered, this, &MainWindow::saveItems);
ui->menu_File->addAction(m_saveAct.get());
ui->menu_File->addSeparator();
m_importAct = make_unique<QAction>(tr("&Import CSV..."), this);
m_importAct->setStatusTip(tr("Import an existing CSV file"));
connect(m_importAct.get(), &QAction::triggered, this, &MainWindow::importCSV);
ui->menu_File->addAction(m_importAct.get());
m_exportAct = make_unique<QAction>(tr("&Export CSV..."), this);
m_exportAct->setStatusTip(tr("Export content to a CSV document to disk"));
connect(m_exportAct.get(), &QAction::triggered, this, &MainWindow::exportCSV);
ui->menu_File->addAction(m_exportAct.get());
ui->menu_File->addSeparator();
m_printAct = make_unique<QAction>(tr("&Print..."), this);
m_printAct->setShortcuts(QKeySequence::Print);
m_printAct->setStatusTip(tr("Print the document"));
// connect(m_printAct, &QAction::triggered, this, &MainWindow::print);
m_printAct->setEnabled(false);
ui->menu_File->addAction(m_printAct.get());
ui->menu_File->addSeparator();
m_exitAct = make_unique<QAction>(tr("E&xit"), this);
m_exitAct->setShortcuts(QKeySequence::Quit);
m_exitAct->setStatusTip(tr("Exit the application"));
connect(m_exitAct.get(), &QAction::triggered, this, &QWidget::close);
ui->menu_File->addAction(m_exitAct.get());
}
void MainWindow::createUndoActions() {
if (!m_core) {
qCritical() << "No core instance set, aborting...";
return;
}
m_modelUndoStack = m_core->getModelUndoStack();
connect(m_modelUndoStack, &QUndoStack::cleanChanged, this, &MainWindow::onCleanStateChanged);
m_undoAct.reset(m_modelUndoStack->createUndoAction(this, tr("&Undo")));
m_undoAct->setShortcuts(QKeySequence::Undo);
m_undoAct->setStatusTip(tr("Undo the last operation"));
ui->menu_Edit->addAction(m_undoAct.get());
m_redoAct.reset(m_modelUndoStack->createRedoAction(this, tr("&Redo")));
m_redoAct->setShortcuts(QKeySequence::Redo);
m_redoAct->setStatusTip(tr("Redo the last operation"));
ui->menu_Edit->addAction(m_redoAct.get());
m_modelUndoView = make_unique<QUndoView>(m_modelUndoStack);
m_modelUndoView->setWindowTitle(tr("Undo list"));
m_modelUndoView->setAttribute(Qt::WA_QuitOnClose, false);
m_modelUndoView->setMinimumSize(450, 600);
m_showModelUndoViewAct = make_unique<QAction>(tr("Show undo/redo window"), this);
m_showModelUndoViewAct->setStatusTip(tr("Opens a window displaying the items on the undo stack"));
// TODO showUndoViewAction should be checkable
// m_showUndoViewAction->setCheckable(true);
// connect(m_showUndoViewAction, &QAction::toggled, this, &MainWindow::onShowUndoViewToggled);
connect(m_showModelUndoViewAct.get(), &QAction::triggered, this,
&MainWindow::onShowUndoViewToggled);
ui->menu_View->addAction(m_showModelUndoViewAct.get());
}
void MainWindow::createEditActions() {
m_cutAct = make_unique<QAction>(tr("Cu&t"), this);
m_cutAct->setShortcuts(QKeySequence::Cut);
m_cutAct->setStatusTip(
tr("Cut the current selection's contents to the "
"clipboard"));
// connect(m_cutAct, &QAction::triggered, this, &MainWindow::cut);
m_cutAct->setEnabled(false);
ui->menu_Edit->addAction(m_cutAct.get());
m_copyAct = make_unique<QAction>(tr("&Copy"), this);
m_copyAct->setShortcuts(QKeySequence::Copy);
m_copyAct->setStatusTip(
tr("Copy the current selection's contents to the "
"clipboard"));
// connect(m_copyAct, &QAction::triggered, this, &MainWindow::copy);
m_copyAct->setEnabled(false);
ui->menu_Edit->addAction(m_copyAct.get());
m_pasteAct = make_unique<QAction>(tr("&Paste"), this);
m_pasteAct->setShortcuts(QKeySequence::Paste);
m_pasteAct->setStatusTip(
tr("Paste the clipboard's contents into the current "
"selection"));
// connect(m_pasteAct, &QAction::triggered, this, &MainWindow::paste);
m_pasteAct->setEnabled(false);
ui->menu_Edit->addAction(m_pasteAct.get());
ui->menu_Edit->addSeparator();
m_openNewItemDialogAct = make_unique<QAction>(tr("&New item"), this);
m_openNewItemDialogAct->setShortcut(QKeySequence::New);
m_openNewItemDialogAct->setStatusTip(tr("Opens a dialog to add a new item"));
connect(m_openNewItemDialogAct.get(), &QAction::triggered, this, &MainWindow::openNewItemDialog);
ui->menu_Edit->addAction(m_openNewItemDialogAct.get());
m_openEditItemDialogAct = make_unique<QAction>(tr("&Edit item"), this);
m_openEditItemDialogAct->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_E));
m_openEditItemDialogAct->setStatusTip(tr("Opens a edit dialog for the current item"));
connect(m_openEditItemDialogAct.get(), &QAction::triggered, this,
&MainWindow::openEditItemDialog);
ui->menu_Edit->addAction(m_openEditItemDialogAct.get());
m_deleteItemAct = make_unique<QAction>(tr("&Delete item(s)"), this);
m_deleteItemAct->setShortcut(QKeySequence::Delete);
m_deleteItemAct->setStatusTip(tr("Delete currently selected item(s)"));
// connect(m_deleteItemAct.get(), &QAction::triggered, this, &MainWindow::deleteSelectedtItems);
connect(m_deleteItemAct.get(), &QAction::triggered, this, &MainWindow::deleteCurrentItem);
ui->menu_Edit->addAction(m_deleteItemAct.get());
ui->menu_Edit->addSeparator();
m_findItemAct = make_unique<QAction>(tr("&Find item(s)"), this);
m_findItemAct->setShortcuts(QKeySequence::Find);
m_findItemAct->setStatusTip(tr("Opens a dialog to find item(s) by containing text"));
connect(m_findItemAct.get(), &QAction::triggered, this, &MainWindow::findItems);
ui->menu_Edit->addAction(m_findItemAct.get());
}
void MainWindow::createServerActions() {
m_fetchItemsAct = make_unique<QAction>(tr("&Fetch item(s)"), this);
m_fetchItemsAct->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Down));
m_fetchItemsAct->setStatusTip(tr("Fetches all item on configured server"));
connect(m_fetchItemsAct.get(), &QAction::triggered, this, &MainWindow::fetchItems);
ui->menu_Server->addAction(m_fetchItemsAct.get());
m_postItemsAct = make_unique<QAction>(tr("&Post item(s)"), this);
m_postItemsAct->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Up));
// m_postItemsAct->setStatusTip(tr("Posts the selected items on configured server"));
m_postItemsAct->setStatusTip(tr("Posts the current item on configured server"));
connect(m_postItemsAct.get(), &QAction::triggered, this, &MainWindow::postItems);
ui->menu_Server->addAction(m_postItemsAct.get());
m_deleteItemsAct = make_unique<QAction>(tr("&Delete item"), this);
m_deleteItemsAct->setShortcut(QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Backspace));
// m_deleteItemsAct->setStatusTip(tr("Deletes the selected items on configured server"));
m_deleteItemsAct->setStatusTip(tr("Deletes the current item on configured server"));
connect(m_deleteItemsAct.get(), &QAction::triggered, this, &MainWindow::deleteItem);
ui->menu_Server->addAction(m_deleteItemsAct.get());
}
void MainWindow::createToolsActions() {
QMenu* menu = ui->menu_Tools;
QAction* settingsAct = menu->addAction(tr("&Settings"), this, &MainWindow::execSettingsDialog);
settingsAct->setStatusTip(tr("Opens a dialog to configure applications settings."));
}
void MainWindow::createHelpMenu() {
QMenu* helpMenu = ui->menu_Help;
helpMenu->addSeparator();
QAction* aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::onAboutClicked);
aboutAct->setStatusTip(tr("Show the application's About box"));
QAction* aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
}
void MainWindow::createGuiDialogs() {
/// new item dialog
m_newItemDialog = make_unique<NewItemDialog>(this);
m_newItemDialog->createContent();
connect(m_newItemDialog.get(), &NewItemDialog::addItems, m_proxyModel.get(),
&GeneralSortFilterModel::appendItems);
/// edit item dialog
m_editItemDialog = make_unique<EditItemDialog>(ui->tableView, this);
m_editItemDialog->createContent();
}

View File

@ -1,17 +1,27 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#include <QItemSelection>
#include <QMainWindow> #include <QMainWindow>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class GenericCore; class QUndoStack;
class QUndoView;
namespace Ui { namespace Ui {
class MainWindow; class MainWindow;
} }
QT_END_NAMESPACE QT_END_NAMESPACE
class GenericCore;
class TableModel;
class GeneralSortFilterModel;
class NewItemDialog;
class EditItemDialog;
using namespace std;
class MainWindow : public QMainWindow { class MainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
@ -19,12 +29,95 @@ class MainWindow : public QMainWindow {
MainWindow(QWidget* parent = nullptr); MainWindow(QWidget* parent = nullptr);
~MainWindow(); ~MainWindow();
signals:
void displayStatusMessage(QString message);
void checkForUpdates();
protected:
void closeEvent(QCloseEvent* event) override;
private slots: private slots:
void showStatusMessage(const QString text);
void onCurrentChanged(const QModelIndex& current, const QModelIndex& previous);
void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void onAboutClicked();
void on_actionCheck_for_update_triggered();
void on_pushButton_clicked(); void on_pushButton_clicked();
/// slots for menu actions
void openNewItemDialog();
void openEditItemDialog();
void deleteCurrentItem();
void deleteSelectedtItems();
void onCleanStateChanged(bool clean);
void onShowUndoViewToggled(bool checked);
/// 'File' slots
void saveItems();
void importCSV();
void exportCSV();
/// 'Edit' slots
void findItems();
/// 'Server' slots
void fetchItems();
void postItems();
void deleteItem();
/// 'Tools' slots
void execSettingsDialog();
private: private:
Ui::MainWindow* ui; Ui::MainWindow* ui;
std::shared_ptr<GenericCore> m_core; unique_ptr<GenericCore> m_core;
shared_ptr<GeneralSortFilterModel> m_proxyModel;
QUndoStack* m_modelUndoStack;
unique_ptr<QUndoView> m_modelUndoView;
/// File actions
unique_ptr<QAction> m_newFileAct;
unique_ptr<QAction> m_openAct;
unique_ptr<QAction> m_saveAct;
unique_ptr<QAction> m_importAct;
unique_ptr<QAction> m_exportAct;
unique_ptr<QAction> m_printAct;
unique_ptr<QAction> m_exitAct;
/// Edit actions
unique_ptr<QAction> m_undoAct;
unique_ptr<QAction> m_redoAct;
unique_ptr<QAction> m_cutAct;
unique_ptr<QAction> m_copyAct;
unique_ptr<QAction> m_pasteAct;
unique_ptr<QAction> m_openNewItemDialogAct;
unique_ptr<QAction> m_openEditItemDialogAct;
unique_ptr<QAction> m_deleteItemAct;
unique_ptr<QAction> m_findItemAct;
/// Server actions
unique_ptr<QAction> m_fetchItemsAct;
unique_ptr<QAction> m_postItemsAct;
unique_ptr<QAction> m_deleteItemsAct;
/// View actions
unique_ptr<QAction> m_showModelUndoViewAct;
/// Dialogs
unique_ptr<NewItemDialog> m_newItemDialog;
unique_ptr<EditItemDialog> m_editItemDialog;
/// Setup functions
void setupModelViews();
void createActions();
void createFileActions();
void createUndoActions();
void createEditActions();
void createServerActions();
void createToolsActions();
void createHelpMenu();
void createGuiDialogs();
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View File

@ -15,6 +15,9 @@
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableView" name="tableView"/>
</item>
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
@ -37,11 +40,53 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>800</width> <width>800</width>
<height>25</height> <height>22</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menu_File">
<property name="title">
<string>&amp;File</string>
</property>
</widget>
<widget class="QMenu" name="menu_Edit">
<property name="title">
<string>&amp;Edit</string>
</property>
</widget>
<widget class="QMenu" name="menu_View">
<property name="title">
<string>&amp;View</string>
</property>
</widget>
<widget class="QMenu" name="menu_Help">
<property name="title">
<string>&amp;Help</string>
</property>
<addaction name="actionCheck_for_update"/>
</widget>
<widget class="QMenu" name="menu_Server">
<property name="title">
<string>&amp;Server</string>
</property>
</widget>
<widget class="QMenu" name="menu_Tools">
<property name="title">
<string>&amp;Tools</string>
</property>
</widget>
<addaction name="menu_File"/>
<addaction name="menu_Edit"/>
<addaction name="menu_View"/>
<addaction name="menu_Server"/>
<addaction name="menu_Tools"/>
<addaction name="menu_Help"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"/> <widget class="QStatusBar" name="statusbar"/>
<action name="actionCheck_for_update">
<property name="text">
<string>Check for &amp;update</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -1,164 +0,0 @@
#ifndef MESSAGEHANDLER_H
#define MESSAGEHANDLER_H
/**
* Color and formatting codes
* @see: http://misc.flogisoft.com/bash/tip_colors_and_formatting
*/
#include <QObject>
// qSetMessagePattern("%{file}(%{line}): %{message}");
// qSetMessagePattern("%{type}(%{line}):\t%{message}");
// qSetMessagePattern("%{type}%{file}(%{line}):\t%{message}");
void consoleHandlerColoredVerbose(QtMsgType type,
const QMessageLogContext& context,
const QString& msg) {
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
// fprintf(stderr, "\033[1;30mDebug: (%s:%u, %s) \t%s\n\033[0m", context.file,
// context.line, context.function, localMsg.constData()); // bold
fprintf(stderr, "\033[107;30mDebug: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtInfoMsg:
fprintf(stderr, "\033[107;32mInfo: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtWarningMsg:
fprintf(stderr, "\033[43;30mWarning: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtCriticalMsg:
fprintf(stderr, "\033[41;30mCritical: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtFatalMsg:
fprintf(stderr, "\033[41;30mFatal: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
abort();
}
}
void consoleHandlerColoredVerboseInDarkTheme(QtMsgType type,
const QMessageLogContext& context,
const QString& msg) {
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
// fprintf(stderr, "\033[1;30mDebug: (%s:%u, %s) \t%s\n\033[0m", context.file,
// context.line, context.function, localMsg.constData()); // bold
fprintf(stderr, "\033[107;37mDebug: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtInfoMsg:
fprintf(stderr, "\033[107;32mInfo: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtWarningMsg:
fprintf(stderr, "\033[43;30mWarning: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtCriticalMsg:
fprintf(stderr, "\033[41;30mCritical: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
break;
case QtFatalMsg:
fprintf(stderr, "\033[41;30mFatal: (%s:%u, %s) \t%s\n\033[0m", context.file, context.line,
context.function, localMsg.constData());
abort();
}
}
void consoleHandlerColored(QtMsgType type, const QMessageLogContext& context, const QString& msg) {
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
fprintf(stderr, "\033[1;30mDebug: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtInfoMsg:
fprintf(stderr, "\033[0;30mInfo: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtWarningMsg:
fprintf(stderr, "\033[1;33mWarning: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtCriticalMsg:
fprintf(stderr, "\033[31mCritical: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
break;
case QtFatalMsg:
fprintf(stderr, "\033[31mFatal: (%s:%u) \t%s\n\033[0m", context.file, context.line,
localMsg.constData());
abort();
}
}
void myMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) {
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
break;
case QtInfoMsg:
fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file,
context.line, context.function);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
abort();
}
}
#ifdef Q_OS_ANDROID
#include <android/log.h>
const char* const applicationName = "Pensieve";
void androidMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) {
QString report = msg;
if (context.file && !QString(context.file).isEmpty()) {
report += " in file ";
report += QString(context.file);
report += " line ";
report += QString::number(context.line);
}
if (context.function && !QString(context.function).isEmpty()) {
report += +" function ";
report += QString(context.function);
}
const char* const local = report.toLocal8Bit().constData();
switch (type) {
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG, applicationName, local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO, applicationName, local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN, applicationName, local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR, applicationName, local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL, applicationName, local);
abort();
}
}
#endif
#endif // MESSAGEHANDLER_H

191
views/itemdetailmapper.cpp Normal file
View File

@ -0,0 +1,191 @@
#include "itemdetailmapper.h"
#include <QAbstractItemModel>
#include <QDataWidgetMapper>
#include <QGridLayout>
#include <QJsonArray>
#include <QJsonObject>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QSpinBox>
#include <QTableView>
#include "../widgethelper.h"
ItemDetailMapper::ItemDetailMapper(QTableView* tableView, QWidget* parent)
: QWidget{parent} {
/// model mapping
m_mapper = std::make_unique<QDataWidgetMapper>(this);
/// BUG: If multiple columns are changed not all changes are applied.
/// Multiple changes are set individually by calling setData().
/// Probably due to a conflicting dataChanged signal for too many roles&columns the data of
/// the remaining columns is reset before setData is called.
/// And a manual submit would also create multiple undo steps anyway.
/// Workaround: ManualSubmit -> AutoSubmit
// m_mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
m_mapper->setSubmitPolicy(QDataWidgetMapper::AutoSubmit);
qDebug() << "Setting model...";
m_tableView = tableView;
m_model = tableView->model();
m_selectionModel = tableView->selectionModel();
m_mapper->setModel(m_model);
setupWidgets();
setupConnections();
}
bool ItemDetailMapper::submit() { return m_mapper->submit(); }
void ItemDetailMapper::revert() { m_mapper->revert(); }
void ItemDetailMapper::onCurrentIndexChanged(const QModelIndex& current,
const QModelIndex& /*previous*/) {
if (!isEnabled()) {
setEnabled(true);
}
m_mapper->setCurrentModelIndex(current);
updateButtons(current.row());
emitContentChanged(current);
}
void ItemDetailMapper::rowsInserted(const QModelIndex& parent, int start, int end) {
updateButtons(m_mapper->currentIndex());
}
void ItemDetailMapper::rowsRemoved(const QModelIndex& parent, int start, int end) {
if (m_model->rowCount() == 0) {
clearControlWidgets();
}
updateButtons(m_mapper->currentIndex());
}
void ItemDetailMapper::toPrevious() {
int currentRow = m_selectionModel->currentIndex().row();
QModelIndex previousIndex = m_selectionModel->currentIndex().siblingAtRow(currentRow - 1);
if (previousIndex.isValid()) {
m_selectionModel->setCurrentIndex(previousIndex, QItemSelectionModel::ClearAndSelect);
}
}
void ItemDetailMapper::toNext() {
int currentRow = m_selectionModel->currentIndex().row();
QModelIndex nextIndex = m_selectionModel->currentIndex().siblingAtRow(currentRow + 1);
if (nextIndex.isValid()) {
m_selectionModel->setCurrentIndex(nextIndex, QItemSelectionModel::ClearAndSelect);
}
}
void ItemDetailMapper::updateButtons(int row) {
m_previousButton->setEnabled(row > 0);
m_nextButton->setEnabled(row < m_model->rowCount() - 1);
}
void ItemDetailMapper::emitContentChanged(const QModelIndex& currentIndex) {
// BUG QR-Code isn't updated after changes through the ItemDetailMapper #18
QString toStringText = "";
if (currentIndex.isValid()) {
toStringText = currentIndex.data(ToStringRole).toString();
}
emit contentChanged(toStringText);
}
void ItemDetailMapper::setupConnections() {
m_mapper->setCurrentIndex(m_selectionModel->currentIndex().row());
connect(m_model, &QAbstractItemModel::rowsInserted, this, &ItemDetailMapper::rowsInserted);
connect(m_model, &QAbstractItemModel::rowsRemoved, this, &ItemDetailMapper::rowsRemoved);
connect(m_selectionModel, &QItemSelectionModel::currentChanged, this,
&ItemDetailMapper::onCurrentIndexChanged);
updateButtons(m_selectionModel->currentIndex().row());
}
void ItemDetailMapper::setupNavigationButtons() {
m_nextButton = new QPushButton(tr("Ne&xt"));
m_previousButton = new QPushButton(tr("&Previous"));
connect(m_previousButton, &QAbstractButton::clicked, this, &ItemDetailMapper::toPrevious);
connect(m_nextButton, &QAbstractButton::clicked, this, &ItemDetailMapper::toNext);
const int nRows = m_layout->rowCount();
m_layout->addWidget(m_previousButton, nRows, 0);
m_layout->addWidget(m_nextButton, nRows, 1);
}
void ItemDetailMapper::setupWidgets() {
m_layout = new QGridLayout();
for (int i = 0; i < USER_FACING_ROLES.size(); ++i) {
setupWidgetPairForColumn(i);
}
setupNavigationButtons();
setLayout(m_layout);
}
void ItemDetailMapper::setupWidgetPairForColumn(const int column) {
// REFACTOR consolidate ItemDetailMapper::setupWidgetPairForColumn(...)
// with NewItemDialog::setupWidgetPairForColumn(...)
const UserRoles role = GET_ROLE_FOR_COLUMN(column);
QWidget* control = WidgetHelper::createControlWidget(role, this);
const QString string = QString("&%1").arg(GET_HEADER_FOR_COLUMN(column));
QLabel* label = new QLabel(string);
label->setBuddy(control);
m_layout->addWidget(label, column, 0);
m_layout->addWidget(control, column, 1);
m_controlWidgets.append(control);
if (TYPE_ROLES.contains(role)) {
m_mapper->addMapping(control, column, "currentText");
} else {
m_mapper->addMapping(control, column);
}
}
void ItemDetailMapper::clearControlWidgets() {
setEnabled(false);
for (int i = 0; i < m_controlWidgets.size(); ++i) {
const UserRoles role = GET_ROLE_FOR_COLUMN(i);
if (STRING_ROLES.contains(role)) {
clearLineEdit(i);
} else if (TYPE_ROLES.contains(role)) {
clearComboBox(i);
} else if (INT_ROLES.contains(role)) {
clearSpinBox(i);
} else if (DOUBLE_ROLES.contains(role)) {
clearSpinBox(i);
} else {
qCritical() << "Could not reset content for control widget in column:" << i << "!";
}
}
}
void ItemDetailMapper::clearLineEdit(const int column) {
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(m_controlWidgets[column]);
if (lineEdit) {
lineEdit->clear();
}
}
void ItemDetailMapper::clearComboBox(const int column) {
QComboBox* comboBox = dynamic_cast<QComboBox*>(m_controlWidgets[column]);
if (comboBox) {
comboBox->setCurrentText("");
}
}
void ItemDetailMapper::clearSpinBox(const int column) {
QAbstractSpinBox* spinBox = dynamic_cast<QAbstractSpinBox*>(m_controlWidgets[column]);
if (spinBox) {
spinBox->clear();
}
}

68
views/itemdetailmapper.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef ITEMDETAILMAPPER_H
#define ITEMDETAILMAPPER_H
#include <QComboBox>
#include <QDataWidgetMapper>
#include <QStringListModel>
#include <QWidget>
#include "model/metadata.h"
class QGridLayout;
class QLabel;
class QLineEdit;
class QDoubleSpinBox;
class QSpinBox;
class QPushButton;
class QAbstractItemModel;
class QItemSelectionModel;
class QTableView;
class ItemDetailMapper : public QWidget {
Q_OBJECT
public:
explicit ItemDetailMapper(QTableView* tableView, QWidget* parent = nullptr);
bool submit();
void revert();
signals:
void contentChanged(const QString text);
private slots:
void onCurrentIndexChanged(const QModelIndex& current, const QModelIndex& previous);
void rowsInserted(const QModelIndex& parent, int start, int end);
void rowsRemoved(const QModelIndex& parent, int start, int end);
void toPrevious();
void toNext();
void updateButtons(int row);
void emitContentChanged(const QModelIndex& currentIndex);
private:
/// *** members ***
/// Model stuff
QTableView* m_tableView = nullptr;
QAbstractItemModel* m_model = nullptr;
QItemSelectionModel* m_selectionModel = nullptr;
std::unique_ptr<QDataWidgetMapper> m_mapper;
/// GUI elements
QGridLayout* m_layout;
QList<QWidget*> m_controlWidgets;
QPushButton* m_nextButton;
QPushButton* m_previousButton;
void setupConnections();
void setupNavigationButtons();
void setupWidgets();
void setupWidgetPairForColumn(const int column);
void clearControlWidgets();
void clearLineEdit(const int column);
void clearComboBox(const int column);
void clearSpinBox(const int column);
};
#endif // ITEMDETAILMAPPER_H

66
widgethelper.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "widgethelper.h"
#include <QComboBox>
#include <QLineEdit>
#include <QSpinBox>
#include <QStringListModel>
QWidget* WidgetHelper::createControlWidget(const UserRoles role, QWidget* parent) {
QWidget* control;
if (STRING_ROLES.contains(role)) {
control = createLineEdit(role, parent);
} else if (TYPE_ROLES.contains(role)) {
control = createComboBox(role, parent);
} else if (NUMBER_ROLES.contains(role)) {
control = createSpinBox(role, parent);
} else {
qCritical() << QString("Unsupported role %1!!!").arg(role);
qDebug() << "Using line edit as well and pretend it's a string role...";
control = createLineEdit(role, parent);
}
return control;
}
WidgetHelper::WidgetHelper() {}
QWidget* WidgetHelper::createLineEdit(const UserRoles role, QWidget* /*parent*/) {
QLineEdit* lineEdit = new QLineEdit();
if (READ_ONLY_ROLES.contains(role)) {
lineEdit->setReadOnly(true);
}
return lineEdit;
}
QWidget* WidgetHelper::createSpinBox(const UserRoles role, QWidget* /*parent*/) {
QAbstractSpinBox* abstractSpinBox;
if (DOUBLE_ROLES.contains(role)) {
QDoubleSpinBox* spinBox = new QDoubleSpinBox();
spinBox->setMaximum(1000);
abstractSpinBox = spinBox;
} else {
QSpinBox* spinBox = new QSpinBox();
spinBox->setMaximum(1000);
abstractSpinBox = spinBox;
}
if (READ_ONLY_ROLES.contains(role)) {
abstractSpinBox->setReadOnly(true);
}
return abstractSpinBox;
}
QWidget* WidgetHelper::createComboBox(const UserRoles role, QWidget* parent) {
// TODO add support for read only type roles?
QStringListModel* typeModel;
if (role == TypeRole) {
typeModel = new QStringListModel(TYPES, parent);
} else {
qCritical() << "Unsupported type with role:" << role
<< "- Using string list model with only one empty string!";
typeModel = new QStringListModel({""}, parent);
}
QComboBox* comboBox = new QComboBox();
comboBox->setModel(typeModel);
comboBox->setCurrentText("");
return comboBox;
}

20
widgethelper.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef WIDGETHELPER_H
#define WIDGETHELPER_H
#include "model/metadata.h"
class QWidget;
class WidgetHelper {
public:
static QWidget* createControlWidget(const UserRoles role, QWidget* parent);
private:
explicit WidgetHelper();
static QWidget* createLineEdit(const UserRoles role, QWidget* parent);
static QWidget* createSpinBox(const UserRoles role, QWidget* parent);
static QWidget* createComboBox(const UserRoles role, QWidget* parent);
};
#endif // WIDGETHELPER_H

View File

@ -0,0 +1,57 @@
#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 isType = TYPE_ROLES.contains(role);
/// Put the value into the SpinBox
if (isType) {
const QString valueString = index.model()->data(index, TypeRole).toString();
int value = TYPES.indexOf(valueString);
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,31 @@
#ifndef COMBOBOXDELEGATE_H
#define COMBOBOXDELEGATE_H
#include <QStyledItemDelegate>
class QStringListModel;
class ComboboxDelegate : public QStyledItemDelegate {
// TODO move source code files into subfolder "widgets/delegate"
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,27 @@
#ifndef SPINBOXDELEGATE_H
#define SPINBOXDELEGATE_H
#include <QStyledItemDelegate>
class SpinboxDelegate : public QStyledItemDelegate {
// TODO move source code files into subfolder "widgets/delegate"
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