diff --git a/CMakeLists.txt b/CMakeLists.txt index d502a78..a8572f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,6 +166,7 @@ else() ${PROJECT_SOURCES} ${SOURCES} widgets/checkqueuetablemodel.h widgets/checkqueuetablemodel.cpp + # widgets/checkqueuetableview.h widgets/checkqueuetableview.cpp ) endif() diff --git a/mainwindow.cpp b/mainwindow.cpp index d8aacf0..3cc8176 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -39,6 +39,8 @@ MainWindow::MainWindow(QWidget *parent) model = new CheckQueueTableModel(&checks, this); ui->checkQueueTable->setModel(model); + ui->checkQueueTable->viewport()->setAcceptDrops(true); + ui->checkQueueTable->setDragDropMode(QAbstractItemView::DragDrop); // ui-> // connect(this, &MainWindow::deleteCheckFromList, this, &MainWindow::deleteCheckFromListHandler); @@ -236,15 +238,17 @@ void MainWindow::on_parse_button_clicked() { // } void MainWindow::on_add_new_check_button_clicked() { - Check *new_check = parse_new_check(); + Check *new_check = new Check();/* parse_new_check(); if (new_check == nullptr) { return; - } + }*/ + new_check->set_date("123"); + new_check->set_total(rand() * 1800); - checks.push_back(*new_check); + // checks.push_back(*new_check); unsigned int newRowIndex = checks.size(); - model->insertRow(newRowIndex, 1); + model->insertRows(newRowIndex, 1); model->setData(model->index(newRowIndex, 0), QVariant::fromValue(new_check->get_date())); model->setData(model->index(newRowIndex, 1), QVariant::fromValue(new_check->get_total())); diff --git a/scenes/mainwindow.ui b/scenes/mainwindow.ui index 1ff732c..e8a5c61 100644 --- a/scenes/mainwindow.ui +++ b/scenes/mainwindow.ui @@ -209,13 +209,13 @@ QAbstractItemView::DragDropMode::InternalMove - Qt::DropAction::TargetMoveAction + Qt::DropAction::MoveAction true - QAbstractItemView::SelectionMode::SingleSelection + QAbstractItemView::SelectionMode::MultiSelection QAbstractItemView::SelectionBehavior::SelectRows diff --git a/translations/en_US.ts b/translations/en_US.ts index 22025e1..a49bcbd 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -228,77 +228,77 @@ checks parser - + QR code for binaryeye to connect QR code for binaryeye to connect - + I've scanned I've scanned - + Could not start http server. 10 times in a row random port was occupied. Either you should run for a lottery ticket, or the problem is in the program. If the lottery ticket wasn't lucky, please, contact the developer. Could not start http server. 10 times in a row random port was occupied. Either you should run for a lottery ticket, or the problem is in the program. If the lottery ticket wasn't lucky, please, contact the developer. - + Could not start http server. Could not start http server. - + Selected image: Selected image: - + This feature is under development. Wait it to appear in next updates. This feature is under development. Wait for it to appear in next updates. - + Under development Under development - + Please, add check(s) to parse Please, add check(s) to parse - + No checks to parse No checks to parse - + Captcha was not solved correctly! Captcha was not solved correctly! - + Captcha is incorrect Captcha is incorrect - + Internal server error. Please, try again later. Internal server error. Please, try again later. - + Internal server error Internal server error - + Check not found. Please, ensure correctness of entered data. Check not found. Please, ensure correctness of entered data. - + Check was not found Check was not found @@ -311,12 +311,12 @@ Error in parsing - + Please, select a picture where QR code that contains info about check is present Please, select a picture where QR code that contains info about check is present - + Picture was not selected Picture was not selected diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index d13457b..201f5ea 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -224,77 +224,77 @@ Парсер чеков - + QR code for binaryeye to connect QR код для подключения BinaryEye - + I've scanned Просканировал - + Could not start http server. 10 times in a row random port was occupied. Either you should run for a lottery ticket, or the problem is in the program. If the lottery ticket wasn't lucky, please, contact the developer. Не смог поднять HTTP сервер. 10 раз подряд случайно выбранный порт был занят. Либо Вам следует бежать за лоттерейным билетом, или в программе баг. Если лотерейный билет не был выигрышным, пожалуйста, сообщите разработчику. - + Could not start http server. Не получилось запустить HTTP сервер. - + Selected image: Выбранное изображение: - + This feature is under development. Wait it to appear in next updates. - + Under development - + Please, add check(s) to parse - + No checks to parse - + Captcha was not solved correctly! Капча была решена неверно! - + Captcha is incorrect Капча введена неверно - + Internal server error. Please, try again later. Внутренняя ошибка сервера. Пожалуйста, попробуйте снова позже. - + Internal server error Внутренняя ошибка сервера - + Check not found. Please, ensure correctness of entered data. Чек не найден. Пожалуйста, убедитесь в правильности введённых данных. - + Check was not found Чек не найден @@ -307,12 +307,12 @@ Ошибка в парсинге - + Please, select a picture where QR code that contains info about check is present Пожалуйста, выберете изображение, содержащее QR код с информацией о чеке - + Picture was not selected Изображение не было выбрано diff --git a/utils/utils.cpp b/utils/utils.cpp index b460367..4e342e2 100644 --- a/utils/utils.cpp +++ b/utils/utils.cpp @@ -181,9 +181,9 @@ std::vector find_in_html(std::string& html, std::string regex, std it != end; it++) { std::wstring found_entry = from_utf8(it->str()); - std::cout << "Found: " << to_utf8(found_entry) << std::endl; + // std::cout << "Found: " << to_utf8(found_entry) << std::endl; std::wstring extracted = substring_from_to(found_entry, from_utf8(html_start), from_utf8(html_end)); - std::cout << "Extracted: " << to_utf8(extracted) << std::endl; + // std::cout << "Extracted: " << to_utf8(extracted) << std::endl; parsed.push_back(extracted); } return parsed; @@ -293,8 +293,8 @@ Check parseOfdRuAnswer(std::string html) { Check c; for (int i = 0; i < products.size(); i ++) { - std::cout << "Adding to check: "; - std::cout << to_utf8(products[i]) << " " << to_utf8(prices[i]) << " " << to_utf8(net_weights[i]) << " " << to_utf8(amounts[i]) << std::endl; + // std::cout << "Adding to check: "; + // std::cout << to_utf8(products[i]) << " " << to_utf8(prices[i]) << " " << to_utf8(net_weights[i]) << " " << to_utf8(amounts[i]) << std::endl; Goods goods(to_utf8(products[i]), std::stod(prices[i]), to_utf8(net_weights[i]), std::stod(amounts[i])); c.add_goods(goods); } diff --git a/widgets/checkqueuetablemodel.cpp b/widgets/checkqueuetablemodel.cpp index 51a7d91..bac1766 100644 --- a/widgets/checkqueuetablemodel.cpp +++ b/widgets/checkqueuetablemodel.cpp @@ -1,6 +1,7 @@ #include "checkqueuetablemodel.h" -#include +#include +#include CheckQueueTableModel::CheckQueueTableModel(std::vector *checks, QObject *parent) : checks(checks), QAbstractTableModel{parent} @@ -10,8 +11,10 @@ int CheckQueueTableModel::rowCount(const QModelIndex &parent) const { return che int CheckQueueTableModel::columnCount(const QModelIndex &parent) const { return 3; } QVariant CheckQueueTableModel::data(const QModelIndex &index, int role) const { + if (!index.isValid() || index.row() >= checks->size()) + return QVariant(); if (role != Qt::DisplayRole) return QVariant(); - Check& c = (*checks).at(index.row()); + Check& c = checks->at(index.row()); switch (index.column()) { case 0: return QVariant::fromValue(QString::fromStdString(c.get_date())); @@ -33,24 +36,16 @@ bool CheckQueueTableModel::setData(const QModelIndex &index, const QVariant &val return false; unsigned int row = index.row(); switch (index.column()) { - case 0: + case 0: { checks->at(row).set_date(value.value()); break; - case 1: + } case 1: checks->at(row).set_total(value.value()); break; case 2: // delete Button break; } - - //for presentation purposes only: build and emit a joined string - // QString result = "dick"; - // for (int row = 0; row < checks->size(); row++) { - // for (int col= 0; col < 3; col++) - // result += m_gridData[row][col] + ' '; - // } - // emit editCompleted(result); return true; } return false; @@ -74,21 +69,100 @@ QVariant CheckQueueTableModel::headerData(int section, Qt::Orientation orientati } Qt::ItemFlags CheckQueueTableModel::flags(const QModelIndex &index) const { - Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index); + auto flags = QAbstractItemModel::flags(index); if (index.isValid()) - return Qt::ItemIsDragEnabled | defaultFlags; + flags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled; else - return Qt::ItemIsDropEnabled | defaultFlags; - // return Qt::ItemIsSelectable | QAbstractTableModel::flags(index); + flags |= Qt::ItemIsDropEnabled; + + return flags; } -bool CheckQueueTableModel::insertRow(int row, int count, const QModelIndex &parent) { - beginInsertRows(QModelIndex(), row, row+count-1); +Qt::DropActions CheckQueueTableModel::supportedDropActions() const { + return Qt::DropActions() | Qt::MoveAction; +} - for (int i = 0; i < count; ++i) - checks->emplace(checks->begin() + row, Check()); +bool CheckQueueTableModel::insertRows(int position, int rows, const QModelIndex &index) { + beginInsertRows(QModelIndex(), position, position+rows-1); + + for (int i = 0; i < rows; ++i) + checks->emplace(checks->begin() + position, Check()); endInsertRows(); return true; } + +bool CheckQueueTableModel::removeRows(int position, int rows, const QModelIndex &index) { + beginRemoveRows(QModelIndex(), position, position+rows-1); + + for (int row = 0; row < rows; ++row) + checks->erase(std::next(checks->begin(), position)); + + endRemoveRows(); + return true; +} + +QStringList CheckQueueTableModel::mimeTypes() const { + QStringList types; + types << CheckQueueTableModel::MimeType; + return types; +} + +bool CheckQueueTableModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int, int, const QModelIndex &) { + if (action != Qt::MoveAction || !data->hasFormat(CheckQueueTableModel::MimeType)) + return false; + return true; +} + +QMimeData* CheckQueueTableModel::mimeData(const QModelIndexList &indexes) const { + QMimeData* mimeData = new QMimeData; + QByteArray encodedData; + + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + for (const QModelIndex &i : indexes) { + if (i.isValid() && i.column() == 0) { + QString date = data(i, Qt::DisplayRole).toString(); + double total = data(index(i.row(), i.column() + 1), Qt::DisplayRole).toDouble(); + stream << date << total; + } + } + mimeData->setData(CheckQueueTableModel::MimeType, encodedData); + return mimeData; +} + +bool CheckQueueTableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { + if (!canDropMimeData(data, action, row, column, parent)) + return false; + if (action == Qt::IgnoreAction) + return true; + else if (action != Qt::MoveAction) + return false; + QByteArray encodedData = data->data(CheckQueueTableModel::MimeType); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + std::vector newItems; + int rows = 0; + + while (!stream.atEnd()) { + QString date; + double total; + stream >> date >> total; + Check c; + c.set_date(date.toStdString()); + c.set_total(total); + newItems.push_back(c); + ++rows; + } + + insertRows(row, rows, QModelIndex()); + for (Check &c : newItems) { + QModelIndex date_index = index(row, 0, QModelIndex()); + QModelIndex total_index = index(row, 1, QModelIndex()); + setData(date_index, QVariant::fromValue(c.get_date()), Qt::EditRole); + setData(total_index, QVariant::fromValue(c.get_total()), Qt::EditRole); + row++; + } + + return true; +} diff --git a/widgets/checkqueuetablemodel.h b/widgets/checkqueuetablemodel.h index 3d759bc..0c00617 100644 --- a/widgets/checkqueuetablemodel.h +++ b/widgets/checkqueuetablemodel.h @@ -8,6 +8,7 @@ class CheckQueueTableModel : public QAbstractTableModel { Q_OBJECT + static constexpr const char* MimeType = "application/check.queue.model"; public: explicit CheckQueueTableModel(std::vector *checks, QObject *parent = nullptr); @@ -19,8 +20,16 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; + Qt::DropActions supportedDropActions() const override; - bool insertRow(int row, int count, const QModelIndex &parent = QModelIndex()); + // bool insertRow(int row, int count, const QModelIndex &parent = QModelIndex()); + bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; + bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; + + QStringList mimeTypes() const override; + bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int, int, const QModelIndex &); + QMimeData* mimeData(const QModelIndexList &indexes) const override; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; private: std::vector *checks; signals: diff --git a/widgets/checkqueuetableview.cpp b/widgets/checkqueuetableview.cpp new file mode 100644 index 0000000..a06fab8 --- /dev/null +++ b/widgets/checkqueuetableview.cpp @@ -0,0 +1,47 @@ +#include "checkqueuetableview.h" + +CheckQueueTableView::CheckQueueTableView(QWidget *parent) + : QTableView(parent), m_dropRow(0) { + setSelectionMode(QAbstractItemView::SingleSelection); + setSelectionBehavior(QAbstractItemView::SelectRows); + setDragEnabled(true); + setAcceptDrops(true); + setDragDropMode(QAbstractItemView::DragDrop); + setDefaultDropAction(Qt::MoveAction); + setDragDropOverwriteMode(false); + setDropIndicatorShown(true); +} + +int CheckQueueTableView::selectedRow() const { + QItemSelectionModel *selection = selectionModel(); + return selection->hasSelection() ? selection->selectedRows().front().row() : -1; +} + +void CheckQueueTableView::reset() { + QTableView::reset(); + + QObject::connect(model(), &QAbstractTableModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) { + Q_UNUSED(parent) + Q_UNUSED(last) + m_dropRow = first; + }); +} + +void CheckQueueTableView::dropEvent(QDropEvent *e) { + if (e->source() != this || e->dropAction() != Qt::MoveAction) + return; + + int dragRow = selectedRow(); + + QTableView::dropEvent(e); // m_dropRow is set by inserted row + + if (m_dropRow > dragRow) + --m_dropRow; + + QMetaObject::invokeMethod(this, + std::bind(&CheckQueueTableView::selectRow, this, m_dropRow), + Qt::QueuedConnection); // Postpones selection +} + + + diff --git a/widgets/checkqueuetableview.h b/widgets/checkqueuetableview.h new file mode 100644 index 0000000..2482bca --- /dev/null +++ b/widgets/checkqueuetableview.h @@ -0,0 +1,19 @@ +#ifndef CHECKQUEUETABLEVIEW_H +#define CHECKQUEUETABLEVIEW_H + +#include +#include + +class CheckQueueTableView : public QTableView +{ + Q_OBJECT + int m_dropRow; +public: + CheckQueueTableView(QWidget *parent = nullptr); + Q_INVOKABLE int selectedRow() const; + void reset(); + void dropEvent(QDropEvent *e); + +}; + +#endif // CHECKQUEUETABLEVIEW_H diff --git a/widgets/outputcolumnmodel.cpp b/widgets/outputcolumnmodel.cpp index bbf84d6..bdd8d32 100644 --- a/widgets/outputcolumnmodel.cpp +++ b/widgets/outputcolumnmodel.cpp @@ -77,12 +77,6 @@ bool OutputColumnModel::removeRows(int position, int rows, const QModelIndex &pa return true; } -QStringList OutputColumnModel::mimeTypes() const { - QStringList types; - types << OutputColumnModel::MimeType; - return types; -} - bool OutputColumnModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int, int, const QModelIndex &) { if ( action != Qt::MoveAction || !data->hasFormat(OutputColumnModel::MimeType)) return false; diff --git a/widgets/outputcolumnmodel.h b/widgets/outputcolumnmodel.h index 23ad8be..ee1ed7e 100644 --- a/widgets/outputcolumnmodel.h +++ b/widgets/outputcolumnmodel.h @@ -28,7 +28,7 @@ public: bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override; - QStringList mimeTypes() const override; + // QStringList mimeTypes() const override; bool canDropMimeData(const QMimeData *, Qt::DropAction, int, int, const QModelIndex&); QMimeData* mimeData(const QModelIndexList &indexes) const; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);