main.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. #include "ccnotepad.h"
  2. #include "nddsetting.h"
  3. #include "styleset.h"
  4. #include <QtWidgets/QApplication>
  5. #include <QTextCodec>
  6. #include <QMessageBox>
  7. #include <QSharedMemory>
  8. #include <QFile>
  9. #include <QStatusBar>
  10. #include <qobject.h>
  11. #include <QThread>
  12. #include <QDir>
  13. #ifdef Q_OS_UNIX
  14. #include <QStyleFactory>
  15. #include <signal.h>
  16. #include <unistd.h>
  17. #include <QDebug>
  18. #include <QWidget>
  19. #endif
  20. #ifdef Q_OS_WIN
  21. #pragma comment(lib, "user32.lib")
  22. #if _DEBUG
  23. #pragma comment(lib, "qmyedit_qt5d.lib")
  24. #else
  25. #pragma comment(lib, "qmyedit_qt5.lib")
  26. #endif
  27. #include <qt_windows.h>
  28. const ULONG_PTR CUSTOM_TYPE = 10000;
  29. const ULONG_PTR OPEN_NOTEPAD_TYPE = 10001;
  30. const ULONG_PTR CUSTOM_TYPE_FILE_LINENUM = 10002;
  31. bool s_isAdminAuth = false;
  32. #endif
  33. const QString c_strTitle = "Ndd";
  34. #ifdef Q_OS_UNIX
  35. #if defined(Q_OS_MAC)
  36. QSharedMemory shared("CCNotebook123");;//mac下面后面带一个版本号,避免新的打不开
  37. #else
  38. QSharedMemory shared("CCNotebook");
  39. #endif
  40. QSharedMemory nppShared("notepad--");
  41. static void sig_usr(int signo)
  42. {
  43. if(signo == SIGUSR1)
  44. {
  45. qlonglong winId;
  46. shared.lock();
  47. memcpy(&winId,shared.data(),sizeof(qlonglong));
  48. shared.unlock();
  49. QWidget *pMain = QWidget::find((WId)winId);
  50. CCNotePad* pNotePad = dynamic_cast<CCNotePad*>(pMain);
  51. if(pNotePad != nullptr)
  52. {
  53. QString filePath((char*)nppShared.data()+4);
  54. if(!filePath.isEmpty())
  55. {
  56. pNotePad->openFile(filePath);
  57. }
  58. pNotePad->activateWindow();
  59. pNotePad->showNormal();
  60. qDebug() << "sig_usr" << filePath;
  61. }
  62. }
  63. }
  64. #endif
  65. #ifdef Q_OS_MAC
  66. static void openfile(QString filePath)
  67. {
  68. qlonglong winId;
  69. shared.lock();
  70. memcpy(&winId,shared.data(),sizeof(qlonglong));
  71. shared.unlock();
  72. QWidget *pMain = QWidget::find((WId)winId);
  73. CCNotePad* pNotePad = dynamic_cast<CCNotePad*>(pMain);
  74. if(pNotePad != nullptr)
  75. {
  76. if(!filePath.isEmpty())
  77. {
  78. pNotePad->openFile(filePath);
  79. }
  80. pNotePad->activateWindow();
  81. pNotePad->showNormal();
  82. }
  83. }
  84. class MyApplication : public QApplication
  85. {
  86. public:
  87. MyApplication(int &argc, char **argv)
  88. : QApplication(argc, argv)
  89. {
  90. }
  91. bool event(QEvent *event)
  92. {
  93. if (event->type() == QEvent::FileOpen) {
  94. QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
  95. qDebug() << "Open file" << openEvent->file();
  96. s_openfile = openEvent->file();
  97. openfile(s_openfile);
  98. }
  99. return QApplication::event(event);
  100. }
  101. QString s_openfile;
  102. };
  103. #endif
  104. int main(int argc, char *argv[])
  105. {
  106. #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
  107. QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
  108. #elif (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
  109. #ifdef Q_OS_WIN
  110. HDC hdc = CreateDC(L"display", NULL, NULL, NULL);
  111. int ndpi = GetDeviceCaps(hdc, LOGPIXELSY);
  112. qputenv("QT_SCALE_FACTOR", QString::number(ndpi / 96.0).toUtf8());
  113. #endif // Q_OS_WIN
  114. #endif
  115. #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
  116. QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
  117. #endif
  118. #ifdef Q_OS_MAC
  119. MyApplication a(argc, argv);
  120. #else
  121. QApplication a(argc, argv);
  122. #endif
  123. #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
  124. a.setAttribute(Qt::AA_UseHighDpiPixmaps);
  125. #endif
  126. //不能开启,开启后相对路径打开文件失败
  127. //QDir::setCurrent(QCoreApplication::applicationDirPath());
  128. #if defined(Q_OS_UNIX)
  129. QApplication::setStyle(QStyleFactory::create("fusion"));
  130. #endif
  131. a.setApplicationDisplayName(c_strTitle);
  132. a.setApplicationName(c_strTitle);
  133. QStringList arguments = QCoreApplication::arguments();
  134. //目前就三种
  135. //1) ndd filepath
  136. //2) ndd filepath -n linenum
  137. //3) ndd -multi filepath
  138. //只有 1 2 需要处理短路径
  139. if ((arguments.size() == 2) || (arguments.size() == 4))
  140. {
  141. QFileInfo fi(arguments[1]);
  142. if (fi.isRelative())
  143. {
  144. QString absDir = QDir::currentPath();
  145. //获取绝对路径
  146. arguments[1] = QString("%1/%2").arg(absDir).arg(arguments.at(1));
  147. }
  148. }
  149. #ifdef uos
  150. QFont font("Noto Sans CJK SC,9,-1,5,50,0,0,0,0,0,Regular", 9);
  151. QApplication::setFont(font);
  152. #endif
  153. #ifdef Q_OS_MAC
  154. //这里的字体大小,务必要和查找结果框的高度匹配,否则会结构字体拥挤
  155. QFont font("Courier New,11,-1,5,50,0,0,0,0,0,Regular", 11);
  156. // qDebug() << "font name mac";
  157. QApplication::setFont(font);
  158. // qDebug() << QApplication::font().toString();
  159. #endif
  160. bool isGotoLine = false;
  161. #ifdef Q_OS_WIN
  162. QSharedMemory shared("ccnotepad");
  163. if (arguments.size() > 2)
  164. {
  165. //如果是多开请求,这种是从管理员权限申请后重开过来的
  166. if (arguments[1] == QString("-muti"))
  167. {
  168. s_isAdminAuth = true;
  169. QString title = QString(u8"%1 管理员").arg(c_strTitle);
  170. a.setApplicationDisplayName(title);
  171. //删除-muti这个参数
  172. arguments.removeAt(1);
  173. //管理员不占用共享标志。这样的目的是,当管理员窗口存在时
  174. //打开原来的文件,原来的文件可以占用共享标志,作为主窗口打开。
  175. //管理员窗口永远不做主窗口打开
  176. goto authAdmin;
  177. }
  178. else if ((arguments.size() == 4) && arguments[2] == QString("-n"))
  179. {
  180. //使用的是 file -n lineNums 方式。目前只有windows下支持 xxxfile -n linenum的格式
  181. isGotoLine = true;
  182. }
  183. }
  184. #else
  185. if ((arguments.size() == 4) && (arguments[2] == QString("-n")))
  186. {
  187. //使用的是 file -n lineNums 方式。目前只有windows下支持 xxxfile -n linenum的格式
  188. isGotoLine = true;
  189. }
  190. #endif
  191. //attach成功表示已经存在该内存了,表示当前存在实例
  192. if (shared.attach())//共享内存被占用则直接返回
  193. {
  194. //发现在文件中如果存在空格时,参数不止1个,所以不能单纯用2个参数表示
  195. if (arguments.size() > 1)
  196. {
  197. #if defined(Q_OS_WIN)
  198. int tryTimes = 0;
  199. do {
  200. qlonglong hwndId;
  201. shared.lock();
  202. memcpy(&hwndId, shared.data(), sizeof(qlonglong));
  203. shared.unlock();
  204. HWND hwnd = (HWND)hwndId;
  205. if (::IsWindow(hwnd))
  206. {
  207. if (!isGotoLine)
  208. {
  209. //就是ndd filepath的命令行格式
  210. //去掉第一个参数,后续的参数拼接起来。其实参数中间有空格还是需要使用""引用起来,避免空格参数分隔为多个
  211. arguments.takeFirst();
  212. QString filename = arguments.join("");
  213. QByteArray data = filename.toUtf8();
  214. COPYDATASTRUCT copydata;
  215. copydata.dwData = CUSTOM_TYPE; //自定义类型
  216. copydata.lpData = data.data(); //数据大小
  217. copydata.cbData = data.size(); // 指向数据的指针
  218. ::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(&copydata));
  219. }
  220. else
  221. {
  222. //是 filepath -n linenums 方式。不考虑filepath含有空格的情况,因为前面做了严格判断
  223. QString para = QString("%1|%2").arg(arguments[1]).arg(arguments[3]);
  224. QByteArray data = para.toUtf8();
  225. COPYDATASTRUCT copydata;
  226. copydata.dwData = CUSTOM_TYPE_FILE_LINENUM; //自定义类型
  227. copydata.lpData = data.data(); //数据大小
  228. copydata.cbData = data.size(); // 指向数据的指针
  229. ::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(&copydata));
  230. }
  231. break;
  232. }
  233. else
  234. {
  235. //20230304 右键多个文件同时打开,比如3个。此时只有第1个可获取锁,其余2个均走这里。
  236. //因为第个还没有来的及写入hwnd。此时不要goto drop_old。等一下再重试
  237. QThread::sleep(1);
  238. ++tryTimes;
  239. //2次识别后,没法了,只能通过继续往下走。
  240. //失败了,此时说明前一个窗口极可能状态错误了。如果不处理,则再也打不开程序了
  241. if (tryTimes > 2)
  242. {
  243. goto drop_old;
  244. }
  245. }
  246. } while (true);
  247. #elif defined (Q_OS_MAC)
  248. {
  249. //mac下面不需要,有他自身的机制保证
  250. }
  251. #else
  252. pid_t pid;
  253. arguments.takeFirst();
  254. QString filename = arguments.join("");
  255. QByteArray data = filename.toUtf8();
  256. nppShared.attach();
  257. nppShared.lock();
  258. memcpy(&pid, nppShared.data(), sizeof(pid_t));
  259. memset((char*)nppShared.data()+sizeof(pid_t),0, 1024-sizeof(pid_t));
  260. memcpy((char*)nppShared.data()+sizeof(pid_t),data.data(),data.size());
  261. nppShared.unlock();
  262. //if kill failed, then open a new process
  263. if(0 != kill(pid,SIGUSR1))
  264. {
  265. goto unix_goon;
  266. }
  267. #endif
  268. }
  269. else if (arguments.size() == 1)
  270. {
  271. #if defined(Q_OS_WIN)
  272. //把窗口设置到最前
  273. qlonglong hwndId;
  274. shared.lock();
  275. memcpy(&hwndId, shared.data(), sizeof(qlonglong));
  276. shared.unlock();
  277. HWND hwnd = (HWND)hwndId;
  278. if (::IsWindow(hwnd))
  279. {
  280. QString filename("open");
  281. QByteArray data = filename.toUtf8();
  282. COPYDATASTRUCT copydata;
  283. copydata.dwData = OPEN_NOTEPAD_TYPE; //自定义类型
  284. copydata.lpData = data.data(); //数据大小
  285. copydata.cbData = data.size(); // 指向数据的指针
  286. ::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(&copydata));
  287. }
  288. else
  289. {
  290. //失败了,此时说明前一个窗口极可能状态错误了。如果不处理,则再也打不开程序了
  291. //继续新开一个窗口,放弃之前的旧内容
  292. goto drop_old;
  293. }
  294. #elif defined (Q_OS_MAC)
  295. {
  296. //mac下面不需要,有他自身的机制保证
  297. }
  298. #else
  299. pid_t pid;
  300. nppShared.attach();
  301. nppShared.lock();
  302. memcpy(&pid, nppShared.data(), sizeof(pid_t));
  303. memset((char*)nppShared.data()+sizeof(pid_t),0, 1024-sizeof(pid_t));
  304. nppShared.unlock();
  305. qDebug()<<"empty file send";
  306. if(0 != kill(pid,SIGUSR1))
  307. {
  308. goto unix_goon;
  309. }
  310. #endif
  311. }
  312. return 0;
  313. }
  314. #if defined(Q_OS_WIN)
  315. shared.create(32);
  316. #elif defined (Q_OS_MAC)
  317. {
  318. //mac下面不需要,有他自身的机制保证。当程序已经在线时,再打开程序,系统会自动调用已经存在的程序出现
  319. //不需要使用类似linux下面的机制。
  320. shared.create(32);
  321. nppShared.create(32);
  322. }
  323. #else
  324. unix_goon:
  325. shared.create(32);
  326. nppShared.create(2048);
  327. if(signal(SIGUSR1,sig_usr) == SIG_ERR)
  328. {
  329. qDebug()<<"linux create sign failed";
  330. }
  331. #endif
  332. #if defined(Q_OS_WIN)
  333. authAdmin:
  334. drop_old:
  335. #endif
  336. //20221009发现有小概率出现窗口没有,但是进程还在的诡异问题,加个保护一下
  337. QApplication::setQuitOnLastWindowClosed(true);
  338. NddSetting::init();
  339. int id = NddSetting::getKeyValueFromNumSets(SKIN_KEY);
  340. StyleSet::setSkin(id);
  341. CCNotePad *pMainNotepad = new CCNotePad(true);
  342. pMainNotepad->setAttribute(Qt::WA_DeleteOnClose);
  343. pMainNotepad->setShareMem(&shared);
  344. pMainNotepad->quickshow();
  345. pMainNotepad->syncCurSkinToMenu(id);
  346. #ifdef Q_OS_WIN
  347. //HWND hwnd = ::FindWindowA("Qt5QWindowIcon", "CCNotebook");
  348. //发现hwnd就是和effectiveWinId相等的,不需要查询了
  349. //管理员可以多开,暂时不把管理员的权限作为主窗口,因为其他用户没有权限右键菜单发送消息给管理员窗口去打开文件
  350. if (!s_isAdminAuth)
  351. {
  352. qlonglong winId = (qlonglong)pMainNotepad->effectiveWinId();
  353. shared.lock();
  354. memcpy(shared.data(), &winId, sizeof(qlonglong));
  355. shared.unlock();
  356. }
  357. #else
  358. qlonglong winId = (qlonglong)pMainNotepad->effectiveWinId();
  359. shared.lock();
  360. memcpy(shared.data(), &winId, sizeof(qlonglong));
  361. shared.unlock();
  362. nppShared.attach();
  363. //get proceess id to share memory
  364. pid_t pid = getpid();
  365. nppShared.lock();
  366. memcpy(nppShared.data(), &pid, sizeof(pid_t));
  367. nppShared.unlock();
  368. #endif // Q_OS_WIN
  369. //恢复上次关闭时的文件
  370. #ifdef Q_OS_WIN
  371. if (!s_isAdminAuth)
  372. {
  373. if (0 == pMainNotepad->restoreLastFiles() && (arguments.size() == 1))
  374. {
  375. pMainNotepad->initTabNewOne();
  376. }
  377. }
  378. #else
  379. if (0 == pMainNotepad->restoreLastFiles())
  380. {
  381. pMainNotepad->initTabNewOne();
  382. }
  383. #endif
  384. if (arguments.size() == 2)
  385. {
  386. #ifdef Q_OS_WIN
  387. if (!s_isAdminAuth)
  388. {
  389. pMainNotepad->openFile(arguments[1]);
  390. }
  391. else
  392. {
  393. //如果是管理员,还不能直接打开文件,需要恢复之前文件的修改内容
  394. //恢复不了,再直接打开
  395. pMainNotepad->tryRestoreFile(arguments[1]);
  396. }
  397. #else
  398. pMainNotepad->openFile(arguments[1]);
  399. #endif
  400. }
  401. else if (isGotoLine)
  402. {
  403. //是filepath -n xxx 格式。
  404. bool ok = true;
  405. int lineNum = arguments[3].toInt(&ok);
  406. if (!ok)
  407. {
  408. lineNum = -1;
  409. }
  410. pMainNotepad->openFile(arguments[1], lineNum);
  411. }
  412. #ifdef Q_OS_WIN
  413. pMainNotepad->checkAppFont();
  414. #endif
  415. a.exec();
  416. NddSetting::close();
  417. return 0;
  418. }