- implement: register file associations
This commit is contained in:
parent
13bd395134
commit
0ae9b92924
1
NEWS.md
1
NEWS.md
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -377,6 +377,8 @@ private slots:
|
|||
|
||||
void on_btnReplace_clicked();
|
||||
|
||||
void on_btnCancelReplace_clicked();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
EditorList *mEditorList;
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue