2021-12-26 23:18:28 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2021-10-04 19:23:52 +08:00
|
|
|
#include "environmentfileassociationwidget.h"
|
|
|
|
#include "ui_environmentfileassociationwidget.h"
|
|
|
|
#include "../systemconsts.h"
|
|
|
|
#include "../settings.h"
|
|
|
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
#include <windows.h>
|
|
|
|
|
|
|
|
EnvironmentFileAssociationWidget::EnvironmentFileAssociationWidget(const QString& name, const QString& group, QWidget *parent) :
|
|
|
|
SettingsWidget(name,group,parent),
|
|
|
|
ui(new Ui::EnvironmentFileAssociationWidget)
|
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
mModel.addItem("C Source File","c",1);
|
|
|
|
mModel.addItem("C++ Source File","cpp",2);
|
|
|
|
mModel.addItem("C++ Source File","cxx",2);
|
|
|
|
mModel.addItem("C++ Source File","cc",2);
|
|
|
|
mModel.addItem("C/C++ Header File","h",3);
|
|
|
|
mModel.addItem("C++ Header File","hpp",4);
|
|
|
|
mModel.addItem("C++ Header File","hxx",4);
|
2021-12-30 19:25:47 +08:00
|
|
|
mModel.addItem("Red Panda C++ Project File","dev",5);
|
2022-10-10 18:05:18 +08:00
|
|
|
QItemSelectionModel* m = ui->lstFileTypes->selectionModel();
|
2021-10-04 19:23:52 +08:00
|
|
|
ui->lstFileTypes->setModel(&mModel);
|
2022-10-10 18:05:18 +08:00
|
|
|
delete m;
|
2021-10-04 19:23:52 +08:00
|
|
|
connect(&mModel, &FileAssociationModel::associationChanged,
|
|
|
|
[this](){
|
|
|
|
setSettingsChanged();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
EnvironmentFileAssociationWidget::~EnvironmentFileAssociationWidget()
|
|
|
|
{
|
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EnvironmentFileAssociationWidget::doLoad()
|
|
|
|
{
|
2022-02-28 22:40:09 +08:00
|
|
|
if (pSettings->environment().openFilesInSingleInstance())
|
|
|
|
ui->rbOpenInSingleApplication->setChecked(true);
|
|
|
|
else
|
|
|
|
ui->rbOpenInMultiApplication->setChecked(true);
|
2021-10-04 19:23:52 +08:00
|
|
|
mModel.updateAssociationStates();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EnvironmentFileAssociationWidget::doSave()
|
|
|
|
{
|
|
|
|
mModel.saveAssociations();
|
|
|
|
mModel.updateAssociationStates();
|
2022-02-28 22:40:09 +08:00
|
|
|
pSettings->environment().setOpenFilesInSingleInstance(ui->rbOpenInSingleApplication->isChecked());
|
|
|
|
pSettings->environment().save();
|
2021-10-04 19:23:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
FileAssociationModel::FileAssociationModel(QObject *parent) : QAbstractListModel(parent)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileAssociationModel::addItem(const QString &name, const QString &suffix, int icon)
|
|
|
|
{
|
|
|
|
beginInsertRows(QModelIndex(), mItems.count(), mItems.count());
|
|
|
|
PFileAssociationItem item = std::make_shared<FileAssociationItem>();
|
|
|
|
item->name = name;
|
|
|
|
item->suffix = suffix;
|
|
|
|
item->icon = icon;
|
|
|
|
item->selected = false;
|
|
|
|
item->defaultSelected = false;
|
|
|
|
mItems.append(item);
|
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileAssociationModel::updateAssociationStates()
|
|
|
|
{
|
|
|
|
beginResetModel();
|
|
|
|
foreach (const PFileAssociationItem& item, mItems) {
|
|
|
|
item->selected = checkAssociation(
|
|
|
|
"."+item->suffix,
|
|
|
|
"DevCpp."+item->suffix,
|
2021-10-20 18:05:43 +08:00
|
|
|
// item->name,
|
2021-10-04 20:05:24 +08:00
|
|
|
"Open",
|
2021-10-04 19:23:52 +08:00
|
|
|
pSettings->dirs().executable()+" \"%1\""
|
|
|
|
);
|
|
|
|
item->defaultSelected = item->selected;
|
|
|
|
}
|
|
|
|
endResetModel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileAssociationModel::saveAssociations()
|
|
|
|
{
|
|
|
|
QSet<QString> fileTypeUsed;
|
|
|
|
QSet<QString> fileTypes;
|
|
|
|
QMap<QString,PFileAssociationItem> fileTypeDescriptions;
|
|
|
|
|
|
|
|
foreach (const PFileAssociationItem& item, mItems) {
|
2021-10-04 20:36:20 +08:00
|
|
|
if (item->selected == item->defaultSelected
|
|
|
|
&& !item->selected)
|
2021-10-04 19:23:52 +08:00
|
|
|
continue;
|
|
|
|
bool ok;
|
|
|
|
fileTypes.insert("DevCpp."+item->suffix);
|
|
|
|
fileTypeDescriptions.insert("DevCpp."+item->suffix,item);
|
|
|
|
if (!item->selected) {
|
|
|
|
ok = unregisterAssociation("."+item->suffix);
|
|
|
|
} else {
|
|
|
|
fileTypeUsed.insert("DevCpp."+item->suffix);
|
|
|
|
ok = registerAssociation(
|
|
|
|
"."+item->suffix,
|
|
|
|
"DevCpp."+item->suffix
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (!ok) {
|
2022-10-26 09:33:45 +08:00
|
|
|
QMessageBox::critical(nullptr,
|
2021-10-04 19:23:52 +08:00
|
|
|
tr("Register File Association Error"),
|
|
|
|
tr("Don't have privilege to register file types!"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
foreach (const QString& fileType, fileTypes) {
|
|
|
|
bool ok;
|
|
|
|
if (fileTypeUsed.contains(fileType)) {
|
|
|
|
PFileAssociationItem item = fileTypeDescriptions[fileType];
|
|
|
|
ok = registerFileType(fileType,
|
|
|
|
item->name,
|
2021-10-04 20:05:24 +08:00
|
|
|
"Open",
|
2021-10-04 19:23:52 +08:00
|
|
|
pSettings->dirs().executable(),
|
|
|
|
item->icon);
|
|
|
|
} else {
|
|
|
|
ok = unregisterFileType(fileType);
|
|
|
|
}
|
|
|
|
if (!ok) {
|
2022-10-26 09:33:45 +08:00
|
|
|
QMessageBox::critical(nullptr,
|
2021-10-04 19:23:52 +08:00
|
|
|
tr("Register File Type Error"),
|
|
|
|
tr("Don't have privilege to register file types!"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-10-20 18:05:43 +08:00
|
|
|
bool FileAssociationModel::checkAssociation(const QString &extension, const QString &filetype, const QString &verb, const QString &serverApp)
|
2021-10-04 19:23:52 +08:00
|
|
|
{
|
|
|
|
HKEY key;
|
|
|
|
LONG result;
|
|
|
|
|
|
|
|
result = RegOpenKeyExA(HKEY_CLASSES_ROOT,extension.toLocal8Bit(),0,KEY_READ,&key);
|
|
|
|
RegCloseKey(key);
|
|
|
|
if (result != ERROR_SUCCESS )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
result = RegOpenKeyExA(HKEY_CLASSES_ROOT,filetype.toLocal8Bit(),0,KEY_READ,&key);
|
|
|
|
RegCloseKey(key);
|
|
|
|
if (result != ERROR_SUCCESS )
|
|
|
|
return false;
|
|
|
|
|
2021-10-04 20:05:24 +08:00
|
|
|
QString keyString = QString("%1\\Shell\\%2\\Command").arg(filetype).arg(verb);
|
2021-10-04 19:23:52 +08:00
|
|
|
QString value1,value2;
|
2021-11-05 12:37:40 +08:00
|
|
|
if (!readRegistry(HKEY_CLASSES_ROOT,keyString.toLocal8Bit(),"",value1))
|
2021-10-04 19:23:52 +08:00
|
|
|
return false;
|
2021-11-05 12:37:40 +08:00
|
|
|
if (!readRegistry(HKEY_CLASSES_ROOT,extension.toLocal8Bit(),"",value2))
|
2021-10-04 19:23:52 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return (value2 == filetype)
|
|
|
|
&& (value1.compare(serverApp,PATH_SENSITIVITY)==0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool writeRegistry(HKEY parentKey, const QByteArray& subKey, const QByteArray& value) {
|
|
|
|
DWORD disposition;
|
|
|
|
HKEY key;
|
|
|
|
LONG result = RegCreateKeyExA(
|
|
|
|
parentKey,
|
|
|
|
subKey,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
REG_OPTION_NON_VOLATILE,
|
|
|
|
KEY_ALL_ACCESS,
|
|
|
|
NULL,
|
|
|
|
&key,
|
|
|
|
&disposition);
|
|
|
|
RegCloseKey(key);
|
|
|
|
if (result != ERROR_SUCCESS ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
result = RegSetKeyValueA(
|
|
|
|
HKEY_CLASSES_ROOT,
|
|
|
|
subKey,
|
|
|
|
"",
|
|
|
|
REG_SZ,
|
|
|
|
(const BYTE*)value.data(),
|
|
|
|
value.length()+1);
|
|
|
|
return result == ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
bool FileAssociationModel::registerAssociation(const QString &extension, const QString &filetype)
|
|
|
|
{
|
|
|
|
if (!writeRegistry(HKEY_CLASSES_ROOT,
|
|
|
|
extension.toLocal8Bit(),
|
|
|
|
filetype.toLocal8Bit())){
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FileAssociationModel::unregisterAssociation(const QString &extension)
|
|
|
|
{
|
|
|
|
LONG result;
|
|
|
|
HKEY key;
|
|
|
|
result = RegOpenKeyExA(HKEY_CLASSES_ROOT,extension.toLocal8Bit(),0,KEY_READ,&key);
|
|
|
|
if (result != ERROR_SUCCESS )
|
|
|
|
return true;
|
|
|
|
RegCloseKey(key);
|
|
|
|
|
|
|
|
result = RegDeleteTreeA(HKEY_CLASSES_ROOT,extension.toLocal8Bit());
|
|
|
|
return result == ERROR_SUCCESS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FileAssociationModel::unregisterFileType(const QString &fileType)
|
|
|
|
{
|
|
|
|
LONG result;
|
|
|
|
HKEY key;
|
|
|
|
result = RegOpenKeyExA(HKEY_CLASSES_ROOT,fileType.toLocal8Bit(),0,KEY_READ,&key);
|
|
|
|
if (result != ERROR_SUCCESS )
|
|
|
|
return true;
|
|
|
|
RegCloseKey(key);
|
|
|
|
|
|
|
|
result = RegDeleteTreeA(HKEY_CLASSES_ROOT,fileType.toLocal8Bit());
|
|
|
|
return result == ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FileAssociationModel::registerFileType(const QString &filetype, const QString &description, const QString &verb, const QString &serverApp, int icon)
|
|
|
|
{
|
|
|
|
if (!writeRegistry(HKEY_CLASSES_ROOT,
|
|
|
|
filetype.toLocal8Bit(),
|
|
|
|
description.toLocal8Bit()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
QString keyString = QString("%1\\DefaultIcon").arg(filetype);
|
|
|
|
QString value = QString("%1,%2").arg(serverApp).arg(icon);
|
|
|
|
if (!writeRegistry(HKEY_CLASSES_ROOT,
|
|
|
|
keyString.toLocal8Bit(),
|
|
|
|
value.toLocal8Bit()))
|
|
|
|
return false;
|
2021-10-04 20:05:24 +08:00
|
|
|
keyString = QString("%1\\Shell\\%2\\Command").arg(filetype).arg(verb);
|
2021-10-04 19:23:52 +08:00
|
|
|
value = serverApp+" \"%1\"";
|
|
|
|
if (!writeRegistry(HKEY_CLASSES_ROOT,
|
|
|
|
keyString.toLocal8Bit(),
|
|
|
|
value.toLocal8Bit()))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-10-20 18:05:43 +08:00
|
|
|
int FileAssociationModel::rowCount(const QModelIndex &) const
|
2021-10-04 19:23:52 +08:00
|
|
|
{
|
|
|
|
return mItems.count();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant FileAssociationModel::data(const QModelIndex &index, int role) const
|
|
|
|
{
|
|
|
|
if (!index.isValid()) {
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
PFileAssociationItem item = mItems[index.row()];
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
|
|
return QString("%1 (*.%2)").arg(item->name).arg(item->suffix);
|
|
|
|
} else if (role == Qt::CheckStateRole) {
|
|
|
|
return (item->selected)? Qt::Checked : Qt::Unchecked;
|
|
|
|
}
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FileAssociationModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
|
|
{
|
|
|
|
if (!index.isValid())
|
|
|
|
return false;
|
|
|
|
if (role == Qt::CheckStateRole) {
|
|
|
|
PFileAssociationItem item = mItems[index.row()];
|
|
|
|
item->selected = value.toBool();
|
|
|
|
emit associationChanged();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::ItemFlags FileAssociationModel::flags(const QModelIndex &) const
|
|
|
|
{
|
|
|
|
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
|
|
|
|
}
|