RabbitCommon v2.3.4
Loading...
Searching...
No Matches
RabbitCommonTools.cpp
1// Copyright Copyright (c) Kang Lin studio, All Rights Reserved
2// Author Kang Lin <kl222@126.com>
3
4#include <QLocale>
5#include <QDir>
6#include <QLibraryInfo>
7#include <QStandardPaths>
8#include <QSslSocket>
9#include <QThread>
10#include <QSettings>
11#include <QMimeData>
12#include <QProcessEnvironment>
13
14#if defined(Q_OS_ANDROID)
15#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
16#include <QtCore/private/qandroidextras_p.h>
17#endif
18#endif
19
20#ifdef HAVE_RABBITCOMMON_GUI
21 #include <QIcon>
22 #include <QMainWindow>
23 #include <QMessageBox>
24 #include <QPushButton>
25 #include <QApplication>
26 #include <QDesktopServices>
27 #include <QClipboard>
28 #include "Style.h"
29 #ifdef HAVE_ABOUT
30 #include "Information.h"
31 #endif
32#else
33 #include <QCoreApplication>
34#endif
35
36#include "RabbitCommonTools.h"
37#include "RabbitCommonDir.h"
38#ifdef HAVE_ADMINAUTHORISER
39#include "AdminAuthoriser/adminauthoriser.h"
40#endif
41#include "RabbitCommonRegister.h"
42#include "Log/Log.h"
43
44#if defined(HAVE_OPENSSL)
45 #include "openssl/opensslv.h"
46 #include "openssl/crypto.h"
47 #include "openssl/ssl.h"
48#endif
49
50#if defined(Q_OS_WIN)
51 #include <windows.h>
52 #include <tchar.h>
53// #include <stdio.h>
54 //#include "lm.h"
55 //#pragma comment(lib,"netapi32.lib")
56#else
57 #include <pwd.h>
58 #include <unistd.h>
59 #include <signal.h>
60#endif
61
62#if defined(Q_OS_WIN)
63 #include "CoreDump/MiniDumper.h"
64#endif
65#include "CoreDump/StackTrace.h"
66
67#ifdef HAVE_RABBITCOMMON_GUI
68 #include "Log/DockDebugLog.h"
69 #include "Log/DlgFilter.h"
70 extern CDockDebugLog* g_pDcokDebugLog;
71#endif
72
73#ifdef HAVE_CMARK
74#include "cmark.h"
75#endif
76#ifdef HAVE_CMARK_GFM
77#include "cmark-gfm.h"
78#include "cmark-gfm-core-extensions.h"
79#endif
80
81inline void g_RabbitCommon_InitResource()
82{
83 Q_INIT_RESOURCE(ResourceRabbitCommon);
84
85#ifdef BUILD_QUIWidget
86 Q_INIT_RESOURCE(QUIWidget);
87#endif
88#if DEBUG
89 // Must set cmake parameters: -DCMAKE_BUILD_TYPE=Debug
90 //Q_INIT_RESOURCE(translations_RabbitCommon);
91#ifdef BUILD_QUIWidget
92 Q_INIT_RESOURCE(QUIWidgetQss);
93#endif
94#endif
95}
96
97inline void g_RabbitCommon_CleanResource()
98{
99 Q_CLEANUP_RESOURCE(ResourceRabbitCommon);
100#ifdef BUILD_QUIWidget
101 Q_CLEANUP_RESOURCE(QUIWidget);
102#endif
103#if DEBUG
104 //Q_CLEANUP_RESOURCE(translations_RabbitCommon);
105#ifdef BUILD_QUIWidget
106 Q_CLEANUP_RESOURCE(QUIWidgetQss);
107#endif
108#endif
109}
110
111namespace RabbitCommon {
112
113static Q_LOGGING_CATEGORY(log, "RabbitCommon.Tools")
114static Q_LOGGING_CATEGORY(logTranslation, "RabbitCommon.Tools.Translation")
115
116static CCallTrace* g_pCallStack = nullptr;
117
118CTools::CTools() : QObject()
119 , m_Initialized(false)
120#if defined(Q_OS_ANDROID)
121 , m_bShowMaxWindow(true)
122#else
123 , m_bShowMaxWindow(false)
124#endif
125{
126#ifdef HAVE_CMARK_GFM
127 cmark_gfm_core_extensions_ensure_registered();
128// // free extensions at application exit (cmark-gfm is not able to register/unregister more than once)
129// std::atexit(cmark_release_plugins);
130#endif
131}
132
133CTools::~CTools()
134{
135 qDebug(log) << "CTools::~CTools()";
136#if HAVE_RABBITCOMMON_GUI
137 if(g_pDcokDebugLog)
138 delete g_pDcokDebugLog;
139#endif
140}
141
142CTools* CTools::Instance()
143{
144 static CTools* pTools = nullptr;
145 if(nullptr == pTools)
146 pTools = new CTools();
147 return pTools;
148}
149
150static QString g_szLanguage;
151int CTools::SetLanguage(const QString szLanguage)
152{
153 g_szLanguage = szLanguage;
154 return 0;
155}
156
158{
159 QString szLANG = QProcessEnvironment::systemEnvironment().value("LANG");
160 if(szLANG.isEmpty()) {
161 if(g_szLanguage.isEmpty())
162 return QLocale::system().name();
163 } else {
164 // Separate zh_CN from zh_CN.utf-8
165 int underscoreIndex = szLANG.indexOf('.');
166 if (underscoreIndex != -1) {
167 szLANG.truncate(underscoreIndex);
168 }
169 return szLANG;
170 }
171 return g_szLanguage;
172}
173
175{
176 QString szReturn;
177 QString szVersion(RabbitCommon_VERSION);
178 QString szRevision(RabbitCommon_REVISION);
179 if(szRevision.isEmpty()) {
180 szReturn = tr("Version: ") + szVersion;
181 } else {
182 szReturn = tr("Version: ") + szVersion + tr(" (From revision: ");
183#if (defined(HAVE_CMARK) || defined(HAVE_CMARK_GFM))
184 szReturn += "[" + szRevision + "](http://github.com/KangLin/RabbitCommon/tree/" + szRevision + ")";
185#else
186 szReturn += szRevision;
187#endif
188 szReturn += ") ";
189 }
190 return szReturn;
191}
192
194{
195 QString szInfo;
196 szInfo = tr("- Functions:") + "\n";
197 szInfo += tr(" - Locale: ") + GetLanguage() + "\n";
198#if defined(HAVE_RABBITCOMMON_GUI)
199 szInfo += tr(" - Have GUI") + "\n";
200#if defined(HAVE_ABOUT)
201 szInfo += tr(" - Have About dialog") + "\n";
202#ifdef HAVE_CMARK_GFM
203 szInfo += tr(" - Use cmark-gfm") + "\n";
204#elif HAVE_CMARK
205 szInfo += tr(" - Use cmark") + "\n";
206#endif
207#endif
208#if defined(HAVE_UPDATE)
209 szInfo += tr(" - Have Update") + "\n";
210#endif
211 szInfo += tr(" - Custom title bar for QWidget") + "\n";
212 szInfo += tr(" - Dock Folder browser") + "\n";
213 szInfo += tr(" - Recent menu") + "\n";
214 szInfo += tr(" - Style") + "\n";
215 szInfo += tr(" - Icon theme: ") + QIcon::themeName() + "\n";
216 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
217 szInfo += tr(" - Fall back icon theme: ") + QIcon::fallbackThemeName() + "\n";
218 #endif
219#endif // #if defined(HAVE_RABBITCOMMON_GUI)
220 szInfo += tr(" - Log") + "\n";
221 szInfo += tr(" - Core dump") + "\n";
222 szInfo += tr(" - Log file: ") + CLog::Instance()->GetLogFile() + "\n";
223#if defined(HAVE_OPENSSL)
224 szInfo += tr(" - Have encrypt(OPENSSL)") + "\n";
225#endif
226#if defined(BUILD_QUIWidget)
227 szInfo += tr(" - Have QUIWidget") + "\n";
228#endif
229 szInfo += tr(" - Application paths and files: ") + "\n";
230 szInfo += tr(" - Installation root path: ") + RabbitCommon::CDir::Instance()->GetDirApplicationInstallRoot() + "\n";
231 szInfo += tr(" - Application path: ") + RabbitCommon::CDir::Instance()->GetDirApplication() + "\n";
232 szInfo += tr(" - Configure path: ") + RabbitCommon::CDir::Instance()->GetDirConfig() + "\n";
233 szInfo += tr(" - Configure file: ") + RabbitCommon::CDir::Instance()->GetFileApplicationConfigure() + "\n";
234 szInfo += tr(" - Translations path: ") + RabbitCommon::CDir::Instance()->GetDirTranslations() + "\n";
235 szInfo += tr(" - Log path: ") + RabbitCommon::CDir::Instance()->GetDirLog() + "\n";
236 szInfo += tr(" - Data path: ") + RabbitCommon::CDir::Instance()->GetDirData() + "\n";
237 szInfo += tr(" - Icons path: ") + RabbitCommon::CDir::Instance()->GetDirIcons() + "\n";
238 szInfo += tr(" - Database path: ") + RabbitCommon::CDir::Instance()->GetDirDatabase() + "\n";
239 szInfo += tr(" - Database file: ") + RabbitCommon::CDir::Instance()->GetDirDatabaseFile() + "\n";
240 szInfo += tr(" - Plugins path: ") + RabbitCommon::CDir::Instance()->GetDirPlugins() + "\n";
241 szInfo += tr(" - User folders and files: ") + "\n";
242 szInfo += tr(" - Documents path: ") + RabbitCommon::CDir::Instance()->GetDirUserDocument() + "\n";
243 szInfo += tr(" - Configure path: ") + RabbitCommon::CDir::Instance()->GetDirUserConfig() + "\n";
244 szInfo += tr(" - Configure file: ") + RabbitCommon::CDir::Instance()->GetFileUserConfigure() + "\n";
245 szInfo += tr(" - Data path: ") + RabbitCommon::CDir::Instance()->GetDirUserData() + "\n";
246 szInfo += tr(" - Image path: ") + RabbitCommon::CDir::Instance()->GetDirUserImage() + "\n";
247 szInfo += tr(" - Database path: ") + RabbitCommon::CDir::Instance()->GetDirUserDatabase() + "\n";
248 szInfo += tr(" - Database file: ") + RabbitCommon::CDir::Instance()->GetDirUserDatabaseFile() + "\n";
249
250 szInfo += tr("- Dependent libraries:") + "\n";
251 szInfo += tr(" - OpenSSL:") + "\n";
252#if defined(HAVE_OPENSSL)
253 szInfo += " - " + tr("Build Version: ") + OPENSSL_VERSION_TEXT + "\n";
254 szInfo += " - " + tr("Runtime Version: ") + OpenSSL_version(OPENSSL_VERSION) + "\n";
255#endif
256 if(QSslSocket::supportsSsl())
257 {
258#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 3))
259 szInfo += " - Qt " + tr("Build Version: ") + QSslSocket::sslLibraryBuildVersionString() + "\n";
260#endif
261 szInfo += " - Qt " + tr("Installed Version: ") + QSslSocket::sslLibraryVersionString() + "\n";
262 } else {
263#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 3))
264 szInfo += " - Qt " + tr("Build Version: ") + QSslSocket::sslLibraryBuildVersionString() + "\n";
265#endif
266 szInfo += " - Qt " + tr("Don't install OPENSSL dynamic library. Please install it") + "\n";
267 }
268#if HAVE_StackWalker
269 szInfo += tr(" - StackWalker") + "\n";
270#endif
271#ifdef HAVE_CMARK_GFM
272 szInfo += tr(" - cmark-gfm") + "\n";
273#elif HAVE_CMARK
274 szInfo += tr(" - cmark") + "\n";
275#endif
276 return szInfo;
277}
278
279int CTools::AndroidRequestPermission(const QString &permission)
280{
281#if defined (Q_OS_ANDROID)
282 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
283 // get android storage permission
284 // 该接口也可以直接使用字符串,可自行跳转到头文件自行查看
285
286 if (QtAndroidPrivate::checkPermission(permission).result()
287 == QtAndroidPrivate::Denied)
288 {
289 QtAndroidPrivate::requestPermission(permission).waitForFinished();
290 }
291 qInfo(log) << "android permission" << permission << ";status:"
292 << QtAndroidPrivate::checkPermission(permission).result();
293 #endif
294#endif
295
296 return 0;
297}
298
299int CTools::AndroidRequestPermission(const QStringList &permissions)
300{
301 foreach (auto p, permissions) {
302 AndroidRequestPermission(p);
303 }
304 return 0;
305}
306
307void CTools::Init(int argc, char *argv[], QString szApplicationName)
308{
309 if(1 > argc || !argv) {
310 qCritical(log) << "The parameters is error";
311 return;
312 }
313
314 QString szName;
315 QString szPath;
316 QFileInfo fi(argv[0]);
317 if(szApplicationName.isEmpty())
318 szName = fi.baseName();
319 else
320 szName = szApplicationName;
321 szPath = fi.absolutePath();
322 Init(szName, szPath, szPath + QDir::separator() + "..");
323}
324
325void CTools::Init(QString szApplicationName,
326 QString szApplicationDirPath,
327 QString szApplicationInstallRoot,
328 const QString szLanguage)
329{
330 if(m_Initialized) {
331 qWarning(log) << "CTools is already initialized";
332 Q_ASSERT(false);
333 return;
334 }
335#if defined(HAVE_WebEngineWidgets)
336 // 修复 qtwebengine 沙箱权限问题
337 if(!qEnvironmentVariable("SNAP").isEmpty()) {
338 qputenv("QTWEBENGINE_DISABLE_SANDBOX", "1");
339 }
340#endif
341 if(QCoreApplication::applicationName().isEmpty())
342 {
343 QCoreApplication::setApplicationName(szApplicationName);
344 CDir::Instance()->SetDirUserDocument();
345 }
346 if(QCoreApplication::applicationDirPath().isEmpty()
347 && CDir::Instance()->GetDirApplication().isEmpty())
348 CDir::Instance()->SetDirApplication(szApplicationDirPath);
349 if(CDir::Instance()->GetDirApplicationInstallRoot().isEmpty())
350 CDir::Instance()->SetDirApplicationInstallRoot(szApplicationInstallRoot);
351 if(QCoreApplication::applicationName().isEmpty()
352 || CDir::Instance()->GetDirApplication().isEmpty()
353 || CDir::Instance()->GetDirApplicationInstallRoot().isEmpty())
354 {
355 qCritical(log)
356 << "CTools::Instance()->Init() is called after QApplication a(argc, argv);"
357 "Or call this function with the parameters"
358 "szApplicationName, "
359 "szApplicationDirPath, "
360 "and szApplicationInstallRoot";
361 Q_ASSERT(false);
362 return;
363 }
364
365 QStringList permissions;
366 permissions << "android.permission.WRITE_EXTERNAL_STORAGE"
367 << "android.permission.INTERNET"
368 << "android.permission.ACCESS_NETWORK_STATE"
369 << "android.permission.CHANGE_WIFI_STATE"
370 << "android.permission.ACCESS_WIFI_STATE"
371 << "android.permission.ACCESS_NETWORK_STATE"
372 << "android.permission.CHANGE_NETWORK_STATE";
373 AndroidRequestPermission(permissions);
375 RabbitCommon::CLog::Instance();
376 SetLanguage(szLanguage);
377 qInfo(logTranslation) << "Language:" << szLanguage;
378 InitResource();
379 InstallTranslator("RabbitCommon", TranslationType::Library, szLanguage);
380 //Init qt translation
381 QString szQtTranslationPath;
382#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
383 szQtTranslationPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
384#else
385 szQtTranslationPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath);
386#endif
387 QStringList qtTranslations;
388 qtTranslations << "qt" << "qtbase" << "qtmultimedia"
389 << "qtlocation";
390#if HAVE_WebEngineWidgets
391 qtTranslations << "qtwebengine";
392#endif
393 foreach(auto f, qtTranslations) {
394 QString szFile = szQtTranslationPath + QDir::separator()
395 + f + "_" + szLanguage + ".qm";
396 QFile file(szFile);
397 if(file.exists())
398 InstallTranslatorFile(szFile);
399 else
400 qWarning(logTranslation) << "The file doesn't exists: " << szFile;
401 }
402
403#ifdef HAVE_RABBITCOMMON_GUI
404 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
405 QSettings::IniFormat);
406 m_bShowMaxWindow = set.value("Tools/Window/ShowMax", m_bShowMaxWindow).toBool();
407
408 CStyle::Instance()->LoadStyle();
409#ifdef HAVE_ABOUT
410 CInformation info("RabbitCommon, Qt and System information:", "");
411#endif
412#else
413 qDebug(log) << "RabbitCommon:" << "\n" << Information();
414#endif //HAVE_RABBITCOMMON_GUI
415}
416
418{
419 foreach(auto t, m_Translator) {
420 QCoreApplication::removeTranslator(t.data());
421 }
422 m_Translator.clear();
423 CleanResource();
424 delete RabbitCommon::CLog::Instance();
425}
426
427QSharedPointer<QTranslator> CTools::InstallTranslatorFile(const QString szFile)
428{
429 QSharedPointer<QTranslator> translator
430 = QSharedPointer<QTranslator>(new QTranslator());
431 if(!translator) {
432 qCritical(logTranslation) << "new QTranslator fail";
433 return translator;
434 }
435 bool bRet = translator->load(szFile);
436 if(bRet)
437 {
438 bRet = QCoreApplication::installTranslator(translator.data());
439 if(bRet)
440 {
441 m_Translator.push_back(translator);
442 qDebug(logTranslation) << "Install translator:" << szFile;
443 return translator;
444 }
445 else {
446 qCritical(logTranslation) << "Install translator fail:" << szFile;
447 }
448 } else
449 qCritical(logTranslation) << "Load translator file fail:" << szFile;
450 return QSharedPointer<QTranslator>();
451}
452
453QSharedPointer<QTranslator> CTools::InstallTranslator(
454 const QString szName,
455 TranslationType type,
456 const QString szPluginDir,
457 const QString szLanguage)
458{
459 QString szTranslationName = szName;
460
461 QString szSuffix;
462 QString szPath;
463 szSuffix = QDir::separator() + szTranslationName + "_" + szLanguage + ".qm";
464 switch(type) {
465 case TranslationType::Application:
466 szPath = CDir::Instance()->GetDirTranslations();
467 if(szTranslationName.isEmpty()) {
468 szTranslationName = QCoreApplication::applicationName();
469 szSuffix = QDir::separator() + szTranslationName + "_" + szLanguage + ".qm";
470 }
471 break;
472 case TranslationType::Library: {
473 szPath = CDir::Instance()->GetDirTranslations();
474 if(szTranslationName.isEmpty()) {
475 qCritical(logTranslation) << "Please set translation name";
476 Q_ASSERT(false);
477 }
478#if defined(Q_OS_LINUX)
479 QFile file(szPath + szSuffix);
480 if(!file.exists()) {
481 szPath = CDir::Instance()->GetDirTranslations("/usr/share");
482 file.setFileName(szPath + szSuffix);
483 if(!file.exists()) {
484 szPath = CDir::Instance()->GetDirTranslations(
485 "/usr/local/share");
486 }
487 }
488#endif
489 break;
490 }
491 case TranslationType::Plugin:
492 szPath = CDir::Instance()->GetDirPluginsTranslation(szPluginDir);
493 if(szTranslationName.isEmpty()) {
494 qCritical(logTranslation) << "Please set translation name";
495 Q_ASSERT(false);
496 }
497 break;
498 }
499
500 QString szFile = szPath + szSuffix;
501 QFile file(szFile);
502 if(!file.exists())
503 {
504 qCritical(logTranslation) << "File isn't exit:" << szFile;
505 return QSharedPointer<QTranslator>();
506 }
507
508 return InstallTranslatorFile(szFile);
509}
510
511int CTools::RemoveTranslator(QSharedPointer<QTranslator> translator)
512{
513 foreach(auto t, m_Translator) {
514 if(t == translator) {
515 QCoreApplication::removeTranslator(t.data());
516 m_Translator.removeAll(t);
517 return 0;
518 }
519 }
520 return -1;
521}
522
523void CTools::InitResource()
524{
525 g_RabbitCommon_InitResource();
526}
527
528void CTools::CleanResource()
529{
530 g_RabbitCommon_CleanResource();
531}
532
533#if defined(Q_OS_UNIX)
534void SigHandler(int sig)
535{
536 switch(sig)
537 {
538 case SIGSEGV: // 试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
539 case SIGBUS: // 非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。
540 case SIGILL: // illeage,非法的。执行了非法指令, 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出也有可能产生这个信号。
541 qCritical(log) << "Receive exception signal:" << sig << "\n"
542 << g_pCallStack->GetStack(3).toStdString().c_str();
543 break;
544 default:
545 qDebug(log) << "Receive signal:" << sig;
546 return;
547 };
548 exit(sig);
549}
550#endif
551
552bool CTools::EnableCoreDump(bool bPrompt)
553{
554 Q_UNUSED(bPrompt);
555 g_pCallStack = new CCallTrace();
556
557#ifdef Q_OS_WIN
558 //static RabbitCommon::CMiniDumper dumper(bPrompt);
559 RabbitCommon::EnableMiniDumper();
560#else
561 //SIGSEGV: 试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
562 signal(SIGSEGV, SigHandler);
563 // 非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)。
564 signal(SIGBUS, SigHandler);
565 // illeage,非法的。执行了非法指令, 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出也有可能产生这个信号。
566 signal(SIGILL, SigHandler);
567#endif
568 return true;
569}
570
572{
573#ifdef HAVE_ADMINAUTHORISER
574 return CAdminAuthoriser::Instance()->hasAdminRights();
575#else
576 return false;
577#endif
578}
579
580bool CTools::ExecuteWithAdministratorPrivilege(const QString &program,
581 const QStringList &arguments,
582 bool bDetached)
583{
584#ifdef HAVE_ADMINAUTHORISER
585 CAdminAuthoriser::Instance()->SetDetached(bDetached);
586 return CAdminAuthoriser::Instance()->execute(program, arguments);
587#else
588 return false;
589#endif
590}
591
592bool CTools::executeByRoot(const QString &program, const QStringList &arguments)
593{
594 return ExecuteWithAdministratorPrivilege(program, arguments);
595}
596
598{
599 bool bRet = true;
601 auto para = QApplication::arguments();
602 QString szApp = QApplication::applicationFilePath();
603 QString szAppImage;
604#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0) && defined(Q_OS_WIN)
605 szAppImage = qEnvironmentVariable("APPIMAGE");
606#else
607 szAppImage = QString::fromLocal8Bit(qgetenv("APPIMAGE"));
608#endif
609 qDebug(log) << "App:" << szApp << szAppImage;
610 if(!szAppImage.isEmpty())
611 szApp = szAppImage;
612 if(!para.isEmpty())
613 para.removeFirst();
614 if(para.isEmpty())
615 bRet = ExecuteWithAdministratorPrivilege(szApp);
616 else
617 bRet = ExecuteWithAdministratorPrivilege(szApp, para);
618 if(bRet) {
619 if(bQuitOld) {
620#ifdef HAVE_RABBITCOMMON_GUI
621 auto pMainWindow = GetMainWindow();
622 if(pMainWindow) {
623 //pMainWindow->close(); // Note that method is not thread-safe
624 QMetaObject::invokeMethod(pMainWindow, "close", Qt::QueuedConnection);
625 } else
626#endif
627 //QCoreApplication::quit(); // Note that method is not thread-safe
628 if(qApp)
629 QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
630 }
631 }
632 else
633 qCritical(log) << "Start by root fail:" << QCoreApplication::applicationFilePath()
634 << para;
635 }
636 return bRet;
637}
638
639int CTools::InstallStartRun(const QString &szName, const QString &szPath, bool bAllUser)
640{
641 Q_UNUSED(szName)
642 Q_UNUSED(szPath)
643 Q_UNUSED(bAllUser)
644
645 QString appPath;
646 appPath = QCoreApplication::applicationFilePath();
647
648 if(!szPath.isEmpty())
649 appPath = szPath;
650 if(appPath.isEmpty())
651 {
652 qCCritical(log) << "szPath is empty";
653 return -1;
654 }
655 if(bAllUser)
656 return RabbitCommon::CRegister::InstallStartRun();
657 return RabbitCommon::CRegister::InstallStartRunCurrentUser();
658 //See: debian/postinst and Install/install.sh
659 //Ubuntu use gnome-session-properties
660 // - Current user: ~/.config/autostart
661 // - All user: /etc/xdg/autostart/
662 //https://blog.csdn.net/DinnerHowe/article/details/79025282
663 QString szLink = CRegister::GetDesktopFileLink(szName, bAllUser);
664 if(QFile::exists(szLink))
665 if(RemoveStartRun(szName, bAllUser))
666 {
667 qCCritical(log) << "RemoveStartRun" << szName << "fail";
668 return -1;
669 }
670
671 appPath = CRegister::GetDesktopFileName(szPath, szName);
672 QFile f(appPath);
673 if(!f.exists())
674 {
675 qCCritical(log) << "The desktop file doesn't exist: " << appPath;
676 return -2;
677 }
678 bool ret = f.link(szLink);
679 if(!ret)
680 {
681 QString szCmd = "ln -s " + appPath + " " + szLink;
682 if(!ExecuteWithAdministratorPrivilege(szCmd))
683 qCritical(log) << "CTools::InstallStartRun: file link"
684 << f.fileName() << " to " << szLink << f.error();
685 return -1;
686 }
687 return 0;
688
689}
690
691int CTools::RemoveStartRun(const QString &szName, bool bAllUser)
692{
693 Q_UNUSED(szName);
694 QString appName = QCoreApplication::applicationName();
695 if(!szName.isEmpty())
696 appName = szName;
697 if(bAllUser)
698 return RabbitCommon::CRegister::RemoveStartRun();
699 return RabbitCommon::CRegister::RemoveStartRunCurrentUser();
700
701 QString szLink = CRegister::GetDesktopFileLink(szName, bAllUser);
702 if(!QFile::exists(szLink)) return 0;
703
704 QDir d;
705 if(d.remove(szLink)) return 0;
706
707 QString szCmd = "rm " + szLink;
708 if(ExecuteWithAdministratorPrivilege(szCmd)) return 0;
709 qCritical(log) << "execute" << szCmd << "fail";
710 return -1;
711
712}
713
714bool CTools::IsStartRun(const QString &szName, bool bAllUser)
715{
716 Q_UNUSED(szName);
717
718 if(bAllUser)
719 return RabbitCommon::CRegister::IsStartRun();
720 return RabbitCommon::CRegister::IsStartRunCurrentUser();
721
722 QString szLink = CRegister::GetDesktopFileLink(szName, bAllUser);
723 if(QFile::exists(szLink))
724 return true;
725 return false;
726}
727
728int CTools::GenerateDesktopFile(const QString &szPath,
729 const QString &szName)
730{
731 int nRet = 0;
732
733 QString szFile = CRegister::GetDesktopFileName(szPath, szName);
734
735 QString szContent;
736 szContent = "[Desktop Entry]\n";
737 szContent += "Name=" + QCoreApplication::applicationName() + "\n";
738 szContent += "Comment=" + QCoreApplication::applicationName() + "\n";
739#ifdef HAVE_RABBITCOMMON_GUI
740 szContent += "Name[" + QLocale::system().name() + "]=" + QApplication::applicationDisplayName() + "\n";
741 szContent += "Comment[" + QLocale::system().name() + "]=" + QApplication::applicationDisplayName() + "\n";
742#else
743 szContent += "Name[" + QLocale::system().name() + "]=" + QCoreApplication::applicationName() + "\n";
744 szContent += "Comment[" + QLocale::system().name() + "]=" + QCoreApplication::applicationName() + "\n";
745#endif
746 szContent += "Icon=" + QCoreApplication::applicationName() + "\n";
747 szContent += "Exec=" + QCoreApplication::applicationFilePath() + "\n";
748 szContent += "Categories=Application;Development;\n";
749 szContent += "Terminal=false\n";
750 szContent += "StartupNotify=true\n";
751
752 QFile f(szFile);
753 if(!f.open(QFile::WriteOnly))
754 {
755 return f.error();
756 }
757 f.write(szContent.toStdString().c_str());
758 f.close();
759 return nRet;
760}
761
762QString CTools::GetCurrentUser()
763{
764#ifdef UNIX
765 struct passwd *pwd;
766 pwd = getpwuid(getuid());
767 return pwd->pw_name;
768#elif WINDOWS
769 CHAR szUserName[MAX_PATH] = {0};
770 DWORD dwSize=MAX_PATH;
771 ::GetUserNameA(szUserName, &dwSize);
772 return szUserName;
773 //return QString::fromWCharArray(szUserName, dwSize);
774#else
775 return QDir::home().dirName();
776#endif
777}
778
779QString CTools::GetHostName()
780{
781 char buf[255];
782 gethostname(buf, sizeof(buf));
783 return buf;
784}
785
786QString CTools::MarkDownToHtml(const QString &szText)
787{
788 QString szHtml = szText;
789#if defined(HAVE_CMARK_GFM) || defined(HAVE_CMARK)
790 char* pHtml = cmark_markdown_to_html(szText.toStdString().c_str(),
791 szText.toStdString().length(),
792 CMARK_OPT_DEFAULT);
793 if(pHtml)
794 {
795 szHtml = pHtml;
796 free(pHtml);
797 } else {
798 qCritical(log) << "cmark_markdown_to_html fail";
799 }
800 return szHtml;
801#endif
802/*
803#ifdef HAVE_CMARK_GFM
804
805 // TODO make this method which takes input and provides output: cmark_to_html()
806 cmark_mem* mem = cmark_get_default_mem_allocator();
807 // TODO control which extensions to use in MindForger config
808 cmark_llist* syntax_extensions = cmark_list_syntax_extensions(mem);
809 // TODO parse options
810 cmark_parser* parser = cmark_parser_new(CMARK_OPT_DEFAULT | CMARK_OPT_UNSAFE);
811 for (cmark_llist* tmp = syntax_extensions; tmp; tmp = tmp->next) {
812 cmark_parser_attach_syntax_extension(parser, (cmark_syntax_extension*)tmp->data);
813 }
814 cmark_parser_feed(parser, szText.toStdString().c_str(), szText.toStdString().length());
815
816 //cmark_node* doc = cmark_parse_document (markdown->c_str(), markdown->size(), CMARK_OPT_DEFAULT | CMARK_OPT_UNSAFE);
817 cmark_node* doc = cmark_parser_finish(parser);
818 if(doc) {
819 char *rendered_html = cmark_render_html_with_mem(doc, CMARK_OPT_DEFAULT | CMARK_OPT_UNSAFE, parser->syntax_extensions, mem);
820 if (rendered_html) {
821 szHtml = rendered_html;
822 free(rendered_html);
823 }
824 cmark_node_free(doc);
825 }
826 cmark_llist_free(mem, syntax_extensions);
827 cmark_parser_free(parser);
828
829#endif
830*/
831 return szHtml;
832}
833
834#ifdef HAVE_RABBITCOMMON_GUI
835QAction* CTools::AddStyleMenu(QMenu *pMenu, QWidget *parent)
836{
837 return pMenu->addAction(QIcon::fromTheme("style"),
838 tr("Style"),
839 [=](){
840 CFrmStyle* s = new CFrmStyle(parent);
841 if(s)
842 s->show();
843 });
844}
845
846void CTools::InsertStyleMenu(QMenu *pMenu, QAction *before, QWidget *parent)
847{
848 QAction* pAction = new QAction(QIcon::fromTheme("style"),
849 tr("Style"), parent);
850 if(!pAction)
851 return;
852 pAction->setStatusTip(tr("Style"));
853 QObject::connect(pAction, &QAction::triggered, [=](){
854 CFrmStyle* s = new CFrmStyle(parent);
855 if(s)
856 s->show();
857 });
858 pMenu->insertAction(before, pAction);
859}
860
861QMenu* CTools::GetLogMenu(QWidget *parentMainWindow)
862{
863 QMenu* pMenu = new QMenu(tr("Log"), parentMainWindow);
864 if(!pMenu) return pMenu;
865 pMenu->setStatusTip(tr("Log"));
866 pMenu->setIcon(QIcon::fromTheme("folder-open"));
867 QAction* pAction = pMenu->addAction(QIcon::fromTheme("emblem-system"),
868 tr("Settings"),
869 [](){
870 CDlgFilter dlg;
871 QString szInclude, szExclude;
872 CLog::Instance()->GetFilter(szInclude, szExclude);
873 dlg.SetFilter(szInclude, szExclude);
874 if(QDialog::Accepted == RC_SHOW_WINDOW(&dlg))
875 {
876 dlg.GetFilter(szInclude, szExclude);
877 CLog::Instance()->SetFilter(szInclude, szExclude);
878 }
879 });
880 pAction->setStatusTip(pAction->text());
881 pAction = pMenu->addAction(QIcon::fromTheme("document-open"),
882 tr("Open Log configure file"),
883 [](){RabbitCommon::OpenLogConfigureFile();});
884 pAction->setStatusTip(pAction->text());
885 pAction = pMenu->addAction(QIcon::fromTheme("document-open"),
886 tr("Open Log file"),
887 [](){RabbitCommon::OpenLogFile();});
888 pAction->setStatusTip(pAction->text());
889 pAction = pMenu->addAction(QIcon::fromTheme("folder-open"),
890 tr("Open Log folder"),
891 [](){RabbitCommon::OpenLogFolder();});
892 pAction->setStatusTip(pAction->text());
893
894 QMainWindow* pMainWindow = qobject_cast<QMainWindow*>(parentMainWindow);
895 if(pMainWindow) {
896 pMenu->addSeparator();
897 if(!g_pDcokDebugLog)
898 g_pDcokDebugLog = new CDockDebugLog(pMainWindow);
899 pMainWindow->addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea,
900 g_pDcokDebugLog);
901 // Must set ObjectName then restore it. See: saveState help document
902 g_pDcokDebugLog->setObjectName("dockDebugLog");
903 QAction* pDock = g_pDcokDebugLog->toggleViewAction();
904 pMenu->addAction(pDock);
905 pDock->setText(tr("Log dock"));
906 pDock->setIcon(QIcon::fromTheme("floating"));
907 pDock->setStatusTip(tr("Log dock"));
908 } else {
909 qWarning(log) << "CTools::GetLogMenu: Don't use dock debug log."
910 << "The parent suggest is MainWindow pointer";
911 }
912
913 pMenu->addSeparator();
914 pAction = pMenu->addAction(QIcon::fromTheme("edit-copy"),
915 tr("Copy log file path to clipboard"),
916 [](){RabbitCommon::CopyLogFileToClipboard();});
917 pAction->setStatusTip(pAction->text());
918 pAction = pMenu->addAction(QIcon::fromTheme("edit-copy"),
919 tr("Copy log folder to clipboard"),
920 [](){RabbitCommon::CopyLogFolderToClipboard();});
921 pAction->setStatusTip(pAction->text());
922
923 return pMenu;
924}
925
926int CTools::RestoreWidget(QWidget *pWidget)
927{
928 int nRet = 0;
929 Q_ASSERT(pWidget);
930 if(!pWidget) return -1;
931 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
932 QSettings::IniFormat);
933 QByteArray geometry
934 = set.value("MainWindow/Status/Geometry").toByteArray();
935 if(!geometry.isEmpty()) {
936 if(!pWidget->restoreGeometry(geometry))
937 qWarning(log) << "Restore geometry fail";
938 }
939 QMainWindow* pMainWindow = qobject_cast<QMainWindow*>(pWidget);
940 if(pMainWindow) {
941 QByteArray state = set.value("MainWindow/Status/State").toByteArray();
942 if(!state.isEmpty()) {
943 if(!pMainWindow->restoreState(state))
944 qWarning(log) << "Restore state fail";
945 }
946 }
947 return nRet;
948}
949
950int CTools::SaveWidget(QWidget *pWidget)
951{
952 int nRet = 0;
953 Q_ASSERT(pWidget);
954 if(!pWidget) return -1;
955 QSettings set(RabbitCommon::CDir::Instance()->GetFileUserConfigure(),
956 QSettings::IniFormat);
957 set.setValue("MainWindow/Status/Geometry", pWidget->saveGeometry());
958 QMainWindow* pMainWindow = qobject_cast<QMainWindow*>(pWidget);
959 if(pMainWindow) {
960 set.setValue("MainWindow/Status/State", pMainWindow->saveState());
961 }
962 return nRet;
963}
964
965int CTools::ShowWidget(QWidget *pWin)
966{
967 if(!pWin) {
968 qCritical(log) << Q_FUNC_INFO << "The parameter is nullptr";
969 Q_ASSERT(pWin);
970 return -1;
971 }
972
973 QDialog* pDlg = qobject_cast<QDialog*>(pWin);
974 if(pDlg) {
975 if(m_bShowMaxWindow) {
976 pDlg->showMaximized();
977 }
978 return pDlg->exec();
979 } else {
980 if(m_bShowMaxWindow) {
981 pWin->showMaximized();
982 } else
983 pWin->show();
984 }
985 return 0;
986}
987
988void CTools::ShowCoreDialog(QString szTitle, QString szContent, QString szDetail, QString szCoreDumpFile)
989{
990 g_pCallStack->ShowCoreDialog(szTitle, szContent, szDetail, szCoreDumpFile);
991}
992
993#if defined(Q_OS_LINUX)
994// 检测当前桌面环境
995QString detectDesktopEnvironment() {
996 QString desktop = qgetenv("XDG_CURRENT_DESKTOP");
997 if (desktop.isEmpty()) {
998 desktop = qgetenv("DESKTOP_SESSION");
999 }
1000 return desktop.toLower();
1001}
1002
1003// 通用的打开文件目录并选中文件的方法
1004bool openFileLocation(const QString &szFile) {
1005 bool bRet = false;
1006 QFileInfo fileInfo(szFile);
1007 if (!fileInfo.exists()) {
1008 qCritical(log) << "File does not exist:" << szFile;
1009 return false;
1010 }
1011
1012 QString desktopEnv = detectDesktopEnvironment();
1013 qDebug(log) << "Detected desktop environment:" << desktopEnv;
1014 QStringList lstPara;
1015 QString szProgram;
1016 lstPara << "--select" << fileInfo.absoluteFilePath();
1017 if (desktopEnv.contains("gnome") || desktopEnv.contains("ubuntu") || desktopEnv.contains("unity")) {
1018 szProgram = "nautilus";
1019 } else if (desktopEnv.contains("kde") || desktopEnv.contains("plasma")) {
1020 szProgram = "dolphin";
1021 } else if (desktopEnv.contains("xfce")) {
1022 szProgram = "thunar";
1023 } else if (desktopEnv.contains("lxde") || desktopEnv.contains("lubuntu")) {
1024 szProgram = "pcmanfm";
1025 } else if(desktopEnv.contains("mate")) {
1026 szProgram = "caja";
1027 }
1028 qDebug(log) << "Program:" << szProgram << "Para:" << lstPara;
1029 if(!szProgram.isEmpty()) {
1030 bRet = QProcess::startDetached(szProgram, lstPara);
1031 } else {
1032 bRet = QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.absolutePath()));
1033 }
1034 // if(!bRet) {
1035 // // 或者尝试使用 DBus 方法
1036 // QUrl fileUrl = QUrl::fromLocalFile(fileInfo.absoluteFilePath());
1037 // bRet = QProcess::startDetached(
1038 // "dbus-send", QStringList()
1039 // << "--session"
1040 // << "--type=method_call"
1041 // << "--dest=org.freedesktop.FileManager1"
1042 // << "/org/freedesktop/FileManager1"
1043 // << "org.freedesktop.FileManager1.ShowItems"
1044 // << QString("array:string:%1").arg(fileUrl.toString())
1045 // << "string:");
1046 // }
1047
1048 return bRet;
1049}
1050#endif
1051
1052bool CTools::LocateFileWithExplorer(const QString szFile)
1053{
1054 bool bRet = true;
1055#ifdef Q_OS_WIN
1056 QString command = "explorer";
1057 QStringList args = {"/select,", QDir::toNativeSeparators(szFile)};
1058 bRet = QProcess::startDetached(command, args);
1059#elif defined(Q_OS_MAC)
1060 QStringList args;
1061 args << "-e" << QString("tell application \"Finder\" to reveal POSIX file \"%1\"").arg(szFile);
1062 args << "-e" << "tell application \"Finder\" to activate";
1063 bRet = QProcess::startDetached("osascript", args);
1064#elif defined(Q_OS_LINUX)
1065 bRet = openFileLocation(szFile);
1066#endif
1067 if(!bRet) {
1068 // Linux: 打开目录
1069 QFileInfo fileInfo(szFile);
1070 if (!fileInfo.exists()) {
1071 qCritical(log) << "File does not exist:" << szFile;
1072 return false;
1073 }
1074 bRet = QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.absolutePath()));
1075 }
1076 return bRet;
1077}
1078
1079QMainWindow* CTools::GetMainWindow()
1080{
1081 foreach (QWidget *w, qApp->topLevelWidgets()) {
1082 if (QMainWindow* mainWin = qobject_cast<QMainWindow*>(w)) {
1083 return mainWin;
1084 }
1085 }
1086 return nullptr;
1087}
1088#endif // #ifdef HAVE_RABBITCOMMON_GUI
1089
1090} //namespace RabbitCommon
Set filter dialog.
Definition DlgFilter.h:18
The CDockDebugLog class.
Set style and icon theme form.
Definition FrmStyle.h:89
Get call stack.
Definition StackTrace.h:17
QString GetDirData(bool bReadOnly=false)
Get data directory.
QString GetDirTranslations(QString szPrefix=QString())
static QString GetDesktopFileLink(const QString &szName=QString(), bool bAllUser=false)
GetDesktopFileLink.
static QString GetDesktopFileName(const QString &szPath=QString(), const QString &szName=QString())
Generate the name of desktop file.
static bool EnableCoreDump(bool bPrompt=true)
Enable core dump.
static int GenerateDesktopFile(const QString &szPath=QString(), const QString &szName=QString())
GenerateDesktopFile: Generate desktop file.
static bool HasAdministratorPrivilege()
Has administrator privilege.
static QString Information()
RabbitCommon information.
static bool StartWithAdministratorPrivilege(bool bQuitOld=false)
Start program with administrator privilege.
static bool executeByRoot(const QString &program, const QStringList &arguments=QStringList())
executeByRoot: Run with administrator privileges
static int InstallStartRun(const QString &szName=QString(), const QString &szPath=QString(), bool bAllUser=false)
InstallStartRun: auto run when startup.
static QString GetLanguage()
Get language Acquisition order:
static QString Version()
RabbitCommon version.
QSharedPointer< QTranslator > InstallTranslator(const QString szName=QString(), TranslationType type=TranslationType::Application, const QString szPluginDir="plugins", const QString szLanguage=GetLanguage())
Install translation.
void Init(QString szApplicationName=QCoreApplication::applicationName(), QString szApplicationDirPath=QCoreApplication::applicationDirPath(), QString szApplicationInstallRoot=QString(), const QString szLanguage=GetLanguage())
Initialize the resource, which is called only once at the beginning of the program,...