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 15 kB

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

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

Contributors (1)