diff --git a/NEWS.md b/NEWS.md
index 46b9b802..ebcaa011 100644
--- a/NEWS.md
+++ b/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;
diff --git a/RedPandaIDE/RedPandaIDE.pro b/RedPandaIDE/RedPandaIDE.pro
index 731d2ef5..0e7709c9 100644
--- a/RedPandaIDE/RedPandaIDE.pro
+++ b/RedPandaIDE/RedPandaIDE.pro
@@ -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
diff --git a/RedPandaIDE/mainwindow.h b/RedPandaIDE/mainwindow.h
index c1c58419..66ed7fcc 100644
--- a/RedPandaIDE/mainwindow.h
+++ b/RedPandaIDE/mainwindow.h
@@ -377,6 +377,8 @@ private slots:
void on_btnReplace_clicked();
+ void on_btnCancelReplace_clicked();
+
private:
Ui::MainWindow *ui;
EditorList *mEditorList;
diff --git a/RedPandaIDE/mainwindow.ui b/RedPandaIDE/mainwindow.ui
index 8124e831..57c1dc46 100644
--- a/RedPandaIDE/mainwindow.ui
+++ b/RedPandaIDE/mainwindow.ui
@@ -776,6 +776,13 @@
+ -
+
+
+ Cancel
+
+
+
-
diff --git a/RedPandaIDE/settings.cpp b/RedPandaIDE/settings.cpp
index 9f6545e9..9e9d498d 100644
--- a/RedPandaIDE/settings.cpp
+++ b/RedPandaIDE/settings.cpp
@@ -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()
{
diff --git a/RedPandaIDE/settings.h b/RedPandaIDE/settings.h
index 868d9fd5..fc99b1c8 100644
--- a/RedPandaIDE/settings.h
+++ b/RedPandaIDE/settings.h
@@ -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:
diff --git a/RedPandaIDE/settingsdialog/environmentfileassociationwidget.cpp b/RedPandaIDE/settingsdialog/environmentfileassociationwidget.cpp
new file mode 100644
index 00000000..eec6920e
--- /dev/null
+++ b/RedPandaIDE/settingsdialog/environmentfileassociationwidget.cpp
@@ -0,0 +1,301 @@
+#include "environmentfileassociationwidget.h"
+#include "ui_environmentfileassociationwidget.h"
+#include "../systemconsts.h"
+#include "../settings.h"
+
+#include
+#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("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();
+ 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)
+ 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;
+}
diff --git a/RedPandaIDE/settingsdialog/environmentfileassociationwidget.h b/RedPandaIDE/settingsdialog/environmentfileassociationwidget.h
new file mode 100644
index 00000000..9d67c627
--- /dev/null
+++ b/RedPandaIDE/settingsdialog/environmentfileassociationwidget.h
@@ -0,0 +1,74 @@
+#ifndef ENVIRONMENTFILEASSOCIATIONWIDGET_H
+#define ENVIRONMENTFILEASSOCIATIONWIDGET_H
+
+#include
+#include
+#include "settingswidget.h"
+
+namespace Ui {
+class EnvironmentFileAssociationWidget;
+}
+struct FileAssociationItem {
+ QString name;
+ QString suffix;
+ int icon;
+ bool selected;
+ bool defaultSelected;
+};
+using PFileAssociationItem = std::shared_ptr;
+
+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 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
diff --git a/RedPandaIDE/settingsdialog/environmentfileassociationwidget.ui b/RedPandaIDE/settingsdialog/environmentfileassociationwidget.ui
new file mode 100644
index 00000000..24122b3b
--- /dev/null
+++ b/RedPandaIDE/settingsdialog/environmentfileassociationwidget.ui
@@ -0,0 +1,46 @@
+
+
+ EnvironmentFileAssociationWidget
+
+
+
+ 0
+ 0
+ 708
+ 350
+
+
+
+ Form
+
+
+
-
+
+
+ File Types:
+
+
+
-
+
+
+
+
+
+ -
+
+
+ Just check or uncheck for which file types Dev-C++ wil be registered as the default application to open them ...
+
+
+ Qt::AlignCenter
+
+
+ true
+
+
+
+
+
+
+
+
diff --git a/RedPandaIDE/settingsdialog/settingsdialog.cpp b/RedPandaIDE/settingsdialog/settingsdialog.cpp
index d2eb21c1..b0b895cb 100644
--- a/RedPandaIDE/settingsdialog/settingsdialog.cpp
+++ b/RedPandaIDE/settingsdialog/settingsdialog.cpp
@@ -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);