File indexing completed on 2023-09-24 07:59:32
0001 /* 0002 SPDX-FileCopyrightText: 2003 Jason Keirstead <jason@keirstead.org> 0003 SPDX-FileCopyrightText: 2006 Michel Hermier <michel.hermier@gmail.com> 0004 SPDX-FileCopyrightText: 2007 Nick Shaforostoff <shafff@ukr.net> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "kcodecaction.h" 0010 #include "kconfigwidgets_debug.h" 0011 0012 #include <KCharsets> 0013 #include <KEncodingProber> 0014 #include <KLocalizedString> 0015 0016 #include <QMenu> 0017 #include <QTextCodec> 0018 #include <QVariant> 0019 0020 // According to http://www.iana.org/assignments/ianacharset-mib 0021 // the default/unknown mib value is 2. 0022 #define MIB_DEFAULT 2 0023 0024 class KCodecActionPrivate 0025 { 0026 public: 0027 KCodecActionPrivate(KCodecAction *parent) 0028 : q(parent) 0029 { 0030 } 0031 0032 void init(bool); 0033 0034 void subActionTriggered(QAction *); 0035 0036 KCodecAction *const q; 0037 QAction *defaultAction = nullptr; 0038 QAction *currentSubAction = nullptr; 0039 }; 0040 0041 KCodecAction::KCodecAction(QObject *parent, bool showAutoOptions) 0042 : KSelectAction(parent) 0043 , d(new KCodecActionPrivate(this)) 0044 { 0045 d->init(showAutoOptions); 0046 } 0047 0048 KCodecAction::KCodecAction(const QString &text, QObject *parent, bool showAutoOptions) 0049 : KSelectAction(text, parent) 0050 , d(new KCodecActionPrivate(this)) 0051 { 0052 d->init(showAutoOptions); 0053 } 0054 0055 KCodecAction::KCodecAction(const QIcon &icon, const QString &text, QObject *parent, bool showAutoOptions) 0056 : KSelectAction(icon, text, parent) 0057 , d(new KCodecActionPrivate(this)) 0058 { 0059 d->init(showAutoOptions); 0060 } 0061 0062 KCodecAction::~KCodecAction() = default; 0063 0064 void KCodecActionPrivate::init(bool showAutoOptions) 0065 { 0066 q->setToolBarMode(KSelectAction::MenuMode); 0067 defaultAction = q->addAction(i18nc("Encodings menu", "Default")); 0068 0069 const auto lstEncodings = KCharsets::charsets()->encodingsByScript(); 0070 for (const QStringList &encodingsForScript : lstEncodings) { 0071 KSelectAction *tmp = new KSelectAction(encodingsForScript.at(0), q); 0072 if (showAutoOptions) { 0073 KEncodingProber::ProberType scri = KEncodingProber::proberTypeForName(encodingsForScript.at(0)); 0074 if (scri != KEncodingProber::None) { 0075 tmp->addAction(i18nc("Encodings menu", "Autodetect"))->setData(QVariant((uint)scri)); 0076 tmp->menu()->addSeparator(); 0077 } 0078 } 0079 for (int i = 1; i < encodingsForScript.size(); ++i) { 0080 tmp->addAction(encodingsForScript.at(i)); 0081 } 0082 q->connect(tmp, qOverload<QAction *>(&KSelectAction::triggered), q, [this](QAction *action) { 0083 subActionTriggered(action); 0084 }); 0085 tmp->setCheckable(true); 0086 q->addAction(tmp); 0087 } 0088 q->setCurrentItem(0); 0089 0090 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 78) 0091 // forward deprecated signals to undeprecated, to be backward-compatible to unported subclasses 0092 QObject::connect(q, qOverload<QTextCodec *>(&KCodecAction::triggered), q, &KCodecAction::codecTriggered); 0093 QObject::connect(q, qOverload<KEncodingProber::ProberType>(&KCodecAction::triggered), q, &KCodecAction::encodingProberTriggered); 0094 #endif 0095 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 103) 0096 QObject::connect(q, &KCodecAction::codecTriggered, q, [this](QTextCodec *codec) { 0097 Q_EMIT q->codecNameTriggered(QString::fromUtf8(codec->name())); 0098 }); 0099 #endif 0100 } 0101 0102 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 103) 0103 int KCodecAction::mibForName(const QString &codecName, bool *ok) const 0104 { 0105 // FIXME logic is good but code is ugly 0106 0107 bool success = false; 0108 int mib = MIB_DEFAULT; 0109 KCharsets *charsets = KCharsets::charsets(); 0110 0111 if (codecName == d->defaultAction->text()) { 0112 success = true; 0113 } else { 0114 QTextCodec *codec = charsets->codecForName(codecName, success); 0115 if (!success) { 0116 // Maybe we got a description name instead 0117 codec = charsets->codecForName(charsets->encodingForName(codecName), success); 0118 } 0119 0120 if (codec) { 0121 mib = codec->mibEnum(); 0122 } 0123 } 0124 0125 if (ok) { 0126 *ok = success; 0127 } 0128 0129 if (success) { 0130 return mib; 0131 } 0132 0133 qCWarning(KCONFIG_WIDGETS_LOG) << "Invalid codec name: " << codecName; 0134 return MIB_DEFAULT; 0135 } 0136 #endif 0137 0138 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 103) 0139 QTextCodec *KCodecAction::codecForMib(int mib) const 0140 { 0141 if (mib == MIB_DEFAULT) { 0142 // FIXME offer to change the default codec 0143 return QTextCodec::codecForLocale(); 0144 } else { 0145 return QTextCodec::codecForMib(mib); 0146 } 0147 } 0148 #endif 0149 0150 void KCodecAction::actionTriggered(QAction *action) 0151 { 0152 // we don't want to emit any signals from top-level items 0153 // except for the default one 0154 if (action == d->defaultAction) { 0155 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 78) 0156 Q_EMIT triggered(KEncodingProber::Universal); 0157 #elif KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 102) 0158 Q_EMIT encodingProberTriggered(KEncodingProber::Universal); 0159 #endif 0160 Q_EMIT defaultItemTriggered(); 0161 } 0162 } 0163 0164 void KCodecActionPrivate::subActionTriggered(QAction *action) 0165 { 0166 if (currentSubAction == action) { 0167 return; 0168 } 0169 currentSubAction = action; 0170 bool ok = true; 0171 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 103) 0172 int mib = q->mibForName(action->text(), &ok); 0173 #endif 0174 if (ok) { 0175 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 78) 0176 QT_WARNING_PUSH 0177 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") 0178 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") 0179 // will also indirectly emit textTriggered, due to signal connection in KSelectAction 0180 Q_EMIT q->triggered(action->text()); 0181 QT_WARNING_POP 0182 #else 0183 Q_EMIT q->textTriggered(action->text()); 0184 #endif 0185 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 103) 0186 QTextCodec *codec = q->codecForMib(mib); 0187 #endif 0188 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 78) 0189 // will also indirectly emit codecTriggered, due to signal connection in init() 0190 Q_EMIT q->triggered(codec); 0191 #elif KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 103) 0192 Q_EMIT q->codecTriggered(codec); 0193 #else 0194 Q_EMIT q->codecNameTriggered(action->text()); 0195 #endif 0196 } else { 0197 if (!action->data().isNull()) { 0198 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 102) 0199 const auto encodingProberType = static_cast<KEncodingProber::ProberType>(action->data().toUInt()); 0200 #endif 0201 0202 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 78) 0203 // will also indirectly emit encodingProberTriggered, due to signal connection in init() 0204 Q_EMIT q->triggered(encodingProberType); 0205 #elif KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 102) 0206 Q_EMIT q->encodingProberTriggered(encodingProberType); 0207 #endif 0208 } 0209 } 0210 } 0211 0212 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 103) 0213 QTextCodec *KCodecAction::currentCodec() const 0214 { 0215 return codecForMib(currentCodecMib()); 0216 } 0217 #endif 0218 0219 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 103) 0220 bool KCodecAction::setCurrentCodec(QTextCodec *codec) 0221 { 0222 if (!codec) { 0223 return false; 0224 } 0225 0226 for (int i = 0; i < actions().size(); ++i) { 0227 if (actions().at(i)->menu()) { 0228 for (int j = 0; j < actions().at(i)->menu()->actions().size(); ++j) { 0229 if (!j && !actions().at(i)->menu()->actions().at(j)->data().isNull()) { 0230 continue; 0231 } 0232 if (codec == KCharsets::charsets()->codecForName(actions().at(i)->menu()->actions().at(j)->text())) { 0233 d->currentSubAction = actions().at(i)->menu()->actions().at(j); 0234 d->currentSubAction->trigger(); 0235 return true; 0236 } 0237 } 0238 } 0239 } 0240 return false; 0241 } 0242 #endif 0243 0244 QString KCodecAction::currentCodecName() const 0245 { 0246 return d->currentSubAction->text(); 0247 } 0248 0249 bool KCodecAction::setCurrentCodec(const QString &codecName) 0250 { 0251 if (codecName.isEmpty()) { 0252 return false; 0253 } 0254 0255 for (int i = 0; i < actions().size(); ++i) { 0256 if (actions().at(i)->menu()) { 0257 for (int j = 0; j < actions().at(i)->menu()->actions().size(); ++j) { 0258 if (!j && !actions().at(i)->menu()->actions().at(j)->data().isNull()) { 0259 continue; 0260 } 0261 if (codecName == actions().at(i)->menu()->actions().at(j)->text()) { 0262 d->currentSubAction = actions().at(i)->menu()->actions().at(j); 0263 d->currentSubAction->trigger(); 0264 return true; 0265 } 0266 } 0267 } 0268 } 0269 return false; 0270 } 0271 0272 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 103) 0273 int KCodecAction::currentCodecMib() const 0274 { 0275 return mibForName(currentCodecName()); 0276 } 0277 #endif 0278 0279 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 103) 0280 bool KCodecAction::setCurrentCodec(int mib) 0281 { 0282 if (mib == MIB_DEFAULT) { 0283 return setCurrentAction(d->defaultAction); 0284 } else { 0285 return setCurrentCodec(codecForMib(mib)); 0286 } 0287 } 0288 #endif 0289 0290 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 102) 0291 KEncodingProber::ProberType KCodecAction::currentProberType() const 0292 { 0293 return d->currentSubAction->data().isNull() ? KEncodingProber::None : (KEncodingProber::ProberType)d->currentSubAction->data().toUInt(); 0294 } 0295 #endif 0296 0297 #if KCONFIGWIDGETS_BUILD_DEPRECATED_SINCE(5, 102) 0298 bool KCodecAction::setCurrentProberType(KEncodingProber::ProberType scri) 0299 { 0300 if (scri == KEncodingProber::Universal) { 0301 d->currentSubAction = d->defaultAction; 0302 d->currentSubAction->trigger(); 0303 return true; 0304 } 0305 0306 for (int i = 0; i < actions().size(); ++i) { 0307 if (actions().at(i)->menu()) { 0308 if (!actions().at(i)->menu()->actions().isEmpty() && !actions().at(i)->menu()->actions().at(0)->data().isNull() 0309 && actions().at(i)->menu()->actions().at(0)->data().toUInt() == (uint)scri) { 0310 d->currentSubAction = actions().at(i)->menu()->actions().at(0); 0311 d->currentSubAction->trigger(); 0312 return true; 0313 } 0314 } 0315 } 0316 return false; 0317 } 0318 #endif 0319 0320 #include "moc_kcodecaction.cpp"