From dbf3b74b0c8d857313c79d7f443b6fcba5b08c2e Mon Sep 17 00:00:00 2001 From: shuibing811 Date: Mon, 10 Feb 2025 16:22:33 +0800 Subject: [PATCH] --- --- CMakeLists.txt | 15 +++ LoggerWidget.cpp | 13 +++ LoggerWidget.h | 7 ++ LoggerWidget.ui | 8 +- RecordQuery.cpp | 216 ++++++++++++++++++++++++++++++++++++ RecordQuery.h | 48 ++++++++ RecordQuery.ui | 248 +++++++++++++++++++++++++++++------------- mainwindow.cpp | 49 ++++++++- mainwindow.h | 7 ++ res/first_page.png | Bin 541 -> 475 bytes res/last_page.png | Bin 554 -> 455 bytes res/next_page.png | Bin 767 -> 570 bytes res/previous_page.png | Bin 723 -> 562 bytes 13 files changed, 528 insertions(+), 83 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e2b7d4..06427b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,21 @@ endif() target_link_libraries(Satellite PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Charts Qt${QT_VERSION_MAJOR}::Network) +#spdlog +target_include_directories(Satellite PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdparty/spdlog_x64-windows/include) +target_link_libraries(Satellite PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdparty/spdlog_x64-windows/lib/spdlog.lib) +#fmt +target_include_directories(Satellite PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdparty/fmt_x64-windows/include) +target_link_libraries(Satellite PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdparty/fmt_x64-windows/lib/fmt.lib) +#sqlite3 +target_include_directories(Satellite PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdparty/sqlite3_x64-windows/include) +target_link_libraries(Satellite PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdparty/sqlite3_x64-windows/lib/sqlite3.lib) +#sqlite-orm +target_include_directories(Satellite PRIVATE ${CMAKE_CURRENT_LIST_DIR}/3rdparty/sqlite-orm_x64-windows/include) + +target_compile_definitions(Satellite PRIVATE _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING) + + # Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. # If you are developing for iOS or macOS you should consider setting an # explicit, fixed bundle identifier manually though. diff --git a/LoggerWidget.cpp b/LoggerWidget.cpp index eaf1c50..2333312 100644 --- a/LoggerWidget.cpp +++ b/LoggerWidget.cpp @@ -1,4 +1,7 @@ #include "LoggerWidget.h" +#include "spdlog/sinks/qt_sinks.h" +#include "spdlog/sinks/basic_file_sink.h" + #include "ui_LoggerWidget.h" LoggerWidget::LoggerWidget(QWidget *parent) @@ -6,9 +9,19 @@ LoggerWidget::LoggerWidget(QWidget *parent) , ui(new Ui::LoggerWidget) { ui->setupUi(this); + + m_logger = spdlog::qt_color_logger_mt("logger", ui->txtLogger, 500); + m_logger->set_level(spdlog::level::info); + } LoggerWidget::~LoggerWidget() { delete ui; } + +void LoggerWidget::on_btnClear_clicked() +{ + ui->txtLogger->clear(); +} + diff --git a/LoggerWidget.h b/LoggerWidget.h index 8c358d0..322c9d6 100644 --- a/LoggerWidget.h +++ b/LoggerWidget.h @@ -2,6 +2,8 @@ #define LOGGERWIDGET_H #include +#include "spdlog/spdlog.h" +#include namespace Ui { class LoggerWidget; @@ -15,8 +17,13 @@ public: explicit LoggerWidget(QWidget *parent = nullptr); ~LoggerWidget(); +private slots: + void on_btnClear_clicked(); + private: Ui::LoggerWidget *ui; + + std::shared_ptr m_logger; }; #endif // LOGGERWIDGET_H diff --git a/LoggerWidget.ui b/LoggerWidget.ui index dcd5e07..b4ebdbc 100644 --- a/LoggerWidget.ui +++ b/LoggerWidget.ui @@ -6,8 +6,8 @@ 0 0 - 584 - 495 + 1026 + 614 @@ -15,7 +15,7 @@ - + @@ -33,7 +33,7 @@ - + 清空 diff --git a/RecordQuery.cpp b/RecordQuery.cpp index 557ff37..c6dddb5 100644 --- a/RecordQuery.cpp +++ b/RecordQuery.cpp @@ -1,14 +1,230 @@ #include "RecordQuery.h" + +#include + #include "ui_RecordQuery.h" +using namespace sqlite_orm; + RecordQuery::RecordQuery(QWidget *parent) : QWidget(parent) , ui(new Ui::RecordQuery) { ui->setupUi(this); + InitDatabase(); + m_logger = spdlog::get("logger"); } RecordQuery::~RecordQuery() { delete ui; } +static auto& GetRecordStorage() { + std::string db_path = QCoreApplication::applicationDirPath().toStdString() + "/record.db"; + static auto storage = make_storage(db_path, + make_table("record", + make_column("id", &DbRecord::id, primary_key().autoincrement()), + make_column("begin_time", &DbRecord::beginTime), + make_column("channel", &DbRecord::channel), + make_column("type", &DbRecord::type), + make_column("direction", &DbRecord::direction), + make_column("number", &DbRecord::number), + make_column("content", &DbRecord::content)) + ); + return storage; +} +void RecordQuery::InitDatabase(){ + auto& storage = GetRecordStorage(); + storage.sync_schema(); +} +void RecordQuery::InsertRecord(const Record& record) { + DbRecord r; + r.beginTime = record.beginTime.toString("yyyy-MM-dd hh:mm:ss").toStdString(); + r.channel = int(record.channel); + r.type = int(record.type); + r.direction = int(record.type); + r.number = record.number.toStdString(); + r.content = record.content.toStdString(); + r.callDuration = record.callDuration; + try { + auto& storage = GetRecordStorage(); + storage.insert(r);//uint64_t id = + } + catch (std::exception& e) { + m_logger->error("向数据库插入记录数据错误: {}", e.what()); + } +} +void RecordQuery::InsertRecords(const std::vector& data){ + for(const auto& r : data){ + InsertRecord(r); + } +} + +void RecordQuery::UpdateRecordList(const std::vector& records){ + ui->tblRecordList->clear(); + QStringList column_labels; + column_labels << "开始时间" + << "通道" + << "通信类型" + << "方向" + << "号码" + << "通话时长" + << "消息内容"; + // 设置列表行数和列数 + int rowCount = records.size(); + ui->tblRecordList->setRowCount(rowCount); // 设置行数 + ui->tblRecordList->setColumnCount(column_labels.size()); // 设置列数 + // 设置水平表头 + ui->tblRecordList->setHorizontalHeaderLabels(column_labels); + // 设置垂直表头 + // self.ui.tblTestList.setVerticalHeaderLabels([str(v) for v in + // range(1,len(self.test_list)+1)]) 设置列宽 + int column_index = 0; + ui->tblRecordList->setColumnWidth(column_index, 180); // 开始时间 + column_index = column_index + 1; + ui->tblRecordList->setColumnWidth(column_index, 60); // 通道 + column_index = column_index + 1; + ui->tblRecordList->setColumnWidth(column_index, 80); // 通信类型 + column_index = column_index + 1; + ui->tblRecordList->setColumnWidth(column_index, 60); // 方向 + column_index = column_index + 1; + ui->tblRecordList->setColumnWidth(column_index, 120); // 号码 + column_index = column_index + 1; + ui->tblRecordList->setColumnWidth(column_index, 80); // 通话时长 + column_index = column_index + 1; + ui->tblRecordList->setColumnWidth(column_index, 400); // 消息内容 + column_index = column_index + 1; + + //插入所有项 + int rowIndex = 0; + for (int index = 0; index < records.size(); index++) { + const DbRecord& r = records[index]; + int column_index = 0; + // 开始时间 + auto item = new QTableWidgetItem(QString::fromStdString(r.beginTime)); + item->setTextAlignment(Qt::AlignCenter); + ui->tblRecordList->setItem(rowIndex, column_index, item); + column_index = column_index + 1; + // 通道 + item = new QTableWidgetItem(r.channel == int(Channel::Uhf)?"UHF":(r.channel== int(Channel::S)?"S":"北斗")); + item->setTextAlignment(Qt::AlignCenter); + ui->tblRecordList->setItem(rowIndex, column_index, item); + column_index = column_index + 1; + // 通信类型 + item = new QTableWidgetItem(r.type == int(CommType::Call) ? "通话" : "短信息"); + item->setTextAlignment(Qt::AlignCenter); + ui->tblRecordList->setItem(rowIndex, column_index, item); + column_index = column_index + 1; + // 方向 + item = new QTableWidgetItem(r.direction==int(CommDirection::Receive)?"接收":"发送"); + item->setTextAlignment(Qt::AlignCenter); + ui->tblRecordList->setItem(rowIndex, column_index, item); + column_index = column_index + 1; + // 号码 + item = new QTableWidgetItem(QString::fromStdString(r.number)); + item->setTextAlignment(Qt::AlignCenter); + ui->tblRecordList->setItem(rowIndex, column_index, item); + column_index = column_index + 1; + // 通话时长 + item = new QTableWidgetItem(QString::asprintf("%d s",r.callDuration)); + item->setTextAlignment(Qt::AlignCenter); + ui->tblRecordList->setItem(rowIndex, column_index, item); + column_index = column_index + 1; + // 消息内容 + item = new QTableWidgetItem(QString::fromStdString(r.content)); + ui->tblRecordList->setItem(rowIndex, column_index, item); + column_index = column_index + 1; + + rowIndex++; + } +} +//-------------------------------------------------------------------------------------- +// 槽函数 +//-------------------------------------------------------------------------------------- + +void RecordQuery::on_btnQuery_clicked() +{ + std::string begin_date_time = ui->dteBeginTime->dateTime().toString("yyyy-MM-dd hh:mm:ss").toStdString(); + std::string end_date_time = ui->dteEndTime->dateTime().toString("yyyy-MM-dd hh:mm:ss").toStdString(); + auto& storage = GetRecordStorage(); + auto timeCond = c(&DbRecord::beginTime) >= begin_date_time and c(&DbRecord::beginTime) <= end_date_time; + std::vector v = storage.get_all();//where(timeCond) + + UpdateRecordList(v); +} + +void RecordQuery::on_btnFirstPage_clicked() +{ + +} + + +void RecordQuery::on_btnPreviousPage_clicked() +{ + +} + + +void RecordQuery::on_btnNextPage_clicked() +{ + +} + + +void RecordQuery::on_btnLastPage_2_clicked() +{ + +} + + +void RecordQuery::on_spbGoToPage_valueChanged(int arg1) +{ + +} + + +void RecordQuery::on_tblRecordList_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous) +{ + int currentRow = ui->tblRecordList->currentRow(); + if(currentRow == -1){ + ui->txtCurrentItemMessageContent->clear(); + }else{ + ui->txtCurrentItemMessageContent->setText(ui->tblRecordList->item(currentRow,6)->text());//NOTE:注意此处的列数 + } +} + + +void RecordQuery::on_chbTime_stateChanged(int arg1) +{ + ui->dteBeginTime->setEnabled(arg1 == Qt::CheckState::Checked); + ui->dteEndTime->setEnabled(arg1 == Qt::CheckState::Checked); +} + + +void RecordQuery::on_chbChannel_stateChanged(int arg1) +{ + ui->chbChannelUHF->setEnabled(arg1 == Qt::CheckState::Checked); + ui->chbChannelS->setEnabled(arg1 == Qt::CheckState::Checked); + ui->chbChannelBeiDou->setEnabled(arg1 == Qt::CheckState::Checked); +} + + +void RecordQuery::on_chbType_stateChanged(int arg1) +{ + ui->chbCall->setEnabled(arg1 == Qt::CheckState::Checked); + ui->chbMessage->setEnabled(arg1 == Qt::CheckState::Checked); +} + + +void RecordQuery::on_chbDirection_stateChanged(int arg1) +{ + ui->chbSend->setEnabled(arg1 == Qt::CheckState::Checked); + ui->chbReceive->setEnabled(arg1 == Qt::CheckState::Checked); +} + + +void RecordQuery::on_chbNumber_stateChanged(int arg1) +{ + ui->cmbNumber->setEnabled(arg1 == Qt::CheckState::Checked); +} + diff --git a/RecordQuery.h b/RecordQuery.h index a6239ab..b1f4530 100644 --- a/RecordQuery.h +++ b/RecordQuery.h @@ -3,6 +3,11 @@ #include #include +#include +#include + +#include +#include "spdlog/spdlog.h" enum class Channel{ Uhf, @@ -28,6 +33,17 @@ struct Record{ QString number; QString content; bool isUnread; + uint32_t callDuration;//s +}; +struct DbRecord{ + uint64_t id; + std::string beginTime; + int channel; + int type; + int direction; + std::string number; + std::string content; + uint32_t callDuration;//s }; @@ -35,6 +51,7 @@ namespace Ui { class RecordQuery; } +class QTableWidgetItem; class RecordQuery : public QWidget { Q_OBJECT @@ -42,9 +59,40 @@ class RecordQuery : public QWidget public: explicit RecordQuery(QWidget *parent = nullptr); ~RecordQuery(); + void InsertRecord(const Record& data); + void InsertRecords(const std::vector& data); +private slots: + void on_btnQuery_clicked(); + + void on_btnFirstPage_clicked(); + + void on_btnPreviousPage_clicked(); + + void on_btnNextPage_clicked(); + + void on_btnLastPage_2_clicked(); + + void on_spbGoToPage_valueChanged(int arg1); + + void on_tblRecordList_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous); + + void on_chbTime_stateChanged(int arg1); + + void on_chbChannel_stateChanged(int arg1); + + void on_chbType_stateChanged(int arg1); + + void on_chbDirection_stateChanged(int arg1); + + void on_chbNumber_stateChanged(int arg1); private: Ui::RecordQuery *ui; + + std::shared_ptr m_logger; + + void InitDatabase(); + void UpdateRecordList(const std::vector& r); }; #endif // RECORDQUERY_H diff --git a/RecordQuery.ui b/RecordQuery.ui index 2f7a481..961b681 100644 --- a/RecordQuery.ui +++ b/RecordQuery.ui @@ -19,7 +19,7 @@ 记录查询 - + @@ -31,63 +31,7 @@ - - - - 通话 - - - - - - - 接收 - - - - - - - 北斗 - - - - - - - 发送 - - - - - - - 开始时间: - - - - - - - 短消息 - - - - - - - 方向: - - - - - - - 通道: - - - - + 结束时间: @@ -95,41 +39,166 @@ - + 类型: + + false + + + + + + + + + + 时间: + + + false + + + + + + + 通道: + + + false + + + + + + + false + + + + + + + false + + + + + + + 方向: + + + false + - + 号码: + + false + - - + + - S + 开始时间: + + false + UHF - - + + + + false + + + S + + + false + + - - + + + + false + + + 北斗 + + - + + + + false + + + 通话 + + + + + + + false + + + 短消息 + + + true + + + + + + + false + + + 发送 + + + false + + + + + + + false + + + 接收 + + + true + + + + + + false + true @@ -143,6 +212,30 @@ + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 查询 + + + + + @@ -154,6 +247,15 @@ + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + 开始时间 @@ -207,7 +309,7 @@ - + ... @@ -227,7 +329,7 @@ - + ... @@ -244,7 +346,7 @@ - + 80 @@ -254,7 +356,7 @@ - + ... @@ -271,7 +373,7 @@ - + ... diff --git a/mainwindow.cpp b/mainwindow.cpp index 7285620..b588658 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -23,16 +23,24 @@ qreal get_rand(qreal maxValue) { return rand() * 1.0 / RAND_MAX * maxValue; } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); + + m_loggerWidget = std::make_shared();//需要在RecordQuery创建前 + m_loggerWidget->hide(); + + m_recordQueryWidget = std::make_shared(); + m_recordQueryWidget->hide(); + + createBeiDouSignalStrengthChart(); createAzimuthMapChart(); ui->btnCallReject->hide(); - m_udpSocket = std::make_shared(); - m_recordQueryWidget = std::make_shared(); - m_recordQueryWidget->hide(); - m_loggerWidget = std::make_shared(); - m_loggerWidget->hide(); + m_udpSocket = std::make_shared(); + m_udpServerSocket = std::make_shared(); + connect(&*m_udpServerSocket, &QUdpSocket::readyRead, this, &MainWindow::readPendingDatagrams); + + m_logger = spdlog::get("logger"); test(); } @@ -47,6 +55,23 @@ void MainWindow::keyPressEvent(QKeyEvent* event){ } } +void MainWindow::readPendingDatagrams(){ + while (m_udpServerSocket->hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(m_udpServerSocket->pendingDatagramSize()); + + QHostAddress sender; + quint16 senderPort; + + // 读取数据报 + m_udpServerSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); + + //TODO:处理数据包 + //qDebug() << "Received datagram from" << sender.toString() << ":" << senderPort; + //qDebug() << "Data:" << datagram; + } +} + void MainWindow::test() { // 随机生成信号能量数据 @@ -70,6 +95,7 @@ void MainWindow::test() { r1.number = "10001"; r1.content = "地阿斯顿发射点发大水发大水发"; r1.isUnread = true; + r1.callDuration = 0; Record r2; r2.beginTime = QDateTime::currentDateTime(); @@ -79,6 +105,7 @@ void MainWindow::test() { r2.number = "10002"; r2.content = "dsfgdsfgsdfgsdfgdfsgdsfgdsfg"; r2.isUnread = false; + r1.callDuration = 0; Record r3; r3.beginTime = QDateTime::currentDateTime(); @@ -88,11 +115,17 @@ void MainWindow::test() { r3.number = "10003"; r3.content = "fdghfgdhfghdfghdfghdfghf"; r3.isUnread = true; + r1.callDuration = 30; m_msgList.push_back(r1); m_msgList.push_back(r2); m_msgList.push_back(r3); UpdateMessageList(); + + m_recordQueryWidget->InsertRecords(m_msgList); + + + m_logger->info("{}---{}---{}",1,"22",30); } //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- @@ -204,7 +237,7 @@ void MainWindow::UpdateMessageList() { //插入所有项 int rowIndex = 0; for (int index = 0; index < m_msgList.size(); index++) { - Record r = m_msgList[index]; + Record& r = m_msgList[index]; int column_index = 0; // 读取状态 auto item = new QTableWidgetItem(r.isUnread ? "未读" : "已读"); @@ -335,6 +368,10 @@ void MainWindow::on_tblMessageList_currentItemChanged(QTableWidgetItem *current, ui->txtCurrentItemMessageContent->clear(); }else{ ui->txtCurrentItemMessageContent->setText(m_msgList[currentRow].content); + if(m_msgList[currentRow].isUnread){ + m_msgList[currentRow].isUnread = true; + ui->tblMessageList->item(currentRow,0)->setText("已读"); + } } } diff --git a/mainwindow.h b/mainwindow.h index d513ed6..6600f7a 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -9,6 +9,8 @@ #include "RecordQuery.h" #include "LoggerWidget.h" +#include "spdlog/spdlog.h" + QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; @@ -53,6 +55,8 @@ private slots: void on_btnRecordQuery_clicked(); + void readPendingDatagrams(); + void on_tblMessageList_currentItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous); protected: void keyPressEvent(QKeyEvent* event) override; @@ -80,6 +84,7 @@ private: Ui::MainWindow *ui; CallStatus m_callStatus = CallStatus::Idle; std::shared_ptr m_udpSocket; + std::shared_ptr m_udpServerSocket; std::shared_ptr m_recordQueryWidget; std::shared_ptr m_loggerWidget; QChart* m_signalStrengthChart; @@ -87,6 +92,8 @@ private: QBarCategoryAxis* m_signalStrengthChartXAxis; QValueAxis* m_signalStrengthChartYAxis; + std::shared_ptr m_logger; + std::vector m_msgList; }; #endif // MAINWINDOW_H diff --git a/res/first_page.png b/res/first_page.png index b118b2e5692d5d9688021746a8dff2114a07b888..e37d2311974cb992bfb40b414d68562e726c8e94 100644 GIT binary patch delta 436 zcmV;l0Zaa!1lt3UF@KdwL_t(oN9~uvN*hrWhQB}1RdLhOg}Vx&1t|!PP$-I$*C^;N zkD|}e?z)t2MY7OE&88FtapTJ9LLvxJqC~ps{AEU?5+-_QLNZ-sb~D_$-*?YF_gszu zRvXB&AKALDt|)2C0%IsXZLFn@E&s_ zFo{6c2uuTKPbRHAg>@a8*uesl%EndO8$z8lfZ?OWJoo_XSBONg07{Bg#gR89wfZNw zm=k=u`@W57j!nSQ2jR~#aFF02nV}5*AE1so4^7J4$8caN7FYi3-L!I@CY3=z@4(Bz56tD$mk~|Oc1RR5 z2=Ky@-#ojrQC-I&tI0DaJOX)U5BO&y@E=5Ax4SmBrY*ETCq%pW<}{XyJ7lAoaW$z) eQ;Lt{_WU2{7YY!Tbft9w0000TtXn-8>4&;ED17tba+EE9Lh5$(+5ET_Axop|8=lBw>p`jt~`Sa&L z6PvJ!c0hG?wMt7%i<5PkzK^!JIjBjFOX+O;)d7eSaw|EbIg^jv(3r3l=Oe zj*pKwj){rcvUKUvXXu7cojO%ND=W(!Bwkutx~He7=N{c0Fk!+3o&5ZKE0Be_9Ei&? z6o)`ZM~7B$8xeAaPi{B zkC!iBjvg=vfU@C$M*+z91DXh7aSqFdfB*jdmywaNgRa>S<_K6p^ySN!AMx?=JJzjR z_lnp=Nl`g~YzeF=e*OCOr^3R*L)eOIY>pz@0SyfeYH#)R^$rdW4yz9yJop}8WpwoD z(R(&FHhYK(1)?1wDk{qA=H@0!$Y(UGj*0ONIckUw0dfo{M$f1N21N+8(XmsCo-E!D siq0v>RiL_Ek&d0zcMJ>+_d(rr0Hx&@lXUWrq5uE@07*qoM6N<$g4&?w^Z)<= diff --git a/res/last_page.png b/res/last_page.png index 8462c1bf120bed0c5248f175580a141b1c9aeb36..46037690166e24dfbe8c3fa6cd58debfb6e407ac 100644 GIT binary patch delta 416 zcmV;R0bl;A1jhrAF@J$cL_t(oN9~t8F9cx}hM#v^g}78I3W>xmZgC5VUME^QKZV~w zr|>t#S|JiE5#kmJmC91Ba$aU;R}f((yWccB&CDc|?|tUH=bZ050$44S*D&3fj`+Rd zmnb0~w&z;5l!!-+HC*&3fgDAx6kazJxjhR2=k%=Ae0ehq@qemj6QCJ^|3yHwVP1Yq zTH9Tc9&1hP{{Rdkrfp6Hr#}iPX=F>7qxbuHEf6&WB!8CEV!Wu~Smx}zz^8ADm||T4 z(#711w2(ZRL&3#okpKXRbRW`u%Hsm)stOSZ01*AaY$~ih?Aw&*!=^@A@Ws_3#8gFi zSLiM@Kpcq?V1FXDgRS5IhEa*}iWNJKqSi{KG~NP>VZmf378nL3X41$zFY6{;5=wpG zqznxQGM53%P`$`$BSMF=!!@GJTX7ZOkIkqJSjOH5UDtmvh<7_v`fXw5)al4iwTLHu zw0S6PMj-nL?3dSr4W-{pLzJ=~PGhKeLN@9NHKZm(DH*TtXn-8>4&;ED17tba+EE9Lh5$(+U~g|Pc<|uC_xKWRbab@T(xpqE z5u31ycEE%Q6Ljaqo$_D4#e*0=r~+iS$Q4i2)Z}`WLbB2w{}TMi8YAb*w}cWp`qaxh)p*K zfGnFld9q$kPJfOCh+SD(xv!(6;|^UN0J3=Y?AeAXDJiDlGM$le7Tp~%XU-g>+^$h3e9o>a*CfaWr}`wcD6Z4tg5PNUweBy{i2|^w^ygAsK^Rrc~eu< z!TS38n{-Qrt*xyZKp%s%VP|LOk&23nYjn$owY9aXKztvA3!;e=Cmt&(D4<_KG~h)s z%mMf+BT#L5;J|^PKIDxmfx5(D5Ti+04Ik@~7ZIq6N zz+ec0Had1n(KE-}LD4w{xeC-hSEOSn^&JBP!+lWq900PL1C!*7yd(es002ovPDHLk FV1kja>|6i< diff --git a/res/next_page.png b/res/next_page.png index 1b998f76829bc45aca5431c3a43185f41f11dea9..fe23ac17a7c84363704a27e7069df89bd369d272 100644 GIT binary patch delta 533 zcmV+w0_y$$1-b-~FnDv%+7`Xz@UPW6PI*-Y=6?ymFIxpnXD zD}ZXPIgJ^{l*4`DmuMh9J-RtCqbT0ua^a$X6DYBwd$q3*)PM4cUI4hH&#KNfANE4L zt=R|YJCN*v?IB4!zZ_U`9P?#Ahd2#Du|VAcK4PBj3Z6q;vso6bb_$UO5LfqIq+_5> z?Aho@NmKC&yp(y}2}C-8R7S2~jshE)8h8}xc7!l>!FpbZbbxH9gOVn}1Bi86)WKs& zw=$Dl18wAi$bSV87ZyVhAr67puoe+#fE$^xX0bO@A@T!cE1$Bc2o3?;tOb z#=tQR4GS`FQ^jM>r2dc?PAU{4bOieyXdX0*oE0KemtABb>Uk+<`sZfU?gd?IM*nEX zWlSSa%zuA3`3&qwhs_y1H7mrKQCYWN}|#-?8H2;wz*&f@lYT3=R$s7N0X`j*EbR0Qbg?8?OWe z1RQH_Zq}%&seiEp$#-;g9ImXayiSTENOAzk;Mmw$sp-?FyKr-JbF5yy`cha}*op4$ zZtaqi5^IorV`JlihK7b)#5#gx2Y{>2v^3dClP0;cv$L}-S+eAOY;5f5$&)AR<>cg8 zfaEJHEBAGDblf4v5o9?4WN<-2fkJO@uL~0s6H`n~%zqY87M(qNwqZ(2iYd5^XJnj3 zQwQYd=PQC7z|73dSXx@Tr>Cdq-kdpejFOX+O+Xgw>gq1Na^=cbLXIFS1k%&f)bC88qRaN`i+uLaq1#xk4Qq!hQbLHmdW?!>r&85)L z&=bAAy?;7IMMYL1%bS{-4%XM#-z4O7m^?`#5E2q1K5N!2S3yBRu1%XZUG?|(Kib;b zssZ#dI2(3$b{?sysJKR~VVM@0EIvs9XqAy$?@%==$wMv7z_*yigfIxzGGluxDV={0|1fn2b0Nsrb_?- N002ovPDHLkV1nqlVYvVR diff --git a/res/previous_page.png b/res/previous_page.png index 4b2fda0e6511d56224932476a1428450e4d182ce..cbdd86627a895cf4d0a383037fc2cef0d335fd59 100644 GIT binary patch delta 525 zcmV+o0`mRS1+oN?Fn-3pM=)6MSJ+keWve-Z-D^hryP`*$V* zub5MTX#zthAhE;ddtJ!#9;`31h2300%mItQXUzQqXgDKr4p_rUk7eWfTS0ru3z>9ZM6F7*}0q?5d;FCxT;m_cK&Bs`H zs=!PM-wnLPynjGC4!%49`XSc46nKh2TjHu=AL0!;PmPaBx)Oj=m)vWq@OXiYYnWN^ zTdYR|D9a`AIp$Rpc(i~ruG<^{)-kI{t)DVCN@(bUkqQ89ZZ9R%preS&ll}&XSi4AgA1}9KoM$w(oT%Z!k*h$W}I;F8)$0?Qe zk9UVpU`Uavz<-dylm4x-Ev0|fi`bm}a#~0g8?w>SaV@D~DaG%L_524~IPn3N4D74` P0000hsl7)OxhfV8wU z*{M^fy0NgZFfUxV@N7at!r7@)r|M^AWtoF4EiEnG)6>&)k0uU?i;I(*F=K`sCnqNx z%<|seUY(+%A}f%EF)=Y)mM&fTj2O#FE(gNG!X)O*nd8dK%gYI~yuH0$v!bHH7Gzjz zYU;K*bLKoD)_-!69N_2YCpv%rd{-eMA)dvH7oUrZi#yZM(4badU+(}isJy& zT~aJ3+5s*uF2YNfE_D$T6XV~wapRSMfPiBa6%{J&?d?t=LpnM-4p&xIUMJOZq8)Jc z>Qx^tEiI{i`}W;-c6Qzm()j=Xe^3?9Fk{AyQ<<5W7k|jHoM;E!x^>H6RaI5|=+UG1 zY;0`y=;-KhU%q@f8e{-0Ezr~fZf*VWb8Q{o7s9RRWfIUB+p(b3VNRbF0h1CpOOapLiUf`ZGW zLi0q2a{z|H V3zH$6@*w~K002ovPDHLkV1iaIMF0Q*