notepad--/src/main.cpp

469 lines
13 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ccnotepad.h"
#include "nddsetting.h"
#include "styleset.h"
#include <QtWidgets/QApplication>
#include <QTextCodec>
#include <QMessageBox>
#include <QSharedMemory>
#include <QFile>
#include <QStatusBar>
#include <qobject.h>
#include <QThread>
#include <QDir>
#ifdef Q_OS_UNIX
#include <QStyleFactory>
#include <signal.h>
#include <unistd.h>
#include <QDebug>
#include <QWidget>
#endif
#ifdef Q_OS_WIN
#pragma comment(lib, "user32.lib")
#if _DEBUG
// Bug: 在 Visual Studio 中引发无法构建的问题,此处为无法找到 qmyedit_x 库
//#pragma comment(lib, "qmyedit_qt5d.lib")
#else
//#pragma comment(lib, "qmyedit_qt5.lib")
#endif
#include <qt_windows.h>
const ULONG_PTR CUSTOM_TYPE = 10000;
const ULONG_PTR OPEN_NOTEPAD_TYPE = 10001;
const ULONG_PTR CUSTOM_TYPE_FILE_LINENUM = 10002;
bool s_isAdminAuth = false;
#endif
const QString c_strTitle = "Ndd";
#ifdef Q_OS_UNIX
#if defined(Q_OS_MAC)
QSharedMemory shared("CCNotebook122");;//mac下面后面带一个版本号避免新的打不开
#else
QSharedMemory shared("CCNotebook");
#endif
QSharedMemory nppShared("notepad--");
static void sig_usr(int signo)
{
if(signo == SIGUSR1)
{
qlonglong winId;
shared.lock();
memcpy(&winId,shared.data(),sizeof(qlonglong));
shared.unlock();
QWidget *pMain = QWidget::find((WId)winId);
CCNotePad* pNotePad = dynamic_cast<CCNotePad*>(pMain);
if(pNotePad != nullptr)
{
QString filePath((char*)nppShared.data()+4);
if(!filePath.isEmpty())
{
pNotePad->openFile(filePath);
}
pNotePad->activateWindow();
pNotePad->showNormal();
qDebug() << "sig_usr" << filePath;
}
}
}
#endif
#ifdef Q_OS_MAC
static void openfile(QString filePath)
{
qlonglong winId;
shared.lock();
memcpy(&winId,shared.data(),sizeof(qlonglong));
shared.unlock();
QWidget *pMain = QWidget::find((WId)winId);
CCNotePad* pNotePad = dynamic_cast<CCNotePad*>(pMain);
if(pNotePad != nullptr)
{
if(!filePath.isEmpty())
{
pNotePad->openFile(filePath);
}
pNotePad->activateWindow();
pNotePad->showNormal();
}
}
class MyApplication : public QApplication
{
public:
MyApplication(int &argc, char **argv)
: QApplication(argc, argv)
{
}
bool event(QEvent *event)
{
if (event->type() == QEvent::FileOpen) {
QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
qDebug() << "Open file" << openEvent->file();
s_openfile = openEvent->file();
openfile(s_openfile);
}
return QApplication::event(event);
}
QString s_openfile;
};
#endif
int main(int argc, char *argv[])
{
//可以防止某些屏幕下的字体拥挤重叠问题
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#ifdef Q_OS_MAC
MyApplication a(argc, argv);
#else
QApplication a(argc, argv);
#endif
//不能开启,开启后相对路径打开文件失败
//QDir::setCurrent(QCoreApplication::applicationDirPath());
#if defined(Q_OS_UNIX)
QApplication::setStyle(QStyleFactory::create("fusion"));
#endif
a.setApplicationDisplayName(c_strTitle);
a.setApplicationName(c_strTitle);
QStringList arguments = QCoreApplication::arguments();
//目前就三种
//1) ndd filepath
//2) ndd filepath -n linenum
//3) ndd -multi filepath
//只有 1 2 需要处理短路径
if ((arguments.size() == 2) || (arguments.size() == 4))
{
QFileInfo fi(arguments[1]);
if (fi.isRelative())
{
QString absDir = QDir::currentPath();
//获取绝对路径
arguments[1] = QString("%1/%2").arg(absDir).arg(arguments.at(1));
}
}
#ifdef uos
QFont font("Noto Sans CJK SC,9,-1,5,50,0,0,0,0,0,Regular", 9);
QApplication::setFont(font);
#endif
#ifdef Q_OS_MAC
//这里的字体大小,务必要和查找结果框的高度匹配,否则会结构字体拥挤
QFont font("Courier New,11,-1,5,50,0,0,0,0,0,Regular", 11);
// qDebug() << "font name mac";
QApplication::setFont(font);
// qDebug() << QApplication::font().toString();
#endif
bool isGotoLine = false;
#ifdef Q_OS_WIN
QSharedMemory shared("ccnotepad");
if (arguments.size() > 2)
{
//如果是多开请求,这种是从管理员权限申请后重开过来的
if (arguments[1] == QString("-muti"))
{
s_isAdminAuth = true;
QString title = QString(u8"%1 管理员").arg(c_strTitle);
a.setApplicationDisplayName(title);
//删除-muti这个参数
arguments.removeAt(1);
//管理员不占用共享标志。这样的目的是,当管理员窗口存在时
//打开原来的文件,原来的文件可以占用共享标志,作为主窗口打开。
//管理员窗口永远不做主窗口打开
goto authAdmin;
}
else if ((arguments.size() == 4) && arguments[2] == QString("-n"))
{
//使用的是 file -n lineNums 方式。目前只有windows下支持 xxxfile -n linenum的格式
isGotoLine = true;
}
}
#else
if ((arguments.size() == 4) && (arguments[2] == QString("-n")))
{
//使用的是 file -n lineNums 方式。目前只有windows下支持 xxxfile -n linenum的格式
isGotoLine = true;
}
#endif
//attach成功表示已经存在该内存了表示当前存在实例
if (shared.attach())//共享内存被占用则直接返回
{
//发现在文件中如果存在空格时参数不止1个所以不能单纯用2个参数表示
if (arguments.size() > 1)
{
#if defined(Q_OS_WIN)
int tryTimes = 0;
do {
qlonglong hwndId;
shared.lock();
memcpy(&hwndId, shared.data(), sizeof(qlonglong));
shared.unlock();
HWND hwnd = (HWND)hwndId;
if (::IsWindow(hwnd))
{
if (!isGotoLine)
{
//就是ndd filepath的命令行格式
//去掉第一个参数,后续的参数拼接起来。其实参数中间有空格还是需要使用""引用起来,避免空格参数分隔为多个
arguments.takeFirst();
QString filename = arguments.join("");
QByteArray data = filename.toUtf8();
COPYDATASTRUCT copydata;
copydata.dwData = CUSTOM_TYPE; //自定义类型
copydata.lpData = data.data(); //数据大小
copydata.cbData = data.size(); // 指向数据的指针
::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(&copydata));
}
else
{
//是 filepath -n linenums 方式。不考虑filepath含有空格的情况因为前面做了严格判断
QString para = QString("%1|%2").arg(arguments[1]).arg(arguments[3]);
QByteArray data = para.toUtf8();
COPYDATASTRUCT copydata;
copydata.dwData = CUSTOM_TYPE_FILE_LINENUM; //自定义类型
copydata.lpData = data.data(); //数据大小
copydata.cbData = data.size(); // 指向数据的指针
::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(&copydata));
}
break;
}
else
{
//20230304 右键多个文件同时打开比如3个。此时只有第1个可获取锁其余2个均走这里。
//因为第个还没有来的及写入hwnd。此时不要goto drop_old。等一下再重试
QThread::sleep(1);
++tryTimes;
//2次识别后没法了只能通过继续往下走。
//失败了,此时说明前一个窗口极可能状态错误了。如果不处理,则再也打不开程序了
if (tryTimes > 2)
{
goto drop_old;
}
}
} while (true);
#elif defined (Q_OS_MAC)
{
//mac下面不需要有他自身的机制保证
}
#else
pid_t pid;
arguments.takeFirst();
QString filename = arguments.join("");
QByteArray data = filename.toUtf8();
nppShared.attach();
nppShared.lock();
memcpy(&pid, nppShared.data(), sizeof(pid_t));
memset((char*)nppShared.data()+sizeof(pid_t),0, 1024-sizeof(pid_t));
memcpy((char*)nppShared.data()+sizeof(pid_t),data.data(),data.size());
nppShared.unlock();
//if kill failed, then open a new process
if(0 != kill(pid,SIGUSR1))
{
goto unix_goon;
}
#endif
}
else if (arguments.size() == 1)
{
#if defined(Q_OS_WIN)
//把窗口设置到最前
qlonglong hwndId;
shared.lock();
memcpy(&hwndId, shared.data(), sizeof(qlonglong));
shared.unlock();
HWND hwnd = (HWND)hwndId;
if (::IsWindow(hwnd))
{
QString filename("open");
QByteArray data = filename.toUtf8();
COPYDATASTRUCT copydata;
copydata.dwData = OPEN_NOTEPAD_TYPE; //自定义类型
copydata.lpData = data.data(); //数据大小
copydata.cbData = data.size(); // 指向数据的指针
::SendMessage(hwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(nullptr), reinterpret_cast<LPARAM>(&copydata));
}
else
{
//失败了,此时说明前一个窗口极可能状态错误了。如果不处理,则再也打不开程序了
//继续新开一个窗口,放弃之前的旧内容
goto drop_old;
}
#elif defined (Q_OS_MAC)
{
//mac下面不需要有他自身的机制保证
}
#else
pid_t pid;
nppShared.attach();
nppShared.lock();
memcpy(&pid, nppShared.data(), sizeof(pid_t));
memset((char*)nppShared.data()+sizeof(pid_t),0, 1024-sizeof(pid_t));
nppShared.unlock();
qDebug()<<"empty file send";
if(0 != kill(pid,SIGUSR1))
{
goto unix_goon;
}
#endif
}
return 0;
}
#if defined(Q_OS_WIN)
shared.create(32);
#elif defined (Q_OS_MAC)
{
//mac下面不需要有他自身的机制保证。当程序已经在线时再打开程序系统会自动调用已经存在的程序出现
//不需要使用类似linux下面的机制。
shared.create(32);
nppShared.create(32);
}
#else
unix_goon:
shared.create(32);
nppShared.create(2048);
if(signal(SIGUSR1,sig_usr) == SIG_ERR)
{
qDebug()<<"linux create sign failed";
}
#endif
#if defined(Q_OS_WIN)
authAdmin:
drop_old:
#endif
//20221009发现有小概率出现窗口没有但是进程还在的诡异问题加个保护一下
QApplication::setQuitOnLastWindowClosed(true);
NddSetting::init();
int id = NddSetting::getKeyValueFromNumSets(SKIN_KEY);
StyleSet::setSkin(id);
CCNotePad *pMainNotepad = new CCNotePad(true);
pMainNotepad->setAttribute(Qt::WA_DeleteOnClose);
pMainNotepad->setShareMem(&shared);
pMainNotepad->quickshow();
pMainNotepad->syncCurSkinToMenu(id);
#ifdef Q_OS_WIN
//HWND hwnd = ::FindWindowA("Qt5QWindowIcon", "CCNotebook");
//发现hwnd就是和effectiveWinId相等的不需要查询了
//管理员可以多开,暂时不把管理员的权限作为主窗口,因为其他用户没有权限右键菜单发送消息给管理员窗口去打开文件
if (!s_isAdminAuth)
{
qlonglong winId = (qlonglong)pMainNotepad->effectiveWinId();
shared.lock();
memcpy(shared.data(), &winId, sizeof(qlonglong));
shared.unlock();
}
#else
qlonglong winId = (qlonglong)pMainNotepad->effectiveWinId();
shared.lock();
memcpy(shared.data(), &winId, sizeof(qlonglong));
shared.unlock();
nppShared.attach();
//get proceess id to share memory
pid_t pid = getpid();
nppShared.lock();
memcpy(nppShared.data(), &pid, sizeof(pid_t));
nppShared.unlock();
#endif // Q_OS_WIN
//恢复上次关闭时的文件
#ifdef Q_OS_WIN
if (!s_isAdminAuth)
{
if (0 == pMainNotepad->restoreLastFiles() && (arguments.size() == 1))
{
pMainNotepad->initTabNewOne();
}
}
#else
if (0 == pMainNotepad->restoreLastFiles())
{
pMainNotepad->initTabNewOne();
}
#endif
if (arguments.size() == 2)
{
#ifdef Q_OS_WIN
if (!s_isAdminAuth)
{
pMainNotepad->openFile(arguments[1]);
}
else
{
//如果是管理员,还不能直接打开文件,需要恢复之前文件的修改内容
//恢复不了,再直接打开
pMainNotepad->tryRestoreFile(arguments[1]);
}
#else
pMainNotepad->openFile(arguments[1]);
#endif
}
else if (isGotoLine)
{
//是filepath -n xxx 格式。
bool ok = true;
int lineNum = arguments[3].toInt(&ok);
if (!ok)
{
lineNum = -1;
}
pMainNotepad->openFile(arguments[1], lineNum);
}
#ifdef Q_OS_WIN
pMainNotepad->checkAppFont();
#endif
a.exec();
NddSetting::close();
return 0;
}