- implement: register file associations

This commit is contained in:
royqh1979@gmail.com 2021-10-04 19:23:52 +08:00
parent 13bd395134
commit 0ae9b92924
10 changed files with 446 additions and 1 deletions

View File

@ -13,6 +13,7 @@ Version 0.6.0
- enhancement: replace in files
- enhancement: refactor in project (using search symbol occurence and replace in files)
- fix: search in files
- implement: register file associations
Version 0.5.0
- enhancement: support C++ using type alias;

View File

@ -48,6 +48,7 @@ SOURCES += \
settingsdialog/editormiscwidget.cpp \
settingsdialog/editorsnippetwidget.cpp \
settingsdialog/editortooltipswidget.cpp \
settingsdialog/environmentfileassociationwidget.cpp \
settingsdialog/formattergeneralwidget.cpp \
settingsdialog/projectcompileparamaterswidget.cpp \
settingsdialog/projectcompilerwidget.cpp \
@ -151,6 +152,7 @@ HEADERS += \
settingsdialog/editormiscwidget.h \
settingsdialog/editorsnippetwidget.h \
settingsdialog/editortooltipswidget.h \
settingsdialog/environmentfileassociationwidget.h \
settingsdialog/formattergeneralwidget.h \
settingsdialog/projectcompileparamaterswidget.h \
settingsdialog/projectcompilerwidget.h \
@ -227,6 +229,7 @@ FORMS += \
settingsdialog/editormiscwidget.ui \
settingsdialog/editorsnippetwidget.ui \
settingsdialog/editortooltipswidget.ui \
settingsdialog/environmentfileassociationwidget.ui \
settingsdialog/formattergeneralwidget.ui \
settingsdialog/projectcompileparamaterswidget.ui \
settingsdialog/projectcompilerwidget.ui \
@ -275,7 +278,7 @@ RESOURCES += \
icons.qrc \
translations.qrc
RC_ICONS = images/devcpp.ico
RC_ICONS = images/devcpp.ico images/associations/c.ico images/associations/cpp.ico images/associations/h.ico images/associations/hpp.ico images/associations/dev.ico
#win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../QScintilla/src/release/ -lqscintilla2_qt5d
#else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../QScintilla/src/debug/ -lqscintilla2_qt5d

View File

@ -377,6 +377,8 @@ private slots:
void on_btnReplace_clicked();
void on_btnCancelReplace_clicked();
private:
Ui::MainWindow *ui;
EditorList *mEditorList;

View File

@ -776,6 +776,13 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnCancelReplace">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">

View File

@ -189,6 +189,11 @@ QString Settings::Dirs::config(Settings::Dirs::DataType dataType) const
return "";
}
QString Settings::Dirs::executable() const
{
return QApplication::instance()->applicationFilePath();
}
void Settings::Dirs::doSave()
{

View File

@ -87,6 +87,7 @@ public:
QString projectDir() const;
QString data(DataType dataType = DataType::None) const;
QString config(DataType dataType = DataType::None) const;
QString executable() const;
// _Base interface
protected:

View File

@ -0,0 +1,301 @@
#include "environmentfileassociationwidget.h"
#include "ui_environmentfileassociationwidget.h"
#include "../systemconsts.h"
#include "../settings.h"
#include <QMessageBox>
#include <QSettings>
#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);
mModel.addItem("Dev-C++ Project File","dev",5);
ui->lstFileTypes->setModel(&mModel);
connect(&mModel, &FileAssociationModel::associationChanged,
[this](){
setSettingsChanged();
});
}
EnvironmentFileAssociationWidget::~EnvironmentFileAssociationWidget()
{
delete ui;
}
void EnvironmentFileAssociationWidget::doLoad()
{
mModel.updateAssociationStates();
}
void EnvironmentFileAssociationWidget::doSave()
{
mModel.saveAssociations();
mModel.updateAssociationStates();
}
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,
item->name,
"open",
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) {
if (item->selected == item->defaultSelected)
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 readRegistry(HKEY key,QByteArray subKey, QString& value) {
DWORD dataSize;
LONG result;
result = RegGetValueA(HKEY_CLASSES_ROOT,subKey,
"", RRF_RT_REG_SZ | RRF_RT_REG_MULTI_SZ,
NULL,
NULL,
&dataSize);
if (result!=ERROR_SUCCESS)
return false;
char * buffer = new char[dataSize+10];
result = RegGetValueA(HKEY_CLASSES_ROOT,subKey,
"", RRF_RT_REG_SZ | RRF_RT_REG_MULTI_SZ,
NULL,
buffer,
&dataSize);
if (result!=ERROR_SUCCESS) {
delete[] buffer;
return false;
}
value=QString::fromLocal8Bit(buffer);
delete [] buffer;
return true;
}
bool FileAssociationModel::checkAssociation(const QString &extension, const QString &filetype, const QString &description, 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 &parent) 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;
}

