diff --git a/src/lib/settings/ApplicationSettings.cpp b/src/lib/settings/ApplicationSettings.cpp index a958e16c..4adaf66e 100644 --- a/src/lib/settings/ApplicationSettings.cpp +++ b/src/lib/settings/ApplicationSettings.cpp @@ -117,6 +117,16 @@ void ApplicationSettings::setFontSize(int fontSize) setValue("application/font_size", fontSize); } +std::string ApplicationSettings::getTextEncoding() const +{ + return getValue("application/text_encoding", "UTF-8"); +} + +void ApplicationSettings::setTextEncoding(const std::string& textEncoding) +{ + setValue("application/text_encoding", textEncoding); +} + bool ApplicationSettings::getUseAnimations() const { return getValue("application/use_animations", true); diff --git a/src/lib/settings/ApplicationSettings.h b/src/lib/settings/ApplicationSettings.h index 23ac0687..8b44402a 100644 --- a/src/lib/settings/ApplicationSettings.h +++ b/src/lib/settings/ApplicationSettings.h @@ -32,6 +32,9 @@ public: int getFontSize() const; void setFontSize(int fontSize); + std::string getTextEncoding() const; + void setTextEncoding(const std::string& textEncoding); + FilePath getColorSchemePath() const; void setColorSchemePath(const FilePath& colorSchemePath); diff --git a/src/lib_gui/CMakeLists.txt b/src/lib_gui/CMakeLists.txt index f834fca6..81331a63 100644 --- a/src/lib_gui/CMakeLists.txt +++ b/src/lib_gui/CMakeLists.txt @@ -58,6 +58,8 @@ add_files( qt/element/QtStatusBar.h qt/element/QtTable.cpp qt/element/QtTable.h + qt/element/QtTextEncodingPicker.cpp + qt/element/QtTextEncodingPicker.h qt/element/QtTooltip.cpp qt/element/QtTooltip.h qt/element/QtUndoRedo.cpp diff --git a/src/lib_gui/qt/element/QtCodeField.cpp b/src/lib_gui/qt/element/QtCodeField.cpp index e638ce47..d14a9931 100644 --- a/src/lib_gui/qt/element/QtCodeField.cpp +++ b/src/lib_gui/qt/element/QtCodeField.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "data/location/SourceLocation.h" #include "data/location/SourceLocationFile.h" @@ -47,8 +48,15 @@ QtCodeField::QtCodeField( displayCode.pop_back(); } - setPlainText(QString::fromUtf8(displayCode.c_str())); + QTextCodec* codec = QTextCodec::codecForName(ApplicationSettings::getInstance()->getTextEncoding().c_str()); + QString convertedDisplayCode(codec->toUnicode(displayCode.c_str())); + setPlainText(convertedDisplayCode); createLineLengthCache(); + if (displayCode.size() != convertedDisplayCode.length()) + { + LOG_INFO("Converting displayed code to " + codec->name().toStdString() + " resulted in offset of source locations. Correcting this now."); + createMultibyteCharacterLocationCache(); + } this->setMouseTracking(true); @@ -333,9 +341,12 @@ void QtCodeField::createAnnotations(std::shared_ptr location } else if (startLocation->getLineNumber() <= endLineNumber) { - annotation.start = toTextEditPosition(startLocation->getLineNumber(), startLocation->getColumnNumber() - 1); - annotation.startLine = startLocation->getLineNumber(); - annotation.startCol = startLocation->getColumnNumber() - 1; + const int startLine = startLocation->getLineNumber(); + const int startCol = getColumnCorrectedForMultibyteCharacters(startLine, startLocation->getColumnNumber() - 1); + + annotation.start = toTextEditPosition(startLine, startCol); + annotation.startLine = startLine; + annotation.startCol = startCol; } else { @@ -351,9 +362,12 @@ void QtCodeField::createAnnotations(std::shared_ptr location } else if (endLocation->getLineNumber() >= m_startLineNumber) { - annotation.end = toTextEditPosition(endLocation->getLineNumber(), endLocation->getColumnNumber()); - annotation.endLine = endLocation->getLineNumber(); - annotation.endCol = endLocation->getColumnNumber(); + const int endLine = endLocation->getLineNumber(); + const int endCol = getColumnCorrectedForMultibyteCharacters(endLine, endLocation->getColumnNumber()); + + annotation.end = toTextEditPosition(endLine, endCol); + annotation.endLine = endLine; + annotation.endCol = endCol; } else { @@ -641,3 +655,40 @@ void QtCodeField::createLineLengthCache() m_endTextEditPosition += it.length(); } } + +void QtCodeField::createMultibyteCharacterLocationCache() +{ + m_multibyteCharacterLocations.clear(); + QTextCodec* codec = QTextCodec::codecForName(ApplicationSettings::getInstance()->getTextEncoding().c_str()); + + for (QTextBlock itLine = document()->begin(); itLine != document()->end(); itLine = itLine.next()) + { + std::vector> columnsToOffsets; + const QString line = itLine.text(); + for (int i = 0; i < line.size(); i++) + { + if (line[i].unicode() > 127) + { + int ss = codec->fromUnicode(line[i]).size(); + columnsToOffsets.push_back(std::make_pair(i, ss)); + } + } + m_multibyteCharacterLocations.push_back(columnsToOffsets); + } +} + +int QtCodeField::getColumnCorrectedForMultibyteCharacters(const int line, int column) const +{ + const int relativeLineNumber = line - m_startLineNumber; + if (relativeLineNumber < m_multibyteCharacterLocations.size()) + { + for (const std::pair m_multibyteCharacterLocation : m_multibyteCharacterLocations[relativeLineNumber]) + { + if (column > m_multibyteCharacterLocation.first) + { + column -= m_multibyteCharacterLocation.second - 1; + } + } + } + return column; +} diff --git a/src/lib_gui/qt/element/QtCodeField.h b/src/lib_gui/qt/element/QtCodeField.h index b7ed38b8..2d16b635 100644 --- a/src/lib_gui/qt/element/QtCodeField.h +++ b/src/lib_gui/qt/element/QtCodeField.h @@ -105,6 +105,8 @@ private: static std::vector s_annotationColors; void createLineLengthCache(); + void createMultibyteCharacterLocationCache(); + int getColumnCorrectedForMultibyteCharacters(const int line, int column) const; const uint m_startLineNumber; const std::string m_code; @@ -114,6 +116,7 @@ private: QtHighlighter* m_highlighter; std::vector m_lineLengths; + std::vector>> m_multibyteCharacterLocations; int m_endTextEditPosition; }; diff --git a/src/lib_gui/qt/element/QtFontPicker.cpp b/src/lib_gui/qt/element/QtFontPicker.cpp index 70234b3d..63a1a3b7 100644 --- a/src/lib_gui/qt/element/QtFontPicker.cpp +++ b/src/lib_gui/qt/element/QtFontPicker.cpp @@ -1,7 +1,6 @@ #include "qt/element/QtFontPicker.h" #include -#include #include QtFontPicker::QtFontPicker(QWidget *parent) @@ -15,7 +14,7 @@ QtFontPicker::QtFontPicker(QWidget *parent) layout->setAlignment(Qt::AlignTop); m_box = new QFontComboBox(); - m_box->setFontFilters( QFontComboBox::MonospacedFonts ); + m_box->setFontFilters(QFontComboBox::MonospacedFonts); m_box->setEditable(false); layout->addWidget(m_box); @@ -33,4 +32,3 @@ void QtFontPicker::setText(QString text) { m_box->setCurrentText(text); } - diff --git a/src/lib_gui/qt/element/QtTextEncodingPicker.cpp b/src/lib_gui/qt/element/QtTextEncodingPicker.cpp new file mode 100644 index 00000000..c04a7c53 --- /dev/null +++ b/src/lib_gui/qt/element/QtTextEncodingPicker.cpp @@ -0,0 +1,39 @@ +#include "qt/element/QtTextEncodingPicker.h" + +#include +#include +#include + +QtTextEncodingPicker::QtTextEncodingPicker(QWidget *parent) + : QWidget(parent) +{ + setObjectName("picker"); + + QBoxLayout* layout = new QHBoxLayout(); + layout->setSpacing(0); + layout->setContentsMargins(1, 1, 1, 1); + layout->setAlignment(Qt::AlignTop); + + m_box = new QComboBox(); + for (int mib : QTextCodec::availableMibs()) + { + m_box->addItem(QTextCodec::codecForMib(mib)->name()); + } + m_box->setEditable(false); + + layout->addWidget(m_box); + + setLayout(layout); + setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); +} + +QString QtTextEncodingPicker::getText() +{ + return m_box->currentText(); +} + +void QtTextEncodingPicker::setText(QString text) +{ + m_box->setCurrentText(text); +} + diff --git a/src/lib_gui/qt/element/QtTextEncodingPicker.h b/src/lib_gui/qt/element/QtTextEncodingPicker.h new file mode 100644 index 00000000..e5038786 --- /dev/null +++ b/src/lib_gui/qt/element/QtTextEncodingPicker.h @@ -0,0 +1,23 @@ +#ifndef QT_TEXT_ENCODING_PICKER_H +#define QT_TEXT_ENCODING_PICKER_H + +#include + +class QComboBox; + +class QtTextEncodingPicker + : public QWidget +{ + Q_OBJECT + +public: + QtTextEncodingPicker(QWidget *parent); + + QString getText(); + void setText(QString text); + +private: + QComboBox* m_box; +}; + +#endif // QT_TEXT_ENCODING_PICKER_H diff --git a/src/lib_gui/qt/window/project_wizzard/QtProjectWizzardContentPreferences.cpp b/src/lib_gui/qt/window/project_wizzard/QtProjectWizzardContentPreferences.cpp index c34afd22..e9b771eb 100644 --- a/src/lib_gui/qt/window/project_wizzard/QtProjectWizzardContentPreferences.cpp +++ b/src/lib_gui/qt/window/project_wizzard/QtProjectWizzardContentPreferences.cpp @@ -1,5 +1,7 @@ #include "qt/window/project_wizzard/QtProjectWizzardContentPreferences.h" +#include "qt/element/QtFontPicker.h" +#include "qt/element/QtTextEncodingPicker.h" #include "qt/utility/utilityQt.h" #include "settings/ApplicationSettings.h" #include "utility/file/FileSystem.h" @@ -39,17 +41,25 @@ void QtProjectWizzardContentPreferences::populate(QGridLayout* layout, int& row) m_fontFace->setObjectName("name"); m_fontFace->setAttribute(Qt::WA_MacShowFocusRect, 0); - addLabelAndWidget("Font face", m_fontFace, layout, row); + addLabelAndWidget("Font Face", m_fontFace, layout, row); row++; // font size - m_fontSize = addComboBox("Font size", appSettings->getFontSizeMin(), appSettings->getFontSizeMax(), "", layout, row); + m_fontSize = addComboBox("Font Size", appSettings->getFontSizeMin(), appSettings->getFontSizeMax(), "", layout, row); // tab width - m_tabWidth = addComboBox("Tab width", 1, 16, "", layout, row); + m_tabWidth = addComboBox("Tab Width", 1, 16, "", layout, row); + + // text encoding + m_textEncoding = new QtTextEncodingPicker(this); + m_textEncoding->setObjectName("text encoding"); + m_textEncoding->setAttribute(Qt::WA_MacShowFocusRect, 0); + + addLabelAndWidget("Text Encoding", m_textEncoding, layout, row); + row++; // color scheme - m_colorSchemes = addComboBox("Color scheme", 0, 0, "", layout, row); + m_colorSchemes = addComboBox("Color Scheme", "", layout, row); for (size_t i = 0; i < m_colorSchemePaths.size(); i++) { m_colorSchemes->insertItem(i, m_colorSchemePaths[i].withoutExtension().fileName().c_str()); @@ -292,6 +302,8 @@ void QtProjectWizzardContentPreferences::load() m_fontSize->setCurrentIndex(appSettings->getFontSize() - appSettings->getFontSizeMin()); m_tabWidth->setCurrentIndex(appSettings->getCodeTabWidth() - 1); + m_textEncoding->setText(QString::fromStdString(appSettings->getTextEncoding())); + FilePath colorSchemePath = appSettings->getColorSchemePath(); for (size_t i = 0; i < m_colorSchemePaths.size(); i++) { @@ -346,6 +358,8 @@ void QtProjectWizzardContentPreferences::save() appSettings->setFontSize(m_fontSize->currentIndex() + appSettings->getFontSizeMin()); appSettings->setCodeTabWidth(m_tabWidth->currentIndex() + 1); + appSettings->setTextEncoding(m_textEncoding->getText().toStdString()); + appSettings->setColorSchemePath(m_colorSchemePaths[m_colorSchemes->currentIndex()]); m_oldColorSchemeIndex = -1; @@ -578,11 +592,26 @@ QCheckBox* QtProjectWizzardContentPreferences::addCheckBox( } QComboBox* QtProjectWizzardContentPreferences::addComboBox( - QString label, int min, int max, QString helpText, QGridLayout* layout, int& row) + QString label, QString helpText, QGridLayout* layout, int& row) { QComboBox* comboBox = new QComboBox(this); addLabelAndWidget(label, comboBox, layout, row, Qt::AlignLeft); + if (helpText.size()) + { + addHelpButton(label, helpText, layout, row); + } + + row++; + + return comboBox; +} + +QComboBox* QtProjectWizzardContentPreferences::addComboBox( + QString label, int min, int max, QString helpText, QGridLayout* layout, int& row) +{ + QComboBox* comboBox = addComboBox(label, helpText, layout, row); + if (min != max) { for (int i = min; i <= max; i++) @@ -591,13 +620,6 @@ QComboBox* QtProjectWizzardContentPreferences::addComboBox( } } - if (helpText.size()) - { - addHelpButton(label, helpText, layout, row); - } - - row++; - return comboBox; } diff --git a/src/lib_gui/qt/window/project_wizzard/QtProjectWizzardContentPreferences.h b/src/lib_gui/qt/window/project_wizzard/QtProjectWizzardContentPreferences.h index bc49fd2b..8d3fad68 100644 --- a/src/lib_gui/qt/window/project_wizzard/QtProjectWizzardContentPreferences.h +++ b/src/lib_gui/qt/window/project_wizzard/QtProjectWizzardContentPreferences.h @@ -6,12 +6,14 @@ #include #include -#include "qt/element/QtFontPicker.h" #include "qt/element/QtLocationPicker.h" #include "qt/element/QtDirectoryListBox.h" #include "qt/window/project_wizzard/QtProjectWizzardContent.h" #include "utility/path_detector/CombinedPathDetector.h" +class QtFontPicker; +class QtTextEncodingPicker; + class QtProjectWizzardContentPreferences : public QtProjectWizzardContent { @@ -47,12 +49,14 @@ private: void addGap(QGridLayout* layout, int& row); QCheckBox* addCheckBox(QString label, QString text, QString helpText, QGridLayout* layout, int& row); + QComboBox* addComboBox(QString label, QString helpText, QGridLayout* layout, int& row); QComboBox* addComboBox(QString label, int min, int max, QString helpText, QGridLayout* layout, int& row); QLineEdit* addLineEdit(QString label, QString helpText, QGridLayout* layout, int& row); QtFontPicker* m_fontFace; QComboBox* m_fontSize; QComboBox* m_tabWidth; + QtTextEncodingPicker* m_textEncoding; QComboBox* m_colorSchemes; std::vector m_colorSchemePaths;