You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

mainwindow.cpp 18 kB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include <thread>
  4. #include <QDir>
  5. #include <QDebug>
  6. #include <QMessageBox>
  7. #include <QTextCodec>
  8. #include <QFileDialog>
  9. #include <QDateTime>
  10. #include <QtMac>
  11. #include "addfellowdialog.h"
  12. #include <QProcess>
  13. #include "platformdepend.h"
  14. #include "feiqwin.h"
  15. MainWindow::MainWindow(QWidget *parent) :
  16. QMainWindow(parent),
  17. ui(new Ui::MainWindow)
  18. {
  19. ui->setupUi(this);
  20. qRegisterMetaType<shared_ptr<ViewEvent>>("ViewEventSharedPtr");
  21. connect(this, SIGNAL(showErrorAndQuit(QString)), this, SLOT(onShowErrorAndQuit(QString)));
  22. //加载配置
  23. auto settingFilePath = QDir::home().filePath(".feiq_setting.ini");
  24. mSettings = new Settings(settingFilePath, QSettings::IniFormat);
  25. mSettings->setIniCodec(QTextCodec::codecForName("UTF-8"));
  26. mTitle = mSettings->value("app/title", "mac飞秋").toString();
  27. setWindowTitle(mTitle);
  28. //初始化搜索对话框
  29. mSearchFellowDlg = new SearchFellowDlg(this);
  30. connect(mSearchFellowDlg, SIGNAL(onFellowSelected(const Fellow*)),
  31. this, SLOT(finishSearch(const Fellow*)));
  32. connect(ui->actionRefreshFellows, SIGNAL(triggered(bool)), this, SLOT(refreshFellowList()));
  33. connect(ui->actionAddFellow, SIGNAL(triggered(bool)), this, SLOT(addFellow()));
  34. mSearchFellowDlg->setSearchDriver(std::bind(&MainWindow::fellowSearchDriver, this, placeholders::_1));
  35. //初始化文件管理对话框
  36. mDownloadFileDlg = new FileManagerDlg(this);
  37. mDownloadFileDlg->setEngine(&mFeiq);
  38. connect(this, SIGNAL(statChanged(FileTask*)), mDownloadFileDlg, SLOT(statChanged(FileTask*)));
  39. connect(this, SIGNAL(progressChanged(FileTask*)), mDownloadFileDlg, SLOT(progressChanged(FileTask*)));
  40. //初始化好友列表
  41. mFellowList.bindTo(ui->fellowListWidget);
  42. connect(&mFellowList, SIGNAL(select(const Fellow*)), this, SLOT(openChartTo(const Fellow*)));
  43. //初始化接收文本框
  44. mRecvTextEdit = ui->recvEdit;
  45. connect(mRecvTextEdit, SIGNAL(navigateToFileTask(IdType,IdType,bool)), this, SLOT(navigateToFileTask(IdType,IdType,bool)));
  46. //初始化发送文本框
  47. mSendTextEdit = ui->sendEdit;
  48. connect(mSendTextEdit, SIGNAL(acceptDropFiles(QList<QFileInfo>)), this, SLOT(sendFiles(QList<QFileInfo>)));
  49. if (mSettings->value("app/send_by_enter", true).toBool())
  50. {
  51. connect(mSendTextEdit, SIGNAL(enterPressed()), this, SLOT(sendText()));
  52. connect(mSendTextEdit, SIGNAL(ctrlEnterPressed()), mSendTextEdit, SLOT(newLine()));
  53. }
  54. else
  55. {
  56. connect(mSendTextEdit, SIGNAL(ctrlEnterPressed()), this, SLOT(sendText()));
  57. connect(mSendTextEdit, SIGNAL(enterPressed()), mSendTextEdit, SLOT(newLine()));
  58. }
  59. //初始化Emoji对话框
  60. mChooseEmojiDlg = new ChooseEmojiDlg(this);
  61. connect(ui->actionInsertEmoji, SIGNAL(triggered(bool)), this, SLOT(openChooseEmojiDlg()));
  62. connect(mChooseEmojiDlg, SIGNAL(choose(QString)),mSendTextEdit, SLOT(insertPlainText(QString)));
  63. //初始化菜单
  64. connect(ui->actionSearchFellow, SIGNAL(triggered(bool)), this, SLOT(openSearchDlg()));
  65. connect(ui->actionSettings, SIGNAL(triggered(bool)), this, SLOT(openSettings()));
  66. connect(ui->actionOpendl, SIGNAL(triggered(bool)), this, SLOT(openDownloadDlg()));
  67. connect(ui->actionSendText, SIGNAL(triggered(bool)), this, SLOT(sendText()));
  68. connect(ui->actionSendKnock, SIGNAL(triggered(bool)), this, SLOT(sendKnock()));
  69. connect(ui->actionSendFile, SIGNAL(triggered(bool)), this, SLOT(sendFile()));
  70. //初始化平台相关特性
  71. PlatformDepend::instance().setMainWnd(this);
  72. //初始化飞秋引擎
  73. connect(this, SIGNAL(feiqViewEvent(shared_ptr<ViewEvent>)), this, SLOT(handleFeiqViewEvent(shared_ptr<ViewEvent>)));
  74. //后台初始化通信
  75. std::thread thd(&MainWindow::initFeiq, this);
  76. thd.detach();
  77. }
  78. MainWindow::~MainWindow()
  79. {
  80. mFeiq.stop();
  81. mSettings->sync();
  82. delete mSettings;
  83. delete mSearchFellowDlg;
  84. delete mDownloadFileDlg;
  85. delete mChooseEmojiDlg;
  86. delete ui;
  87. }
  88. void MainWindow::setFeiqWin(FeiqWin *feiqWin)
  89. {
  90. mFeiqWin = feiqWin;
  91. mFeiqWin->init(this);
  92. }
  93. void MainWindow::onNotifyClicked(const QString& fellowIp)
  94. {
  95. qDebug()<<fellowIp;
  96. auto fellow = mFeiq.getModel().findFirstFellowOf(fellowIp.toStdString());
  97. if (fellow)
  98. openChartTo(fellow.get());
  99. raise();
  100. activateWindow();
  101. showNormal();
  102. }
  103. void MainWindow::onNotifyReplied(long notifyId, const QString &fellowIp, const QString &reply)
  104. {
  105. auto fellow = mFeiq.getModel().findFirstFellowOf(fellowIp.toStdString());
  106. if (fellow)
  107. {
  108. //回复消息
  109. auto content = make_shared<TextContent>();
  110. content->text = reply.toStdString();
  111. mFeiq.send(fellow, content);
  112. //设为已回复
  113. auto msgRepliedTo = findUnshownMessage(notifyId);
  114. if (msgRepliedTo)
  115. {
  116. msgRepliedTo->replied=true;
  117. }
  118. //将自己的回复放入未显示列表
  119. auto event = make_shared<MessageViewEvent>();
  120. event->contents.push_back(content);
  121. event->fellow = nullptr;
  122. auto& msg = addUnshownMessage(fellow.get(), event);
  123. msg.read = true;
  124. updateUnshownHint(fellow.get());
  125. }
  126. }
  127. void MainWindow::enterEvent(QEvent *event)
  128. {
  129. auto fellow = mRecvTextEdit->curFellow();
  130. if (fellow)
  131. {
  132. flushUnshown(fellow);
  133. updateUnshownHint(fellow);
  134. }
  135. PlatformDepend::instance().hideAllNotify();
  136. }
  137. void MainWindow::openChartTo(const Fellow *fellow)
  138. {
  139. mFellowList.top(*fellow);
  140. mRecvTextEdit->setCurFellow(fellow);
  141. setWindowTitle(mTitle + " - 与"+fellow->getName().c_str()+"会话中");
  142. flushUnshown(fellow);
  143. updateUnshownHint(fellow);
  144. }
  145. shared_ptr<Fellow> MainWindow::checkCurFellow()
  146. {
  147. auto fellow = mFeiq.getModel().getShared(mRecvTextEdit->curFellow());
  148. if (fellow == nullptr)
  149. {
  150. mRecvTextEdit->addWarning("这是要发给谁?");
  151. }
  152. return fellow;
  153. }
  154. void MainWindow::showResult(pair<bool, string> ret, const Content* content)
  155. {
  156. if (ret.first)
  157. mRecvTextEdit->addMyContent(content, QDateTime::currentDateTime().currentMSecsSinceEpoch());
  158. else
  159. mRecvTextEdit->addWarning(ret.second.c_str());
  160. }
  161. void MainWindow::onStateChanged(FileTask *fileTask)
  162. {
  163. if (fileTask->getState()==FileTaskState::Finish)
  164. {
  165. auto title = QString(fileTask->getTaskTypeDes().c_str())+"完成";
  166. PlatformDepend::instance().showNotify(title,
  167. fileTask->getContent()->filename.c_str(),
  168. fileTask->fellow()->getIp().c_str());
  169. }
  170. else if (fileTask->getState()==FileTaskState::Error)
  171. {
  172. auto title = QString(fileTask->getTaskTypeDes().c_str())+"失败";
  173. auto content = QString(fileTask->getContent()->filename.c_str());
  174. content += "\n";
  175. content += fileTask->getDetailInfo().c_str();
  176. PlatformDepend::instance().showNotify(title,
  177. content,
  178. fileTask->fellow()->getIp().c_str());
  179. }
  180. if (mDownloadFileDlg->isVisible())
  181. {
  182. emit statChanged(fileTask);
  183. }
  184. }
  185. void MainWindow::onProgress(FileTask *fileTask)
  186. {
  187. if (mDownloadFileDlg->isVisible())
  188. {
  189. emit progressChanged(fileTask);
  190. }
  191. }
  192. void MainWindow::onEvent(shared_ptr<ViewEvent> event)
  193. {
  194. emit feiqViewEvent(event);
  195. }
  196. void MainWindow::onShowErrorAndQuit(const QString &text)
  197. {
  198. QMessageBox::warning(this, "出错了,为什么?你猜!", text, "退出应用");
  199. QApplication::exit(-1);
  200. }
  201. void MainWindow::handleFeiqViewEvent(shared_ptr<ViewEvent> event)
  202. {
  203. if (event->what == ViewEventType::FELLOW_UPDATE)
  204. {
  205. auto e = static_cast<FellowViewEvent*>(event.get());
  206. mFellowList.update(*(e->fellow.get()));
  207. }
  208. else if (event->what == ViewEventType::SEND_TIMEO || event->what == ViewEventType::MESSAGE)
  209. {
  210. //地球人都知道这个分支中的ViewEvent继承自FellowViewEvent
  211. auto e = static_cast<FellowViewEvent*>(event.get());
  212. auto fellow = e->fellow.get();
  213. if (isActiveWindow() && fellow == mRecvTextEdit->curFellow())
  214. {//窗口可见,处理当前用户消息,其他用户消息则放入通知队列
  215. readEvent(event.get());
  216. }
  217. else
  218. {//窗口不可见,放入未读队列并通知
  219. auto& umsg = addUnshownMessage(fellow, event);
  220. notifyUnshown(umsg);
  221. updateUnshownHint(fellow);
  222. }
  223. }
  224. }
  225. void MainWindow::refreshFellowList()
  226. {
  227. mFeiq.sendImOnLine();
  228. }
  229. void MainWindow::addFellow()
  230. {
  231. AddFellowDialog dlg(this);
  232. if (dlg.exec() == QDialog::Accepted)
  233. {
  234. auto ip = dlg.getIp();
  235. userAddFellow(ip);
  236. }
  237. }
  238. void MainWindow::openChooseEmojiDlg()
  239. {
  240. mChooseEmojiDlg->exec();
  241. }
  242. void MainWindow::sendFiles(QList<QFileInfo> files)
  243. {
  244. auto fellow = checkCurFellow();
  245. if (!fellow)
  246. return;
  247. for (auto file : files)
  248. {
  249. if (file.isFile())
  250. {
  251. sendFile(file.absoluteFilePath().toStdString());
  252. }
  253. else
  254. {
  255. mRecvTextEdit->addWarning("不支持发送:"+file.absoluteFilePath());
  256. }
  257. }
  258. }
  259. void MainWindow::userAddFellow(QString ip)
  260. {
  261. //创建好友
  262. auto fellow = make_shared<Fellow>();
  263. fellow->setIp(ip.toStdString());
  264. fellow->setOnLine(true);
  265. mFeiq.getModel().addFellow(fellow);
  266. //添加到列表
  267. auto& ref = *(fellow.get());
  268. mFellowList.update(ref);
  269. mFellowList.top(ref);
  270. //发送在线
  271. mFeiq.sendImOnLine(fellow->getIp());
  272. }
  273. void MainWindow::notifyUnshown(UnshownMessage& umsg)
  274. {
  275. auto event = umsg.event.get();
  276. if (event->what == ViewEventType::SEND_TIMEO)
  277. {
  278. auto e = static_cast<const SendTimeoEvent*>(event);
  279. auto fellow = e->fellow.get();
  280. umsg.notifyId = showNotification(fellow, "发送超时:"+simpleTextOf(e->content.get()));
  281. }
  282. else if (event->what == ViewEventType::MESSAGE)
  283. {
  284. auto e = static_cast<const MessageViewEvent*>(event);
  285. auto fellow = e->fellow.get();
  286. QString text="";
  287. bool first=false;
  288. for (auto content : e->contents)
  289. {
  290. auto t = simpleTextOf(content.get());
  291. if (first)
  292. text = t;
  293. else
  294. text = text+"\n"+t;
  295. }
  296. umsg.notifyId = showNotification(fellow, text);
  297. }
  298. }
  299. long MainWindow::showNotification(const Fellow *fellow, const QString &text)
  300. {
  301. QString content(text);
  302. if (content.length()>100)
  303. content = content.left(100)+"...";
  304. return PlatformDepend::instance().showNotify(QString(fellow->getName().c_str())+":", content, fellow->getIp().c_str());
  305. }
  306. void MainWindow::navigateToFileTask(IdType packetNo, IdType fileId, bool upload)
  307. {
  308. auto task = mFeiq.getModel().findTask(packetNo, fileId, upload ? FileTaskType::Upload : FileTaskType::Download);
  309. openDownloadDlg();
  310. mDownloadFileDlg->select(task.get());
  311. }
  312. void MainWindow::sendFile(std::string filepath)
  313. {
  314. auto content = FileContent::createFileContentToSend(filepath);
  315. auto fellow = checkCurFellow();
  316. if (!fellow)
  317. return;
  318. if (content == nullptr)
  319. {
  320. mRecvTextEdit->addWarning("获取文件"+QString(filepath.c_str())+"的信息失败,不发送");
  321. }
  322. else
  323. {
  324. auto fileContent = shared_ptr<FileContent>(std::move(content));
  325. auto ret = mFeiq.send(fellow, fileContent);
  326. showResult(ret, fileContent.get());
  327. }
  328. }
  329. void MainWindow::sendFile()
  330. {
  331. auto fellow = checkCurFellow();
  332. if (!fellow)
  333. return;
  334. //文件多选
  335. QFileDialog fdlg(this, "选择要发送的文件");
  336. fdlg.setFileMode(QFileDialog::ExistingFiles);
  337. if (fdlg.exec() == QDialog::Accepted)
  338. {
  339. auto list = fdlg.selectedFiles();
  340. auto count = list.count();
  341. for (int i = 0; i < count; i++)
  342. {
  343. auto path = list.at(i);
  344. sendFile(path.toStdString());
  345. }
  346. }
  347. }
  348. void MainWindow::sendKnock()
  349. {
  350. auto fellow = checkCurFellow();
  351. if (fellow)
  352. {
  353. auto content = make_shared<KnockContent>();
  354. auto ret = mFeiq.send(fellow, content);
  355. showResult(ret, content.get());
  356. }
  357. }
  358. void MainWindow::sendText()
  359. {
  360. auto text = mSendTextEdit->toPlainText();
  361. if (text.isEmpty())
  362. {
  363. mRecvTextEdit->addWarning("发送空文本是不科学的,驳回");
  364. return;
  365. }
  366. auto fellow = checkCurFellow();
  367. if (fellow)
  368. {
  369. auto content = make_shared<TextContent>();
  370. content->text = text.toStdString();
  371. auto ret = mFeiq.send(fellow, content);
  372. showResult(ret, content.get());
  373. mSendTextEdit->clear();
  374. }
  375. }
  376. void MainWindow::finishSearch(const Fellow *fellow)
  377. {
  378. mFellowList.top(*fellow);
  379. openChartTo(fellow);
  380. }
  381. void MainWindow::openSettings()
  382. {
  383. QMessageBox::information(this, "设置", "设置文件在:"+mSettings->fileName()+"\n重启后生效",
  384. QMessageBox::Ok);
  385. }
  386. void MainWindow::openSearchDlg()
  387. {
  388. mSearchFellowDlg->exec();
  389. }
  390. void MainWindow::openDownloadDlg()
  391. {
  392. mDownloadFileDlg->show();
  393. mDownloadFileDlg->raise();
  394. }
  395. vector<const Fellow *> MainWindow::fellowSearchDriver(const QString &text)
  396. {
  397. auto fellows = mFeiq.getModel().searchFellow(text.toStdString());
  398. vector<const Fellow*> result;
  399. for (auto fellow : fellows)
  400. {
  401. result.push_back(fellow.get());
  402. }
  403. return result;
  404. }
  405. void MainWindow::initFeiq()
  406. {
  407. //配置飞秋
  408. auto name = mSettings->value("user/name").toString();
  409. if (name.isEmpty())
  410. {
  411. emit showErrorAndQuit("请先打开【"+mSettings->fileName()+"】设置用户名(user/name)");
  412. return;
  413. }
  414. mFeiq.setMyName(name.toStdString());
  415. mFeiq.setMyHost(mSettings->value("user/host","feiq by cy").toString().toStdString());
  416. auto customGrroup = mSettings->value("network/custom_group", "").toString();
  417. if (!customGrroup.isEmpty())
  418. {
  419. auto list = customGrroup.split("|");
  420. for (int i = 0; i < list.size(); i++)
  421. {
  422. QString ipPrefix = list[i];
  423. if (ipPrefix.endsWith("."))
  424. {
  425. for (int j = 2; j < 254; j++)
  426. {
  427. auto ip = ipPrefix+QString::number(j);
  428. mFeiq.addToBroadcast(ip.toStdString());
  429. }
  430. }
  431. }
  432. }
  433. mFeiq.setView(this);
  434. mFeiq.enableIntervalDetect(60);
  435. //启动飞秋
  436. auto ret = mFeiq.start();
  437. if (!ret.first)
  438. {
  439. emit showErrorAndQuit(ret.second.c_str());
  440. }
  441. qDebug()<<"feiq started";
  442. }
  443. void MainWindow::updateUnshownHint(const Fellow *fellow)
  444. {
  445. auto it = mUnshownEvents.find(fellow);
  446. if (it != mUnshownEvents.end())
  447. {
  448. auto count = it->second.size();
  449. if (count == 0)
  450. {
  451. mFellowList.mark(*fellow, "");
  452. }
  453. else
  454. {
  455. mFellowList.mark(*fellow, QString::number(count));
  456. }
  457. }
  458. setBadgeNumber(getUnreadCount());
  459. }
  460. int MainWindow::getUnreadCount()
  461. {
  462. auto begin = mUnshownEvents.begin();
  463. auto end = mUnshownEvents.end();
  464. auto count = 0;
  465. for (auto it = begin; it != end; it++)
  466. {
  467. for (auto msg : it->second)
  468. {
  469. if (msg.isUnread())
  470. ++count;
  471. }
  472. }
  473. return count;
  474. }
  475. void MainWindow::flushUnshown(const Fellow *fellow)
  476. {
  477. auto it = mUnshownEvents.find(fellow);
  478. if (it != mUnshownEvents.end())
  479. {
  480. auto& list = (*it).second;
  481. while (!list.empty())
  482. {
  483. auto msg = list.front();
  484. readEvent(msg.event.get());
  485. list.pop_front();
  486. }
  487. }
  488. }
  489. void MainWindow::readEvent(const ViewEvent *event)
  490. {
  491. if (event->what == ViewEventType::SEND_TIMEO)
  492. {
  493. auto e = static_cast<const SendTimeoEvent*>(event);
  494. auto simpleText = simpleTextOf(e->content.get());
  495. if (simpleText.length()>20){
  496. simpleText = simpleText.left(20)+"...";
  497. }
  498. mRecvTextEdit->addWarning("发送超时:"+simpleText);
  499. }
  500. else if (event->what == ViewEventType::MESSAGE)
  501. {
  502. auto e = static_cast<const MessageViewEvent*>(event);
  503. auto time = e->when.time_since_epoch().count();
  504. for (auto content : e->contents)
  505. {
  506. if (e->fellow == nullptr)
  507. mRecvTextEdit->addMyContent(content.get(), time);
  508. else
  509. mRecvTextEdit->addFellowContent(content.get(), time);
  510. }
  511. }
  512. }
  513. void MainWindow::setBadgeNumber(int number)
  514. {
  515. PlatformDepend::instance().setBadgeNumber(number);
  516. }
  517. QString MainWindow::simpleTextOf(const Content *content)
  518. {
  519. switch (content->type()) {
  520. case ContentType::Text:
  521. return static_cast<const TextContent*>(content)->text.c_str();
  522. break;
  523. case ContentType::File:
  524. return static_cast<const FileContent*>(content)->filename.c_str();
  525. case ContentType::Knock:
  526. return "窗口抖动";
  527. default:
  528. return "***";
  529. break;
  530. }
  531. }
  532. UnshownMessage &MainWindow::addUnshownMessage(const Fellow *fellow, shared_ptr<ViewEvent> event)
  533. {
  534. UnshownMessage msg;
  535. msg.event = event;
  536. mUnshownEvents[fellow].push_back(msg);
  537. return mUnshownEvents[fellow].back();
  538. }
  539. UnshownMessage* MainWindow::findUnshownMessage(int id)
  540. {
  541. auto begin = mUnshownEvents.begin();
  542. auto end = mUnshownEvents.end();
  543. for (auto it = begin; it != end; it++)
  544. {
  545. for (auto& msg : it->second){
  546. if (msg.notifyId == id)
  547. return &msg;
  548. }
  549. }
  550. return nullptr;
  551. }

mac下的“飞秋”大多数只是飞鸽传书协议,而且未发现令人满意的开源项目,所以基于c++与qt实现了基础的飞秋协议。

Contributors (1)