View File

@ -0,0 +1,74 @@
#ifndef ENVIRONMENTFILEASSOCIATIONWIDGET_H
#define ENVIRONMENTFILEASSOCIATIONWIDGET_H
#include <QAbstractListModel>
#include <QWidget>
#include "settingswidget.h"
namespace Ui {
class EnvironmentFileAssociationWidget;
}
struct FileAssociationItem {
QString name;
QString suffix;
int icon;
bool selected;
bool defaultSelected;
};
using PFileAssociationItem = std::shared_ptr<FileAssociationItem>;
class FileAssociationModel:public QAbstractListModel {
Q_OBJECT
public:
explicit FileAssociationModel(QObject* parent = nullptr);
void addItem(const QString& name, const QString& suffix, int icon);
void updateAssociationStates();
void saveAssociations();
signals:
void associationChanged();
private:
bool checkAssociation(const QString& extension,
const QString& filetype,
const QString& description,
const QString& verb,
const QString& serverApp);
bool registerAssociation(const QString& extension,
const QString& filetype);
bool unregisterAssociation(const QString& extension);
bool unregisterFileType(const QString& fileType);
bool registerFileType(const QString& filetype,
const QString& description,
const QString& verb,
const QString& serverApp,
int icon);
private:
QList<PFileAssociationItem> mItems;
// QAbstractItemModel interface
public:
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
};
class EnvironmentFileAssociationWidget : public SettingsWidget
{
Q_OBJECT
public:
explicit EnvironmentFileAssociationWidget(const QString& name, const QString& group, QWidget *parent = nullptr);
~EnvironmentFileAssociationWidget();
private:
Ui::EnvironmentFileAssociationWidget *ui;
FileAssociationModel mModel;
// SettingsWidget interface
protected:
void doLoad() override;
void doSave() override;
};
#endif // ENVIRONMENTFILEASSOCIATIONWIDGET_H

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EnvironmentFileAssociationWidget</class>
<widget class="QWidget" name="EnvironmentFileAssociationWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>708</width>
<height>350</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>File Types:</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QListView" name="lstFileTypes"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Just check or uncheck for which file types Dev-C++ wil be registered as the default application to open them ... </string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -15,6 +15,7 @@
#include "editorsnippetwidget.h"
#include "editormiscwidget.h"
#include "environmentappearencewidget.h"
#include "environmentfileassociationwidget.h"
#include "executorgeneralwidget.h"
#include "debuggeneralwidget.h"
#include "formattergeneralwidget.h"
@ -95,6 +96,10 @@ PSettingsDialog SettingsDialog::optionDialog()
widget->init();
dialog->addWidget(widget);
widget = new EnvironmentFileAssociationWidget(tr("FileAssociation"),tr("Environment"));
widget->init();
dialog->addWidget(widget);
widget = new CompilerSetOptionWidget(tr("Compiler Set"),tr("Compiler"));
widget->init();
dialog->addWidget(widget);