/* Copyright © 2015 Gluzskiy Alexandr (sss) This file is part of Unknown Download Manager (UDM). UDM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. UDM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with UDM. If not, see . */ #include "udm_main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "connect_widget.h" #include "client_session.h" #include "downloads_model.h" #include "filters_model.h" #include "download_add_widget.h" udm_main::udm_main(QWidget *parent) : QMainWindow(parent) { io_service_ = new boost::asio::io_service; thread_client_session = new QThread(this); session = nullptr; auto menu_main = this->menuBar()->addMenu(tr("Main")); auto menu_connect = menu_main->addAction(tr("Connect")); connect(menu_connect, SIGNAL(triggered()), this, SLOT(show_connect_widget())); auto action_exit = menu_main->addAction(tr("Exit")); connect(action_exit, SIGNAL(triggered(bool)), this, SLOT(close())); //TODO: something better lbl_state = new QLabel; lbl_state->setText(tr("State") + ": " + tr("Offline")); this->statusBar()->addPermanentWidget(lbl_state); tbl_downloads = new QTableView; tbl_downloads->setShowGrid(false); tbl_downloads->setUpdatesEnabled(true); mdl_downloads = new downloads_model(downloads); tbl_downloads->setModel(mdl_downloads); tbl_downloads->setContextMenuPolicy(Qt::CustomContextMenu); tbl_downloads->setSelectionBehavior(QAbstractItemView::SelectRows); tbl_downloads->setSortingEnabled(true); tbl_downloads->horizontalHeader()->setHighlightSections(false); tbl_downloads->horizontalHeader()->setMovable(true); tbl_downloads->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); tbl_downloads->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); connect(tbl_downloads, SIGNAL(customContextMenuRequested(QPoint)), SLOT(downloads_menu(QPoint))); connect(tbl_downloads->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(downloads_selection_changed(QItemSelection,QItemSelection))); create_menu(); tree_filters = new QTreeView; mdl_filters = new filters_model; tree_filters->setModel(mdl_filters); tree_filters->header()->hide(); tree_filters->setSelectionMode(QAbstractItemView::ExtendedSelection); tree_filters->setSelectionBehavior(QAbstractItemView::SelectRows); tree_filters->setIndentation(10); spl_hor = new QSplitter(Qt::Horizontal); spl_hor->addWidget(tree_filters); spl_hor->addWidget(tbl_downloads); spl_hor->setStretchFactor(0, 2); spl_hor->setStretchFactor(1, 10); tabs_info = new QTabWidget; spl_vert = new QSplitter(Qt::Vertical); spl_vert->addWidget(spl_hor); spl_vert->addWidget(tabs_info); spl_vert->setStretchFactor(0, 3); spl_vert->setStretchFactor(1, 1); setCentralWidget(spl_vert); create_buttons(); } void udm_main::create_menu() { download_menu = new QMenu; QAction *a = download_menu->addAction(tr("Start")); connect(a, SIGNAL(triggered(bool)), this, SLOT(download_start())); a = download_menu->addAction(tr("Stop")); connect(a, SIGNAL(triggered(bool)), this, SLOT(download_stop())); a = download_menu->addAction(tr("Delete")); connect(a, SIGNAL(triggered(bool)), this, SLOT(download_delete())); //TODO: dynamic menu } void udm_main::downloads_menu(const QPoint &/*pos*/) { download_menu->popup(QCursor::pos()); } void udm_main::download_start() { client_msg msg; msg.set_auth_token(auth_token); for(auto i : tbl_downloads->selectionModel()->selectedRows()) { msg.set_type(CLIENT_MSG_TYPE::CLIENT_DOWNLOAD_START); client_download_start_request *r = msg.add_download_start_request(); r->set_download_id(downloads[i.row()].id()); } session->send_message(msg); } void udm_main::download_stop() { client_msg msg; msg.set_auth_token(auth_token); for(auto i : tbl_downloads->selectionModel()->selectedRows()) { msg.set_type(CLIENT_MSG_TYPE::CLIENT_DOWNLOAD_STOP); client_download_stop_request *r = msg.add_download_stop_request(); r->set_download_id(downloads[i.row()].id()); } session->send_message(msg); } void udm_main::download_delete() { client_msg msg; msg.set_auth_token(auth_token); for(auto i : tbl_downloads->selectionModel()->selectedRows()) { msg.set_type(CLIENT_MSG_TYPE::CLIENT_DOWNLOAD_DELETE); client_download_delete_request *r = msg.add_download_delete_request(); r->set_download_id(downloads[i.row()].id()); } session->send_message(msg); } void udm_main::downloads_selection_changed(QItemSelection s, QItemSelection) { if(s.size()) { btn_start->setEnabled(true); btn_stop->setEnabled(true); btn_del->setEnabled(true); } else { btn_start->setEnabled(false); btn_stop->setEnabled(false); btn_del->setEnabled(false); } } udm_main::~udm_main() { if(thread_client_session) { thread_client_session->quit(); thread_client_session->deleteLater(); //delete thread_client_session; } //TODO: clear all remaining data } void udm_main::show_connect_widget() { connect_widget *w = new connect_widget; connect(w, SIGNAL(client_connect(QString&,QString&,int)), this, SLOT(client_connect(QString&,QString&,int))); //TODO: make connection to other slots related to connection //TODO: destroy widget on close w->show(); } void udm_main::client_pre_connect_init() { if(session) { session->moveToThread(QApplication::instance()->thread()); thread_client_session->quit(); io_service_->stop(); delete session; } session = new client_session(0, io_service_); //parent must be 0 here connect(session, SIGNAL(server_message_received(server_msg)), this, SLOT(server_message_received(server_msg))); connect(session, SIGNAL(client_connected(bool,QString)), this, SLOT(client_connected(bool,QString))); connect(session, SIGNAL(client_disconnected()), this, SLOT(client_disconnected())); connect(session, SIGNAL(terminate_thread()), thread_client_session, SLOT(quit())); } void udm_main::client_connect_finalize() { session->moveToThread(thread_client_session); thread_client_session->start(); } void udm_main::client_connect(QString &host, QString &password, int port) { client_pre_connect_init(); connect(this, SIGNAL(connect_signal(QString,QString,int)), session, SLOT(client_connect(QString,QString,int))); client_connect_finalize(); emit connect_signal(host, password, port); } void udm_main::client_connect_ssl(QString &host, QString &password, int port, QString &ssl_ca, QString &ssl_crt, QString &ssl_key) { client_pre_connect_init(); connect(this, SIGNAL(connect_signal(QString,QString,int,QString,QString)), session, SLOT(client_connect(QString,QString,int,QString,QString))); client_connect_finalize(); emit connect_signal_ssl(host, password, port, ssl_ca, ssl_crt, ssl_key); } void udm_main::server_message_received(server_msg msg) { BOOST_LOG_TRIVIAL(trace)<<"recieved server message\n"<setText(tr("State") + ": " + tr("Connected") + ", " + tr("Authenticated")); //TODO: subscript to events //request modules and settings here { client_msg msg; msg.set_type(CLIENT_MSG_TYPE::CLIENT_CORE_INFO_REQUEST); msg.set_auth_token(auth_token); session->send_message(msg); } { client_msg msg; msg.set_type(CLIENT_MSG_TYPE::CLIENT_MODULES_REQUEST); msg.set_auth_token(auth_token); session->send_message(msg); } { client_msg msg; msg.set_type(CLIENT_MSG_TYPE::CLIENT_DOWNLOADS_LIST_REQUEST); msg.set_auth_token(auth_token); session->send_message(msg); } //subscript to needed events { client_msg msg; msg.set_type(CLIENT_MSG_TYPE::CLIENT_SUBSCRIPTION_REQUEST); msg.set_auth_token(auth_token); //subscript to download added event auto s = msg.add_subscription_request(); s->set_type(SUBSCRIPTION_TYPE::ST_DOWNLOAD_ADDED); //fire event than new download added s->set_mode(SUBSCRIPTION_MODE::SM_EVENT); //this type of event support only "event" mode s->set_module_name(""); //we need this event for all installed modules s = msg.add_subscription_request(); s->set_type(SUBSCRIPTION_TYPE::ST_DOWNLOAD_DELETED); //fire event than new download deleted s->set_mode(SUBSCRIPTION_MODE::SM_EVENT); //this type of event support only "event" mode s->set_module_name(""); //we need this event for all installed modules s = msg.add_subscription_request(); s->set_type(SUBSCRIPTION_TYPE::ST_DOWNLOAD_STATE_CHANGE); //fire event than download state changed s->set_mode(SUBSCRIPTION_MODE::SM_EVENT); s->set_module_name(""); s->mutable_download_state_change()->add_states(SDS_STARTED); //subscript to download started event s->mutable_download_state_change()->add_states(SDS_STOPPED); //subscript to download stopped event s->mutable_download_state_change()->add_states(SDS_DELETED); //subscript to download deleted event s->mutable_download_state_change()->add_states(SDS_COMPLETED); //subscript to download completed event session->send_message(msg); } btn_add->setEnabled(true); //we can add downloads now } else { QMessageBox msg_box; msg_box.setText(tr("Authentication failed with error:") + "\n" + msg.auth_reply().error_description().c_str()); msg_box.exec(); lbl_state->setText(tr("State") + ": " + tr("auth error")); } } break; case SERVER_MSG_TYPE::SERVER_CORE_INFO_REPLY: server_info = msg.server_core_info_reply(); break; case SERVER_MSG_TYPE::SERVER_MODULES_REPLY: { modules.clear(); //TODO: something better for(auto i : msg.server_modules_reply()) modules.push_back(i); } break; case SERVER_MSG_TYPE::SERVER_DOWNLOADS_LIST_REPLY: { if(!downloads.empty()) { mdl_downloads->removeRows(0, downloads.size()); downloads.clear(); //TODO: something better } for(auto i : msg.downloads()) downloads.push_back(i.download()); //TODO: something better than whole list rebuild mdl_downloads->insertRows(0, downloads.size()); mdl_downloads->refresh(); } break; case SERVER_MSG_TYPE::SERVER_DOWNLOAD_INFO_REPLY: { bool found = false; for(auto i : downloads) { if(i.id() == msg.download().download().id()) { //TODO: merge download, not replace ? i = msg.download().download(); found = true; break; } } if(!found) downloads.push_back(msg.download().download()); mdl_downloads->insertRows(downloads.size(), 1); mdl_downloads->refresh(); } break; case SERVER_MSG_TYPE::SERVER_DOWNLOAD_STATE_CHANGE: { for(auto i : msg.download_state_changes()) { switch(i.state()) { case SUBSCRIPTION_DOWNLOAD_STATE::SDS_STARTED: for(auto d = downloads.begin(), end = downloads.end(); d != end; ++d) { if(d->id() == i.download_id()) { d->set_state(1); //TODO: use human readable enum for states ? mdl_downloads->refresh(); //TODO: udapte only changed item break; } } break; case SUBSCRIPTION_DOWNLOAD_STATE::SDS_STOPPED: for(auto d = downloads.begin(), end = downloads.end(); d != end; ++d) { if(d->id() == i.download_id()) { d->set_state(0); //TODO: use human readable enum for states ? mdl_downloads->refresh(); //TODO: udapte only changed item break; } } break; case SUBSCRIPTION_DOWNLOAD_STATE::SDS_COMPLETED: for(auto d = downloads.begin(), end = downloads.end(); d != end; ++d) { if(d->id() == i.download_id()) { d->set_state(2); //TODO: use human readable enum for states ? mdl_downloads->refresh(); //TODO: udapte only changed item break; } } break; case SUBSCRIPTION_DOWNLOAD_STATE::SDS_DELETED: { bool found = false; unsigned int pos = 0; for(auto d = downloads.begin(), end = downloads.end(); d != end; ++d, ++pos) { if(d->id() == i.download_id()) { found = true; downloads.erase(d); mdl_downloads->removeRows(pos, 1); break; } if(!found) BOOST_LOG_TRIVIAL(error)<<"error: deleted download which is not exist\n"<setEnabled(false); connect(btn_start, SIGNAL(clicked(bool)), this, SLOT(btn_start_clicked())); btn_stop = new QPushButton(this); btn_stop->setEnabled(false); connect(btn_stop, SIGNAL(clicked(bool)), this, SLOT(btn_stop_clicked())); btn_del = new QPushButton(this); btn_del->setEnabled(false); connect(btn_del, SIGNAL(clicked(bool)), this, SLOT(btn_del_clicked())); btn_add = new QPushButton(this); btn_add->setEnabled(false); connect(btn_add, SIGNAL(clicked(bool)), this, SLOT(btn_add_clicked())); button_bar = new QToolBar(this); button_bar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); //TODO: support dynamic action buttons for modules button_bar->addWidget(btn_start); button_bar->addWidget(btn_stop); button_bar->addSeparator(); button_bar->addWidget(btn_del); button_bar->addWidget(btn_add); btn_start->setText(tr("Start")); btn_start->setToolTip(tr("Start download")); btn_start->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); btn_stop->setText(tr("Stop")); btn_stop->setToolTip(tr("Stop download")); btn_stop->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); btn_del->setText(tr("Delete")); btn_del->setToolTip(tr("Delete download")); btn_del->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); btn_add->setText(tr("Add")); btn_add->setToolTip(tr("Add download")); btn_add->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); this->addToolBar(button_bar); } void udm_main::btn_start_clicked() { download_start(); } void udm_main::btn_stop_clicked() { download_stop(); } void udm_main::btn_add_clicked() { download_add_widget *w = new download_add_widget(modules); connect(w, SIGNAL(got_download_settings(std::string, std::map)), this, SLOT(got_download_settings(std::string, std::map))); w->show(); } void udm_main::got_download_settings(std::string module_name, std::map settings) { if(!settings.empty()) { client_msg msg; msg.set_auth_token(auth_token); msg.set_type(CLIENT_MSG_TYPE::CLIENT_DOWNLOAD_ADD); client_download_add_request *r = msg.mutable_download_add_request(); r->set_module_name(module_name); for(auto i : settings) { int_string_pair *p = r->add_params(); p->set_id(i.first); p->set_value(i.second); } session->send_message(msg); } } void udm_main::btn_del_clicked() { download_delete(); } void udm_main::client_connected(bool success, QString error_text) { if(!success) { QMessageBox msg; msg.setText(tr("client connection error with error:") + error_text); msg.exec(); lbl_state->setText(tr("State") + ": " + tr("Connection error")); btn_add->setEnabled(false); btn_del->setEnabled(false); btn_start->setEnabled(false); btn_stop->setEnabled(false); } else { lbl_state->setText(tr("State") + ": " + tr("Connected")); } /* QMessageBox msg; msg.setText(success?"client connected with message:":"client connection error with error:" + error_text); msg.exec(); */ } void udm_main::client_disconnected() { btn_add->setEnabled(false); btn_del->setEnabled(false); btn_start->setEnabled(false); btn_stop->setEnabled(false); //TODO: disable all actions which require alive connection lbl_state->setText(tr("State") + ": " + tr("Disconnected")); }