Files
mdemory/src/qtapp/mdemList.cpp
2024-10-27 13:26:45 +02:00

1137 lines
29 KiB
C++

#include <cstdio>
#include <ctime>
#include <fstream>
#include <iostream>
#include <map>
#include <filesystem>
#include <regex>
#include <sstream>
#include <string>
#include <qabstractbutton.h>
#include <qapplication.h>
#include <qboxlayout.h>
#include <qcombobox.h>
#include <qdialog.h>
#include <qlabel.h>
#include <qlayoutitem.h>
#include <qmainwindow.h>
#include <qnamespace.h>
#include <qobjectdefs.h>
#include <qsettings.h>
#include <qsizepolicy.h>
#include <qt/QtWidgets/qwidget.h>
#include <qtoolbutton.h>
#include <qwidget.h>
#include <qwindow.h>
#include <qwindowdefs.h>
#include <Qsci/qsciscintilla.h>
#include <Qsci/qscilexercpp.h>
#include <QApplication>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
#include <QFileSystemModel>
#include <QTreeView>
#include <QSplitter>
#include <QVariant>
#include <QMessageBox>
#include <QScrollArea>
#include <QSpacerItem>
#include <QFile>
#include <QTime>
#include <QRegularExpression>
#include <QStringList>
#include <QListView>
#include <QWindow>
#include <QMenuBar>
#include <QMenu>
#include <QFileDialog>
#include <qabstractitemmodel.h>
#include <QFormLayout>
#include <QSettings>
#include <QLineEdit>
#include <QComboBox>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox>
#include <QPushButton>
#include <QStandardPaths>
#include <QShortcut>
#include <QWidget>
#include "config.h"
#include "settings.h"
#include "mdemList.h"
#include "trainWindow.h"
#include "api.h"
#include "parser.h"
#include "qscilexer.h"
// Memorybase.
QString currentPath = "";
std::string currentMdem = "";
QFileSystemModel *model;
QTreeView *mdemList;
std::map<std::string, MdemBuffer*> buffers;
MdemBuffer *currentMdemBuffer;
// Mdem list.
std::vector<Mdem*> mdems = std::vector<Mdem*>();
QVBoxLayout *hMdemScroll;
QSpacerItem *mdemSpacer;
ErrorView *errorView;
Pagination *pagination;
int perPage;
// Editor
QsciScintilla *editor;
QMainWindow *editorWindow;
Mdem *editMdem;
// Top labels.
QLabel *membaseLabel;
QLabel *mdemLabel;
QLabel *lastPracticeLabel;
QMainWindow *trainWindow;
void showBacklabels(Mdem *mdem) {
for (size_t i = 0; i < mdem->backLabels.size(); ++i) {
if (i < mdem->labelCount) {
if (!mdem->backLabels[i]->isVisible()) {
mdem->backLabels[i]->show();
}
} else {
if (mdem->backLabels[i]->isVisible()) {
mdem->backLabels[i]->hide();
}
}
}
}
std::string outputMdem(std::vector<Question*> questions, time_t time = 0) {
std::stringstream ss;
int wrap_width = 80;
if (settings->contains(SETTING_CHARACTER_WRAP)) {
wrap_width = settings->value(SETTING_CHARACTER_WRAP).toInt();
}
if (time > 0) {
auto timezoneOffset = settings->value(SETTING_TIMEZONE).toInt();
/*time = time + 3600 * timezoneOffset;*/
std::tm* tm = std::localtime(&time);
char buffer[100];
std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H\\:%M", tm);
auto time = std::string(buffer);
ss << time << std::endl;
}
for (auto question: questions) {
ss << std::endl;
std::string cooldownPart;
if (question->cooldown != 0) {
cooldownPart = std::format(" [{:.2f}]", question->cooldown);
}
ss << wrapText(
std::format("-{}{} >\n",
cooldownPart,
" " + escapeText(question->questionText)),
wrap_width
);
if (MultiElementQuestion* mw = dynamic_cast<MultiElementQuestion*>(question)) {
for (auto choice: mw->choices) {
char opener;
if (choice.isCorrect) {
opener = '+';
} else {
opener = '-';
}
std::string orderModifier;
if (mw->type == MultiElementType::Order) {
orderModifier = "^";
}
ss <<
wrapText(
std::format(
"\t{}{} {}\n",
opener,
orderModifier,
escapeText(choice.answer)
)
, wrap_width);
}
} else if (GroupQuestion* gq = dynamic_cast<GroupQuestion*>(question)) {
for (auto group: gq->groups) {
ss << wrapText(
std::format(
"\t- {}:\n",
escapeText(group.name)
)
, wrap_width);
for (auto element: group.elements) {
ss << wrapText(
std::format(
"\t\t- {}\n",
escapeText(element)
)
, wrap_width);
}
}
}
}
return ss.str();
}
void makePages() {
pagination->pages.clear();
auto len = currentMdemBuffer->questions.size();
perPage = settings->value(SETTING_PER_PAGE).toInt();
auto pageAmount = len / perPage;
if (len % perPage != 0) {
pageAmount += 1;
}
for (size_t i = 0; i < pageAmount; i++) {
size_t startingIndex = perPage * i ;
size_t amount = perPage;
if (i == currentMdemBuffer->questions.size() / perPage) {
amount = currentMdemBuffer->questions.size() % perPage;
}
pagination->pages.push_back(
Page{startingIndex, startingIndex + amount}
);
}
}
void setupMdem(Mdem *mdem, Question *question) {
std::stringstream ss;
if (question->cooldown > 0) {
ss << std::format("[{:.2f}] ", question->cooldown);
}
ss << question->questionText;
mdem->wFrontText.setText(
QString::fromStdString(ss.str())
);
if (MultiElementQuestion* mw = dynamic_cast<MultiElementQuestion*>(question)) {
auto choices = mw->choices;
for (size_t k = 0; k < choices.size(); ++k) {
auto answer = choices[k].answer;
switch (mw->type) {
case MultiElementType::Order:
answer = std::format("{}. {}", k + 1, answer);
break;
case MultiElementType::MultiChoice:
if (choices[k].isCorrect) {
answer = std::format("+ {}", answer);
} else {
answer = std::format("- {}", answer);
}
break;
case MultiElementType::Regular:
answer = std::format("- {}", answer);
break;
}
if (k < mdem->backLabels.size()) {
mdem->backLabels[k]->setText(QString::fromStdString(answer));
} else {
auto label = new QLabel();
label->setText(QString::fromStdString(answer));
mdem->backLabels.push_back(label);
mdem->wBack.layout()->addWidget(label);
}
}
mdem->labelCount = choices.size();
} else if (GroupQuestion* mw = dynamic_cast<GroupQuestion*>(question)) {
auto groups = mw->groups;
std::vector<std::string> elements;
for (size_t k = 0; k < groups.size(); ++k) {
auto answer = groups[k].name;
elements.push_back(std::format("- {}:", answer));
for (size_t l = 0; l < groups[k].elements.size(); ++l) {
elements.push_back(std::format(" - {}", groups[k].elements[l]));
}
}
for (size_t k = 0; k < elements.size(); ++k) {
if (k < mdem->backLabels.size()) {
mdem->backLabels[k]->setText(QString::fromStdString(elements[k]));
} else {
auto label = new QLabel();
label->setText(QString::fromStdString(elements[k]));
mdem->backLabels.push_back(label);
mdem->wBack.layout()->addWidget(label);
}
}
mdem->labelCount = elements.size();
}
}
void switchPage(int pageIdx);
std::string getFilename(std::string path) {
static const std::regex lastPathElementExp = std::regex("(.+\\/)*(.+)");
std::smatch matches;
auto filenameMatched = std::regex_search(path, matches, lastPathElementExp);
return matches[2].str();
}
void updateMdemInfo(std::string filename, bool isChanged) {
currentMdemBuffer->isModified = isChanged;
if (filename.length() > 0) {
std::stringstream ss;
ss << std::format("mdem: {}", filename);
if (isChanged) {
ss << "*";
}
mdemLabel->setText(QString::fromStdString(ss.str()));
ss.str("");
if (currentMdemBuffer->trainedAt > 0) {
std::tm* tm = std::localtime(&currentMdemBuffer->trainedAt);
char buffer[100];
std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H:%M", tm);
ss << "Last practiced: " << std::string(buffer);
}
lastPracticeLabel->setText(QString::fromStdString(ss.str()));
} else {
mdemLabel->setText("");
lastPracticeLabel->setText("");
}
}
Mdem* makeMdem() {
auto mdem = new Mdem;
mdem->wMdem.setLayout(&mdem->vMdem);
QString id = QString("mdem_%1").arg(1);
mdem->wMdem.setObjectName(id);
// Front
mdem->wFront.setMinimumHeight(60);
mdem->wFront.setLayout(&mdem->hFront);
mdem->wFront.setProperty("first", "true");
mdem->wMdem.setStyleSheet(QString(
"QWidget#%1 > QWidget {"
"border-right: 1px solid gray;"
"border-bottom: 1px solid gray;"
"border-left: 1px solid gray;"
"background: %2;"
"} "
"QWidget#%1 > QWidget[first=\"true\"] {"
"border-top: 1px solid gray;"
"}"
).arg(id, MDEM_BACKGROUND));
// Add Front Content
mdem->hFront.addWidget(&mdem->wFrontText);
mdem->hFront.addStretch(1);
mdem->editButton.setText("Edit");
QObject::connect(
&mdem->editButton,
&QToolButton::clicked,
[mdem]() {
editMdem = mdem;
if (mdem->question) {
editor->setText(
QString::fromStdString(
outputMdem(std::vector<Question*>{mdem->question})
)
);
editorWindow->show();
editor->setCursorPosition(1, 2);
}
}
);
mdem->hFront.addWidget(&mdem->editButton);
mdem->deleteButton.setText("Delete");
QObject::connect(
&mdem->deleteButton,
&QToolButton::clicked,
[mdem]() {
if (mdem->question) {
Question* deleted = nullptr;
for (size_t i = 0; i < currentMdemBuffer->questions.size(); ++i) {
if (currentMdemBuffer->questions[i] == mdem->question) {
currentMdemBuffer->questions.erase(currentMdemBuffer->questions.begin() + i);
delete mdem->question;
mdem->question = nullptr;
updateMdemInfo(getFilename(currentMdem), true);
break;
}
}
makePages();
switchPage(pagination->currentPage);
}
if (editMdem == mdem) {
editorWindow->hide();
}
}
);
mdem->hFront.addWidget(&mdem->deleteButton);
mdem->toggleVisibility.setText("Show");
mdem->hFront.addWidget(&mdem->toggleVisibility);
// Back
QVBoxLayout *hBack = new QVBoxLayout();
mdem->wBack.setLayout(&mdem->hBack);
mdem->vMdem.addWidget(&mdem->wBack);
// Add Back Content
for (size_t i = 0; i < 20; ++i) {
// @Improve: back label pooling
QLabel *elBackText = new QLabel();
mdem->hBack.addWidget(elBackText);
mdem->backLabels.push_back(elBackText);
}
mdem->vMdem.addWidget(&mdem->wFront);
mdem->vMdem.addWidget(&mdem->wBack);
mdem->vMdem.setContentsMargins(0, 0, 0, 0);
mdem->vMdem.setSpacing(0);
mdem->wBack.hide();
mdem->wMdem.hide();
// Connect button to toggle view
QObject::connect(&mdem->toggleVisibility, &QToolButton::clicked, [mdem]() {
if (mdem->wBack.isVisible()) {
mdem->wBack.hide();
mdem->toggleVisibility.setText("Show");
} else {
mdem->wBack.show();
mdem->toggleVisibility.setText("Hide");
}
showBacklabels(mdem);
});
return mdem;
}
void CreateMdems(std::vector<Question*>& questions) {
hMdemScroll->removeItem(mdemSpacer);
for (Mdem *mdem : mdems) {
if (mdem->wMdem.isVisible()) {
mdem->wMdem.hide();
}
}
perPage = settings->value(SETTING_PER_PAGE).toInt();
if (perPage > mdems.size()) {
for (size_t i = mdems.size(); i < perPage; i++) {
if (i >= mdems.size()) {
auto mdem = makeMdem();
mdems.push_back(mdem);
hMdemScroll->addWidget(&mdem->wMdem);
}
}
}
for (size_t i = 0; i < questions.size(); ++i) {
mdems[i]->question = questions[i];
setupMdem(mdems[i], questions[i]);
if (!mdems[i]->wMdem.isVisible()) {
mdems[i]->wMdem.show();
}
}
hMdemScroll->addItem(mdemSpacer);
}
void update(bool isChanged) {
if (pagination->currentPage > -1) {
switchPage(pagination->currentPage);
}
if (currentMdem.length() > 0) {
updateMdemInfo(getFilename(currentMdem), isChanged);
}
}
void switchPage(int pageIdx) {
pagination->currentPage = pageIdx;
// Hide all pagination buttons
for (auto& button : pagination->paginationButtons) {
button->hide();
}
int l = 0;
char buffer[50];
snprintf(buffer, sizeof(buffer), "Page: %d", pageIdx + 1);
pagination->paginationLabel.setText(buffer);
// Adjust mdem amount, hide widgets in mdems
for (auto& mdem : mdems) {
if (mdem->wBack.isVisible()) {
mdem->wBack.hide();
mdem->toggleVisibility.setText("Show");
}
}
// Update pagination buttons
for (int k = -DISTANCE; k <= DISTANCE; ++k) {
if (pageIdx + k >= 0 && pageIdx + k < pagination->pages.size()) {
auto button = pagination->paginationButtons[l];
snprintf(buffer, sizeof(buffer), "%d", pageIdx + k + 1);
button->setText(buffer);
if (pageIdx + k != pageIdx) {
button->show();
} else {
button->hide();
}
++l;
}
}
// Handle first and last buttons
if (pageIdx > 0 && pagination->pages.size() > 1) {
pagination->firstButton.show();
} else {
pagination->firstButton.hide();
}
if (pageIdx < pagination->pages.size() - 1 && pagination->pages.size() > 1) {
pagination->lastButton.show();
} else {
pagination->lastButton.hide();
}
// Handle next and previous buttons
if (!pagination->pages.empty() && pagination->currentPage < pagination->pages.size() - 1) {
pagination->nextButton.show();
} else {
pagination->nextButton.hide();
}
if (!pagination->pages.empty() && pagination->currentPage >= 1) {
pagination->prevButton.show();
} else {
pagination->prevButton.hide();
}
Page page;
if (pagination->pages.size() <= 0) {
page = Page();
} else if (pageIdx < pagination->pages.size()){
page = pagination->pages[pageIdx];
} else {
if (pageIdx - 1 < pagination->pages.size()) {
page = pagination->pages[pageIdx -1];
} else {
page = pagination->pages[0];
}
}
std::vector<Question*> pageSlice(
currentMdemBuffer->questions.begin() + page.start,
currentMdemBuffer->questions.begin() + page.end
);
CreateMdems(pageSlice);
}
void reloadMdem(std::string path) {
if (path == "") {
return;
}
auto toRemove = std::vector<std::string>();
for (auto it = buffers.begin(); it != buffers.end(); ++it) {
auto pair = *it;
if (currentMdem.compare(path) != 0 &&
(!pair.second->isModified || pair.second->error)) {
toRemove.push_back(pair.first);
}
}
for (auto key: toRemove) {
buffers.erase(key);
}
MdemBuffer *buffer;
auto filename = getFilename(path);
if (currentMdem.compare(path) == 0) {
buffer = currentMdemBuffer;
currentMdem = path;
if (buffers.contains(path)) {
buffers.erase(path);
}
} else if (!buffers.contains(path)) {
buffer = new MdemBuffer;
buffers[path] = buffer;
currentMdemBuffer = buffer;
currentMdem = path;
} else {
buffer = buffers[path];
currentMdemBuffer = buffer;
makePages();
switchPage(0);
updateMdemInfo(getFilename(filename), buffer->isModified);
currentMdem = path;
errorView->box.hide();
return;
}
start = std::chrono::high_resolution_clock::now();
auto file = std::ifstream(path);
std::string content;
// Reset the mdem list.
for (auto mdem: mdems) {
mdem->wMdem.hide();
}
for (auto question: currentMdemBuffer->questions) {
delete question;
}
currentMdemBuffer->questions.clear();
if (file) {
std::stringstream buffer;
buffer << file.rdbuf();
content = buffer.str();
end = std::chrono::high_resolution_clock::now();
showTimes = settings->value(SETTING_SHOW_TIMES).toBool();
if (showTimes) {
std::cout << showTime("I/O time") << std::endl;
}
debug = settings->value(SETTING_DEBUG).toBool();
auto res = transpile(content);
currentMdemBuffer->error = res.error.length() > 0;
if (res.error == "") {
if (res.value.lastTrainedAt == 0) {
currentMdemBuffer->trainedAt = 0;
} else {
auto timezoneOffset = settings->value("timezone").toInt();
currentMdemBuffer->trainedAt = res.value.lastTrainedAt - 3600 * timezoneOffset;
}
if (settings->value(SETTING_DEBUG).toBool()) {
std::cout << std::format("Last trained at: {}", currentMdemBuffer->trainedAt)
<< std::endl;
}
currentMdemBuffer->questions = res.value.questions;
errorView->box.hide();
} else {
currentMdemBuffer->trainedAt = 0;
std::cout << std::format("Compilation error: {}", res.error) << std::endl;
for (auto question: res.value.questions) {
delete question;
}
// Show errors.
hMdemScroll->removeItem(mdemSpacer);
errorView->label.setText(
QString::fromStdString(
std::format(
"Error while transpiling {}: {} ({}:{})",
filename,
res.error,
res.row,
res.column
)
)
);
errorView->box.show();
hMdemScroll->addWidget(&errorView->box);
hMdemScroll->addItem(mdemSpacer);
}
makePages();
switchPage(0);
updateMdemInfo(filename, false);
} else {
std::cout << std::format("Could not open the file: {}", currentPath.toStdString()) << std::endl;
}
for (auto mdem: mdems) {
mdem->wBack.hide();
mdem->toggleVisibility.setText("Show");
}
hideQuestionElements();
}
void pickDirectory(QString directory) {
auto path = std::filesystem::path(directory.toStdString());
if (
!std::filesystem::exists(directory.toStdString()) ||
!std::filesystem::is_directory(directory.toStdString())
) {
QMessageBox::information(
nullptr,
"Error",
"The directory that is specified as the default memorybase does not exist."
);
return;
}
currentPath = directory;
// Update tree view.
if (directory.length() <= 0) {
membaseLabel->setText(directory);
return;
}
mdemList->setRootIndex(model->setRootPath(directory));
std::smatch matches;
// Update label.
membaseLabel->setText(QString::fromStdString(
std::format(
"memorybase: {}",
getFilename(currentPath.toStdString())
)
));
reloadMdem("");
}
void setupEditorSave() {
debug = settings->value(SETTING_DEBUG).toBool();
showTimes = settings->value(SETTING_SHOW_TIMES).toBool();
auto res = transpile(editor->text().toStdString());
if (res.error.length() > 0) {
currentMdemBuffer->trainedAt = 0;
for (auto question: res.value.questions) {
delete question;
}
QMessageBox::information(
nullptr,
"Editing error",
QString::fromStdString(res.error)
);
} else {
if (editMdem) {
if (res.value.questions.size() <= 0) {
QMessageBox::information(
nullptr,
"Editing error",
"There are no questions in your input."
);
} else if (res.value.questions.size() == 1) {
auto oldQuestion = editMdem->question;
editMdem->question = res.value.questions[0];
for (size_t i = 0; i < currentMdemBuffer->questions.size(); ++i) {
if (currentMdemBuffer->questions[i] == oldQuestion) {
currentMdemBuffer->questions.erase(currentMdemBuffer->questions.begin() + i);
delete oldQuestion;
currentMdemBuffer->questions.insert(currentMdemBuffer->questions.begin() + i, res.value.questions[0]);
break;
}
}
setupMdem(editMdem, res.value.questions[0]);
showBacklabels(editMdem);
editorWindow->hide();
updateMdemInfo(getFilename(currentMdem), true);
} else {
QMessageBox::information(
nullptr,
"Editing error",
"There are more than question in your input. Please enter one."
);
}
} else {
if (res.value.questions.size() <= 0) {
QMessageBox::information(
nullptr,
"Editing error",
"There are no questions in your input."
);
} else {
currentMdemBuffer->questions.insert(
currentMdemBuffer->questions.begin(),
res.value.questions.begin(),
res.value.questions.end()
);
makePages();
switchPage(0);
editorWindow->hide();
updateMdemInfo(getFilename(currentMdem), true);
}
}
}
};
void saveMdem() {
start = std::chrono::high_resolution_clock::now();
auto filename = getFilename(currentMdem);
std::ofstream out(currentMdem);
out << outputMdem(currentMdemBuffer->questions, currentMdemBuffer->trainedAt);
updateMdemInfo(getFilename(currentMdem), false);
end = std::chrono::high_resolution_clock::now();
showTimes = settings->value(SETTING_SHOW_TIMES).toBool();
if (showTimes) {
std::cout << showTime("Saving time") << std::endl;
}
}
QMainWindow *initMdemListWindow() {
QMainWindow* window = new QMainWindow;
pagination = new Pagination;
auto *toolbar = new Toolbar();
// Setup related windows
auto *settingsWindow = initSettings();
trainWindow = initTrainWindow();
{ // Menu bar.
QMenuBar *menuBar = new QMenuBar;
QFileDialog *fileDialog = new QFileDialog;
QMenu *menu = new QMenu("File");
QAction *actionOpen = menu->addAction("Open memorybase");
QObject::connect(actionOpen, &QAction::triggered, [fileDialog]() {
fileDialog->setDirectory(QDir::homePath());
fileDialog->setFileMode(QFileDialog::FileMode::Directory);
fileDialog->open();
QObject::disconnect(fileDialog, 0, 0, 0);
fileDialog->connect(
fileDialog,
&QFileDialog::fileSelected,
pickDirectory
);
});
QAction *openSettings = menu->addAction("Settings");
QObject::connect(
openSettings,
&QAction::triggered,
[settingsWindow]() {
settingsWindow->show();
});
menuBar->addMenu(menu);
window->setMenuBar(menuBar);
}
{ // Editor.
editorWindow = new QMainWindow;
editorWindow->setWindowTitle("QScintilla Simple Editor");
editorWindow->resize(800, 600);
QWidget *wEditor = new QWidget;
QVBoxLayout *vlEditor = new QVBoxLayout;
wEditor->setLayout(vlEditor);
QsciLexerCPP *lexer = new QsciLexerCPP;
editor = new QsciScintilla;
editor->setLexer(lexer);
editor->setUtf8(true);
editor->setMarginWidth(0, 15);
editor->zoomIn(2);
QHBoxLayout *buttonLayout = new QHBoxLayout;
QWidget *editorButtons = new QWidget;
auto btnSaveEditor = new QPushButton;
editorButtons->setLayout(buttonLayout);
btnSaveEditor->setText(QString::fromStdString("Save"));
QObject::connect(
btnSaveEditor,
&QToolButton::clicked,
setupEditorSave
);
buttonLayout->addWidget(btnSaveEditor);
vlEditor->addWidget(editor);
vlEditor->addWidget(editorButtons);
QShortcut* shortcutSave = new QShortcut(QKeySequence("Ctrl+S"), editorWindow);
QObject::connect(shortcutSave, &QShortcut::activated, []() {
if (editor->isVisible()) {
setupEditorSave();
}
});
editorWindow->setCentralWidget(wEditor);
}
auto *wTop = new QWidget();
{ // Top.
// Main top layout.
QHBoxLayout *hlTop = new QHBoxLayout();
wTop->setLayout(hlTop);
// Labels
auto *wLabels = new QWidget();
auto *vlLeftTop = new QVBoxLayout();
wLabels->setLayout(vlLeftTop);
wLabels->setMinimumSize(0, 40);
QString labelStyle = "font-size: 17px; font-weight: 400;";
// Memorybase label.
membaseLabel = new QLabel();
membaseLabel->setStyleSheet(labelStyle);
vlLeftTop->addWidget(membaseLabel);
// Memorybase label.
mdemLabel = new QLabel();
mdemLabel->setStyleSheet(labelStyle);
vlLeftTop->addWidget(mdemLabel);
lastPracticeLabel = new QLabel();
lastPracticeLabel->setStyleSheet(labelStyle);
vlLeftTop->addWidget(lastPracticeLabel);
// Button layout.
auto buttons = new QWidget();
auto vlButtons = new QVBoxLayout();
buttons->setLayout(vlButtons);
auto buttonsTop = new QWidget();
auto hlButtonsTop = new QHBoxLayout();
buttonsTop->setLayout(hlButtonsTop);
auto buttonsBottom = new QWidget();
auto hlButtonsBottom = new QHBoxLayout();
buttonsBottom->setLayout(hlButtonsBottom);
// Define buttons.
QObject::connect(&toolbar->btnAdd, &QToolButton::clicked, []() {
editMdem = nullptr;
editorWindow->show();
editor->setText("");
});
QObject::connect(&toolbar->btnLoad, &QToolButton::clicked, []() {
reloadMdem(currentMdem);
});
QObject::connect(&toolbar->btnSave, &QToolButton::clicked, []() {
saveMdem();
});
QObject::connect(
&toolbar->btnPractice,
&QToolButton::clicked,
[toolbar]() {
if (currentMdemBuffer) {
trainWindow->show();
trainWindow->resize(600, 300);
initiatePractice(
currentMdemBuffer,
static_cast<PracticeAlgorithm>(
toolbar->cbAlgorithm.currentData().toInt()
)
);
}
}
);
// Button content.
toolbar->btnAdd.setText("Add");
toolbar->btnSave.setText("Save");
toolbar->btnLoad.setText("Load");
toolbar->btnPractice.setText("Practice");
toolbar->cbAlgorithm.addItem("Spaced", SPACED);
toolbar->cbAlgorithm.addItem("Random", RANDOM);
toolbar->cbAlgorithm.addItem("Primary", PRIMARY);
// Add buttons.
hlButtonsTop->addWidget(&toolbar->btnAdd);
hlButtonsTop->addWidget(&toolbar->btnSave);
hlButtonsTop->addWidget(&toolbar->btnLoad);
hlButtonsBottom->addWidget(&toolbar->cbAlgorithm);
hlButtonsBottom->addWidget(&toolbar->btnPractice);
vlButtons->addWidget(buttonsTop);
vlButtons->addWidget(buttonsBottom);
// Style buttons
hlButtonsTop->setContentsMargins(0, 0, 0, 0);
hlButtonsBottom->setContentsMargins(0, 5, 0, 0);
// Add top components.
hlTop->addWidget(wLabels);
hlTop->addStretch(1);
hlTop->addWidget(buttons);
}
QWidget *leftWidget = new QWidget();
{ // Left side.
QVBoxLayout *leftLayout = new QVBoxLayout();
leftWidget->setLayout(leftLayout);
model = new QFileSystemModel();
mdemList = new QTreeView();
mdemList->setModel(model);
currentPath = settings->value(SETTING_MEMORYBASE).toString();
if (currentPath.size() > 0) {
pickDirectory(currentPath);
}
/*leftLayout->addWidget(leftTop);*/
QObject::connect(
mdemList,
&QTreeView::doubleClicked,
[](const QModelIndex &index) {
trainWindow->close();
auto fileInfo = model->fileInfo(index);
reloadMdem(fileInfo.filePath().toStdString());
}
);
for (int col = 1; col < model->columnCount(); ++col) {
mdemList->hideColumn(col);
}
model->setHeaderData(0, Qt::Horizontal, QObject::tr("Custom Name"));
leftLayout->addWidget(mdemList);
}
auto wMain = new QWidget;
auto *rightWidget = new QWidget();
auto *rightLayout = new QVBoxLayout();
{ // Main layout.
auto vlMain = new QVBoxLayout;
wMain->setLayout(vlMain);
rightWidget->setLayout(rightLayout);
auto *hSplitter = new QSplitter();
hSplitter->addWidget(leftWidget);
hSplitter->addWidget(rightWidget);
hSplitter->setStretchFactor(1, 3);
hSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
vlMain->addWidget(wTop);
vlMain->addWidget(hSplitter);
}
{ // Error.
errorView = new ErrorView;
errorView->box.setObjectName("error-box");
errorView->box.setLayout(&errorView->layout);
errorView->box.setMinimumHeight(30);
errorView->box.setStyleSheet(
QString(
"QWidget#error-box {"
"border: 1px solid red;"
"background: %2;"
"}"
).arg(MDEM_BACKGROUND)
);
errorView->layout.addWidget(&errorView->label);
errorView->label.setWordWrap(true);
}
{ // Mdems
QScrollArea *mdemScroll = new QScrollArea();
QWidget *mdemContainer = new QWidget();
hMdemScroll = new QVBoxLayout();
mdemScroll->setWidget(mdemContainer);
mdemScroll->setWidgetResizable(true);
mdemContainer->setLayout(hMdemScroll);
rightLayout->addWidget(mdemScroll);
mdemSpacer = new QSpacerItem(
50,
50,
QSizePolicy::Minimum,
QSizePolicy::Expanding
);
/*hMdemScroll->addItem(mdemSpacer);*/
}
{ // Pagination
auto wPagination = new QWidget();
auto hPagination = new QHBoxLayout();
wPagination->setLayout(hPagination);
pagination->firstButton.setText(QString::fromStdString("<<"));
hPagination->addWidget(&pagination->firstButton);
pagination->firstButton.hide();
QObject::connect(&pagination->firstButton, &QToolButton::clicked, []() {
if (pagination->pages.size() > 0) {
switchPage(0);
}
});
pagination->prevButton.setText(QString::fromStdString("<"));
hPagination->addWidget(&pagination->prevButton);
pagination->prevButton.hide();
QObject::connect(&pagination->prevButton, &QToolButton::clicked, []() {
if (pagination->pages.size() > 0) {
switchPage(pagination->currentPage - 1);
}
});
perPage = settings->value(SETTING_PER_PAGE).toInt();
for (size_t i = 0; i < perPage; i++) {
auto elButton = new QToolButton();
elButton->setText(QString("%1").arg(i+1));
hPagination->addWidget(elButton);
elButton->hide();
QObject::connect(elButton, &QToolButton::clicked, [elButton]() {
auto pageNum = std::stoi(elButton->text().toStdString().c_str());
auto pageIdx = pageNum - 1;
if (pageIdx < pagination->pages.size()) {
switchPage(pageIdx);
}
});
pagination->paginationButtons.push_back(elButton);
}
pagination->nextButton.setText(QString::fromStdString(">"));
hPagination->addWidget(&pagination->nextButton);
pagination->nextButton.hide();
QObject::connect(&pagination->nextButton, &QToolButton::clicked, []() {
if (pagination->pages.size() > 0) {
switchPage(pagination->currentPage + 1);
}
});
pagination->lastButton.setText(QString::fromStdString(">>"));
hPagination->addWidget(&pagination->lastButton);
pagination->lastButton.hide();
QObject::connect(&pagination->lastButton, &QToolButton::clicked, []() {
if (pagination->pages.size() > 0) {
switchPage(pagination->pages.size() - 1);
}
});
hPagination->addStretch(1);
hPagination->addWidget(&pagination->paginationLabel);
rightLayout->addWidget(wPagination);
}
{ // Setup shortcuts.
auto addShortcut = [window](QString key, std::function<void()> func) {
QShortcut* shortcut = new QShortcut(QKeySequence(key), window);
QObject::connect(shortcut, &QShortcut::activated, [func]() {
func();
});
};
addShortcut("Ctrl+,", [settingsWindow]() {
settingsWindow->show();
});
addShortcut("Ctrl+S", [toolbar]() {
toolbar->btnSave.click();
});
addShortcut("Ctrl+P", [toolbar]() {
toolbar->btnPractice.click();
});
addShortcut("Ctrl+A", [toolbar]() {
toolbar->btnAdd.click();
});
addShortcut("Ctrl+L", [toolbar]() {
toolbar->btnLoad.click();
});
}
window->setCentralWidget(wMain);
window->show();
return window;
}