File indexing completed on 2024-04-21 03:57:51

0001 /*
0002     SPDX-FileCopyrightText: 2013 Dominik Haumann <dhaumann@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "katestatusbar.h"
0008 
0009 #include "kateabstractinputmode.h"
0010 #include "kateconfig.h"
0011 #include "katedocument.h"
0012 #include "kateglobal.h"
0013 #include "katemodemanager.h"
0014 #include "katemodemenulist.h"
0015 #include "katerenderer.h"
0016 #include "kateview.h"
0017 #include "wordcounter.h"
0018 
0019 #include <KAcceleratorManager>
0020 #include <KActionCollection>
0021 #include <KIconUtils>
0022 #include <Sonnet/Speller>
0023 
0024 #include <QActionGroup>
0025 #include <QHBoxLayout>
0026 #include <QInputDialog>
0027 #include <QStylePainter>
0028 
0029 // BEGIN menu
0030 KateStatusBarOpenUpMenu::KateStatusBarOpenUpMenu(QWidget *parent)
0031     : QMenu(parent)
0032 {
0033 }
0034 
0035 void KateStatusBarOpenUpMenu::setVisible(bool visibility)
0036 {
0037     if (visibility) {
0038         QRect geo = geometry();
0039         QPoint pos = parentWidget()->mapToGlobal(QPoint(0, 0));
0040         geo.moveTopLeft(QPoint(pos.x(), pos.y() - geo.height()));
0041         if (geo.top() < 0) {
0042             geo.moveTop(0);
0043         }
0044         setGeometry(geo);
0045     }
0046 
0047     QMenu::setVisible(visibility);
0048 }
0049 // END menu
0050 
0051 // BEGIN StatusBarButton
0052 StatusBarButton::StatusBarButton(KateStatusBar *parent, const QString &text /*= QString()*/)
0053     : QPushButton(text, parent)
0054 {
0055     setFlat(true);
0056     setFocusProxy(parent->m_view);
0057     setMinimumSize(QSize(1, minimumSizeHint().height()));
0058 }
0059 
0060 void StatusBarButton::paintEvent(QPaintEvent *)
0061 {
0062     QStylePainter p(this);
0063     QStyleOptionButton opt;
0064     initStyleOption(&opt);
0065     opt.features &= (~QStyleOptionButton::HasMenu);
0066     p.drawControl(QStyle::CE_PushButton, opt);
0067 }
0068 
0069 QSize StatusBarButton::sizeHint() const
0070 {
0071     return minimumSizeHint();
0072 }
0073 
0074 QSize StatusBarButton::minimumSizeHint() const
0075 {
0076     const auto fm = QFontMetrics(font());
0077     const int h = fm.lineSpacing();
0078     QSize size = QPushButton::sizeHint();
0079     const int vMmargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin) * 2;
0080     size.setHeight(h + vMmargin);
0081 
0082     const int w = fm.horizontalAdvance(text());
0083     const int bm = style()->pixelMetric(QStyle::PM_ButtonMargin) * 2;
0084     const int hMargin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin) * 2;
0085     size.setWidth(w + bm + hMargin);
0086 
0087     return size;
0088 }
0089 
0090 // END StatusBarButton
0091 
0092 KateStatusBar::KateStatusBar(KTextEditor::ViewPrivate *view)
0093     : KateViewBarWidget(false)
0094     , m_view(view)
0095     , m_selectionMode(-1)
0096     , m_wordCounter(nullptr)
0097 {
0098     KAcceleratorManager::setNoAccel(this);
0099     setFocusProxy(m_view);
0100 
0101     // just add our status bar to central widget, full sized
0102     QHBoxLayout *topLayout = new QHBoxLayout(centralWidget());
0103     topLayout->setContentsMargins(0, 0, 0, 0);
0104     topLayout->setSpacing(0);
0105 
0106     // ensure all elements of the status bar are right aligned
0107     // for Kate this is nice, as on the left side are the tool view buttons
0108     // for KWrite this makes it more consistent with Kate
0109     topLayout->addStretch(1);
0110 
0111     // show Line XXX, Column XXX
0112     m_cursorPosition = new StatusBarButton(this);
0113     topLayout->addWidget(m_cursorPosition);
0114     m_cursorPosition->setWhatsThis(i18n("Current cursor position. Click to go to a specific line."));
0115     connect(m_cursorPosition, &StatusBarButton::clicked, m_view, &KTextEditor::ViewPrivate::gotoLine);
0116 
0117     // show the zoom level of the text
0118     m_zoomLevel = new StatusBarButton(this);
0119     topLayout->addWidget(m_zoomLevel);
0120     connect(m_zoomLevel, &StatusBarButton::clicked, [this] {
0121         m_view->renderer()->resetFontSizes();
0122     });
0123 
0124     // show the current mode, like INSERT, OVERWRITE, VI + modifiers like [BLOCK]
0125     m_inputMode = new StatusBarButton(this);
0126     topLayout->addWidget(m_inputMode);
0127     m_inputMode->setWhatsThis(i18n("Insert mode and VI input mode indicator. Click to change the mode."));
0128     connect(m_inputMode, &StatusBarButton::clicked, [this] {
0129         m_view->currentInputMode()->toggleInsert();
0130     });
0131 
0132     // Add dictionary button which allows user to switch dictionary of the document
0133     m_dictionary = new StatusBarButton(this);
0134     topLayout->addWidget(m_dictionary, 0);
0135     m_dictionary->setWhatsThis(i18n("Change dictionary"));
0136     m_dictionaryMenu = new KateStatusBarOpenUpMenu(m_dictionary);
0137     m_dictionaryMenu->addAction(m_view->action(QStringLiteral("tools_change_dictionary")));
0138     m_dictionaryMenu->addAction(m_view->action(QStringLiteral("tools_clear_dictionary_ranges")));
0139     m_dictionaryMenu->addAction(m_view->action(QStringLiteral("tools_toggle_automatic_spell_checking")));
0140     m_dictionaryMenu->addAction(m_view->action(QStringLiteral("tools_spelling_from_cursor")));
0141     m_dictionaryMenu->addAction(m_view->action(QStringLiteral("tools_spelling")));
0142     m_dictionaryMenu->addSeparator();
0143     m_dictionaryGroup = new QActionGroup(m_dictionaryMenu);
0144     QMapIterator<QString, QString> i(Sonnet::Speller().preferredDictionaries());
0145     while (i.hasNext()) {
0146         i.next();
0147         QAction *action = m_dictionaryGroup->addAction(i.key());
0148         action->setData(i.value());
0149         action->setToolTip(i.key());
0150         action->setCheckable(true);
0151         m_dictionaryMenu->addAction(action);
0152     }
0153     m_dictionary->setMenu(m_dictionaryMenu);
0154     connect(m_dictionaryGroup, &QActionGroup::triggered, this, &KateStatusBar::changeDictionary);
0155 
0156     // allow to change indentation configuration
0157     m_tabsIndent = new StatusBarButton(this);
0158     topLayout->addWidget(m_tabsIndent);
0159 
0160     m_indentSettingsMenu = new KateStatusBarOpenUpMenu(m_tabsIndent);
0161     m_indentSettingsMenu->addSection(i18n("Tab Width"));
0162     m_tabGroup = new QActionGroup(this);
0163     addNumberAction(m_tabGroup, m_indentSettingsMenu, -1);
0164     addNumberAction(m_tabGroup, m_indentSettingsMenu, 8);
0165     addNumberAction(m_tabGroup, m_indentSettingsMenu, 4);
0166     addNumberAction(m_tabGroup, m_indentSettingsMenu, 2);
0167     connect(m_tabGroup, &QActionGroup::triggered, this, &KateStatusBar::slotTabGroup);
0168 
0169     m_indentSettingsMenu->addSection(i18n("Indentation Width"));
0170     m_indentGroup = new QActionGroup(this);
0171     addNumberAction(m_indentGroup, m_indentSettingsMenu, -1);
0172     addNumberAction(m_indentGroup, m_indentSettingsMenu, 8);
0173     addNumberAction(m_indentGroup, m_indentSettingsMenu, 4);
0174     addNumberAction(m_indentGroup, m_indentSettingsMenu, 2);
0175     connect(m_indentGroup, &QActionGroup::triggered, this, &KateStatusBar::slotIndentGroup);
0176 
0177     m_indentSettingsMenu->addSection(i18n("Indentation Mode"));
0178     QActionGroup *radioGroup = new QActionGroup(m_indentSettingsMenu);
0179     m_mixedAction = m_indentSettingsMenu->addAction(i18n("Tabulators && Spaces"));
0180     m_mixedAction->setCheckable(true);
0181     m_mixedAction->setActionGroup(radioGroup);
0182     m_hardAction = m_indentSettingsMenu->addAction(i18n("Tabulators"));
0183     m_hardAction->setCheckable(true);
0184     m_hardAction->setActionGroup(radioGroup);
0185     m_softAction = m_indentSettingsMenu->addAction(i18n("Spaces"));
0186     m_softAction->setCheckable(true);
0187     m_softAction->setActionGroup(radioGroup);
0188     connect(radioGroup, &QActionGroup::triggered, this, &KateStatusBar::slotIndentTabMode);
0189 
0190     m_tabsIndent->setMenu(m_indentSettingsMenu);
0191 
0192     // add encoding button which allows user to switch encoding of document
0193     // this will reuse the encoding action menu of the view
0194     m_encoding = new StatusBarButton(this);
0195     topLayout->addWidget(m_encoding);
0196     m_encoding->setMenu(m_view->encodingAction()->menu());
0197     m_encoding->setWhatsThis(i18n("Encoding"));
0198 
0199     m_eol = new StatusBarButton(this);
0200     m_eol->setWhatsThis(i18n("End of line type"));
0201     m_eol->setToolTip(i18n("End of line type"));
0202     m_eol->setMenu(m_view->getEolMenu());
0203     topLayout->addWidget(m_eol);
0204 
0205     // load the mode menu, which contains a scrollable list + search bar.
0206     // This is an alternative menu to the mode action menu of the view.
0207     m_modeMenuList = new KateModeMenuList(i18n("Mode"), this);
0208     m_modeMenuList->setWhatsThis(i18n(
0209         "Here you can choose which mode should be used for the current document. This will influence the highlighting and folding being used, for example."));
0210     m_modeMenuList->updateMenu(m_view->doc());
0211     // add mode button which allows user to switch mode of document
0212     m_mode = new StatusBarButton(this);
0213     topLayout->addWidget(m_mode);
0214     m_modeMenuList->setButton(m_mode, KateModeMenuList::AlignHInverse, KateModeMenuList::AlignTop, KateModeMenuList::AutoUpdateTextButton(false));
0215     m_mode->setMenu(m_modeMenuList);
0216     m_mode->setWhatsThis(i18n("Syntax highlighting"));
0217 
0218     // signals for the statusbar
0219     connect(m_view, &KTextEditor::View::cursorPositionChanged, this, &KateStatusBar::cursorPositionChanged);
0220     connect(m_view, &KTextEditor::View::viewModeChanged, this, &KateStatusBar::viewModeChanged);
0221     connect(m_view, &KTextEditor::View::selectionChanged, this, &KateStatusBar::selectionChanged);
0222     connect(m_view->doc(), &KTextEditor::Document::configChanged, this, &KateStatusBar::documentConfigChanged);
0223     connect(m_view->document(), &KTextEditor::DocumentPrivate::modeChanged, this, &KateStatusBar::modeChanged);
0224     connect(m_view, &KTextEditor::View::configChanged, this, &KateStatusBar::configChanged);
0225     connect(m_view->doc(), &KTextEditor::DocumentPrivate::defaultDictionaryChanged, this, &KateStatusBar::updateDictionary);
0226     connect(m_view->doc(), &KTextEditor::DocumentPrivate::dictionaryRangesPresent, this, &KateStatusBar::updateDictionary);
0227     connect(m_view, &KTextEditor::ViewPrivate::caretChangedRange, this, &KateStatusBar::updateDictionary);
0228 
0229     updateStatus();
0230     toggleWordCount(KateViewConfig::global()->showWordCount());
0231 }
0232 
0233 bool KateStatusBar::eventFilter(QObject *obj, QEvent *event)
0234 {
0235     return KateViewBarWidget::eventFilter(obj, event);
0236 }
0237 
0238 void KateStatusBar::contextMenuEvent(QContextMenuEvent *event)
0239 {
0240     // TODO Add option "Show Statusbar" and options to show/hide buttons of the status bar
0241     QMenu menu(this);
0242 
0243     if (childAt(event->pos()) == m_inputMode) {
0244         if (QAction *inputModesAction = m_view->actionCollection()->action(QStringLiteral("view_input_modes"))) {
0245             if (QMenu *inputModesMenu = inputModesAction->menu()) {
0246                 const auto actions = inputModesMenu->actions();
0247                 for (int i = 0; i < actions.count(); ++i) {
0248                     menu.addAction(actions.at(i));
0249                 }
0250                 menu.addSeparator();
0251             }
0252         }
0253     }
0254 
0255     QAction *showLines = menu.addAction(i18n("Show line count"), this, &KateStatusBar::toggleShowLines);
0256     showLines->setCheckable(true);
0257     showLines->setChecked(KateViewConfig::global()->showLineCount());
0258     QAction *showWords = menu.addAction(i18n("Show word count"), this, &KateStatusBar::toggleShowWords);
0259     showWords->setCheckable(true);
0260     showWords->setChecked(KateViewConfig::global()->showWordCount());
0261     auto a = menu.addAction(i18n("Line/Column compact mode"), this, [](bool checked) {
0262         KateViewConfig::global()->setValue(KateViewConfig::StatusbarLineColumnCompact, checked);
0263     });
0264     a->setCheckable(true);
0265     a->setChecked(KateViewConfig::global()->value(KateViewConfig::StatusbarLineColumnCompact).toBool());
0266     menu.exec(event->globalPos());
0267 }
0268 
0269 void KateStatusBar::toggleShowLines(bool checked)
0270 {
0271     KateViewConfig::global()->setValue(KateViewConfig::ShowLineCount, checked);
0272 }
0273 
0274 void KateStatusBar::toggleShowWords(bool checked)
0275 {
0276     KateViewConfig::global()->setShowWordCount(checked);
0277 }
0278 
0279 void KateStatusBar::updateStatus()
0280 {
0281     selectionChanged();
0282     viewModeChanged();
0283     cursorPositionChanged();
0284     documentConfigChanged();
0285     modeChanged();
0286     updateDictionary();
0287     updateEOL();
0288 }
0289 
0290 void KateStatusBar::selectionChanged()
0291 {
0292     const unsigned int newSelectionMode = m_view->blockSelection();
0293     if (newSelectionMode == m_selectionMode) {
0294         return;
0295     }
0296 
0297     // remember new mode and update info
0298     m_selectionMode = newSelectionMode;
0299     viewModeChanged();
0300 }
0301 
0302 void KateStatusBar::viewModeChanged()
0303 {
0304     // prepend BLOCK for block selection mode
0305     QString text = m_view->viewModeHuman();
0306     if (m_view->blockSelection()) {
0307         text = i18n("[BLOCK] %1", text);
0308     }
0309 
0310     m_inputMode->setText(text);
0311 }
0312 
0313 void KateStatusBar::cursorPositionChanged()
0314 {
0315     KTextEditor::Cursor position(m_view->cursorPositionVirtual());
0316     const int l = position.line() + 1;
0317     const int c = position.column() + 1;
0318 
0319     // Update line/column label
0320     QString text;
0321     if (KateViewConfig::global()->value(KateViewConfig::StatusbarLineColumnCompact).toBool()) {
0322         if (KateViewConfig::global()->showLineCount()) {
0323             text = i18n("%1/%2:%3", QLocale().toString(l), QLocale().toString(m_view->doc()->lines()), QLocale().toString(c));
0324         } else {
0325             text = i18n("%1:%2", QLocale().toString(l), QLocale().toString(c));
0326         }
0327     } else {
0328         if (KateViewConfig::global()->showLineCount()) {
0329             text = i18n("Line %1 of %2, Column %3", QLocale().toString(l), QLocale().toString(m_view->doc()->lines()), QLocale().toString(c));
0330         } else {
0331             text = i18n("Line %1, Column %2", QLocale().toString(l), QLocale().toString(c));
0332         }
0333     }
0334     if (m_wordCounter) {
0335         text.append(QLatin1String(", ") + m_wordCount);
0336     }
0337     m_cursorPosition->setText(text);
0338 }
0339 
0340 void KateStatusBar::updateDictionary()
0341 {
0342     const auto spellchecker = Sonnet::Speller();
0343     const auto availableDictionaries = spellchecker.availableDictionaries();
0344     // No dictionaries available? => hide
0345     if (availableDictionaries.isEmpty()) {
0346         m_dictionary->hide();
0347         return;
0348     }
0349 
0350     QString newDict;
0351     // Check if at the current cursor position is a special dictionary in use
0352     KTextEditor::Cursor position(m_view->cursorPositionVirtual());
0353     const QList<QPair<KTextEditor::MovingRange *, QString>> dictRanges = m_view->doc()->dictionaryRanges();
0354     for (const auto &rangeDictPair : dictRanges) {
0355         const KTextEditor::MovingRange *range = rangeDictPair.first;
0356         if (range->contains(position) || range->end() == position) {
0357             newDict = rangeDictPair.second;
0358             break;
0359         }
0360     }
0361     // Check if the default dictionary is in use
0362     if (newDict.isEmpty()) {
0363         newDict = m_view->doc()->defaultDictionary();
0364         if (newDict.isEmpty()) {
0365             newDict = spellchecker.defaultLanguage();
0366         }
0367     }
0368     // Update button and menu only on a changed dictionary
0369     if (!m_dictionaryGroup->checkedAction() || (m_dictionaryGroup->checkedAction()->data().toString() != newDict) || m_dictionary->text().isEmpty()) {
0370         bool found = false;
0371         // Remove "-w_accents -variant_0" and such from dict-code to keep it small and clean
0372         m_dictionary->setText(newDict.section(QLatin1Char('-'), 0, 0));
0373         // For maximum user clearness, change the checked menu option
0374         m_dictionaryGroup->blockSignals(true);
0375         const auto acts = m_dictionaryGroup->actions();
0376         for (auto a : acts) {
0377             if (a->data().toString() == newDict) {
0378                 a->setChecked(true);
0379                 found = true;
0380                 break;
0381             }
0382         }
0383         if (!found) {
0384             // User has chose some other dictionary from combo box, we need to add that
0385             QString dictName = availableDictionaries.key(newDict);
0386             if (!dictName.isEmpty()) {
0387                 QAction *action = m_dictionaryGroup->addAction(dictName);
0388                 action->setData(newDict);
0389                 action->setCheckable(true);
0390                 action->setChecked(true);
0391                 m_dictionaryMenu->addAction(action);
0392             }
0393         }
0394         m_dictionaryGroup->blockSignals(false);
0395     }
0396 }
0397 
0398 void KateStatusBar::documentConfigChanged()
0399 {
0400     m_encoding->setText(m_view->document()->encoding());
0401     KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
0402     int tabWidth = config->tabWidth();
0403     int indentationWidth = config->indentationWidth();
0404     bool replaceTabsDyn = config->replaceTabsDyn();
0405 
0406     static const KLocalizedString spacesOnly = ki18n("Soft Tabs: %1");
0407     static const KLocalizedString spacesOnlyShowTabs = ki18n("Soft Tabs: %1 (%2)");
0408     static const KLocalizedString tabsOnly = ki18n("Tab Size: %1");
0409     static const KLocalizedString tabSpacesMixed = ki18n("Indent/Tab: %1/%2");
0410 
0411     if (!replaceTabsDyn) {
0412         if (tabWidth == indentationWidth) {
0413             m_tabsIndent->setText(tabsOnly.subs(tabWidth).toString());
0414             m_tabGroup->setEnabled(false);
0415             m_hardAction->setChecked(true);
0416         } else {
0417             m_tabsIndent->setText(tabSpacesMixed.subs(indentationWidth).subs(tabWidth).toString());
0418             m_tabGroup->setEnabled(true);
0419             m_mixedAction->setChecked(true);
0420         }
0421     } else {
0422         if (tabWidth == indentationWidth) {
0423             m_tabsIndent->setText(spacesOnly.subs(indentationWidth).toString());
0424             m_tabGroup->setEnabled(true);
0425             m_softAction->setChecked(true);
0426         } else {
0427             m_tabsIndent->setText(spacesOnlyShowTabs.subs(indentationWidth).subs(tabWidth).toString());
0428             m_tabGroup->setEnabled(true);
0429             m_softAction->setChecked(true);
0430         }
0431     }
0432 
0433     updateGroup(m_tabGroup, tabWidth);
0434     updateGroup(m_indentGroup, indentationWidth);
0435     updateEOL();
0436 }
0437 
0438 void KateStatusBar::modeChanged()
0439 {
0440     m_mode->setText(KTextEditor::EditorPrivate::self()->modeManager()->fileType(m_view->document()->mode()).nameTranslated());
0441 }
0442 
0443 void KateStatusBar::addNumberAction(QActionGroup *group, QMenu *menu, int data)
0444 {
0445     QAction *a;
0446     if (data != -1) {
0447         a = menu->addAction(QStringLiteral("%1").arg(data));
0448     } else {
0449         a = menu->addAction(i18n("Other..."));
0450     }
0451     a->setData(data);
0452     a->setCheckable(true);
0453     a->setActionGroup(group);
0454 }
0455 
0456 void KateStatusBar::updateGroup(QActionGroup *group, int w)
0457 {
0458     QAction *m1 = nullptr;
0459     bool found = false;
0460     // linear search should be fast enough here, no additional hash
0461     const auto acts = group->actions();
0462     for (QAction *action : acts) {
0463         int val = action->data().toInt();
0464         if (val == -1) {
0465             m1 = action;
0466         }
0467         if (val == w) {
0468             found = true;
0469             action->setChecked(true);
0470         }
0471     }
0472 
0473     if (m1) {
0474         if (found) {
0475             m1->setText(i18n("Other..."));
0476         } else {
0477             m1->setText(i18np("Other (%1)", "Other (%1)", w));
0478             m1->setChecked(true);
0479         }
0480     }
0481 }
0482 
0483 void KateStatusBar::slotTabGroup(QAction *a)
0484 {
0485     int val = a->data().toInt();
0486     bool ok;
0487     KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
0488     if (val == -1) {
0489         val = QInputDialog::getInt(this, i18n("Tab Width"), i18n("Please specify the wanted tab width:"), config->tabWidth(), 1, 200, 1, &ok);
0490         if (!ok) {
0491             val = config->tabWidth();
0492         }
0493     }
0494     config->setTabWidth(val);
0495 }
0496 
0497 void KateStatusBar::slotIndentGroup(QAction *a)
0498 {
0499     int val = a->data().toInt();
0500     bool ok;
0501     KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
0502     if (val == -1) {
0503         val = QInputDialog::getInt(this,
0504                                    i18n("Indentation Width"),
0505                                    i18n("Please specify the wanted indentation width:"),
0506                                    config->indentationWidth(),
0507                                    1,
0508                                    200,
0509                                    1,
0510                                    &ok);
0511         if (!ok) {
0512             val = config->indentationWidth();
0513         }
0514     }
0515     config->configStart();
0516     config->setIndentationWidth(val);
0517     if (m_hardAction->isChecked()) {
0518         config->setTabWidth(val);
0519     }
0520     config->configEnd();
0521 }
0522 
0523 void KateStatusBar::slotIndentTabMode(QAction *a)
0524 {
0525     KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
0526     if (a == m_softAction) {
0527         config->setReplaceTabsDyn(true);
0528     } else if (a == m_mixedAction) {
0529         if (config->replaceTabsDyn()) {
0530             config->setReplaceTabsDyn(false);
0531         }
0532         m_tabGroup->setEnabled(true);
0533     } else if (a == m_hardAction) {
0534         if (config->replaceTabsDyn()) {
0535             config->configStart();
0536             config->setReplaceTabsDyn(false);
0537             config->setTabWidth(config->indentationWidth());
0538             config->configEnd();
0539         } else {
0540             config->setTabWidth(config->indentationWidth());
0541         }
0542         m_tabGroup->setEnabled(false);
0543     }
0544 }
0545 
0546 void KateStatusBar::toggleWordCount(bool on)
0547 {
0548     if ((m_wordCounter != nullptr) == on) {
0549         return;
0550     }
0551 
0552     if (on) {
0553         m_wordCounter = new WordCounter(m_view);
0554         connect(m_wordCounter, &WordCounter::changed, this, &KateStatusBar::wordCountChanged);
0555     } else {
0556         delete m_wordCounter;
0557         m_wordCounter = nullptr;
0558     }
0559 
0560     wordCountChanged(0, 0, 0, 0);
0561 }
0562 
0563 void KateStatusBar::wordCountChanged(int wordsInDocument, int wordsInSelection, int charsInDocument, int charsInSelection)
0564 {
0565     if (m_wordCounter) {
0566         if (charsInSelection > 0) {
0567             m_wordCount = i18nc("%1 and %3 are the selected words/chars count, %2 and %4 are the total words/chars count.",
0568                                 "Words %1/%2, Chars %3/%4",
0569                                 wordsInSelection,
0570                                 wordsInDocument,
0571                                 charsInSelection,
0572                                 charsInDocument);
0573         } else {
0574             m_wordCount = i18nc("%1 and %2 are the total words/chars count.", "Words %1, Chars %2", wordsInDocument, charsInDocument);
0575         }
0576     } else {
0577         m_wordCount.clear();
0578     }
0579 
0580     cursorPositionChanged();
0581 }
0582 
0583 void KateStatusBar::configChanged()
0584 {
0585     toggleWordCount(m_view->config()->showWordCount());
0586     const int zoom = m_view->rendererConfig()->baseFont().pointSizeF() / KateRendererConfig::global()->baseFont().pointSizeF() * 100;
0587     if (zoom != 100) {
0588         m_zoomLevel->setVisible(true);
0589         m_zoomLevel->setText(i18n("Zoom: %1%", zoom));
0590     } else {
0591         m_zoomLevel->hide();
0592     }
0593 
0594     auto cfg = KateViewConfig::global();
0595     auto updateButtonVisibility = [cfg](StatusBarButton *b, KateViewConfig::ConfigEntryTypes c) {
0596         bool v = cfg->value(c).toBool();
0597         if (v != (!b->isHidden())) {
0598             b->setVisible(v);
0599         }
0600     };
0601     updateButtonVisibility(m_inputMode, KateViewConfig::ShowStatusbarInputMode);
0602     updateButtonVisibility(m_mode, KateViewConfig::ShowStatusbarHighlightingMode);
0603     updateButtonVisibility(m_cursorPosition, KateViewConfig::ShowStatusbarLineColumn);
0604     updateButtonVisibility(m_tabsIndent, KateViewConfig::ShowStatusbarTabSettings);
0605     updateButtonVisibility(m_encoding, KateViewConfig::ShowStatusbarFileEncoding);
0606     updateButtonVisibility(m_eol, KateViewConfig::ShowStatusbarEOL);
0607 
0608     bool v = cfg->value(KateViewConfig::ShowStatusbarDictionary).toBool();
0609     if (v != (!m_dictionary->isHidden()) && !Sonnet::Speller().availableDictionaries().isEmpty()) {
0610         updateButtonVisibility(m_dictionary, KateViewConfig::ShowStatusbarDictionary);
0611     }
0612 }
0613 
0614 void KateStatusBar::changeDictionary(QAction *action)
0615 {
0616     const QString dictionary = action->data().toString();
0617     m_dictionary->setText(dictionary);
0618     // Code stolen from KateDictionaryBar::dictionaryChanged
0619     KTextEditor::Range selection = m_view->selectionRange();
0620     if (selection.isValid() && !selection.isEmpty()) {
0621         m_view->doc()->setDictionary(dictionary, selection);
0622     } else {
0623         m_view->doc()->setDefaultDictionary(dictionary);
0624     }
0625 }
0626 
0627 void KateStatusBar::updateEOL()
0628 {
0629     const int eol = m_view->getEol();
0630     QString text;
0631     switch (eol) {
0632     case KateDocumentConfig::eolUnix:
0633         text = QStringLiteral("LF");
0634         break;
0635     case KateDocumentConfig::eolDos:
0636         text = QStringLiteral("CRLF");
0637         break;
0638     case KateDocumentConfig::eolMac:
0639         text = QStringLiteral("CR");
0640         break;
0641     }
0642     if (text != m_eol->text()) {
0643         m_eol->setText(text);
0644     }
0645 }
0646 
0647 KateModeMenuList *KateStatusBar::modeMenu() const
0648 {
0649     return m_modeMenuList;
0650 }
0651 
0652 #include "moc_katestatusbar.cpp"