mirror of
https://github.com/jorenchik/mdemory.git
synced 2026-03-22 00:26:21 +00:00
1196 lines
31 KiB
C++
1196 lines
31 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 <QTextBrowser>
|
|
#include <QHeaderView>
|
|
|
|
#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(¤tMdemBuffer->trainedAt);
|
|
char buffer[100];
|
|
std::strftime(buffer, sizeof(buffer), "%d.%m.%Y %H:%M", tm);
|
|
ss << "Pēdējā mācīšanās reize: " << 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(70);
|
|
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->wFrontText.setWordWrap(true);
|
|
mdem->wFrontText.setSizePolicy(
|
|
QSizePolicy::Expanding,
|
|
QSizePolicy::Minimum
|
|
);
|
|
mdem->hFront.addWidget(&mdem->wFrontText);
|
|
|
|
mdem->editButton.setText("Rediģēt");
|
|
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("Dzēst");
|
|
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("Parādīt");
|
|
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("Parādīt");
|
|
} else {
|
|
mdem->wBack.show();
|
|
mdem->toggleVisibility.setText("Slēpt");
|
|
}
|
|
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), "Lappuse: %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("Parādīt");
|
|
}
|
|
}
|
|
|
|
// 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 laiks") << 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("Pēdējās mācīšanās reize: {}", currentMdemBuffer->trainedAt)
|
|
<< std::endl;
|
|
}
|
|
|
|
currentMdemBuffer->questions = res.value.questions;
|
|
errorView->box.hide();
|
|
} else {
|
|
currentMdemBuffer->trainedAt = 0;
|
|
std::cout << std::format("Transpilācijas kļūda: {}", res.error) << std::endl;
|
|
|
|
for (auto question: res.value.questions) {
|
|
delete question;
|
|
}
|
|
|
|
// Show errors.
|
|
hMdemScroll->removeItem(mdemSpacer);
|
|
errorView->label.setText(
|
|
QString::fromStdString(
|
|
std::format(
|
|
"Kļūda, transpilējot {}: {} ({}:{})",
|
|
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("Nevar atvert failu: {}", currentPath.toStdString()) << std::endl;
|
|
}
|
|
|
|
for (auto mdem: mdems) {
|
|
mdem->wBack.hide();
|
|
mdem->toggleVisibility.setText("Parādīt");
|
|
}
|
|
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",
|
|
"Direktorijs, kas ir norādīts kā noklusētais, neeksistē."
|
|
);
|
|
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,
|
|
"Rediģēšanas kļūda",
|
|
QString::fromStdString(res.error)
|
|
);
|
|
} else {
|
|
if (editMdem) {
|
|
if (res.value.questions.size() <= 0) {
|
|
QMessageBox::information(
|
|
nullptr,
|
|
"Rediģēšanas kļūda",
|
|
"Jūsu ievadē nav jautājumu."
|
|
);
|
|
} 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,
|
|
"Rediģēšanas kļūda",
|
|
"Jūsu ievadē ir vairāki jautājumi. Lūdzu ievadiet vienu!"
|
|
);
|
|
}
|
|
} else {
|
|
if (res.value.questions.size() <= 0) {
|
|
QMessageBox::information(
|
|
nullptr,
|
|
"Rediģēšanas kļūda",
|
|
"Jūsu ievadē nav jautājumu."
|
|
);
|
|
} 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("Saglabāšanas laiks") << std::endl;
|
|
}
|
|
}
|
|
|
|
QMainWindow *initMdemListWindow() {
|
|
QMainWindow* window = new QMainWindow;
|
|
pagination = new Pagination;
|
|
auto *toolbar = new Toolbar();
|
|
|
|
// Setup the related windows.
|
|
auto *settingsWindow = initSettings();
|
|
trainWindow = initTrainWindow();
|
|
|
|
QMainWindow *guideWindow = new QMainWindow;
|
|
{ // Guide window.
|
|
auto guideWidget = new QWidget;
|
|
auto *layout = new QVBoxLayout;
|
|
auto *textBrowser = new QTextBrowser;
|
|
textBrowser->setSource(QUrl("qrc:/html/help.html"));
|
|
textBrowser->zoomIn(2);
|
|
layout->addWidget(textBrowser);
|
|
guideWidget->setLayout(layout);
|
|
guideWindow->setCentralWidget(guideWidget);
|
|
}
|
|
|
|
QAction *actionOpen;
|
|
QAction *openSettings;
|
|
QAction *actionHelp;
|
|
{ // Menu bar.
|
|
QMenuBar *menuBar = new QMenuBar;
|
|
QFileDialog *fileDialog = new QFileDialog;
|
|
|
|
QMenu *menu = new QMenu("Fails");
|
|
menu->setStyleSheet("font-size: 15px;");
|
|
actionOpen = menu->addAction("Atvert 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
|
|
);
|
|
}
|
|
);
|
|
|
|
openSettings = menu->addAction("Iestatījumi");
|
|
QObject::connect(
|
|
openSettings,
|
|
&QAction::triggered,
|
|
[settingsWindow]() {
|
|
settingsWindow->show();
|
|
});
|
|
|
|
actionHelp = menu->addAction("Palīdzība");
|
|
QObject::connect(
|
|
actionHelp,
|
|
&QAction::triggered,
|
|
[guideWindow]() {
|
|
guideWindow->show();
|
|
}
|
|
);
|
|
|
|
menuBar->addMenu(menu);
|
|
window->setMenuBar(menuBar);
|
|
}
|
|
|
|
{ // Editor.
|
|
editorWindow = new QMainWindow;
|
|
|
|
editorWindow->setWindowTitle("Jautājumu redaktors");
|
|
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("Saglabāt"));
|
|
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: 20px; 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);
|
|
buttons->setStyleSheet("font-size: 15px;");
|
|
|
|
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("Pievienot");
|
|
toolbar->btnSave.setText("Saglabāt");
|
|
toolbar->btnLoad.setText("Ielādēt");
|
|
QLabel *algorithmLabel = new QLabel();
|
|
algorithmLabel->setText("Algoritms:");
|
|
toolbar->cbAlgorithm.addItem("Intervālu", SPACED);
|
|
toolbar->cbAlgorithm.addItem("Nejaušais", RANDOM);
|
|
toolbar->cbAlgorithm.addItem("Primārais", PRIMARY);
|
|
toolbar->btnPractice.setText("Mācīties");
|
|
|
|
// Add buttons.
|
|
hlButtonsTop->addWidget(&toolbar->btnAdd);
|
|
hlButtonsTop->addWidget(&toolbar->btnSave);
|
|
hlButtonsTop->addWidget(&toolbar->btnLoad);
|
|
hlButtonsBottom->addWidget(algorithmLabel);
|
|
hlButtonsBottom->addWidget(&toolbar->cbAlgorithm);
|
|
hlButtonsBottom->addWidget(&toolbar->btnPractice);
|
|
vlButtons->addWidget(buttonsTop);
|
|
vlButtons->addWidget(buttonsBottom);
|
|
vlButtons->setAlignment(Qt::AlignTop);
|
|
|
|
// 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();
|
|
|
|
QStringList filters;
|
|
filters << "*.mdem";
|
|
model->setNameFilters(filters);
|
|
model->setNameFilterDisables(false);
|
|
|
|
mdemList->setModel(model);
|
|
mdemList->setStyleSheet(
|
|
"font-size: 15px;"
|
|
);
|
|
auto header = mdemList->header();
|
|
header->setSectionResizeMode(QHeaderView::ResizeToContents);
|
|
currentPath = settings->value(SETTING_MEMORYBASE).toString();
|
|
if (currentPath.size() > 0) {
|
|
pickDirectory(currentPath);
|
|
}
|
|
/*leftLayout->addWidget(leftTop);*/
|
|
|
|
QObject::connect(
|
|
mdemList,
|
|
&QTreeView::doubleClicked,
|
|
[](const QModelIndex &index) {
|
|
auto fileInfo = model->fileInfo(index);
|
|
if (fileInfo.suffix() == "mdem") {
|
|
trainWindow->close();
|
|
reloadMdem(fileInfo.filePath().toStdString());
|
|
}
|
|
}
|
|
);
|
|
|
|
for (int col = 1; col < model->columnCount(); ++col) {
|
|
mdemList->hideColumn(col);
|
|
}
|
|
// TODO: name -> nosaukums in HEADER
|
|
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(70);
|
|
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);
|
|
mdemScroll->setStyleSheet("font-size: 15px");
|
|
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);
|
|
wPagination->setStyleSheet("font-size: 15px;");
|
|
|
|
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+O", [actionOpen]() {
|
|
actionOpen->trigger();
|
|
});
|
|
addShortcut("Ctrl+H", [actionHelp]() {
|
|
actionHelp->trigger();
|
|
});
|
|
addShortcut("Ctrl+,", [openSettings]() {
|
|
openSettings->trigger();
|
|
});
|
|
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;
|
|
}
|