/*
* 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 .
*/
#include "environmentfileassociationwidget.h"
#include "ui_environmentfileassociationwidget.h"
#include "../systemconsts.h"
#include "../settings.h"
#include
#include
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);
mModel.addItem("Red Panda C++ Project File","dev",5);
QItemSelectionModel* m = ui->lstFileTypes->selectionModel();
ui->lstFileTypes->setModel(&mModel);
delete m;
connect(&mModel, &FileAssociationModel::associationChanged,
[this](){
setSettingsChanged();
});
}
EnvironmentFileAssociationWidget::~EnvironmentFileAssociationWidget()
{
delete ui;
}
void EnvironmentFileAssociationWidget::doLoad()
{
if (pSettings->environment().openFilesInSingleInstance())
ui->rbOpenInSingleApplication->setChecked(true);
else
ui->rbOpenInMultiApplication->setChecked(true);
mModel.updateAssociationStates();
}
void EnvironmentFileAssociationWidget::doSave()
{
mModel.saveAssociations();
mModel.updateAssociationStates();
pSettings->environment().setOpenFilesInSingleInstance(ui->rbOpenInSingleApplication->isChecked());
pSettings->environment().save();
}
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();
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,
// item->name,
"Open",
pSettings->dirs().executable()+" \"%1\""
);
item->defaultSelected = item->selected;
}
endResetModel();
}
void FileAssociationModel::saveAssociations()
{
QSet fileTypeUsed;
QSet fileTypes;
QMap fileTypeDescriptions;
foreach (const PFileAssociationItem& item, mItems) {
if (item->selected == item->defaultSelected
&& !item->selected)
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) {
QMessageBox::critical(NULL,
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,
"Open",
pSettings->dirs().executable(),
item->icon);
} else {
ok = unregisterFileType(fileType);
}
if (!ok) {
QMessageBox::critical(NULL,
tr("Register File Type Error"),
tr("Don't have privilege to register file types!"));
return;
}
}
}
bool FileAssociationModel::checkAssociation(const QString &extension, const QString &filetype, const QString &verb, const QString &serverApp)
{
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;
QString keyString = QString("%1\\Shell\\%2\\Command").arg(filetype).arg(verb);
QString value1,value2;
if (!readRegistry(HKEY_CLASSES_ROOT,keyString.toLocal8Bit(),"",value1))
return false;
if (!readRegistry(HKEY_CLASSES_ROOT,extension.toLocal8Bit(),"",value2))
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;
keyString = QString("%1\\Shell\\%2\\Command").arg(filetype).arg(verb);
value = serverApp+" \"%1\"";
if (!writeRegistry(HKEY_CLASSES_ROOT,
keyString.toLocal8Bit(),
value.toLocal8Bit()))
return false;
return true;
}
int FileAssociationModel::rowCount(const QModelIndex &) const
{
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;
}