| 1 | /* |
| 2 | SPDX-FileCopyrightText: 2001-2010 Christoph Cullmann <cullmann@kde.org> |
| 3 | |
| 4 | SPDX-License-Identifier: LGPL-2.0-or-later |
| 5 | */ |
| 6 | |
| 7 | // BEGIN Includes |
| 8 | #include "katemodeconfigpage.h" |
| 9 | |
| 10 | #include "kateautoindent.h" |
| 11 | #include "kateconfig.h" |
| 12 | #include "katedocument.h" |
| 13 | #include "kateglobal.h" |
| 14 | #include "katesyntaxmanager.h" |
| 15 | #include "kateview.h" |
| 16 | |
| 17 | #include "ui_filetypeconfigwidget.h" |
| 18 | |
| 19 | #include "katepartdebug.h" |
| 20 | #include <KMimeTypeChooser> |
| 21 | |
| 22 | #include <QCheckBox> |
| 23 | #include <QComboBox> |
| 24 | #include <QGroupBox> |
| 25 | #include <QLabel> |
| 26 | #include <QLayout> |
| 27 | #include <QPushButton> |
| 28 | #include <QRegularExpression> |
| 29 | #include <QSpinBox> |
| 30 | #include <QToolButton> |
| 31 | // END Includes |
| 32 | |
| 33 | ModeConfigPage::ModeConfigPage(QWidget *parent) |
| 34 | : KateConfigPage(parent) |
| 35 | { |
| 36 | m_lastType = -1; |
| 37 | |
| 38 | // This will let us have more separation between this page and |
| 39 | // the QTabWidget edge (ereslibre) |
| 40 | QVBoxLayout *layout = new QVBoxLayout(this); |
| 41 | QWidget *newWidget = new QWidget(this); |
| 42 | |
| 43 | ui = new Ui::FileTypeConfigWidget(); |
| 44 | ui->setupUi(newWidget); |
| 45 | |
| 46 | ui->cmbHl->addItem(i18n("<Unchanged>" ), auserData: QVariant(QString())); |
| 47 | const auto modeList = KateHlManager::self()->modeList(); |
| 48 | for (const auto &hl : modeList) { |
| 49 | const auto section = hl.translatedSection(); |
| 50 | if (!section.isEmpty()) { |
| 51 | ui->cmbHl->addItem(atext: section + QLatin1Char('/') + hl.translatedName(), auserData: QVariant(hl.name())); |
| 52 | } else { |
| 53 | ui->cmbHl->addItem(atext: hl.translatedName(), auserData: QVariant(hl.name())); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | QStringList indentationModes; |
| 58 | indentationModes << i18n("Use Default" ); |
| 59 | indentationModes << KateAutoIndent::listModes(); |
| 60 | ui->cmbIndenter->addItems(texts: indentationModes); |
| 61 | |
| 62 | connect(sender: ui->cmbFiletypes, signal: &QComboBox::activated, context: this, slot: &ModeConfigPage::typeChanged); |
| 63 | connect(sender: ui->btnNew, signal: &QPushButton::clicked, context: this, slot: &ModeConfigPage::newType); |
| 64 | connect(sender: ui->btnDelete, signal: &QPushButton::clicked, context: this, slot: &ModeConfigPage::deleteType); |
| 65 | ui->btnMimeTypes->setIcon(QIcon::fromTheme(QStringLiteral("tools-wizard" ))); |
| 66 | connect(sender: ui->btnMimeTypes, signal: &QToolButton::clicked, context: this, slot: &ModeConfigPage::showMTDlg); |
| 67 | |
| 68 | reload(); |
| 69 | |
| 70 | connect(sender: ui->edtName, signal: &QLineEdit::textChanged, context: this, slot: &ModeConfigPage::slotChanged); |
| 71 | connect(sender: ui->edtSection, signal: &QLineEdit::textChanged, context: this, slot: &ModeConfigPage::slotChanged); |
| 72 | connect(sender: ui->edtVariables, signal: &VariableLineEdit::textChanged, context: this, slot: &ModeConfigPage::slotChanged); |
| 73 | connect(sender: ui->edtFileExtensions, signal: &QLineEdit::textChanged, context: this, slot: &ModeConfigPage::slotChanged); |
| 74 | connect(sender: ui->edtMimeTypes, signal: &QLineEdit::textChanged, context: this, slot: &ModeConfigPage::slotChanged); |
| 75 | connect(sender: ui->sbPriority, signal: &QSpinBox::valueChanged, context: this, slot: &ModeConfigPage::slotChanged); |
| 76 | connect(sender: ui->cmbHl, signal: &QComboBox::activated, context: this, slot: &ModeConfigPage::slotChanged); |
| 77 | connect(sender: ui->cmbIndenter, signal: &QComboBox::activated, context: this, slot: &ModeConfigPage::slotChanged); |
| 78 | |
| 79 | // make the context help a bit easier to access |
| 80 | ui->sbPriority->setToolTip(ui->sbPriority->whatsThis()); |
| 81 | |
| 82 | layout->addWidget(newWidget); |
| 83 | } |
| 84 | |
| 85 | ModeConfigPage::~ModeConfigPage() |
| 86 | { |
| 87 | qDeleteAll(c: m_types); |
| 88 | delete ui; |
| 89 | } |
| 90 | |
| 91 | void ModeConfigPage::apply() |
| 92 | { |
| 93 | if (!hasChanged()) { |
| 94 | return; |
| 95 | } |
| 96 | |
| 97 | save(); |
| 98 | if (m_lastType != -1) { |
| 99 | ui->gbProperties->setTitle(i18n("Properties of %1" , ui->cmbFiletypes->itemText(m_lastType))); |
| 100 | } |
| 101 | |
| 102 | KTextEditor::EditorPrivate::self()->modeManager()->save(v: m_types); |
| 103 | } |
| 104 | |
| 105 | void ModeConfigPage::reload() |
| 106 | { |
| 107 | qDeleteAll(c: m_types); |
| 108 | m_types.clear(); |
| 109 | |
| 110 | // deep copy... |
| 111 | const QList<KateFileType *> &modeList = KTextEditor::EditorPrivate::self()->modeManager()->list(); |
| 112 | m_types.reserve(asize: modeList.size()); |
| 113 | for (KateFileType *type : modeList) { |
| 114 | KateFileType *t = new KateFileType(); |
| 115 | *t = *type; |
| 116 | m_types.append(t); |
| 117 | } |
| 118 | |
| 119 | update(); |
| 120 | } |
| 121 | |
| 122 | void ModeConfigPage::reset() |
| 123 | { |
| 124 | reload(); |
| 125 | } |
| 126 | |
| 127 | void ModeConfigPage::defaults() |
| 128 | { |
| 129 | reload(); |
| 130 | } |
| 131 | |
| 132 | void ModeConfigPage::update() |
| 133 | { |
| 134 | m_lastType = -1; |
| 135 | |
| 136 | ui->cmbFiletypes->clear(); |
| 137 | |
| 138 | for (KateFileType *type : std::as_const(t&: m_types)) { |
| 139 | if (!type->sectionTranslated().isEmpty()) { |
| 140 | ui->cmbFiletypes->addItem(atext: type->sectionTranslated() + QLatin1Char('/') + type->nameTranslated()); |
| 141 | } else { |
| 142 | ui->cmbFiletypes->addItem(atext: type->nameTranslated()); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | // get current filetype from active view via the host application |
| 147 | int currentIndex = 0; |
| 148 | KTextEditor::ViewPrivate *kv = |
| 149 | qobject_cast<KTextEditor::ViewPrivate *>(object: KTextEditor::EditorPrivate::self()->application()->activeMainWindow()->activeView()); |
| 150 | if (kv) { |
| 151 | const QString filetypeName = kv->doc()->fileType(); |
| 152 | for (int i = 0; i < m_types.size(); ++i) { |
| 153 | if (filetypeName == m_types[i]->name) { |
| 154 | currentIndex = i; |
| 155 | break; |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | ui->cmbFiletypes->setCurrentIndex(currentIndex); |
| 160 | typeChanged(type: currentIndex); |
| 161 | |
| 162 | ui->cmbFiletypes->setEnabled(ui->cmbFiletypes->count() > 0); |
| 163 | } |
| 164 | |
| 165 | void ModeConfigPage::deleteType() |
| 166 | { |
| 167 | int type = ui->cmbFiletypes->currentIndex(); |
| 168 | |
| 169 | if (type > -1 && type < m_types.count()) { |
| 170 | delete m_types[type]; |
| 171 | m_types.removeAt(i: type); |
| 172 | update(); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | void ModeConfigPage::newType() |
| 177 | { |
| 178 | QString newN = i18n("New Filetype" ); |
| 179 | |
| 180 | for (int i = 0; i < m_types.count(); ++i) { |
| 181 | KateFileType *type = m_types.at(i); |
| 182 | if (type->name == newN) { |
| 183 | ui->cmbFiletypes->setCurrentIndex(i); |
| 184 | typeChanged(type: i); |
| 185 | return; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | KateFileType *newT = new KateFileType(); |
| 190 | newT->priority = 0; |
| 191 | newT->name = newN; |
| 192 | newT->hlGenerated = false; |
| 193 | |
| 194 | m_types.prepend(t: newT); |
| 195 | |
| 196 | update(); |
| 197 | // show new filetype so that it is immediately available for editing |
| 198 | ui->cmbFiletypes->setCurrentIndex(0); |
| 199 | typeChanged(type: 0); |
| 200 | } |
| 201 | |
| 202 | void ModeConfigPage::save() |
| 203 | { |
| 204 | if (m_lastType != -1) { |
| 205 | if (!m_types[m_lastType]->hlGenerated) { |
| 206 | m_types[m_lastType]->name = ui->edtName->text(); |
| 207 | m_types[m_lastType]->section = ui->edtSection->text(); |
| 208 | |
| 209 | if (!m_types[m_lastType]->sectionTranslated().isEmpty()) { |
| 210 | ui->cmbFiletypes->setItemText(index: m_lastType, text: m_types[m_lastType]->sectionTranslated() + QLatin1Char('/') + m_types[m_lastType]->nameTranslated()); |
| 211 | } else { |
| 212 | ui->cmbFiletypes->setItemText(index: m_lastType, text: m_types[m_lastType]->nameTranslated()); |
| 213 | } |
| 214 | } |
| 215 | m_types[m_lastType]->varLine = ui->edtVariables->text(); |
| 216 | m_types[m_lastType]->wildcards = ui->edtFileExtensions->text().split(sep: QLatin1Char(';'), behavior: Qt::SkipEmptyParts); |
| 217 | m_types[m_lastType]->mimetypes = ui->edtMimeTypes->text().split(sep: QLatin1Char(';'), behavior: Qt::SkipEmptyParts); |
| 218 | m_types[m_lastType]->priority = ui->sbPriority->value(); |
| 219 | m_types[m_lastType]->hl = ui->cmbHl->itemData(index: ui->cmbHl->currentIndex()).toString(); |
| 220 | |
| 221 | if (ui->cmbIndenter->currentIndex() > 0) { |
| 222 | m_types[m_lastType]->indenter = KateAutoIndent::modeName(mode: ui->cmbIndenter->currentIndex() - 1); |
| 223 | } else { |
| 224 | m_types[m_lastType]->indenter = QString(); |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | void ModeConfigPage::typeChanged(int type) |
| 230 | { |
| 231 | save(); |
| 232 | |
| 233 | ui->cmbHl->setEnabled(true); |
| 234 | ui->btnDelete->setEnabled(true); |
| 235 | ui->edtName->setEnabled(true); |
| 236 | ui->edtSection->setEnabled(true); |
| 237 | |
| 238 | if (type > -1 && type < m_types.count()) { |
| 239 | KateFileType *t = m_types.at(i: type); |
| 240 | |
| 241 | ui->gbProperties->setTitle(i18n("Properties of %1" , ui->cmbFiletypes->itemText(type))); |
| 242 | |
| 243 | ui->gbProperties->setEnabled(true); |
| 244 | ui->btnDelete->setEnabled(true); |
| 245 | |
| 246 | ui->edtName->setText(t->nameTranslated()); |
| 247 | ui->edtSection->setText(t->sectionTranslated()); |
| 248 | ui->edtVariables->setText(t->varLine); |
| 249 | ui->edtFileExtensions->setText(t->wildcards.join(sep: QLatin1Char(';'))); |
| 250 | ui->edtMimeTypes->setText(t->mimetypes.join(sep: QLatin1Char(';'))); |
| 251 | ui->sbPriority->setValue(t->priority); |
| 252 | |
| 253 | ui->cmbHl->setEnabled(!t->hlGenerated); |
| 254 | ui->btnDelete->setEnabled(!t->hlGenerated); |
| 255 | ui->edtName->setEnabled(!t->hlGenerated); |
| 256 | ui->edtSection->setEnabled(!t->hlGenerated); |
| 257 | |
| 258 | // activate current hl... |
| 259 | for (int i = 0; i < ui->cmbHl->count(); ++i) { |
| 260 | if (ui->cmbHl->itemData(index: i).toString() == t->hl) { |
| 261 | ui->cmbHl->setCurrentIndex(i); |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | // activate the right indenter |
| 266 | int indenterIndex = 0; |
| 267 | if (!t->indenter.isEmpty()) { |
| 268 | indenterIndex = KateAutoIndent::modeNumber(name: t->indenter) + 1; |
| 269 | } |
| 270 | ui->cmbIndenter->setCurrentIndex(indenterIndex); |
| 271 | } else { |
| 272 | ui->gbProperties->setTitle(i18n("Properties" )); |
| 273 | |
| 274 | ui->gbProperties->setEnabled(false); |
| 275 | ui->btnDelete->setEnabled(false); |
| 276 | |
| 277 | ui->edtName->clear(); |
| 278 | ui->edtSection->clear(); |
| 279 | ui->edtVariables->clear(); |
| 280 | ui->edtFileExtensions->clear(); |
| 281 | ui->edtMimeTypes->clear(); |
| 282 | ui->sbPriority->setValue(0); |
| 283 | ui->cmbHl->setCurrentIndex(0); |
| 284 | ui->cmbIndenter->setCurrentIndex(0); |
| 285 | } |
| 286 | |
| 287 | m_lastType = type; |
| 288 | } |
| 289 | |
| 290 | void ModeConfigPage::showMTDlg() |
| 291 | { |
| 292 | QString text = |
| 293 | i18n("Select the MimeTypes you want for this file type.\nPlease note that this will automatically edit the associated file extensions as well." ); |
| 294 | QStringList list = ui->edtMimeTypes->text().split(sep: QRegularExpression(QStringLiteral("\\s*;\\s*" )), behavior: Qt::SkipEmptyParts); |
| 295 | KMimeTypeChooserDialog d(i18n("Select Mime Types" ), text, list, QStringLiteral("text" ), this); |
| 296 | if (d.exec() == QDialog::Accepted) { |
| 297 | // do some checking, warn user if mime types or patterns are removed. |
| 298 | // if the lists are empty, and the fields not, warn. |
| 299 | ui->edtFileExtensions->setText(d.chooser()->patterns().join(sep: QLatin1Char(';'))); |
| 300 | ui->edtMimeTypes->setText(d.chooser()->mimeTypes().join(sep: QLatin1Char(';'))); |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | QString ModeConfigPage::name() const |
| 305 | { |
| 306 | return i18n("Modes && Filetypes" ); |
| 307 | } |
| 308 | |