File indexing completed on 2024-05-26 05:56:26

0001 /*
0002     This file is part of the Okteta Kasten module, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2011, 2022 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "charsetconversiontool.hpp"
0010 
0011 // tool
0012 #include "charsetconversionjob.hpp"
0013 // Okteta Kasten gui
0014 #include <Kasten/Okteta/ByteArrayView>
0015 // Okteta Kasten core
0016 #include <Kasten/Okteta/ByteArrayDocument>
0017 // Okteta core
0018 #include <Okteta/CharCodec>
0019 #include <Okteta/AbstractByteArrayModel>
0020 #include <Okteta/ChangesDescribable>
0021 // KF
0022 #include <KConfigGroup>
0023 #include <KSharedConfig>
0024 #include <KLocalizedString>
0025 // Qt
0026 #include <QApplication>
0027 
0028 
0029 template <>
0030 inline Kasten::CharsetConversionTool::ConversionDirection KConfigGroup::readEntry(const char *key,
0031                                                                                    const Kasten::CharsetConversionTool::ConversionDirection &defaultValue) const
0032 {
0033     const QString entry = readEntry(key, QString());
0034     const Kasten::CharsetConversionTool::ConversionDirection direction =
0035         (entry == QLatin1String("From")) ?    Kasten::CharsetConversionTool::ConvertFrom :
0036         (entry == QLatin1String("To")) ?      Kasten::CharsetConversionTool::ConvertTo :
0037         /* else */                            defaultValue;
0038     return direction;
0039 }
0040 
0041 template <>
0042 inline void KConfigGroup::writeEntry(const char *key,
0043                                      const Kasten::CharsetConversionTool::ConversionDirection &value,
0044                                      KConfigBase::WriteConfigFlags flags)
0045 {
0046     const QString valueString =
0047         (value == Kasten::CharsetConversionTool::ConvertFrom) ? QLatin1String("From") : QLatin1String("To");
0048     writeEntry(key, valueString, flags);
0049 }
0050 
0051 namespace Kasten {
0052 // helper class for KConfigGroup API
0053 class ByteParameter
0054 {
0055 public:
0056     Okteta::Byte value;
0057 
0058     operator Okteta::Byte() const { return value; }
0059 };
0060 }
0061 
0062 template <>
0063 inline Kasten::ByteParameter KConfigGroup::readEntry(const char *key,
0064                                                      const Kasten::ByteParameter &defaultValue) const
0065 {
0066     const int storageValue = readEntry(key, -1);
0067     if ((0 <= storageValue) && (storageValue <= 255)) {
0068         return Kasten::ByteParameter{static_cast<Okteta::Byte>(storageValue)};
0069     }
0070     return defaultValue;
0071 }
0072 
0073 template <>
0074 inline void KConfigGroup::writeEntry(const char *key,
0075                                      const Kasten::ByteParameter &value,
0076                                      KConfigBase::WriteConfigFlags flags)
0077 {
0078     writeEntry(key, static_cast<uint>(value.value), flags);
0079 }
0080 
0081 
0082 namespace Kasten {
0083 
0084 CharsetConversionTool::CharsetConversionTool()
0085 {
0086     setObjectName(QStringLiteral("CharsetConversion"));
0087 
0088     const KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0089 
0090     mOtherCharCodecName = configGroup.readEntry(OtherCharCodecNameConfigKey);
0091     mConversionDirection = configGroup.readEntry(ConversionDirectionConfigKey, DefaultConversionDirection);
0092     mSubstitutingMissingChars = configGroup.readEntry(SubstituteMissingCharsConfigKey, DefaultSubstituteMissingChars);
0093     mSubstituteByte = configGroup.readEntry(SubstituteByteConfigKey, ByteParameter{DefaultSubstituteByte});
0094 }
0095 
0096 CharsetConversionTool::~CharsetConversionTool() = default;
0097 
0098 bool CharsetConversionTool::isApplyable() const
0099 {
0100     return (mByteArrayModel &&
0101             mByteArrayView && mByteArrayView->hasSelectedData() &&
0102             !mOtherCharCodecName.isEmpty() &&
0103             mByteArrayView->charCodingName() != mOtherCharCodecName);
0104 }
0105 
0106 QString CharsetConversionTool::title() const
0107 {
0108     return i18nc("@title:window of the tool to convert between charsets",
0109                  "Charset Conversion");
0110 }
0111 
0112 QString CharsetConversionTool::otherCharCodecName() const
0113 {
0114     return mOtherCharCodecName;
0115 }
0116 
0117 CharsetConversionTool::ConversionDirection CharsetConversionTool::conversionDirection() const
0118 {
0119     return mConversionDirection;
0120 }
0121 
0122 bool CharsetConversionTool::isSubstitutingMissingChars() const
0123 {
0124     return mSubstitutingMissingChars;
0125 }
0126 
0127 Okteta::Byte CharsetConversionTool::substituteByte() const
0128 {
0129     return mSubstituteByte;
0130 }
0131 
0132 void CharsetConversionTool::setTargetModel(AbstractModel* model)
0133 {
0134     if (mByteArrayView) {
0135         mByteArrayView->disconnect(this);
0136     }
0137 
0138     mByteArrayView = model ? model->findBaseModel<ByteArrayView*>() : nullptr;
0139 
0140     ByteArrayDocument* document =
0141         mByteArrayView ? qobject_cast<ByteArrayDocument*>(mByteArrayView->baseModel()) : nullptr;
0142     mByteArrayModel = document ? document->content() : nullptr;
0143 
0144     if (mByteArrayView && mByteArrayModel) {
0145         connect(mByteArrayView,  &ByteArrayView::charCodecChanged,
0146                 this, &CharsetConversionTool::onViewChanged);
0147         connect(mByteArrayView,  &ByteArrayView::selectedDataChanged,
0148                 this, &CharsetConversionTool::onViewChanged);
0149     }
0150 
0151     onViewChanged();
0152 }
0153 
0154 void CharsetConversionTool::setOtherCharCodecName(const QString& codecName)
0155 {
0156     if (codecName == mOtherCharCodecName) {
0157         return;
0158     }
0159 
0160     mOtherCharCodecName = codecName;
0161 
0162     KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0163     configGroup.writeEntry(OtherCharCodecNameConfigKey, mOtherCharCodecName);
0164 
0165     Q_EMIT isApplyableChanged(isApplyable());
0166 }
0167 
0168 void CharsetConversionTool::setConversionDirection(int conversionDirection)
0169 {
0170     if (mConversionDirection == (ConversionDirection)conversionDirection) {
0171         return;
0172     }
0173 
0174     mConversionDirection = (ConversionDirection)conversionDirection;
0175 
0176     KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0177     configGroup.writeEntry(ConversionDirectionConfigKey, mConversionDirection);
0178 }
0179 
0180 void CharsetConversionTool::setSubstitutingMissingChars(bool isSubstitutingMissingChars)
0181 {
0182     if (mSubstitutingMissingChars == isSubstitutingMissingChars) {
0183         return;
0184     }
0185 
0186     mSubstitutingMissingChars = isSubstitutingMissingChars;
0187 
0188     KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0189     configGroup.writeEntry(SubstituteMissingCharsConfigKey, mSubstitutingMissingChars);
0190 }
0191 
0192 void CharsetConversionTool::setSubstituteByte(int byte)
0193 {
0194     if (mSubstituteByte == static_cast<Okteta::Byte>(byte)) {
0195         return;
0196     }
0197 
0198     mSubstituteByte = static_cast<Okteta::Byte>(byte);
0199 
0200     KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0201     configGroup.writeEntry(SubstituteByteConfigKey, ByteParameter{mSubstituteByte});
0202 }
0203 
0204 void CharsetConversionTool::convertChars()
0205 {
0206     QApplication::setOverrideCursor(Qt::WaitCursor);
0207 
0208     const Okteta::AddressRange convertedSection = mByteArrayView->selection();
0209     QByteArray conversionResult;
0210     conversionResult.resize(convertedSection.width());
0211 
0212     Okteta::CharCodec* viewCharCodec =
0213         Okteta::CharCodec::createCodec(mByteArrayView->charCodingName());
0214     Okteta::CharCodec* otherCharCodec =
0215         Okteta::CharCodec::createCodec(mOtherCharCodecName);
0216     const bool convertToOther = (mConversionDirection == ConvertTo);
0217     Okteta::CharCodec* fromCharCodec = convertToOther ? viewCharCodec : otherCharCodec;
0218     Okteta::CharCodec* toCharCodec = convertToOther ? otherCharCodec : viewCharCodec;
0219     auto* charsetConversionJob =
0220         new CharsetConversionJob(reinterpret_cast<Okteta::Byte*>(conversionResult.data()),
0221                                  mByteArrayModel, convertedSection,
0222                                  convertToOther ? viewCharCodec : otherCharCodec,
0223                                  convertToOther ? otherCharCodec : viewCharCodec,
0224                                  mSubstitutingMissingChars, mSubstituteByte
0225                                  ); // TODO: report also actually converted bytes
0226     const bool success = charsetConversionJob->exec();
0227 
0228     if (success) { // TODO: if nothing needed to be converted, just report and don't add change
0229         Okteta::ChangesDescribable* changesDescribable =
0230             qobject_cast<Okteta::ChangesDescribable*>(mByteArrayModel);
0231 
0232         if (changesDescribable) {
0233             const QString description =
0234                 i18nc("Converted from charset 1 to charset 2",
0235                       "%1 to %2", fromCharCodec->name(), toCharCodec->name());
0236             changesDescribable->openGroupedChange(description);
0237         }
0238         mByteArrayModel->replace(convertedSection, conversionResult);
0239         if (changesDescribable) {
0240             changesDescribable->closeGroupedChange();
0241         }
0242     }
0243 
0244     delete viewCharCodec;
0245     delete otherCharCodec;
0246 
0247     QApplication::restoreOverrideCursor();
0248 
0249     const QMap<Okteta::Byte, int>& failedPerByteCount = charsetConversionJob->failedPerByteCount();
0250     const int convertedBytesCount = charsetConversionJob->convertedBytesCount();
0251 
0252     mByteArrayView->setFocus();
0253 
0254     Q_EMIT conversionDone(success, convertedBytesCount, failedPerByteCount);
0255 }
0256 
0257 void CharsetConversionTool::onViewChanged()
0258 {
0259     Q_EMIT isApplyableChanged(isApplyable());
0260 }
0261 
0262 }
0263 
0264 #include "moc_charsetconversiontool.cpp"