1 /*
2     SPDX-FileCopyrightText: 2013 Dominik Haumann <dhaumann@kde.org>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "katestatusbar.h"
8 
9 #include "kateabstractinputmode.h"
10 #include "kateconfig.h"
11 #include "katedocument.h"
12 #include "kateglobal.h"
13 #include "katemodemanager.h"
14 #include "katemodemenulist.h"
15 #include "kateview.h"
16 #include "wordcounter.h"
17 
18 #include <KAcceleratorManager>
19 #include <KActionCollection>
20 #include <KIconUtils>
21 #include <Sonnet/Speller>
22 
23 #include <QHBoxLayout>
24 #include <QInputDialog>
25 
26 // BEGIN menu
KateStatusBarOpenUpMenu(QWidget * parent)27 KateStatusBarOpenUpMenu::KateStatusBarOpenUpMenu(QWidget *parent)
28     : QMenu(parent)
29 {
30 }
31 
setVisible(bool visibility)32 void KateStatusBarOpenUpMenu::setVisible(bool visibility)
33 {
34     if (visibility) {
35         QRect geo = geometry();
36         QPoint pos = parentWidget()->mapToGlobal(QPoint(0, 0));
37         geo.moveTopLeft(QPoint(pos.x(), pos.y() - geo.height()));
38         if (geo.top() < 0) {
39             geo.moveTop(0);
40         }
41         setGeometry(geo);
42     }
43 
44     QMenu::setVisible(visibility);
45 }
46 // END menu
47 
48 // BEGIN StatusBarButton
StatusBarButton(KateStatusBar * parent,const QString & text)49 StatusBarButton::StatusBarButton(KateStatusBar *parent, const QString &text /*= QString()*/)
50     : QPushButton(text, parent)
51 {
52     setFlat(true);
53     setFocusProxy(parent->m_view);
54     setMinimumSize(QSize(1, minimumSizeHint().height()));
55 }
56 
57 // END StatusBarButton
58 
KateStatusBar(KTextEditor::ViewPrivate * view)59 KateStatusBar::KateStatusBar(KTextEditor::ViewPrivate *view)
60     : KateViewBarWidget(false)
61     , m_view(view)
62     , m_modifiedStatus(-1)
63     , m_selectionMode(-1)
64     , m_wordCounter(nullptr)
65 {
66     KAcceleratorManager::setNoAccel(this);
67     setFocusProxy(m_view);
68 
69     // just add our status bar to central widget, full sized
70     QHBoxLayout *topLayout = new QHBoxLayout(centralWidget());
71     topLayout->setContentsMargins(0, 0, 0, 0);
72     topLayout->setSpacing(4);
73 
74     // show modification state of the document
75     // TODO Using a (StatusBar)Button is currently pointless but handy due to no "setIcon()" function in QLabel.
76     //      Add some useful action when button is clicked, e.g. save document or show tool-tip
77     //      or find a way to not show a "focus frame" when hovered by mouse
78     m_modified = new StatusBarButton(this);
79     topLayout->addWidget(m_modified);
80 
81     // show Line XXX, Column XXX
82     m_cursorPosition = new StatusBarButton(this);
83     topLayout->addWidget(m_cursorPosition);
84     m_cursorPosition->setWhatsThis(i18n("Current cursor position. Click to go to a specific line."));
85     connect(m_cursorPosition, &StatusBarButton::clicked, m_view, &KTextEditor::ViewPrivate::gotoLine);
86 
87     // Separate the status line in a left and right part
88     topLayout->addStretch(1);
89 
90     // show the zoom level of the text
91     m_zoomLevel = new StatusBarButton(this);
92     topLayout->addWidget(m_zoomLevel);
93     connect(m_zoomLevel, &StatusBarButton::clicked, [=] {
94         m_view->renderer()->resetFontSizes();
95     });
96 
97     // show the current mode, like INSERT, OVERWRITE, VI + modifiers like [BLOCK]
98     m_inputMode = new StatusBarButton(this);
99     topLayout->addWidget(m_inputMode);
100     m_inputMode->setWhatsThis(i18n("Insert mode and VI input mode indicator. Click to change the mode."));
101     connect(m_inputMode, &StatusBarButton::clicked, [=] {
102         m_view->currentInputMode()->toggleInsert();
103     });
104 
105     // Add dictionary button which allows user to switch dictionary of the document
106     m_dictionary = new StatusBarButton(this);
107     topLayout->addWidget(m_dictionary, 0);
108     m_dictionary->setWhatsThis(i18n("Change dictionary"));
109     m_dictionaryMenu = new KateStatusBarOpenUpMenu(m_dictionary);
110     m_dictionaryMenu->addAction(m_view->action("tools_change_dictionary"));
111     m_dictionaryMenu->addAction(m_view->action("tools_clear_dictionary_ranges"));
112     m_dictionaryMenu->addAction(m_view->action("tools_toggle_automatic_spell_checking"));
113     m_dictionaryMenu->addAction(m_view->action("tools_spelling_from_cursor"));
114     m_dictionaryMenu->addAction(m_view->action("tools_spelling"));
115     m_dictionaryMenu->addSeparator();
116     m_dictionaryGroup = new QActionGroup(m_dictionaryMenu);
117     QMapIterator<QString, QString> i(Sonnet::Speller().preferredDictionaries());
118     while (i.hasNext()) {
119         i.next();
120         QAction *action = m_dictionaryGroup->addAction(i.key());
121         action->setData(i.value());
122         action->setToolTip(i.key());
123         action->setCheckable(true);
124         m_dictionaryMenu->addAction(action);
125     }
126     m_dictionary->setMenu(m_dictionaryMenu);
127     connect(m_dictionaryGroup, &QActionGroup::triggered, this, &KateStatusBar::changeDictionary);
128 
129     // allow to change indentation configuration
130     m_tabsIndent = new StatusBarButton(this);
131     topLayout->addWidget(m_tabsIndent);
132 
133     m_indentSettingsMenu = new KateStatusBarOpenUpMenu(m_tabsIndent);
134     m_indentSettingsMenu->addSection(i18n("Tab Width"));
135     m_tabGroup = new QActionGroup(this);
136     addNumberAction(m_tabGroup, m_indentSettingsMenu, -1);
137     addNumberAction(m_tabGroup, m_indentSettingsMenu, 8);
138     addNumberAction(m_tabGroup, m_indentSettingsMenu, 4);
139     addNumberAction(m_tabGroup, m_indentSettingsMenu, 2);
140     connect(m_tabGroup, &QActionGroup::triggered, this, &KateStatusBar::slotTabGroup);
141 
142     m_indentSettingsMenu->addSection(i18n("Indentation Width"));
143     m_indentGroup = new QActionGroup(this);
144     addNumberAction(m_indentGroup, m_indentSettingsMenu, -1);
145     addNumberAction(m_indentGroup, m_indentSettingsMenu, 8);
146     addNumberAction(m_indentGroup, m_indentSettingsMenu, 4);
147     addNumberAction(m_indentGroup, m_indentSettingsMenu, 2);
148     connect(m_indentGroup, &QActionGroup::triggered, this, &KateStatusBar::slotIndentGroup);
149 
150     m_indentSettingsMenu->addSection(i18n("Indentation Mode"));
151     QActionGroup *radioGroup = new QActionGroup(m_indentSettingsMenu);
152     m_mixedAction = m_indentSettingsMenu->addAction(i18n("Tabulators && Spaces"));
153     m_mixedAction->setCheckable(true);
154     m_mixedAction->setActionGroup(radioGroup);
155     m_hardAction = m_indentSettingsMenu->addAction(i18n("Tabulators"));
156     m_hardAction->setCheckable(true);
157     m_hardAction->setActionGroup(radioGroup);
158     m_softAction = m_indentSettingsMenu->addAction(i18n("Spaces"));
159     m_softAction->setCheckable(true);
160     m_softAction->setActionGroup(radioGroup);
161     connect(radioGroup, &QActionGroup::triggered, this, &KateStatusBar::slotIndentTabMode);
162 
163     m_tabsIndent->setMenu(m_indentSettingsMenu);
164 
165     // add encoding button which allows user to switch encoding of document
166     // this will reuse the encoding action menu of the view
167     m_encoding = new StatusBarButton(this);
168     topLayout->addWidget(m_encoding);
169     m_encoding->setMenu(m_view->encodingAction()->menu());
170     m_encoding->setWhatsThis(i18n("Encoding"));
171 
172     // load the mode menu, which contains a scrollable list + search bar.
173     // This is an alternative menu to the mode action menu of the view.
174     m_modeMenuList = new KateModeMenuList(i18n("Mode"), this);
175     m_modeMenuList->setWhatsThis(i18n(
176         "Here you can choose which mode should be used for the current document. This will influence the highlighting and folding being used, for example."));
177     m_modeMenuList->updateMenu(m_view->doc());
178     // add mode button which allows user to switch mode of document
179     m_mode = new StatusBarButton(this);
180     topLayout->addWidget(m_mode);
181     m_modeMenuList->setButton(m_mode, KateModeMenuList::AlignHInverse, KateModeMenuList::AlignTop, KateModeMenuList::AutoUpdateTextButton(false));
182     m_mode->setMenu(m_modeMenuList);
183     m_mode->setWhatsThis(i18n("Syntax highlighting"));
184 
185     // signals for the statusbar
186     connect(m_view, &KTextEditor::View::cursorPositionChanged, this, &KateStatusBar::cursorPositionChanged);
187     connect(m_view, &KTextEditor::View::viewModeChanged, this, &KateStatusBar::viewModeChanged);
188     connect(m_view, &KTextEditor::View::selectionChanged, this, &KateStatusBar::selectionChanged);
189     connect(m_view->document(), &KTextEditor::DocumentPrivate::modifiedChanged, this, &KateStatusBar::modifiedChanged);
190     connect(m_view->doc(), &KTextEditor::DocumentPrivate::modifiedOnDisk, this, &KateStatusBar::modifiedChanged);
191     connect(m_view->doc(), &KTextEditor::DocumentPrivate::readWriteChanged, this, &KateStatusBar::modifiedChanged);
192     connect(m_view->doc(), &KTextEditor::Document::configChanged, this, &KateStatusBar::documentConfigChanged);
193     connect(m_view->document(), &KTextEditor::DocumentPrivate::modeChanged, this, &KateStatusBar::modeChanged);
194     connect(m_view, &KTextEditor::View::configChanged, this, &KateStatusBar::configChanged);
195     connect(m_view->doc(), &KTextEditor::DocumentPrivate::defaultDictionaryChanged, this, &KateStatusBar::updateDictionary);
196     connect(m_view->doc(), &KTextEditor::DocumentPrivate::dictionaryRangesPresent, this, &KateStatusBar::updateDictionary);
197     connect(m_view, &KTextEditor::ViewPrivate::caretChangedRange, this, &KateStatusBar::updateDictionary);
198 
199     updateStatus();
200     toggleWordCount(KateViewConfig::global()->showWordCount());
201 }
202 
eventFilter(QObject * obj,QEvent * event)203 bool KateStatusBar::eventFilter(QObject *obj, QEvent *event)
204 {
205     return KateViewBarWidget::eventFilter(obj, event);
206 }
207 
contextMenuEvent(QContextMenuEvent * event)208 void KateStatusBar::contextMenuEvent(QContextMenuEvent *event)
209 {
210     // TODO Add option "Show Statusbar" and options to show/hide buttons of the status bar
211     QMenu menu(this);
212 
213     if (childAt(event->pos()) == m_inputMode) {
214         if (QAction *inputModesAction = m_view->actionCollection()->action(QStringLiteral("view_input_modes"))) {
215             if (QMenu *inputModesMenu = inputModesAction->menu()) {
216                 const auto actions = inputModesMenu->actions();
217                 for (int i = 0; i < actions.count(); ++i) {
218                     menu.addAction(actions.at(i));
219                 }
220                 menu.addSeparator();
221             }
222         }
223     }
224 
225     QAction *showLines = menu.addAction(i18n("Show line count"), this, &KateStatusBar::toggleShowLines);
226     showLines->setCheckable(true);
227     showLines->setChecked(KateViewConfig::global()->showLineCount());
228     QAction *showWords = menu.addAction(i18n("Show word count"), this, &KateStatusBar::toggleShowWords);
229     showWords->setCheckable(true);
230     showWords->setChecked(KateViewConfig::global()->showWordCount());
231     menu.exec(event->globalPos());
232 }
233 
toggleShowLines(bool checked)234 void KateStatusBar::toggleShowLines(bool checked)
235 {
236     KateViewConfig::global()->setValue(KateViewConfig::ShowLineCount, checked);
237 }
238 
toggleShowWords(bool checked)239 void KateStatusBar::toggleShowWords(bool checked)
240 {
241     KateViewConfig::global()->setShowWordCount(checked);
242 }
243 
updateStatus()244 void KateStatusBar::updateStatus()
245 {
246     selectionChanged();
247     viewModeChanged();
248     cursorPositionChanged();
249     modifiedChanged();
250     documentConfigChanged();
251     modeChanged();
252     updateDictionary();
253 }
254 
selectionChanged()255 void KateStatusBar::selectionChanged()
256 {
257     const unsigned int newSelectionMode = m_view->blockSelection();
258     if (newSelectionMode == m_selectionMode) {
259         return;
260     }
261 
262     // remember new mode and update info
263     m_selectionMode = newSelectionMode;
264     viewModeChanged();
265 }
266 
viewModeChanged()267 void KateStatusBar::viewModeChanged()
268 {
269     // prepend BLOCK for block selection mode
270     QString text = m_view->viewModeHuman();
271     if (m_view->blockSelection()) {
272         text = i18n("[BLOCK] %1", text);
273     }
274 
275     m_inputMode->setText(text);
276 }
277 
cursorPositionChanged()278 void KateStatusBar::cursorPositionChanged()
279 {
280     KTextEditor::Cursor position(m_view->cursorPositionVirtual());
281 
282     // Update line/column label
283     QString text;
284     if (KateViewConfig::global()->showLineCount()) {
285         text = i18n("Line %1 of %2, Column %3",
286                     QLocale().toString(position.line() + 1),
287                     QLocale().toString(m_view->doc()->lines()),
288                     QLocale().toString(position.column() + 1));
289     } else {
290         text = i18n("Line %1, Column %2", QLocale().toString(position.line() + 1), QLocale().toString(position.column() + 1));
291     }
292     if (m_wordCounter) {
293         text.append(QLatin1String(", ") + m_wordCount);
294     }
295     m_cursorPosition->setText(text);
296 }
297 
updateDictionary()298 void KateStatusBar::updateDictionary()
299 {
300     QString newDict;
301     // Check if at the current cursor position is a special dictionary in use
302     KTextEditor::Cursor position(m_view->cursorPositionVirtual());
303     const QList<QPair<KTextEditor::MovingRange *, QString>> dictRanges = m_view->doc()->dictionaryRanges();
304     for (const auto &rangeDictPair : dictRanges) {
305         const KTextEditor::MovingRange *range = rangeDictPair.first;
306         if (range->contains(position) || range->end() == position) {
307             newDict = rangeDictPair.second;
308             break;
309         }
310     }
311     // Check if the default dictionary is in use
312     if (newDict.isEmpty()) {
313         newDict = m_view->doc()->defaultDictionary();
314         if (newDict.isEmpty()) {
315             newDict = Sonnet::Speller().defaultLanguage();
316         }
317     }
318     // Update button and menu only on a changed dictionary
319     if (!m_dictionaryGroup->checkedAction() || (m_dictionaryGroup->checkedAction()->data().toString() != newDict) || m_dictionary->text().isEmpty()) {
320         bool found = false;
321         // Remove "-w_accents -variant_0" and such from dict-code to keep it small and clean
322         m_dictionary->setText(newDict.section(QLatin1Char('-'), 0, 0));
323         // For maximum user clearness, change the checked menu option
324         m_dictionaryGroup->blockSignals(true);
325         for (auto a : m_dictionaryGroup->actions()) {
326             if (a->data().toString() == newDict) {
327                 a->setChecked(true);
328                 found = true;
329                 break;
330             }
331         }
332         if (!found) {
333             // User has chose some other dictionary from combo box, we need to add that
334             QString dictName = Sonnet::Speller().availableDictionaries().key(newDict);
335             QAction *action = m_dictionaryGroup->addAction(dictName);
336             action->setData(newDict);
337             action->setCheckable(true);
338             action->setChecked(true);
339             m_dictionaryMenu->addAction(action);
340         }
341         m_dictionaryGroup->blockSignals(false);
342     }
343 }
344 
modifiedChanged()345 void KateStatusBar::modifiedChanged()
346 {
347     const bool mod = m_view->doc()->isModified();
348     const bool modOnHD = m_view->doc()->isModifiedOnDisc();
349     const bool readOnly = !m_view->doc()->isReadWrite();
350 
351     // combine to modified status, update only if changed
352     unsigned int newStatus = (unsigned int)mod | ((unsigned int)modOnHD << 1) | ((unsigned int)readOnly << 2);
353     if (m_modifiedStatus == newStatus) {
354         return;
355     }
356 
357     m_modifiedStatus = newStatus;
358     switch (m_modifiedStatus) {
359     case 0x0:
360         m_modified->setIcon(QIcon::fromTheme(QStringLiteral("text-plain")));
361         m_modified->setWhatsThis(i18n("Meaning of current icon: Document was not modified since it was loaded"));
362         break;
363 
364     case 0x1:
365     case 0x5:
366         m_modified->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
367         m_modified->setWhatsThis(i18n("Meaning of current icon: Document was modified since it was loaded"));
368         break;
369 
370     case 0x2:
371     case 0x6:
372         m_modified->setIcon(QIcon::fromTheme(QStringLiteral("dialog-warning")));
373         m_modified->setWhatsThis(i18n("Meaning of current icon: Document was modified or deleted by another program"));
374         break;
375 
376     case 0x3:
377     case 0x7:
378         m_modified->setIcon(
379             QIcon(KIconUtils::addOverlay(QIcon::fromTheme(QStringLiteral("document-save")), QIcon(QStringLiteral("emblem-important")), Qt::TopLeftCorner)));
380         m_modified->setWhatsThis(QString());
381         break;
382 
383     default:
384         m_modified->setIcon(QIcon::fromTheme(QStringLiteral("lock")));
385         m_modified->setWhatsThis(i18n("Meaning of current icon: Document is in read-only mode"));
386         break;
387     }
388 }
389 
documentConfigChanged()390 void KateStatusBar::documentConfigChanged()
391 {
392     m_encoding->setText(m_view->document()->encoding());
393     KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
394     int tabWidth = config->tabWidth();
395     int indentationWidth = config->indentationWidth();
396     bool replaceTabsDyn = config->replaceTabsDyn();
397 
398     static const KLocalizedString spacesOnly = ki18n("Soft Tabs: %1");
399     static const KLocalizedString spacesOnlyShowTabs = ki18n("Soft Tabs: %1 (%2)");
400     static const KLocalizedString tabsOnly = ki18n("Tab Size: %1");
401     static const KLocalizedString tabSpacesMixed = ki18n("Indent/Tab: %1/%2");
402 
403     if (!replaceTabsDyn) {
404         if (tabWidth == indentationWidth) {
405             m_tabsIndent->setText(tabsOnly.subs(tabWidth).toString());
406             m_tabGroup->setEnabled(false);
407             m_hardAction->setChecked(true);
408         } else {
409             m_tabsIndent->setText(tabSpacesMixed.subs(indentationWidth).subs(tabWidth).toString());
410             m_tabGroup->setEnabled(true);
411             m_mixedAction->setChecked(true);
412         }
413     } else {
414         if (tabWidth == indentationWidth) {
415             m_tabsIndent->setText(spacesOnly.subs(indentationWidth).toString());
416             m_tabGroup->setEnabled(true);
417             m_softAction->setChecked(true);
418         } else {
419             m_tabsIndent->setText(spacesOnlyShowTabs.subs(indentationWidth).subs(tabWidth).toString());
420             m_tabGroup->setEnabled(true);
421             m_softAction->setChecked(true);
422         }
423     }
424 
425     updateGroup(m_tabGroup, tabWidth);
426     updateGroup(m_indentGroup, indentationWidth);
427 }
428 
modeChanged()429 void KateStatusBar::modeChanged()
430 {
431     m_mode->setText(KTextEditor::EditorPrivate::self()->modeManager()->fileType(m_view->document()->mode()).nameTranslated());
432 }
433 
addNumberAction(QActionGroup * group,QMenu * menu,int data)434 void KateStatusBar::addNumberAction(QActionGroup *group, QMenu *menu, int data)
435 {
436     QAction *a;
437     if (data != -1) {
438         a = menu->addAction(QStringLiteral("%1").arg(data));
439     } else {
440         a = menu->addAction(i18n("Other..."));
441     }
442     a->setData(data);
443     a->setCheckable(true);
444     a->setActionGroup(group);
445 }
446 
updateGroup(QActionGroup * group,int w)447 void KateStatusBar::updateGroup(QActionGroup *group, int w)
448 {
449     QAction *m1 = nullptr;
450     bool found = false;
451     // linear search should be fast enough here, no additional hash
452     for (QAction *action : group->actions()) {
453         int val = action->data().toInt();
454         if (val == -1) {
455             m1 = action;
456         }
457         if (val == w) {
458             found = true;
459             action->setChecked(true);
460         }
461     }
462 
463     if (m1) {
464         if (found) {
465             m1->setText(i18n("Other..."));
466         } else {
467             m1->setText(i18np("Other (%1)", "Other (%1)", w));
468             m1->setChecked(true);
469         }
470     }
471 }
472 
slotTabGroup(QAction * a)473 void KateStatusBar::slotTabGroup(QAction *a)
474 {
475     int val = a->data().toInt();
476     bool ok;
477     KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
478     if (val == -1) {
479         val = QInputDialog::getInt(this, i18n("Tab Width"), i18n("Please specify the wanted tab width:"), config->tabWidth(), 1, 200, 1, &ok);
480         if (!ok) {
481             val = config->tabWidth();
482         }
483     }
484     config->setTabWidth(val);
485 }
486 
slotIndentGroup(QAction * a)487 void KateStatusBar::slotIndentGroup(QAction *a)
488 {
489     int val = a->data().toInt();
490     bool ok;
491     KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
492     if (val == -1) {
493         val = QInputDialog::getInt(this,
494                                    i18n("Indentation Width"),
495                                    i18n("Please specify the wanted indentation width:"),
496                                    config->indentationWidth(),
497                                    1,
498                                    200,
499                                    1,
500                                    &ok);
501         if (!ok) {
502             val = config->indentationWidth();
503         }
504     }
505     config->configStart();
506     config->setIndentationWidth(val);
507     if (m_hardAction->isChecked()) {
508         config->setTabWidth(val);
509     }
510     config->configEnd();
511 }
512 
slotIndentTabMode(QAction * a)513 void KateStatusBar::slotIndentTabMode(QAction *a)
514 {
515     KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
516     if (a == m_softAction) {
517         config->setReplaceTabsDyn(true);
518     } else if (a == m_mixedAction) {
519         if (config->replaceTabsDyn()) {
520             config->setReplaceTabsDyn(false);
521         }
522         m_tabGroup->setEnabled(true);
523     } else if (a == m_hardAction) {
524         if (config->replaceTabsDyn()) {
525             config->configStart();
526             config->setReplaceTabsDyn(false);
527             config->setTabWidth(config->indentationWidth());
528             config->configEnd();
529         } else {
530             config->setTabWidth(config->indentationWidth());
531         }
532         m_tabGroup->setEnabled(false);
533     }
534 }
535 
toggleWordCount(bool on)536 void KateStatusBar::toggleWordCount(bool on)
537 {
538     if ((m_wordCounter != nullptr) == on) {
539         return;
540     }
541 
542     if (on) {
543         m_wordCounter = new WordCounter(m_view);
544         connect(m_wordCounter, &WordCounter::changed, this, &KateStatusBar::wordCountChanged);
545     } else {
546         delete m_wordCounter;
547         m_wordCounter = nullptr;
548     }
549 
550     wordCountChanged(0, 0, 0, 0);
551 }
552 
wordCountChanged(int wordsInDocument,int wordsInSelection,int charsInDocument,int charsInSelection)553 void KateStatusBar::wordCountChanged(int wordsInDocument, int wordsInSelection, int charsInDocument, int charsInSelection)
554 {
555     if (m_wordCounter) {
556         m_wordCount = i18nc("%1 and %3 are the selected words/chars count, %2 and %4 are the total words/chars count.",
557                             "Words %1/%2, Chars %3/%4",
558                             wordsInSelection,
559                             wordsInDocument,
560                             charsInSelection,
561                             charsInDocument);
562     } else {
563         m_wordCount.clear();
564     }
565 
566     cursorPositionChanged();
567 }
568 
configChanged()569 void KateStatusBar::configChanged()
570 {
571     toggleWordCount(m_view->config()->showWordCount());
572     const int zoom = m_view->renderer()->config()->baseFont().pointSizeF() / KateRendererConfig::global()->baseFont().pointSizeF() * 100;
573     if (zoom != 100) {
574         m_zoomLevel->setVisible(true);
575         m_zoomLevel->setText(i18n("Zoom: %1%", zoom));
576     } else {
577         m_zoomLevel->hide();
578     }
579 }
580 
changeDictionary(QAction * action)581 void KateStatusBar::changeDictionary(QAction *action)
582 {
583     const QString dictionary = action->data().toString();
584     m_dictionary->setText(dictionary);
585     // Code stolen from KateDictionaryBar::dictionaryChanged
586     KTextEditor::Range selection = m_view->selectionRange();
587     if (selection.isValid() && !selection.isEmpty()) {
588         m_view->doc()->setDictionary(dictionary, selection);
589     } else {
590         m_view->doc()->setDefaultDictionary(dictionary);
591     }
592 }
593 
modeMenu() const594 KateModeMenuList *KateStatusBar::modeMenu() const
595 {
596     return m_modeMenuList;
597 }
598