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 "katerenderer.h"
16#include "kateview.h"
17#include "wordcounter.h"
18
19#include <KAcceleratorManager>
20#include <KActionCollection>
21#include <KIconUtils>
22#include <Sonnet/Speller>
23
24#include <QActionGroup>
25#include <QHBoxLayout>
26#include <QInputDialog>
27#include <QStylePainter>
28
29// BEGIN menu
30KateStatusBarOpenUpMenu::KateStatusBarOpenUpMenu(QWidget *parent)
31 : QMenu(parent)
32{
33}
34
35void KateStatusBarOpenUpMenu::setVisible(bool visibility)
36{
37 if (visibility) {
38 QRect geo = geometry();
39 QPoint pos = parentWidget()->mapToGlobal(QPoint(0, 0));
40 geo.moveTopLeft(p: QPoint(pos.x(), pos.y() - geo.height()));
41 if (geo.top() < 0) {
42 geo.moveTop(pos: 0);
43 }
44 setGeometry(geo);
45 }
46
47 QMenu::setVisible(visibility);
48}
49// END menu
50
51// BEGIN StatusBarButton
52StatusBarButton::StatusBarButton(KateStatusBar *parent, const QString &text /*= QString()*/)
53 : QPushButton(text, parent)
54{
55 setFlat(true);
56 setFocusProxy(parent->m_view);
57 setMinimumSize(QSize(1, minimumSizeHint().height()));
58}
59
60void StatusBarButton::paintEvent(QPaintEvent *)
61{
62 QStylePainter p(this);
63 QStyleOptionButton opt;
64 initStyleOption(option: &opt);
65 opt.features &= (~QStyleOptionButton::HasMenu);
66 p.drawControl(ce: QStyle::CE_PushButton, opt);
67}
68
69QSize StatusBarButton::sizeHint() const
70{
71 return minimumSizeHint();
72}
73
74QSize StatusBarButton::minimumSizeHint() const
75{
76 const auto fm = QFontMetrics(font());
77 const int h = fm.lineSpacing();
78 QSize size = QPushButton::sizeHint();
79 const int vMmargin = style()->pixelMetric(metric: QStyle::PM_FocusFrameVMargin) * 2;
80 size.setHeight(h + vMmargin);
81
82 const int w = fm.horizontalAdvance(text());
83 const int bm = style()->pixelMetric(metric: QStyle::PM_ButtonMargin) * 2;
84 const int hMargin = style()->pixelMetric(metric: QStyle::PM_FocusFrameHMargin) * 2;
85 size.setWidth(w + bm + hMargin);
86
87 return size;
88}
89
90// END StatusBarButton
91
92KateStatusBar::KateStatusBar(KTextEditor::ViewPrivate *view)
93 : KateViewBarWidget(false)
94 , m_view(view)
95 , m_selectionMode(-1)
96 , m_wordCounter(nullptr)
97{
98 KAcceleratorManager::setNoAccel(this);
99 setFocusProxy(m_view);
100
101 // just add our status bar to central widget, full sized
102 QHBoxLayout *topLayout = new QHBoxLayout(centralWidget());
103 topLayout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
104 topLayout->setSpacing(0);
105
106 // ensure all elements of the status bar are right aligned
107 // for Kate this is nice, as on the left side are the tool view buttons
108 // for KWrite this makes it more consistent with Kate
109 topLayout->addStretch(stretch: 1);
110
111 // show Line XXX, Column XXX
112 m_cursorPosition = new StatusBarButton(this);
113 topLayout->addWidget(m_cursorPosition);
114 m_cursorPosition->setWhatsThis(i18n("Current cursor position. Click to go to a specific line."));
115 connect(sender: m_cursorPosition, signal: &StatusBarButton::clicked, context: m_view, slot: &KTextEditor::ViewPrivate::gotoLine);
116
117 // show the zoom level of the text
118 m_zoomLevel = new StatusBarButton(this);
119 topLayout->addWidget(m_zoomLevel);
120 connect(sender: m_zoomLevel, signal: &StatusBarButton::clicked, slot: [this] {
121 m_view->renderer()->resetFontSizes();
122 });
123
124 // show the current mode, like INSERT, OVERWRITE, VI + modifiers like [BLOCK]
125 m_inputMode = new StatusBarButton(this);
126 topLayout->addWidget(m_inputMode);
127 m_inputMode->setWhatsThis(i18n("Insert mode and VI input mode indicator. Click to change the mode."));
128 connect(sender: m_inputMode, signal: &StatusBarButton::clicked, slot: [this] {
129 m_view->currentInputMode()->toggleInsert();
130 });
131
132 // Add dictionary button which allows user to switch dictionary of the document
133 m_dictionary = new StatusBarButton(this);
134 topLayout->addWidget(m_dictionary, stretch: 0);
135 m_dictionary->setWhatsThis(i18n("Change dictionary"));
136 m_dictionaryMenu = new KateStatusBarOpenUpMenu(m_dictionary);
137 m_dictionaryMenu->addAction(action: m_view->action(QStringLiteral("tools_change_dictionary")));
138 m_dictionaryMenu->addAction(action: m_view->action(QStringLiteral("tools_clear_dictionary_ranges")));
139 m_dictionaryMenu->addAction(action: m_view->action(QStringLiteral("tools_toggle_automatic_spell_checking")));
140 m_dictionaryMenu->addAction(action: m_view->action(QStringLiteral("tools_spelling_from_cursor")));
141 m_dictionaryMenu->addAction(action: m_view->action(QStringLiteral("tools_spelling")));
142 m_dictionaryMenu->addSeparator();
143 m_dictionaryGroup = new QActionGroup(m_dictionaryMenu);
144 QMapIterator<QString, QString> i(Sonnet::Speller().preferredDictionaries());
145 while (i.hasNext()) {
146 i.next();
147 QAction *action = m_dictionaryGroup->addAction(text: i.key());
148 action->setData(i.value());
149 action->setToolTip(i.key());
150 action->setCheckable(true);
151 m_dictionaryMenu->addAction(action);
152 }
153 m_dictionary->setMenu(m_dictionaryMenu);
154 connect(sender: m_dictionaryGroup, signal: &QActionGroup::triggered, context: this, slot: &KateStatusBar::changeDictionary);
155
156 // allow to change indentation configuration
157 m_tabsIndent = new StatusBarButton(this);
158 topLayout->addWidget(m_tabsIndent);
159
160 m_indentSettingsMenu = new KateStatusBarOpenUpMenu(m_tabsIndent);
161 m_indentSettingsMenu->addSection(i18n("Tab Width"));
162 m_tabGroup = new QActionGroup(this);
163 addNumberAction(group: m_tabGroup, menu: m_indentSettingsMenu, data: -1);
164 addNumberAction(group: m_tabGroup, menu: m_indentSettingsMenu, data: 8);
165 addNumberAction(group: m_tabGroup, menu: m_indentSettingsMenu, data: 4);
166 addNumberAction(group: m_tabGroup, menu: m_indentSettingsMenu, data: 2);
167 connect(sender: m_tabGroup, signal: &QActionGroup::triggered, context: this, slot: &KateStatusBar::slotTabGroup);
168
169 m_indentSettingsMenu->addSection(i18n("Indentation Width"));
170 m_indentGroup = new QActionGroup(this);
171 addNumberAction(group: m_indentGroup, menu: m_indentSettingsMenu, data: -1);
172 addNumberAction(group: m_indentGroup, menu: m_indentSettingsMenu, data: 8);
173 addNumberAction(group: m_indentGroup, menu: m_indentSettingsMenu, data: 4);
174 addNumberAction(group: m_indentGroup, menu: m_indentSettingsMenu, data: 2);
175 connect(sender: m_indentGroup, signal: &QActionGroup::triggered, context: this, slot: &KateStatusBar::slotIndentGroup);
176
177 m_indentSettingsMenu->addSection(i18n("Indentation Mode"));
178 QActionGroup *radioGroup = new QActionGroup(m_indentSettingsMenu);
179 m_mixedAction = m_indentSettingsMenu->addAction(i18n("Tabulators && Spaces"));
180 m_mixedAction->setCheckable(true);
181 m_mixedAction->setActionGroup(radioGroup);
182 m_hardAction = m_indentSettingsMenu->addAction(i18n("Tabulators"));
183 m_hardAction->setCheckable(true);
184 m_hardAction->setActionGroup(radioGroup);
185 m_softAction = m_indentSettingsMenu->addAction(i18n("Spaces"));
186 m_softAction->setCheckable(true);
187 m_softAction->setActionGroup(radioGroup);
188 connect(sender: radioGroup, signal: &QActionGroup::triggered, context: this, slot: &KateStatusBar::slotIndentTabMode);
189
190 m_tabsIndent->setMenu(m_indentSettingsMenu);
191
192 // add encoding button which allows user to switch encoding of document
193 // this will reuse the encoding action menu of the view
194 m_encoding = new StatusBarButton(this);
195 topLayout->addWidget(m_encoding);
196 m_encoding->setMenu(m_view->encodingAction()->menu());
197 m_encoding->setWhatsThis(i18n("Encoding"));
198
199 m_eol = new StatusBarButton(this);
200 m_eol->setWhatsThis(i18n("End of line type"));
201 m_eol->setToolTip(i18n("End of line type"));
202 m_eol->setMenu(m_view->getEolMenu());
203 topLayout->addWidget(m_eol);
204
205 // load the mode menu, which contains a scrollable list + search bar.
206 // This is an alternative menu to the mode action menu of the view.
207 m_modeMenuList = new KateModeMenuList(i18n("Mode"), this);
208 m_modeMenuList->setWhatsThis(i18n(
209 "Here you can choose which mode should be used for the current document. This will influence the highlighting and folding being used, for example."));
210 m_modeMenuList->updateMenu(doc: m_view->doc());
211 // add mode button which allows user to switch mode of document
212 m_mode = new StatusBarButton(this);
213 topLayout->addWidget(m_mode);
214 m_modeMenuList->setButton(button: m_mode, positionX: KateModeMenuList::AlignHInverse, positionY: KateModeMenuList::AlignTop, autoUpdateTextButton: KateModeMenuList::AutoUpdateTextButton(false));
215 m_mode->setMenu(m_modeMenuList);
216 m_mode->setWhatsThis(i18n("Syntax highlighting"));
217
218 // signals for the statusbar
219 connect(sender: m_view, signal: &KTextEditor::View::cursorPositionChanged, context: this, slot: &KateStatusBar::cursorPositionChanged);
220 connect(sender: m_view, signal: &KTextEditor::View::viewModeChanged, context: this, slot: &KateStatusBar::viewModeChanged);
221 connect(sender: m_view, signal: &KTextEditor::View::selectionChanged, context: this, slot: &KateStatusBar::selectionChanged);
222 connect(sender: m_view->doc(), signal: &KTextEditor::Document::configChanged, context: this, slot: &KateStatusBar::documentConfigChanged);
223 connect(sender: m_view->document(), signal: &KTextEditor::DocumentPrivate::modeChanged, context: this, slot: &KateStatusBar::modeChanged);
224 connect(sender: m_view, signal: &KTextEditor::View::configChanged, context: this, slot: &KateStatusBar::configChanged);
225 connect(sender: m_view->doc(), signal: &KTextEditor::DocumentPrivate::defaultDictionaryChanged, context: this, slot: &KateStatusBar::updateDictionary);
226 connect(sender: m_view->doc(), signal: &KTextEditor::DocumentPrivate::dictionaryRangesPresent, context: this, slot: &KateStatusBar::updateDictionary);
227 connect(sender: m_view, signal: &KTextEditor::ViewPrivate::caretChangedRange, context: this, slot: &KateStatusBar::updateDictionary);
228
229 updateStatus();
230 toggleWordCount(on: KateViewConfig::global()->showWordCount());
231}
232
233bool KateStatusBar::eventFilter(QObject *obj, QEvent *event)
234{
235 return KateViewBarWidget::eventFilter(watched: obj, event);
236}
237
238void KateStatusBar::contextMenuEvent(QContextMenuEvent *event)
239{
240 // TODO Add option "Show Statusbar" and options to show/hide buttons of the status bar
241 QMenu menu(this);
242
243 if (childAt(p: event->pos()) == m_inputMode) {
244 if (QAction *inputModesAction = m_view->actionCollection()->action(QStringLiteral("view_input_modes"))) {
245 if (QMenu *inputModesMenu = inputModesAction->menu()) {
246 const auto actions = inputModesMenu->actions();
247 for (int i = 0; i < actions.count(); ++i) {
248 menu.addAction(action: actions.at(i));
249 }
250 menu.addSeparator();
251 }
252 }
253 }
254
255 QAction *showLines = menu.addAction(i18n("Show line count"), args: this, args: &KateStatusBar::toggleShowLines);
256 showLines->setCheckable(true);
257 showLines->setChecked(KateViewConfig::global()->showLineCount());
258 QAction *showWords = menu.addAction(i18n("Show word count"), args: this, args: &KateStatusBar::toggleShowWords);
259 showWords->setCheckable(true);
260 showWords->setChecked(KateViewConfig::global()->showWordCount());
261 auto a = menu.addAction(i18n("Line/Column compact mode"), args: this, args: [](bool checked) {
262 KateViewConfig::global()->setValue(key: KateViewConfig::StatusbarLineColumnCompact, value: checked);
263 });
264 a->setCheckable(true);
265 a->setChecked(KateViewConfig::global()->value(key: KateViewConfig::StatusbarLineColumnCompact).toBool());
266 menu.exec(pos: event->globalPos());
267}
268
269void KateStatusBar::toggleShowLines(bool checked)
270{
271 KateViewConfig::global()->setValue(key: KateViewConfig::ShowLineCount, value: checked);
272}
273
274void KateStatusBar::toggleShowWords(bool checked)
275{
276 KateViewConfig::global()->setShowWordCount(checked);
277}
278
279void KateStatusBar::updateStatus()
280{
281 selectionChanged();
282 viewModeChanged();
283 cursorPositionChanged();
284 documentConfigChanged();
285 modeChanged();
286 updateDictionary();
287 updateEOL();
288}
289
290void KateStatusBar::selectionChanged()
291{
292 const unsigned int newSelectionMode = m_view->blockSelection();
293 if (newSelectionMode == m_selectionMode) {
294 return;
295 }
296
297 // remember new mode and update info
298 m_selectionMode = newSelectionMode;
299 viewModeChanged();
300}
301
302void KateStatusBar::viewModeChanged()
303{
304 // prepend BLOCK for block selection mode
305 QString text = m_view->viewModeHuman();
306 if (m_view->blockSelection()) {
307 text = i18n("[BLOCK] %1", text);
308 }
309
310 m_inputMode->setText(text);
311}
312
313void KateStatusBar::cursorPositionChanged()
314{
315 KTextEditor::Cursor position(m_view->cursorPositionVirtual());
316 const int l = position.line() + 1;
317 const int c = position.column() + 1;
318
319 // Update line/column label
320 QString text;
321 if (KateViewConfig::global()->value(key: KateViewConfig::StatusbarLineColumnCompact).toBool()) {
322 if (KateViewConfig::global()->showLineCount()) {
323 text = i18n("%1/%2:%3", QLocale().toString(l), QLocale().toString(m_view->doc()->lines()), QLocale().toString(c));
324 } else {
325 text = i18n("%1:%2", QLocale().toString(l), QLocale().toString(c));
326 }
327 } else {
328 if (KateViewConfig::global()->showLineCount()) {
329 text = i18n("Line %1 of %2, Column %3", QLocale().toString(l), QLocale().toString(m_view->doc()->lines()), QLocale().toString(c));
330 } else {
331 text = i18n("Line %1, Column %2", QLocale().toString(l), QLocale().toString(c));
332 }
333 }
334 if (m_wordCounter) {
335 text.append(s: QLatin1String(", ") + m_wordCount);
336 }
337 m_cursorPosition->setText(text);
338}
339
340void KateStatusBar::updateDictionary()
341{
342 const auto spellchecker = Sonnet::Speller();
343 const auto availableDictionaries = spellchecker.availableDictionaries();
344 // No dictionaries available? => hide
345 if (availableDictionaries.isEmpty()) {
346 m_dictionary->hide();
347 return;
348 }
349
350 QString newDict;
351 // Check if at the current cursor position is a special dictionary in use
352 KTextEditor::Cursor position(m_view->cursorPositionVirtual());
353 const QList<QPair<KTextEditor::MovingRange *, QString>> dictRanges = m_view->doc()->dictionaryRanges();
354 for (const auto &rangeDictPair : dictRanges) {
355 const KTextEditor::MovingRange *range = rangeDictPair.first;
356 if (range->contains(cursor: position) || range->end() == position) {
357 newDict = rangeDictPair.second;
358 break;
359 }
360 }
361 // Check if the default dictionary is in use
362 if (newDict.isEmpty()) {
363 newDict = m_view->doc()->defaultDictionary();
364 if (newDict.isEmpty()) {
365 newDict = spellchecker.defaultLanguage();
366 }
367 }
368 // Update button and menu only on a changed dictionary
369 if (!m_dictionaryGroup->checkedAction() || (m_dictionaryGroup->checkedAction()->data().toString() != newDict) || m_dictionary->text().isEmpty()) {
370 bool found = false;
371 // Remove "-w_accents -variant_0" and such from dict-code to keep it small and clean
372 m_dictionary->setText(newDict.section(asep: QLatin1Char('-'), astart: 0, aend: 0));
373 // For maximum user clearness, change the checked menu option
374 m_dictionaryGroup->blockSignals(b: true);
375 const auto acts = m_dictionaryGroup->actions();
376 for (auto a : acts) {
377 if (a->data().toString() == newDict) {
378 a->setChecked(true);
379 found = true;
380 break;
381 }
382 }
383 if (!found) {
384 // User has chose some other dictionary from combo box, we need to add that
385 QString dictName = availableDictionaries.key(value: newDict);
386 if (!dictName.isEmpty()) {
387 QAction *action = m_dictionaryGroup->addAction(text: dictName);
388 action->setData(newDict);
389 action->setCheckable(true);
390 action->setChecked(true);
391 m_dictionaryMenu->addAction(action);
392 }
393 }
394 m_dictionaryGroup->blockSignals(b: false);
395 }
396}
397
398void KateStatusBar::documentConfigChanged()
399{
400 m_encoding->setText(m_view->document()->encoding());
401 KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
402 int tabWidth = config->tabWidth();
403 int indentationWidth = config->indentationWidth();
404 bool replaceTabsDyn = config->replaceTabsDyn();
405
406 static const KLocalizedString spacesOnly = ki18n("Soft Tabs: %1");
407 static const KLocalizedString spacesOnlyShowTabs = ki18n("Soft Tabs: %1 (%2)");
408 static const KLocalizedString tabsOnly = ki18n("Tab Size: %1");
409 static const KLocalizedString tabSpacesMixed = ki18n("Indent/Tab: %1/%2");
410
411 if (!replaceTabsDyn) {
412 if (tabWidth == indentationWidth) {
413 m_tabsIndent->setText(tabsOnly.subs(a: tabWidth).toString());
414 m_tabGroup->setEnabled(false);
415 m_hardAction->setChecked(true);
416 } else {
417 m_tabsIndent->setText(tabSpacesMixed.subs(a: indentationWidth).subs(a: tabWidth).toString());
418 m_tabGroup->setEnabled(true);
419 m_mixedAction->setChecked(true);
420 }
421 } else {
422 if (tabWidth == indentationWidth) {
423 m_tabsIndent->setText(spacesOnly.subs(a: indentationWidth).toString());
424 m_tabGroup->setEnabled(true);
425 m_softAction->setChecked(true);
426 } else {
427 m_tabsIndent->setText(spacesOnlyShowTabs.subs(a: indentationWidth).subs(a: tabWidth).toString());
428 m_tabGroup->setEnabled(true);
429 m_softAction->setChecked(true);
430 }
431 }
432
433 updateGroup(group: m_tabGroup, w: tabWidth);
434 updateGroup(group: m_indentGroup, w: indentationWidth);
435 updateEOL();
436}
437
438void KateStatusBar::modeChanged()
439{
440 m_mode->setText(KTextEditor::EditorPrivate::self()->modeManager()->fileType(name: m_view->document()->mode()).nameTranslated());
441}
442
443void KateStatusBar::addNumberAction(QActionGroup *group, QMenu *menu, int data)
444{
445 QAction *a;
446 if (data != -1) {
447 a = menu->addAction(QStringLiteral("%1").arg(a: data));
448 } else {
449 a = menu->addAction(i18n("Other..."));
450 }
451 a->setData(data);
452 a->setCheckable(true);
453 a->setActionGroup(group);
454}
455
456void KateStatusBar::updateGroup(QActionGroup *group, int w)
457{
458 QAction *m1 = nullptr;
459 bool found = false;
460 // linear search should be fast enough here, no additional hash
461 const auto acts = group->actions();
462 for (QAction *action : acts) {
463 int val = action->data().toInt();
464 if (val == -1) {
465 m1 = action;
466 }
467 if (val == w) {
468 found = true;
469 action->setChecked(true);
470 }
471 }
472
473 if (m1) {
474 if (found) {
475 m1->setText(i18n("Other..."));
476 } else {
477 m1->setText(i18np("Other (%1)", "Other (%1)", w));
478 m1->setChecked(true);
479 }
480 }
481}
482
483void KateStatusBar::slotTabGroup(QAction *a)
484{
485 int val = a->data().toInt();
486 bool ok;
487 KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
488 if (val == -1) {
489 val = QInputDialog::getInt(parent: this, i18n("Tab Width"), i18n("Please specify the wanted tab width:"), value: config->tabWidth(), minValue: 1, maxValue: 200, step: 1, ok: &ok);
490 if (!ok) {
491 val = config->tabWidth();
492 }
493 }
494 config->setTabWidth(val);
495}
496
497void KateStatusBar::slotIndentGroup(QAction *a)
498{
499 int val = a->data().toInt();
500 bool ok;
501 KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
502 if (val == -1) {
503 val = QInputDialog::getInt(parent: this,
504 i18n("Indentation Width"),
505 i18n("Please specify the wanted indentation width:"),
506 value: config->indentationWidth(),
507 minValue: 1,
508 maxValue: 200,
509 step: 1,
510 ok: &ok);
511 if (!ok) {
512 val = config->indentationWidth();
513 }
514 }
515 config->configStart();
516 config->setIndentationWidth(val);
517 if (m_hardAction->isChecked()) {
518 config->setTabWidth(val);
519 }
520 config->configEnd();
521}
522
523void KateStatusBar::slotIndentTabMode(QAction *a)
524{
525 KateDocumentConfig *config = ((KTextEditor::DocumentPrivate *)m_view->document())->config();
526 if (a == m_softAction) {
527 config->setReplaceTabsDyn(true);
528 } else if (a == m_mixedAction) {
529 if (config->replaceTabsDyn()) {
530 config->setReplaceTabsDyn(false);
531 }
532 m_tabGroup->setEnabled(true);
533 } else if (a == m_hardAction) {
534 if (config->replaceTabsDyn()) {
535 config->configStart();
536 config->setReplaceTabsDyn(false);
537 config->setTabWidth(config->indentationWidth());
538 config->configEnd();
539 } else {
540 config->setTabWidth(config->indentationWidth());
541 }
542 m_tabGroup->setEnabled(false);
543 }
544}
545
546void KateStatusBar::toggleWordCount(bool on)
547{
548 if ((m_wordCounter != nullptr) == on) {
549 return;
550 }
551
552 if (on) {
553 m_wordCounter = new WordCounter(m_view);
554 connect(sender: m_wordCounter, signal: &WordCounter::changed, context: this, slot: &KateStatusBar::wordCountChanged);
555 } else {
556 delete m_wordCounter;
557 m_wordCounter = nullptr;
558 }
559
560 wordCountChanged(0, 0, 0, 0);
561}
562
563void KateStatusBar::wordCountChanged(int wordsInDocument, int wordsInSelection, int charsInDocument, int charsInSelection)
564{
565 if (m_wordCounter) {
566 if (charsInSelection > 0) {
567 m_wordCount = i18nc("%1 and %3 are the selected words/chars count, %2 and %4 are the total words/chars count.",
568 "Words %1/%2, Chars %3/%4",
569 wordsInSelection,
570 wordsInDocument,
571 charsInSelection,
572 charsInDocument);
573 } else {
574 m_wordCount = i18nc("%1 and %2 are the total words/chars count.", "Words %1, Chars %2", wordsInDocument, charsInDocument);
575 }
576 } else {
577 m_wordCount.clear();
578 }
579
580 cursorPositionChanged();
581}
582
583void KateStatusBar::configChanged()
584{
585 toggleWordCount(on: m_view->config()->showWordCount());
586 const int zoom = m_view->rendererConfig()->baseFont().pointSizeF() / KateRendererConfig::global()->baseFont().pointSizeF() * 100;
587 if (zoom != 100) {
588 m_zoomLevel->setVisible(true);
589 m_zoomLevel->setText(i18n("Zoom: %1%", zoom));
590 } else {
591 m_zoomLevel->hide();
592 }
593
594 auto cfg = KateViewConfig::global();
595 auto updateButtonVisibility = [cfg](StatusBarButton *b, KateViewConfig::ConfigEntryTypes c) {
596 bool v = cfg->value(key: c).toBool();
597 if (v != (!b->isHidden())) {
598 b->setVisible(v);
599 }
600 };
601 updateButtonVisibility(m_inputMode, KateViewConfig::ShowStatusbarInputMode);
602 updateButtonVisibility(m_mode, KateViewConfig::ShowStatusbarHighlightingMode);
603 updateButtonVisibility(m_cursorPosition, KateViewConfig::ShowStatusbarLineColumn);
604 updateButtonVisibility(m_tabsIndent, KateViewConfig::ShowStatusbarTabSettings);
605 updateButtonVisibility(m_encoding, KateViewConfig::ShowStatusbarFileEncoding);
606 updateButtonVisibility(m_eol, KateViewConfig::ShowStatusbarEOL);
607
608 bool v = cfg->value(key: KateViewConfig::ShowStatusbarDictionary).toBool();
609 if (v != (!m_dictionary->isHidden()) && !Sonnet::Speller().availableDictionaries().isEmpty()) {
610 updateButtonVisibility(m_dictionary, KateViewConfig::ShowStatusbarDictionary);
611 }
612}
613
614void KateStatusBar::changeDictionary(QAction *action)
615{
616 const QString dictionary = action->data().toString();
617 m_dictionary->setText(dictionary);
618 // Code stolen from KateDictionaryBar::dictionaryChanged
619 KTextEditor::Range selection = m_view->selectionRange();
620 if (selection.isValid() && !selection.isEmpty()) {
621 m_view->doc()->setDictionary(dict: dictionary, range: selection);
622 } else {
623 m_view->doc()->setDefaultDictionary(dictionary);
624 }
625}
626
627void KateStatusBar::updateEOL()
628{
629 const int eol = m_view->getEol();
630 QString text;
631 switch (eol) {
632 case KateDocumentConfig::eolUnix:
633 text = QStringLiteral("LF");
634 break;
635 case KateDocumentConfig::eolDos:
636 text = QStringLiteral("CRLF");
637 break;
638 case KateDocumentConfig::eolMac:
639 text = QStringLiteral("CR");
640 break;
641 }
642 if (text != m_eol->text()) {
643 m_eol->setText(text);
644 }
645}
646
647KateModeMenuList *KateStatusBar::modeMenu() const
648{
649 return m_modeMenuList;
650}
651
652#include "moc_katestatusbar.cpp"
653

source code of ktexteditor/src/view/katestatusbar.cpp