#include "mainwindow.h" #include "ui_mainwindow.h" #include #include #include #include #include #include #include #include #include "addfellowdialog.h" #include #include "platformdepend.h" #include "feiqwin.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); qRegisterMetaType>("ViewEventSharedPtr"); connect(this, SIGNAL(showErrorAndQuit(QString)), this, SLOT(onShowErrorAndQuit(QString))); //加载配置 auto settingFilePath = QDir::home().filePath(".feiq_setting.ini"); mSettings = new Settings(settingFilePath, QSettings::IniFormat); mSettings->setIniCodec(QTextCodec::codecForName("UTF-8")); mTitle = mSettings->value("app/title", "mac飞秋").toString(); setWindowTitle(mTitle); //初始化搜索对话框 mSearchFellowDlg = new SearchFellowDlg(this); connect(mSearchFellowDlg, SIGNAL(onFellowSelected(const Fellow*)), this, SLOT(finishSearch(const Fellow*))); connect(ui->actionRefreshFellows, SIGNAL(triggered(bool)), this, SLOT(refreshFellowList())); connect(ui->actionAddFellow, SIGNAL(triggered(bool)), this, SLOT(addFellow())); mSearchFellowDlg->setSearchDriver(std::bind(&MainWindow::fellowSearchDriver, this, placeholders::_1)); //初始化文件管理对话框 mDownloadFileDlg = new FileManagerDlg(this); mDownloadFileDlg->setEngine(&mFeiq); connect(this, SIGNAL(statChanged(FileTask*)), mDownloadFileDlg, SLOT(statChanged(FileTask*))); connect(this, SIGNAL(progressChanged(FileTask*)), mDownloadFileDlg, SLOT(progressChanged(FileTask*))); //初始化好友列表 mFellowList.bindTo(ui->fellowListWidget); connect(&mFellowList, SIGNAL(select(const Fellow*)), this, SLOT(openChartTo(const Fellow*))); //初始化接收文本框 mRecvTextEdit = ui->recvEdit; connect(mRecvTextEdit, SIGNAL(navigateToFileTask(IdType,IdType,bool)), this, SLOT(navigateToFileTask(IdType,IdType,bool))); //初始化发送文本框 mSendTextEdit = ui->sendEdit; connect(mSendTextEdit, SIGNAL(acceptDropFiles(QList)), this, SLOT(sendFiles(QList))); if (mSettings->value("app/send_by_enter", true).toBool()) { connect(mSendTextEdit, SIGNAL(enterPressed()), this, SLOT(sendText())); connect(mSendTextEdit, SIGNAL(ctrlEnterPressed()), mSendTextEdit, SLOT(newLine())); } else { connect(mSendTextEdit, SIGNAL(ctrlEnterPressed()), this, SLOT(sendText())); connect(mSendTextEdit, SIGNAL(enterPressed()), mSendTextEdit, SLOT(newLine())); } //初始化Emoji对话框 mChooseEmojiDlg = new ChooseEmojiDlg(this); connect(ui->actionInsertEmoji, SIGNAL(triggered(bool)), this, SLOT(openChooseEmojiDlg())); connect(mChooseEmojiDlg, SIGNAL(choose(QString)),mSendTextEdit, SLOT(insertPlainText(QString))); //初始化菜单 connect(ui->actionSearchFellow, SIGNAL(triggered(bool)), this, SLOT(openSearchDlg())); connect(ui->actionSettings, SIGNAL(triggered(bool)), this, SLOT(openSettings())); connect(ui->actionOpendl, SIGNAL(triggered(bool)), this, SLOT(openDownloadDlg())); connect(ui->actionSendText, SIGNAL(triggered(bool)), this, SLOT(sendText())); connect(ui->actionSendKnock, SIGNAL(triggered(bool)), this, SLOT(sendKnock())); connect(ui->actionSendFile, SIGNAL(triggered(bool)), this, SLOT(sendFile())); //初始化平台相关特性 PlatformDepend::instance().setMainWnd(this); //初始化飞秋引擎 connect(this, SIGNAL(feiqViewEvent(shared_ptr)), this, SLOT(handleFeiqViewEvent(shared_ptr))); //后台初始化通信 std::thread thd(&MainWindow::initFeiq, this); thd.detach(); } MainWindow::~MainWindow() { mFeiq.stop(); mSettings->sync(); delete mSettings; delete mSearchFellowDlg; delete mDownloadFileDlg; delete mChooseEmojiDlg; delete ui; } void MainWindow::setFeiqWin(FeiqWin *feiqWin) { mFeiqWin = feiqWin; mFeiqWin->init(this); } void MainWindow::onNotifyClicked(const QString& fellowIp) { qDebug()<(); content->text = reply.toStdString(); mFeiq.send(fellow, content); //设为已回复 auto msgRepliedTo = findUnshownMessage(notifyId); if (msgRepliedTo) { msgRepliedTo->replied=true; } //将自己的回复放入未显示列表 auto event = make_shared(); event->contents.push_back(content); event->fellow = nullptr; auto& msg = addUnshownMessage(fellow.get(), event); msg.read = true; updateUnshownHint(fellow.get()); } } void MainWindow::enterEvent(QEvent *event) { auto fellow = mRecvTextEdit->curFellow(); if (fellow) { flushUnshown(fellow); updateUnshownHint(fellow); } PlatformDepend::instance().hideAllNotify(); } void MainWindow::openChartTo(const Fellow *fellow) { mFellowList.top(*fellow); mRecvTextEdit->setCurFellow(fellow); setWindowTitle(mTitle + " - 与"+fellow->getName().c_str()+"会话中"); flushUnshown(fellow); updateUnshownHint(fellow); } shared_ptr MainWindow::checkCurFellow() { auto fellow = mFeiq.getModel().getShared(mRecvTextEdit->curFellow()); if (fellow == nullptr) { mRecvTextEdit->addWarning("这是要发给谁?"); } return fellow; } void MainWindow::showResult(pair ret, const Content* content) { if (ret.first) mRecvTextEdit->addMyContent(content, QDateTime::currentDateTime().currentMSecsSinceEpoch()); else mRecvTextEdit->addWarning(ret.second.c_str()); } void MainWindow::onStateChanged(FileTask *fileTask) { if (fileTask->getState()==FileTaskState::Finish) { auto title = QString(fileTask->getTaskTypeDes().c_str())+"完成"; PlatformDepend::instance().showNotify(title, fileTask->getContent()->filename.c_str(), fileTask->fellow()->getIp().c_str()); } else if (fileTask->getState()==FileTaskState::Error) { auto title = QString(fileTask->getTaskTypeDes().c_str())+"失败"; auto content = QString(fileTask->getContent()->filename.c_str()); content += "\n"; content += fileTask->getDetailInfo().c_str(); PlatformDepend::instance().showNotify(title, content, fileTask->fellow()->getIp().c_str()); } if (mDownloadFileDlg->isVisible()) { emit statChanged(fileTask); } } void MainWindow::onProgress(FileTask *fileTask) { if (mDownloadFileDlg->isVisible()) { emit progressChanged(fileTask); } } void MainWindow::onEvent(shared_ptr event) { emit feiqViewEvent(event); } void MainWindow::onShowErrorAndQuit(const QString &text) { QMessageBox::warning(this, "出错了,为什么?你猜!", text, "退出应用"); QApplication::exit(-1); } void MainWindow::handleFeiqViewEvent(shared_ptr event) { if (event->what == ViewEventType::FELLOW_UPDATE) { auto e = static_cast(event.get()); mFellowList.update(*(e->fellow.get())); } else if (event->what == ViewEventType::SEND_TIMEO || event->what == ViewEventType::MESSAGE) { //地球人都知道这个分支中的ViewEvent继承自FellowViewEvent auto e = static_cast(event.get()); auto fellow = e->fellow.get(); if (isActiveWindow() && fellow == mRecvTextEdit->curFellow()) {//窗口可见,处理当前用户消息,其他用户消息则放入通知队列 readEvent(event.get()); } else {//窗口不可见,放入未读队列并通知 auto& umsg = addUnshownMessage(fellow, event); notifyUnshown(umsg); updateUnshownHint(fellow); } } } void MainWindow::refreshFellowList() { mFeiq.sendImOnLine(); } void MainWindow::addFellow() { AddFellowDialog dlg(this); if (dlg.exec() == QDialog::Accepted) { auto ip = dlg.getIp(); userAddFellow(ip); } } void MainWindow::openChooseEmojiDlg() { mChooseEmojiDlg->exec(); } void MainWindow::sendFiles(QList files) { auto fellow = checkCurFellow(); if (!fellow) return; for (auto file : files) { if (file.isFile()) { sendFile(file.absoluteFilePath().toStdString()); } else { mRecvTextEdit->addWarning("不支持发送:"+file.absoluteFilePath()); } } } void MainWindow::userAddFellow(QString ip) { //创建好友 auto fellow = make_shared(); fellow->setIp(ip.toStdString()); fellow->setOnLine(true); mFeiq.getModel().addFellow(fellow); //添加到列表 auto& ref = *(fellow.get()); mFellowList.update(ref); mFellowList.top(ref); //发送在线 mFeiq.sendImOnLine(fellow->getIp()); } void MainWindow::notifyUnshown(UnshownMessage& umsg) { auto event = umsg.event.get(); if (event->what == ViewEventType::SEND_TIMEO) { auto e = static_cast(event); auto fellow = e->fellow.get(); umsg.notifyId = showNotification(fellow, "发送超时:"+simpleTextOf(e->content.get())); } else if (event->what == ViewEventType::MESSAGE) { auto e = static_cast(event); auto fellow = e->fellow.get(); QString text=""; bool first=false; for (auto content : e->contents) { auto t = simpleTextOf(content.get()); if (first) text = t; else text = text+"\n"+t; } umsg.notifyId = showNotification(fellow, text); } } long MainWindow::showNotification(const Fellow *fellow, const QString &text) { QString content(text); if (content.length()>100) content = content.left(100)+"..."; return PlatformDepend::instance().showNotify(QString(fellow->getName().c_str())+":", content, fellow->getIp().c_str()); } void MainWindow::navigateToFileTask(IdType packetNo, IdType fileId, bool upload) { auto task = mFeiq.getModel().findTask(packetNo, fileId, upload ? FileTaskType::Upload : FileTaskType::Download); openDownloadDlg(); mDownloadFileDlg->select(task.get()); } void MainWindow::sendFile(std::string filepath) { auto content = FileContent::createFileContentToSend(filepath); auto fellow = checkCurFellow(); if (!fellow) return; if (content == nullptr) { mRecvTextEdit->addWarning("获取文件"+QString(filepath.c_str())+"的信息失败,不发送"); } else { auto fileContent = shared_ptr(std::move(content)); auto ret = mFeiq.send(fellow, fileContent); showResult(ret, fileContent.get()); } } void MainWindow::sendFile() { auto fellow = checkCurFellow(); if (!fellow) return; //文件多选 QFileDialog fdlg(this, "选择要发送的文件"); fdlg.setFileMode(QFileDialog::ExistingFiles); if (fdlg.exec() == QDialog::Accepted) { auto list = fdlg.selectedFiles(); auto count = list.count(); for (int i = 0; i < count; i++) { auto path = list.at(i); sendFile(path.toStdString()); } } } void MainWindow::sendKnock() { auto fellow = checkCurFellow(); if (fellow) { auto content = make_shared(); auto ret = mFeiq.send(fellow, content); showResult(ret, content.get()); } } void MainWindow::sendText() { auto text = mSendTextEdit->toPlainText(); if (text.isEmpty()) { mRecvTextEdit->addWarning("发送空文本是不科学的,驳回"); return; } auto fellow = checkCurFellow(); if (fellow) { auto content = make_shared(); content->text = text.toStdString(); auto ret = mFeiq.send(fellow, content); showResult(ret, content.get()); mSendTextEdit->clear(); } } void MainWindow::finishSearch(const Fellow *fellow) { mFellowList.top(*fellow); openChartTo(fellow); } void MainWindow::openSettings() { QMessageBox::information(this, "设置", "设置文件在:"+mSettings->fileName()+"\n重启后生效", QMessageBox::Ok); } void MainWindow::openSearchDlg() { mSearchFellowDlg->exec(); } void MainWindow::openDownloadDlg() { mDownloadFileDlg->show(); mDownloadFileDlg->raise(); } vector MainWindow::fellowSearchDriver(const QString &text) { auto fellows = mFeiq.getModel().searchFellow(text.toStdString()); vector result; for (auto fellow : fellows) { result.push_back(fellow.get()); } return result; } void MainWindow::initFeiq() { //配置飞秋 auto name = mSettings->value("user/name").toString(); if (name.isEmpty()) { emit showErrorAndQuit("请先打开【"+mSettings->fileName()+"】设置用户名(user/name)"); return; } mFeiq.setMyName(name.toStdString()); mFeiq.setMyHost(mSettings->value("user/host","feiq by cy").toString().toStdString()); auto customGrroup = mSettings->value("network/custom_group", "").toString(); if (!customGrroup.isEmpty()) { auto list = customGrroup.split("|"); for (int i = 0; i < list.size(); i++) { QString ipPrefix = list[i]; if (ipPrefix.endsWith(".")) { for (int j = 2; j < 254; j++) { auto ip = ipPrefix+QString::number(j); mFeiq.addToBroadcast(ip.toStdString()); } } } } mFeiq.setView(this); mFeiq.enableIntervalDetect(60); //启动飞秋 auto ret = mFeiq.start(); if (!ret.first) { emit showErrorAndQuit(ret.second.c_str()); } qDebug()<<"feiq started"; } void MainWindow::updateUnshownHint(const Fellow *fellow) { auto it = mUnshownEvents.find(fellow); if (it != mUnshownEvents.end()) { auto count = it->second.size(); if (count == 0) { mFellowList.mark(*fellow, ""); } else { mFellowList.mark(*fellow, QString::number(count)); } } setBadgeNumber(getUnreadCount()); } int MainWindow::getUnreadCount() { auto begin = mUnshownEvents.begin(); auto end = mUnshownEvents.end(); auto count = 0; for (auto it = begin; it != end; it++) { for (auto msg : it->second) { if (msg.isUnread()) ++count; } } return count; } void MainWindow::flushUnshown(const Fellow *fellow) { auto it = mUnshownEvents.find(fellow); if (it != mUnshownEvents.end()) { auto& list = (*it).second; while (!list.empty()) { auto msg = list.front(); readEvent(msg.event.get()); list.pop_front(); } } } void MainWindow::readEvent(const ViewEvent *event) { if (event->what == ViewEventType::SEND_TIMEO) { auto e = static_cast(event); auto simpleText = simpleTextOf(e->content.get()); if (simpleText.length()>20){ simpleText = simpleText.left(20)+"..."; } mRecvTextEdit->addWarning("发送超时:"+simpleText); } else if (event->what == ViewEventType::MESSAGE) { auto e = static_cast(event); auto time = e->when.time_since_epoch().count(); for (auto content : e->contents) { if (e->fellow == nullptr) mRecvTextEdit->addMyContent(content.get(), time); else mRecvTextEdit->addFellowContent(content.get(), time); } } } void MainWindow::setBadgeNumber(int number) { PlatformDepend::instance().setBadgeNumber(number); } QString MainWindow::simpleTextOf(const Content *content) { switch (content->type()) { case ContentType::Text: return static_cast(content)->text.c_str(); break; case ContentType::File: return static_cast(content)->filename.c_str(); case ContentType::Knock: return "窗口抖动"; default: return "***"; break; } } UnshownMessage &MainWindow::addUnshownMessage(const Fellow *fellow, shared_ptr event) { UnshownMessage msg; msg.event = event; mUnshownEvents[fellow].push_back(msg); return mUnshownEvents[fellow].back(); } UnshownMessage* MainWindow::findUnshownMessage(int id) { auto begin = mUnshownEvents.begin(); auto end = mUnshownEvents.end(); for (auto it = begin; it != end; it++) { for (auto& msg : it->second){ if (msg.notifyId == id) return &msg; } } return nullptr; }